Android mapa v2 zoom, aby pokazać wszystkie znaczniki

286

Mam 10 znaczników w GoogleMap . Chcę powiększyć jak najwięcej i zachować wszystkie znaczniki na widoku? We wcześniejszej wersji można to osiągnąć, zoomToSpan()ale w wersji 2 nie mam pojęcia, jak to zrobić. Ponadto znam promień okręgu, który musi być widoczny.

Asanka Senavirathna
źródło

Odpowiedzi:

810

Powinieneś użyć CameraUpdate klasy do (prawdopodobnie) wszystkich programowych ruchów mapy.

Aby to zrobić, najpierw obliczyć granice wszystkich znaczników:

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

Następnie uzyskaj obiekt opisu ruchu za pomocą fabryki CameraUpdateFactory:

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

Na koniec przenieś mapę:

googleMap.moveCamera(cu);

Lub jeśli chcesz animację:

googleMap.animateCamera(cu);

To wszystko :)

Wyjaśnienie 1

Prawie wszystkie metody ruchu wymagają, aby Mapobiekt przeszedł proces układu. Możesz poczekać, aż tak się stanie, używając addOnGlobalLayoutListenerkonstruktu. Szczegóły można znaleźć w komentarzach do tej odpowiedzi i pozostałych odpowiedzi. Można również znaleźć kompletny kod do ustawiania zakresu przy użyciu mapy addOnGlobalLayoutListenertutaj .

Wyjaśnienie 2

Jeden komentarz zauważa, że ​​użycie tej metody tylko dla jednego znacznika powoduje, że powiększenie mapy ustawione jest na „dziwny” poziom powiększenia (który moim zdaniem jest maksymalnym poziomem powiększenia dostępnym dla danej lokalizacji). Myślę, że jest to oczekiwane, ponieważ:

  1. LatLngBounds boundsWystąpienie będzie miał northeastwłaściwość równej southwest, co oznacza, że część powierzchni ziemi objętej to boundsjest dokładnie zero. (Jest to logiczne, ponieważ pojedynczy znacznik nie ma obszaru.)
  2. Przekazując boundsdo CameraUpdateFactory.newLatLngBoundsty zasadniczo zażądać obliczenia takiego poziomu, że zoom bounds(o zerowej powierzchni) obejmie cały widok mapy.
  3. Możesz faktycznie wykonać te obliczenia na kartce papieru. Teoretyczny poziom powiększenia będący odpowiedzią wynosi + ∞ (dodatnia nieskończoność). W praktyce Mapobiekt nie obsługuje tej wartości, więc jest zaciśnięty na bardziej rozsądnym maksymalnym poziomie dozwolonym dla danej lokalizacji.

Innymi słowy: skąd Mapobiekt może wiedzieć, jaki poziom powiększenia powinien wybrać dla pojedynczej lokalizacji ? Może optymalna wartość powinna wynosić 20 (jeśli reprezentuje określony adres). A może 11 (jeśli reprezentuje miasto). A może 6 (jeśli reprezentuje kraj). Interfejs API nie jest tak inteligentny, a decyzja należy do Ciebie.

Dlatego powinieneś po prostu sprawdzić, czy markersma tylko jedną lokalizację, a jeśli tak, użyj jednego z:

  • CameraUpdate cu = CameraUpdateFactory.newLatLng(marker.getPosition()) - przejdź do pozycji znacznika i pozostaw nienaruszony aktualny poziom powiększenia.
  • CameraUpdate cu = CameraUpdateFactory.newLatLngZoom(marker.getPosition(), 12F) - przejdź do pozycji znacznika, ustaw poziom powiększenia na dowolnie wybraną wartość 12.
andr
źródło
21
Należy zauważyć, że przeniesienie nie może nastąpić z wywołań onCreate, widok musi zostać utworzony. Musiałem użyć addOnGlobalLayoutListener, aby uzyskać odpowiednią próbkę.
Baruch Nawet
6
@ Bar Cóż, to częściowo prawda. Mówiąc dokładniej: niektóre metody ruchu nie będą działać natychmiast po utworzeniu obiektu mapy. Powodem tego jest to, że obiekt mapy nie został jeszcze zmierzony, tj. Nie przeszedł procesu układania. Typową poprawką jest użycie addOnGlobalLayoutListener()lub post()odpowiednie Runnable. To jest właśnie powód, dla którego coraz współrzędne ekranu marker nie można zrobić w onCreate- patrz stackoverflow.com/q/14429877/1820695 Jednak ty możesz korzystać z niektórych metod zanim układ działo się - np. CameraUpdateFactory.newLatLngBounds()z 4 parametrami .
andr
1
Człowieku, uratowałeś mi dzień ... tak naprawdę próbowałem zrobić to sam, obliczyć ręcznie granice i powiększać, gdy jest w nim znacznik ... działał dość brzydko, ale przy twojej prostej metodzie działa jak urok. Dzięki
Bibu,
9
googleMap .setOnMapLoadedCallback (nowy GoogleMap.OnMapLoadedCallback () {@Override public void onMapLoaded () {googleMap.moveCamera (cu);}}); Pozwoli to uniknąć błędu pomiaru.
Ramz
1
działa inaczej, ale gdy na mapie znajduje się pojedynczy znacznik, powiększa się on do jakiegoś dziwnego poziomu, że mapa staje się rozmyta. Jak to naprawić?
Utsav Gupta
124

Google Map V2

