Android map v2 zoom, чтобы показать все маркеры

У меня 10 маркеров в GoogleMap. Я хочу максимально увеличить масштаб и держать все маркеры в поле зрения? В более ранней версии этого можно было добиться с помощью zoomToSpan(), но в версии 2 я понятия не имею, как это сделать. Кроме того, я знаю радиус круга, который должен быть виден.


person Asanka Senavirathna    schedule 12.02.2013    source источник


Ответы (13)


Вы должны использовать класс CameraUpdate для выполнения (возможно) всех программных перемещений карты.

Для этого сначала рассчитайте границы всех маркеров следующим образом:

LatLngBounds.Builder builder = new LatLngBounds.Builder();
for (Marker marker : markers) {
    builder.include(marker.getPosition());
}
LatLngBounds bounds = builder.build();

Затем получите объект описания движения с помощью фабрики: CameraUpdateFactory:

int padding = 0; // offset from edges of the map in pixels
CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);

Наконец переместите карту:

googleMap.moveCamera(cu);

Или, если вам нужна анимация:

googleMap.animateCamera(cu);

Это все :)

Разъяснение 1

Почти все методы перемещения требуют, чтобы объект Map прошел процесс компоновки. Вы можете дождаться этого, используя конструкцию addOnGlobalLayoutListener. Подробности можно найти в комментариях к этому ответу и оставшимся ответам. Вы также можете найти полный код для настройки экстента карты с помощью addOnGlobalLayoutListener здесь.

Пояснение 2

В одном комментарии отмечается, что использование этого метода только для одного маркера приводит к тому, что масштаб карты устанавливается на «причудливый» уровень масштабирования (который, я считаю, является максимальным уровнем масштабирования, доступным для данного местоположения). Я думаю, это ожидаемо, потому что:

  1. Экземпляр LatLngBounds bounds будет иметь свойство northeast, равное southwest, что означает, что часть площади земли, покрытая этим bounds, равна нулю. (Это логично, поскольку у одного маркера нет области.)
  2. Передавая bounds в CameraUpdateFactory.newLatLngBounds, вы, по сути, запрашиваете расчет такого уровня масштабирования, при котором bounds (с нулевой областью) будет охватывать весь вид карты.
  3. Вы действительно можете выполнить этот расчет на листе бумаги. Теоретический уровень масштабирования, который является ответом, равен + (положительная бесконечность). На практике объект Map не поддерживает это значение, поэтому он ограничивается более разумным максимальным уровнем, допустимым для данного местоположения.

Другими словами: как Map объект может узнать, какой уровень масштабирования следует выбрать для отдельного местоположения? Возможно, оптимальное значение должно быть 20 (если оно представляет конкретный адрес). Или, может быть, 11 (если это город). Или, может быть, 6 (если представляет страну). API не так уж и умен, и решение остается за вами.

Итак, вы должны просто проверить, есть ли у markers только одно местоположение, и если да, используйте одно из:

  • CameraUpdate cu = CameraUpdateFactory.newLatLng(marker.getPosition()) - перейти к позиции маркера, оставить текущий масштаб без изменения.
  • CameraUpdate cu = CameraUpdateFactory.newLatLngZoom(marker.getPosition(), 12F) - перейти к позиции маркера, установить произвольно выбранный уровень масштабирования 12.
person andr    schedule 12.02.2013
comment
Следует отметить, что перемещение не может происходить из вызовов onCreate, представление должно быть создано. Мне нужно было использовать addOnGlobalLayoutListener, чтобы получить соответствующий образец. - person Baruch Even; 01.08.2013
comment
@Bar Ну, отчасти это правда. Если быть точным: некоторые методы перемещения не будут работать сразу после создания объекта карты. Причина в том, что объект карты еще не был измерен, т.е. он не прошел процесс компоновки. Типичное решение - использовать addOnGlobalLayoutListener() или post() с соответствующим Runnable. Именно по этой причине невозможно получить координаты экрана маркера в onCreate - см. stackoverflow.com/q/14429877/1820695 Однако вы можете использовать некоторые методы даже до того, как произойдет макет - например. CameraUpdateFactory.newLatLngBounds() с 4 параметрами. - person andr; 01.08.2013
comment
Чувак, ты спас мне день ... на самом деле пытался сделать это самостоятельно, вычислить границы вручную и увеличить, пока маркер находится в нем ... работал довольно некрасиво, но с вашим простым методом это работает как шарм. Спасибо - person Bibu; 09.03.2014
comment
googleMap .setOnMapLoadedCallback (новый GoogleMap.OnMapLoadedCallback () {@Override public void onMapLoaded () {googleMap.moveCamera (cu);}}); Это позволит избежать ошибки измерения. - person Ramz; 11.07.2014
comment
в противном случае он работает, но когда на карте есть единственный маркер, он приближается к некоторому причудливому уровню, и карта становится размытой. Как это исправить? - person Utsav Gupta; 21.07.2014
comment
Нет ли способа установить максимальный предел уровня масштабирования на карте независимо от количества маркеров? - person Utsav Gupta; 24.07.2014
comment
@ user2103379 к сожалению нет. см. здесь для уродливого обходного пути. хотя однажды у меня была идея (которую я никогда не тестировал, но вы можете): вы сохраняете текущее положение на карте в CameraPosition p1. затем вы продолжаете вычисление CameraUpdate cu, как указано выше, и выполняете map.moveCamera(cu). вы сохраняете текущее положение в p2 и проверяете, не выходит ли его масштаб за пределы желаемого диапазона - если да, вы создаете новый CameraPosition p3 на основе p2, но с исправленным масштабированием и выдаете map.moveCamera(p1); map.animateCamera(CameraUpdateFactory.newCameraPosition(p3)); - предупреждение: никогда не тестировалось. - person andr; 24.07.2014
comment
Спасибо, проверим, если будет возможность. - person Utsav Gupta; 24.07.2014
comment
Этот код дал мне ошибку: размер карты не должен быть 0. Итак, что мне нужно было сделать, так это установить ширину и высоту карты следующим образом: final int width = getResources().getDisplayMetrics().widthPixels; final int height = getResources().getDisplayMetrics().heightPixels; final CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngBounds(latLngBounds, width, height, padding);. - person jfmg; 29.08.2015
comment
Спасибо. Если у кого-то возникла проблема с падением приложения на googleMap.moveCamera (cu); проверить эти ответы stackoverflow.com/questions/13692579/ а> - person android_dev; 31.08.2015
comment
Спасибо, сэкономили много времени. - person CrazyMind; 18.06.2017
comment
Отличный ответ, особенно уточнение 2! - person Baz; 11.01.2018
comment
@andr ** Ошибка ** _ 1_ Как я могу решить, помогите мне, пожалуйста - person Gowthaman M; 16.01.2018
comment
@GowthamanM это уже объяснено - см. Разъяснение 1, Разъяснение 2 и мой 2-й комментарий к ответу - person andr; 01.02.2018
comment
@andr ya sir..Я понял .. я тоже решил свою pbm ... Большое вам спасибо - person Gowthaman M; 01.02.2018
comment
Это не сработает, если маркеры находятся очень далеко. - person ankalagba; 20.05.2021

Google Map V2

Следующее решение работает для Android Marshmallow 6 (API 23, API 24, API 25, API 26, API 27, API 28). Он также работает в Xamarin.

LatLngBounds.Builder builder = new LatLngBounds.Builder();

//the include method will calculate the min and max bound.
builder.include(marker1.getPosition());
builder.include(marker2.getPosition());
builder.include(marker3.getPosition());
builder.include(marker4.getPosition());

LatLngBounds bounds = builder.build();

int width = getResources().getDisplayMetrics().widthPixels;
int height = getResources().getDisplayMetrics().heightPixels;
int padding = (int) (width * 0.10); // offset from edges of the map 10% of screen

CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding);

mMap.animateCamera(cu);
person Zumry Mohamed    schedule 24.06.2016
comment
Чтобы заполнение выглядело лучше, используйте высоту вместо ширины и возьмите 20% вместо 10%. - person noob; 31.07.2017
comment
Принятый ответ иногда вызывает странное масштабирование. Это работает как шарм. Это лучший ответ. - person Hüseyin Bülbül; 20.03.2019
comment
Этот ответ работает лучше, чем принятый, принятый вызывает какое-то непонятное поведение. - person Haytham; 25.05.2020

So

Мне нужно было использовать addOnGlobalLayoutListener, чтобы получить соответствующий образец

например, ваша карта Google находится внутри RelativeLayout:

RelativeLayout mapLayout = (RelativeLayout)findViewById(R.id.map_layout);
mapLayout.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            //and write code, which you can see in answer above
        }
    });