Poniższe rozwiązanie działa w systemie Android Marshmallow 6 (API 23, API 24, API 25, API 26, API 27, API 28). Działa również w 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);
Zumry Mohamed
źródło
7
Aby lepiej wyglądać wypełnienie, użyj wysokości zamiast szerokości i weź 20% zamiast 10%.
noob
2
Zaakceptowana odpowiedź czasami powoduje dziwne powiększanie. To działa jak urok. To lepsza odpowiedź.
Hüseyin Bülbül
1
Ta odpowiedź działa lepiej niż zaakceptowana, zaakceptowana powoduje pewne zachowanie związane ze zmęczeniem.
Haytham
13

Więc

Musiałem użyć addOnGlobalLayoutListener, aby uzyskać odpowiednią próbkę

na przykład twoja mapa Google znajduje się w 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
        }
    });
Eugene Voronoy
źródło
1
Przybyli tutaj z samouczka Udemy, świetny robotnik, użyli twojej odpowiedzi, aby rozwiązać problem.
Shantanu Shady
13

Nie mogłem użyć onGlobalLayoutlistener, więc oto inne rozwiązanie, aby zapobiec "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."błędowi:

mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() { 
@Override 
public void onMapLoaded() { 
    mMap.moveCamera(CameraUpdateFactory.newLatLngBounds(builder.build(), 15));
 } 
});
chrjs
źródło
Ten trwa znacznie dłużej niż OnGlobalLayoutListener, więc dla mnie nadal pokazywał domyślny obszar / zoom mapy, zanim przesunę kamerę do moich potrzeb
do
Jak podać padding, gdy moje 2 znaczniki zostaną dotknięte granicami mapy.
Pratik Butani
9

Działa dobrze dla mnie.

Z tego kodu wyświetlam wiele znaczników ze szczególnym powiększeniem na ekranie mapy.

// Zadeklarowane zmienne

private LatLngBounds bounds;
private LatLngBounds.Builder builder;

// Metoda dodawania wielu punktów znaczników za pomocą ikony do rysowania

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());

    }

// Do dodawania wielu znaczników widocznych na mapie

@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);
BK19
źródło
4

Uwaga - To nie jest rozwiązanie pierwotnego pytania. Jest to rozwiązanie jednego z podproblemów omówionych powyżej .

Rozwiązanie @andr Clarification 2 -

Jest to naprawdę problematyczne, gdy w granicach jest tylko jeden znacznik, dzięki czemu poziom powiększenia jest ustawiony na bardzo wysoki poziom ( poziom 21 ). Google nie zapewnia w żaden sposób ustawienia maksymalnego poziomu powiększenia w tym momencie. Może się to również zdarzyć, gdy jest więcej niż 1 znacznik, ale wszystkie są bardzo blisko siebie. Wtedy pojawi się również ten sam problem.

Rozwiązanie - Załóżmy, że chcesz, aby mapa nigdy nie przekraczała 16-krotnego powiększenia. Następnie po zrobieniu -

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

Sprawdź, czy poziom powiększenia przekroczył poziom 16 (lub cokolwiek chcesz) -

float currentZoom = mMap.getCameraPosition().zoom;

A jeśli ten poziom jest większy niż 16, co będzie tylko wtedy, gdy znaczników będzie bardzo mało lub wszystkie znaczniki będą bardzo blisko siebie, po prostu pomniejsz mapę w tej konkretnej pozycji tylko przez ustawienie poziomu powiększenia na 16.

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

W ten sposób nigdy nie będziesz mieć problemu z „dziwacznym” poziomem powiększenia wyjaśnionym bardzo dobrze przez @andr.

Jyotman Singh
źródło
dobre rozwiązanie, ale jeśli umieścisz obiekt „cu” wewnątrz onMapReady (), wówczas wystąpi niewłaściwe zachowanie: otrzymana lokalizacja -> powiększenie jest duże, więc ustaw 16 -> następnie użytkownik powiększy -> lokalizacja otrzymana ponownie, a kamera jest przywróć poziom 16 . Może to stanowić problem, gdy lokalizacja jest odbierana prawie w czasie rzeczywistym, ponieważ będzie nadal wracać do poziomu 16.
Maher Nabil
„W tym momencie Google nie określa sposobu ustawiania maksymalnego poziomu powiększenia.” - Nieprawda: developers.google.com/android/reference/com/google/android/gms/…
user1209216
3

pomogłoby to .. z demo Google apis

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));
                }
            });
        }
    }

dla jasnych informacji spójrz na ten adres URL. https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/MarkerDemoActivity.java

Amit Tumkur
źródło
1

Pokaż wszystkie znaczniki z mapą Google

W tych metodach przechowuj wszystkie markery i automatycznie powiększaj, aby pokazać wszystkie markery na mapie 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);
}
Sathish Kumar TM
źródło
0

Użyj metody „getCenterCoordinate”, aby uzyskać współrzędną środkową i użyj jej w 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();
}
Jonathan Nolasco Barrientos
źródło
0

Mam jeszcze jeden sposób, aby to samo działało idealnie. więc pomysł, aby pokazać wszystkie znaczniki na ekranie, potrzebujemy środkowej długości i poziomu powiększenia. tutaj jest funkcja, która da wam oboje i będzie potrzebowała wszystkich obiektów Latlng znacznika jako danych wejściowych.

 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);
}

Ta funkcja zwraca obiekt Para, którego możesz użyć podobnie

Para para = getCenterWithZoomLevel (l1, l2, l3 ..); mGoogleMap.moveCamera (CameraUpdateFactory.newLatLngZoom (pair.first, pair.second));

możesz zamiast używać wypełnienia, aby trzymać swoje znaczniki z dala od granic ekranu, możesz dostosować powiększenie o -1.

Anuj Jindal
źródło
-3
   //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

PeddiRaju
źródło