person Eugene Voronoy    schedule 04.01.2014
comment
Пришел сюда из учебника Udemy, отличный рабочий, они использовали ваш ответ для решения проблемы. - person Shantanu Bedajna; 22.09.2018

Я не мог использовать onGlobalLayoutlistener, поэтому вот еще одно решение для предотвращения ошибки "Map size can't be 0. Most likely, layout has not yet occured for the map view. Either wait until layout has occurred or use newLatLngBounds(LatLngBounds, int, int, int) which allows you to specify the map's dimensions.":

mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() { 
@Override 
public void onMapLoaded() { 
    mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 15));
 } 
});
person chrjs    schedule 15.07.2014
comment
Это занимает намного больше времени, чем OnGlobalLayoutListener, поэтому для меня он все равно сначала показывал область / масштаб карты по умолчанию, прежде чем перемещать камеру к моим потребностям. - person Till; 05.07.2016
comment
Как добавить отступ, когда два моих маркера касаются границ карты. - person Pratik Butani; 23.05.2019

У меня работает нормально.

Из этого кода я показываю несколько маркеров с определенным увеличением на экране карты.

// Объявленные переменные

private LatLngBounds bounds;
private LatLngBounds.Builder builder;

// Метод добавления нескольких точек маркера с возможностью рисования

private void drawMarker(LatLng point, String text) {

        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.position(point).title(text).icon(BitmapDescriptorFactory.fromResource(R.drawable.icon));
        mMap.addMarker(markerOptions);
        builder.include(markerOptions.getPosition());

    }

// Для добавления нескольких маркеров, видимых на карте

@Override
    public void onMapReady(GoogleMap googleMap) {
        mMap = googleMap;
        builder = new LatLngBounds.Builder();
    for (int i = 0; i < locationList.size(); i++) {

        drawMarker(new LatLng(Double.parseDouble(locationList.get(i).getLatitude()), Double.parseDouble(locationList.get(i).getLongitude())), locationList.get(i).getNo());

     }
     bounds = builder.build();
     CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
     mMap.animateCamera(cu);
person BK19    schedule 05.08.2016

Примечание. Это не решение исходного вопроса. Это решение одной из подзадач, описанных выше.

Решение для @andr Разъяснение 2 -

Это действительно проблематично, когда в границах есть только один маркер и из-за этого уровень масштабирования установлен на очень высокий уровень (уровень 21). И Google не предоставляет никакого способа установить максимальный уровень масштабирования на этом этапе. Это также может произойти, когда имеется более 1 маркера, но все они довольно близки друг к другу. Тогда возникнет такая же проблема.

Решение. Предположим, вы хотите, чтобы ваша карта не выходила за пределы 16 уровня масштабирования. Затем, сделав -

CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding);
mMap.moveCamera(cu);

Проверьте, не пересек ли ваш уровень масштабирования уровень 16 (или что угодно) -

float currentZoom = mMap.getCameraPosition().zoom;

И если этот уровень больше 16, а это будет только в том случае, если маркеров очень мало или все маркеры расположены очень близко друг к другу, тогда просто уменьшите масштаб карты в этом конкретном месте, установив уровень масштабирования на 16.

mMap.moveCamera(CameraUpdateFactory.zoomTo(16));

Таким образом, вы никогда не столкнетесь с проблемой "причудливого" уровня масштабирования, которую также очень хорошо объяснил @andr.

person Jyotman Singh    schedule 02.03.2016
comment
хорошее решение, но если вы поместите объект cu внутрь onMapReady (), тогда в этом случае будет некорректное поведение: местоположение получено - ›масштаб велик, поэтому сделайте его 16 -› затем пользователь увеличивает масштаб - ›местоположение получено снова и камера возвращается на уровень 16. Это может быть проблемой, если местоположение получено почти в реальном времени, поскольку оно будет продолжать возвращаться к уровню 16. - person Maher Nabil; 01.02.2017
comment
И Google не предоставляет никакого способа установить максимальный уровень масштабирования на этом этапе. - Неправда: разработчики .google.com / android / reference / com / google / android / gms / - person user1209216; 22.05.2017

это поможет .. из демонстраций API Google

private List<Marker> markerList = new ArrayList<>();
Marker marker = mGoogleMap.addMarker(new MarkerOptions().position(geoLatLng)
                .title(title));
markerList.add(marker);
    // Pan to see all markers in view.
    // Cannot zoom to bounds until the map has a size.
    final View mapView = getSupportFragmentManager().findFragmentById(R.id.map).getView();
    if (mapView!=null) {
        if (mapView.getViewTreeObserver().isAlive()) {
            mapView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @SuppressWarnings("deprecation") // We use the new method when supported
                @SuppressLint("NewApi") // We check which build version we are using.
                @Override
                public void onGlobalLayout() {
                    //Calculate the markers to get their position
                    LatLngBounds.Builder b = new LatLngBounds.Builder();
                    for (Marker m : markerList) {
                        b.include(m.getPosition());
                    }
                    // also include current location to include in the view
                    b.include(new LatLng(mLocation.getLatitude(),mLocation.getLongitude()));

                    LatLngBounds bounds = b.build();
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                        mapView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                    } else {
                        mapView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    }
                    mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
                }
            });
        }
    }

для получения четкой информации посмотрите этот URL. https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/MarkerDemoActivity.java

person Amit Tumkur    schedule 29.03.2016

У меня была аналогичная проблема, и следующий код решил проблему:

CameraUpdateFactory.newLatLngBounds(bounds, 200, 200, 5) в моем случае, как правило, разница в расположении не более двух соседних городов.

масштабирование, чтобы уместить все маркеры на карте Google Maps v2

person Avishkar Ramjeet    schedule 12.06.2017

У меня есть еще один способ сделать то же самое, и он отлично работает. поэтому для того, чтобы показать все маркеры на экране, нам нужны центральная широта и масштаб. вот функция, которая предоставит вам оба и потребует в качестве входных данных все объекты Latlng маркера.

 public Pair<LatLng, Integer> getCenterWithZoomLevel(LatLng... l) {
    float max = 0;

    if (l == null || l.length == 0) {
        return null;
    }
    LatLngBounds.Builder b = new LatLngBounds.Builder();
    for (int count = 0; count < l.length; count++) {
        if (l[count] == null) {
            continue;
        }
        b.include(l[count]);
    }

    LatLng center = b.build().getCenter();

    float distance = 0;
    for (int count = 0; count < l.length; count++) {
        if (l[count] == null) {
            continue;
        }
        distance = distance(center, l[count]);
        if (distance > max) {
            max = distance;
        }
    }

    double scale = max / 1000;
    int zoom = ((int) (16 - Math.log(scale) / Math.log(2)));
    return new Pair<LatLng, Integer>(center, zoom);
}

Эта функция возвращает объект Pair, который вы можете использовать как

Пара пара = getCenterWithZoomLevel (l1, l2, l3 ..); mGoogleMap.moveCamera (CameraUpdateFactory.newLatLngZoom (pair.first, pair.second));

вы можете вместо использования отступов, чтобы ваши маркеры не попадали в границы экрана, вы можете отрегулировать масштаб на -1.

person Anuj Jindal    schedule 14.12.2017

Показать все маркеры на карте Google

В этих методах сохраняются все маркеры и выполняется автоматическое масштабирование, чтобы отобразить все маркеры на карте Google.

// Declare the Markers List.
List<MarkerOptions> markerList;
private BitmapDescriptor vnrPoint,banPoint;


public void storeAllMarkers()
{
      markerList=new ArrayList<>();
      markerList.removeAll(markerList);


      // latitude and longitude of Virudhunagar

      double latitude1=9.587209;
      double longitude1=77.951431;
   vnrPoint=BitmapDescriptorFactory.fromResource(R.drawable.location_icon_1);
      LatLng vnr = new LatLng(latitude1, longitude1);
      MarkerOptions vnrMarker = new MarkerOptions();
      vnrMarker.position(vnr);
      vnrMarker.icon(vnrPoint);
      markerList.add(vnrMarker);

      // latitude and longitude of Bengaluru

      double latitude2=12.972442;
      double longitude2=77.580643;

    banPoint=BitmapDescriptorFactory.fromResource(R.drawable.location_icon_2);

      LatLng ban = new LatLng(latitude2, longitude2);
      MarkerOptions bengalureMarker = new MarkerOptions();
      bengalureMarker.position(ban);
      bengalureMarker.icon(banPoint);
      markerList.add(bengalureMarker);

      // You can add any numbers of MarkerOptions like this.

     showAllMarkers();

 }


public void showAllMarkers()
{
    LatLngBounds.Builder builder = new LatLngBounds.Builder();

    for (MarkerOptions m : markerList) {
        builder.include(m.getPosition());
    }

    LatLngBounds bounds = builder.build();

    int width = getResources().getDisplayMetrics().widthPixels;
    int height = getResources().getDisplayMetrics().heightPixels;
    int padding = (int) (width * 0.30); 

    // Zoom and animate the google map to show all markers

    CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width, height, padding);
    googleMap.animateCamera(cu);
}
person Sathish kumar T.M    schedule 16.10.2018

Используйте метод getCenterCoordinate, чтобы получить координату центра и использовать в CameraPosition.

private void setUpMap() {
    mMap.setMyLocationEnabled(true);
    mMap.getUiSettings().setScrollGesturesEnabled(true);
    mMap.getUiSettings().setTiltGesturesEnabled(true);
    mMap.getUiSettings().setRotateGesturesEnabled(true);

    clientMarker = mMap.addMarker(new MarkerOptions()
            .position(new LatLng(Double.valueOf(-12.1024174), Double.valueOf(-77.0262274)))
            .icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_taxi))
    );
    clientMarker = mMap.addMarker(new MarkerOptions()
            .position(new LatLng(Double.valueOf(-12.1024637), Double.valueOf(-77.0242617)))
            .icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_location))
    );

    camPos = new CameraPosition.Builder()
            .target(getCenterCoordinate())
            .zoom(17)
            .build();
    camUpd3 = CameraUpdateFactory.newCameraPosition(camPos);
    mMap.animateCamera(camUpd3);
}


public LatLng getCenterCoordinate(){
    LatLngBounds.Builder builder = new LatLngBounds.Builder();
    builder.include(new LatLng(Double.valueOf(-12.1024174), Double.valueOf(-77.0262274)));
    builder.include(new LatLng(Double.valueOf(-12.1024637), Double.valueOf(-77.0242617)));
    LatLngBounds bounds = builder.build();
    return bounds.getCenter();
}
person Jonathan Nolasco Barrientos    schedule 23.12.2015

Я работал с той же проблемой для отображения нескольких маркеров в Котлине с использованием фрагмента

сначала объявите список маркеров

private lateinit var markers: MutableList<Marker>

инициализировать это в методе oncreate фрагмента

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
                         ): View? {
    //initialize markers list

    markers = mutableListOf()
   
    return inflater.inflate(R.layout.fragment_driver_map, container, false)
}

в OnMapReadyCallback добавить маркеры в список маркеров

private val callback = OnMapReadyCallback { googleMap ->

    map = googleMap

    markers.add(
        map.addMarker(
            MarkerOptions().position(riderLatLng)
                .title("Driver")
                .snippet("Driver")
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))))


    markers.add(
        map.addMarker(
            MarkerOptions().position(driverLatLng)
                .title("Driver")
                .snippet("Driver")
                .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))))

Все еще на обратном звонке

//create builder
    val builder = LatLngBounds.builder()

    //loop through the markers list
    for (marker in markers) {

        builder.include(marker.position)
    }
    //create a bound
    val bounds = builder.build()

    //set a 200 pixels padding from the edge of the screen
    val cu = CameraUpdateFactory.newLatLngBounds(bounds,200)
    
    //move and animate the camera
    map.moveCamera(cu)
    //animate camera by providing zoom and duration args, callBack set to null
    map.animateCamera(CameraUpdateFactory.zoomTo(10f), 2000, null)

Веселые кодирующие ребята

person Tonnie    schedule 15.09.2020

   //For adding a marker in Google map
        MarkerOptions mp = new MarkerOptions();
        mp.position(new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude)));
        mp.snippet(strAddress);
        map.addMarker(mp);

        try {

            b = new LatLngBounds.Builder();

            if (MapDetailsList.list != null && MapDetailsList.list.size() > 0) {

                for (int i = 0; i < MapDetailsList.list.size(); i++) {

                    b.include(new LatLng(Double.parseDouble(MapDetailsList.list.get(i).getLatitude()),
                            Double.parseDouble(MapDetailsList.list.get(i).getLongitude())));

                }
                LatLngBounds bounds = b.build();

                DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
                int width = displayMetrics.widthPixels;
                int height = displayMetrics.heightPixels;

                // Change the padding as per needed
                CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, width-200, height-200, 5);
                // map.setCenter(bounds.getCenter());

                map.animateCamera(cu);

            }

        } catch (Exception e) {

        }

http://i64.tinypic.com/2qjybh4.png

http://i63.tinypic.com/flzwus.png

http://i63.tinypic.com/112g5fm.png

person PeddiRaju    schedule 05.01.2016