Google Maps API v3: Czy mogę ustawićZoom po fitBounds?


201

Mam zestaw punktów, które chcę narysować na wbudowanej Mapie Google (API v3). Chciałbym, aby granice obejmowały wszystkie punkty, chyba że poziom powiększenia jest zbyt niski (tj. Zbyt duży oddalenie). Moje podejście wyglądało tak:

var bounds = new google.maps.LatLngBounds();

// extend bounds with each point

gmap.fitBounds(bounds); 
gmap.setZoom( Math.max(6, gmap.getZoom()) );

To nie działa Ostatni wiersz „gmap.setZoom ()” nie zmienia poziomu powiększenia mapy, jeśli zostanie wywołany bezpośrednio po fitBounds.

Czy istnieje sposób na uzyskanie poziomu powiększenia granic bez zastosowania go na mapie? Inne pomysły na rozwiązanie tego?


1
Zobacz stackoverflow.com/questions/4523023/using-setzoom-after-using-fitbounds-with-google-maps-api-v3
Mawg mówi o przywróceniu Moniki

Odpowiedzi:


337

Edycja : Zobacz komentarz Matta Diamonda poniżej.

Rozumiem! Spróbuj tego:

map.fitBounds(bounds);
var listener = google.maps.event.addListener(map, "idle", function() { 
  if (map.getZoom() > 16) map.setZoom(16); 
  google.maps.event.removeListener(listener); 
});

Dostosuj do swoich potrzeb.


76
Świetne rozwiązanie, całkowicie zaoszczędziło mi wiele kłopotów. Chciałem tylko dodać, że możesz to jeszcze bardziej uprościć za pomocą metody addListenerOnce ... w ten sposób nie musisz zapisywać detektora i ręcznie go usuwać, ponieważ metoda zajmie się tym za Ciebie.
Matt Diamond,

9
Zachowuje się to tak, jakby mapa „pomijała”. Zamiast tego nasłuchuj zdarzenia „zoom_change” i skonfiguruj je przed wywołaniem fitBounds (). Zobacz moją odpowiedź poniżej
Benjamin Sussman,

Dzięki! google.maps.event.addListenerOnce (mapa, „bezczynność”, funkcja () {} -> działał doskonale w moim pakiecie map dla SugarCRM.
jjwdesign

Słuchacz nie jest dla mnie wyzwalany. Próbowałem tego w document.ready i window.load. Jakieś pomysły? obiekt mapy jest OK i próbował także addListener: `var map = $ (" # js-main-map-canvas "); var listener = google.maps.event.addListenerOnce (mapa, „bezczynność”, funkcja () {alert ('hello');}); `
Henrik Erlandsson

Henrik, spróbuj $ („# js-main-map-canvas”) [0] lub $ („# js-main-map-canvas”). Get (0);
Rafael

68

Rozwiązałem podobny problem w jednej z moich aplikacji. Byłem trochę zdezorientowany twoim opisem problemu, ale myślę, że masz ten sam cel, który miałem ...

W mojej aplikacji chciałem wykreślić jeden lub więcej znaczników i upewnić się, że mapa pokazuje je wszystkie. Problem polegał na tym, że gdybym polegał wyłącznie na metodzie fitBounds, poziom powiększenia byłby maksymalny, gdyby istniał jeden punkt - to nie było dobre.

Rozwiązaniem było użycie fitBounds, gdy było wiele punktów, i setCenter + setZoom, gdy był tylko jeden punkt.

if (pointCount > 1) {
  map.fitBounds(mapBounds);
}
else if (pointCount == 1) {
  map.setCenter(mapBounds.getCenter());
  map.setZoom(14);
}

5
Twoja odpowiedź rozwiązała mój problem z Google Maps V3. Dzięki!
FR6

1
Miałem dokładnie ten sam problem, a twoje rozwiązanie zadziałało! Dzięki!
manubkk

Próbowałem tego samego podejścia, ale to nie zadziałało, ponieważ przed wywołaniem zoomu nie wywoływałem setCenter () ... Nadal zastanawiam się, dlaczego ten krok jest potrzebny, ale teraz działa ... dzięki dużo Jim!
daveoncode

Centrum map rozwiązało problem, który miałem! Niezły +1
Piotr Kula,

1
Dziękuję za dobrą odpowiedź +1
Bharat Chodvadiya

44

Odwiedziłem tę stronę wiele razy, aby uzyskać odpowiedź i chociaż wszystkie istniejące odpowiedzi były bardzo pomocne, nie rozwiązały dokładnie mojego problemu.

google.maps.event.addListenerOnce(googleMap, 'zoom_changed', function() {
    var oldZoom = googleMap.getZoom();
    googleMap.setZoom(oldZoom - 1); //Or whatever
});

Zasadniczo stwierdziłem, że zdarzenie „zoom_changed” zapobiegło „pomijaniu” interfejsu użytkownika, który miał miejsce, gdy czekałem na zdarzenie „bezczynności”.

Mam nadzieję, że to komuś pomoże!


4
Doskonale, miałem również zachowanie „pomijania / automatycznego powiększania”. Tak jak uwaga dla innych czytelników: pamiętaj, aby używać „addListenerOnce”, tak jak Benjamin powyżej, zamiast „addListener”, lub też zastanów się, dlaczego przeglądarka ciągle się zawiesza ;-)
Geert-Jan

6
Zauważ też, że słuchacz powinien zostać dodany przed wywołaniem, fitBounds()jeśli używaszzoom_changed
gapple

1
Dzięki za to, wygląda na to, że v4 powinna mieć fitBounds (bounds, minZoomLevel) :)
Richard

3
Benjamin, myślę, że lepiej go używać bounds_changed, ponieważ zoom_changednie trzeba go uruchamiać w przypadku, gdy fitBoundsutrzymuje poziom powiększenia. Moja próba jsfiddle.net/rHeMp/10 tego nie potwierdza, ale nie należy polegać na niezdefiniowanym zachowaniu.
TMS

10

Właśnie to naprawiłem, ustawiając maxZoom z góry, a następnie usuwając go później. Na przykład:

map.setOptions({ maxZoom: 15 });
map.fitBounds(bounds);
map.setOptions({ maxZoom: null });

Jak dotąd najlepsze rozwiązanie zapobiegające zbyt dużemu powiększaniu. Prosty i intuicyjny. Brak rysowania i ponownego rysowania mapy.
user2605793

To jest rzeczywiście najlepsze dotychczasowe obejście.
Downhillski,

2
Zauważyłem, że to nie działa, jeśli zresetujesz maxZoom natychmiast po wywołaniu fitBounds, ponieważ powiększenie odbywa się asynchronicznie (więc maxZoom powraca do wartości zerowej do czasu powiększenia). Chyba poczekanie, aż „bezczynny” zresetuje to naprawiłoby to.
user987356

4

Jeśli się nie mylę, zakładam, że chcesz, aby wszystkie twoje punkty były widoczne na mapie z możliwie najwyższym poziomem powiększenia. Osiągnąłem to, inicjując poziom powiększenia mapy do 16 (nie jestem pewien, czy jest to najwyższy możliwy poziom powiększenia w V3).

var map = new google.maps.Map(document.getElementById('map_canvas'), {
  zoom: 16,
  center: marker_point,
  mapTypeId: google.maps.MapTypeId.ROADMAP
});

Potem zrobiłem rzeczy na granicy:

var bounds = new google.maps.LatLngBounds();

// You can have a loop here of all you marker points
// Begin loop
bounds.extend(marker_point);
// End loop

map.fitBounds(bounds);

Wynik: sukces!


3

Używam:

gmap.setZoom(24); //this looks a high enough zoom value
gmap.fitBounds(bounds); //now the fitBounds should make the zoom value only less

Spowoduje to użycie mniejszego z 24 i niezbędnego poziomu powiększenia zgodnie z Twoim kodem, jednak prawdopodobnie i tak to zmieni i nie będzie miało znaczenia, jak bardzo pomniejszyłeś.


Nienawidzę tego mówić, ale jakkolwiek dziwnie wyglądała ta odpowiedź, to jedyna rzecz, która do tej pory działała i szukałem 4-5 godzin w ciągu 3 dni. Nadal jednak będzie szukać lepszego rozwiązania.
Allov

3

Spróbuj tego:

map.fitBounds(bounds);

// CHANGE ZOOM LEVEL AFTER FITBOUNDS
zoomChangeBoundsListener = google.maps.event.addListenerOnce(map, 'bounds_changed', function(event) {
  if (this.getZoom()){
    this.setZoom(15);
  }
});
setTimeout(function(){
  google.maps.event.removeListener(zoomChangeBoundsListener)
}, 2000);

2

Miałem ten sam problem i udało mi się go rozwiązać za pomocą następującego kodu. To google.maps.addListenerOnce()zdarzenie listener ( ) zostanie uruchomione tylko raz, zaraz po map.fitBounds()jego wykonaniu. Więc nie ma takiej potrzeby

  1. Śledź i ręcznie usuń słuchacza, lub
  2. Poczekaj, aż mapa się pojawi idle.

Początkowo ustawia odpowiedni poziom powiększenia i pozwala użytkownikowi powiększać i pomniejszać początkowy poziom powiększenia, ponieważ detektor zdarzeń wygasł. Na przykład, jeśli tylkogoogle.maps.addListener() został wywołany, użytkownik nigdy nie byłby w stanie wykonać powiększenia przekraczającego podany poziom powiększenia (w przypadku 4). Po wdrożeniu google.maps.addListenerOnce()użytkownik będzie mógł przybliżyć się do dowolnego wybranego przez siebie poziomu.

map.fitBounds(bounds);

var zoom_level_for_one_marker = 4;

google.maps.event.addListenerOnce(map, 'bounds_changed', function(event){
   if (this.getZoom() >= zoom_level_for_one_marker){  
       this.setZoom(zoom_level_for_one_marker) 
   }
});

2

Miał ten sam problem, musiał zmieścić wiele znaczników na mapie. To rozwiązało moją sprawę:

  1. Deklaruj granice
  2. Użyj schematu dostarczonego przez koderoid (dla każdego zestawu znaczników bounds.extend(objLatLng))
  3. Wykonaj dopasowania PO ukończeniu mapy:

    google.maps.event.addListenerOnce(map, 'idle', function() { 
        map.fitBounds( bounds );
    });

2

Znalazłem rozwiązanie, które sprawdza, zanim zadzwonisz, fitBoundsaby nie powiększać i nie pomniejszać nagle

var bounds = new google.maps.LatLngBounds();

// extend bounds with each point

var minLatSpan = 0.001;
if (bounds.toSpan().lat() > minLatSpan) {
    gmap.fitBounds(bounds); 
} else {
    gmap.setCenter(bounds.getCenter());
    gmap.setZoom(16);
}

Będziesz musiał pobawić się zmienną minLatSpan, aby dostać się tam, gdzie chcesz. Będzie się różnić w zależności od poziomu powiększenia i wymiarów płótna mapy.

Możesz także użyć długości geograficznej zamiast szerokości geograficznej


Działa dla mnie, ale nie używam minLatSpan - muszę ustawić powiększenie tylko wtedy, gdy 1 znacznik znajduje się na mapie. Dlatego sprawdzam tylko, czy jest więcej niż 1 znacznik.
Micer,

2

Widziałem wiele niepoprawnych lub zbyt skomplikowanych rozwiązań, więc postanowiłem opublikować działające, eleganckie rozwiązanie .

Powód setZoom()nie działa zgodnie z oczekiwaniami, ponieważ fitBounds()jest asynchroniczny, więc nie daje żadnej gwarancji, że natychmiast zaktualizuje powiększenie, ale twoje połączenie setZoom()polega na tym.

To, co możesz rozważyć, to ustawienie minZoomopcji mapy przed wywołaniem, fitBounds()a następnie wyczyszczenie jej po jej zakończeniu (aby użytkownicy nadal mogli ręcznie pomniejszyć, jeśli chcą):

var bounds = new google.maps.LatLngBounds();
// ... (extend bounds with all points you want to fit)

// Ensure the map does not get too zoomed out when fitting the bounds.
gmap.setOptions({minZoom: 6});
// Clear the minZoom only after the map fits the bounds (note that
// fitBounds() is asynchronous). The 'idle' event fires when the map
// becomes idle after panning or zooming.
google.maps.event.addListenerOnce(gmap, 'idle', function() {
  gmap.setOptions({minZoom: null});
});

gmap.fitBounds(bounds);

Ponadto, jeśli chcesz również ograniczyć maksymalny zoom, możesz zastosować tę samą sztuczkę z maxZoomwłaściwością.

Zobacz dokumenty MapOptions .


1

Używam tego, aby upewnić się, że poziom powiększenia nie przekracza ustalonego poziomu, dzięki czemu wiem, że zdjęcia satelitarne będą dostępne.

Dodaj detektor do zoom_changedwydarzenia. Ma to tę dodatkową zaletę, że kontroluje także zoom w interfejsie użytkownika.

Wykonuj tylko w setZoomrazie potrzeby, więc ifinstrukcja jest lepsza niż Math.maxlubMath.min

   google.maps.event.addListener(map, 'zoom_changed', function() { 
      if ( map.getZoom() > 19 ) { 
        map.setZoom(19); 
      } 
    });
    bounds = new google.maps.LatLngBounds( ... your bounds ... )
    map.fitBounds(bounds);

Aby zapobiec zbyt dużemu oddaleniu:

   google.maps.event.addListener(map, 'zoom_changed', function() { 
      if ( map.getZoom() < 6 ) { 
        map.setZoom(6); 
      } 
    });
    bounds = new google.maps.LatLngBounds( ... your bounds ... )
    map.fitBounds(bounds);

1

W tej funkcji musisz dynamicznie dodawać metadane, aby przechowywać typ geometrii tylko dlatego, że funkcja akceptuje dowolną geometrię.

„fitGeometries” to funkcja JSON rozszerzająca obiekt mapy.

„geometrii” to ogólna tablica javascript, a nie MVCArray ().

geometry.metadata = { type: "point" };
var geometries = [geometry];

fitGeometries: function (geometries) {
    // go and determine the latLngBounds...
    var bounds = new google.maps.LatLngBounds();
    for (var i = 0; i < geometries.length; i++) {
        var geometry = geometries[i];
        switch (geometry.metadata.type)
        {
            case "point":
                var point = geometry.getPosition();
                bounds.extend(point);
                break;
            case "polyline":
            case "polygon": // Will only get first path
                var path = geometry.getPath();
                for (var j = 0; j < path.getLength(); j++) {
                    var point = path.getAt(j);
                    bounds.extend(point);
                }
                break;
        }
    }
    this.getMap().fitBounds(bounds);
},

Alternatywnie wykonałem całą pracę, nie wiedząc, że istnieje metoda ext () na obiekcie LatLngBounds. To byłoby o wiele łatwiejsze.
CrazyEnigma

1

ta praca jest dla mnie z API v3, ale z ustawionym stałym powiększeniem:

var bounds = new google.maps.LatLngBounds();
// extend bounds with each point

gmap.setCenter(bounds.getCenter()); 
gmap.setZoom( 6 );

1

Podobnie jak ja, jeśli nie chcesz grać ze słuchaczami, wpadłem na proste rozwiązanie: dodaj metodę na mapie, która działa ściśle według twoich wymagań, takich jak ta:

    map.fitLmtdBounds = function(bounds, min, max){
        if(bounds.isEmpty()) return;
        if(typeof min == "undefined") min = 5;
        if(typeof max == "undefined") max = 15;

        var tMin = this.minZoom, tMax = this.maxZoom;
        this.setOptions({minZoom:min, maxZoom:max});
        this.fitBounds(bounds);
        this.setOptions({minZoom:tMin, maxZoom:tMax});
    }

możesz zadzwonić map.fitLmtdBounds(bounds)zamiast map.fitBounds(bounds)ustawić granice w ramach zdefiniowanego zakresu zoomu ... lub map.fitLmtdBounds(bounds,3,5)zastąpić zakres zoomu ...


0

Spróbuj tego.

// Find out what the map's zoom level is
zoom = map.getZoom();
if (zoom == 1) {
  // If the zoom level is that low, means it's looking around the
world.
  // Swap the sw and ne coords
  viewportBounds = new
google.maps.LatLngBounds(results[0].geometry.location, initialLatLng);
  map.fitBounds(viewportBounds);
}

Jeśli to ci pomoże.

Wszystkiego najlepszego


0

Po obliczeniu granic można sprawdzić odległość między lewym górnym a prawym dolnym rogiem; wtedy możesz zrozumieć poziom powiększenia, testując odległość (jeśli odległość jest zbyt daleko, poziom powiększenia byłby niski), możesz wybrać, czy używasz metody setbound lub setZoom ..


0

Nie lubię tego sugerować, ale jeśli musisz spróbować - najpierw zadzwoń

gmap.fitBounds(bounds);

Następnie utwórz nowy Thread / AsyncTask, pozwól mu spać przez około 20-50ms, a następnie zadzwoń

gmap.setZoom( Math.max(6, gmap.getZoom()) );

z wątku interfejsu użytkownika (użyj modułu obsługi lub onPostExecutemetody AsyncTask).

Nie wiem, czy to działa, tylko sugestia. Poza tym musiałbyś jakoś samodzielnie obliczyć poziom powiększenia na podstawie punktów, sprawdzić, czy jest on zbyt niski, poprawić go, a następnie po prostu zadzwonićgmap.setZoom(correctedZoom)


0

Jeśli „bounds_changed” nie odpala poprawnie (czasem Google wydaje się, że nie akceptuje idealnie współrzędnych), rozważ użycie zamiast tego opcji „center_changed”.

Zdarzenie „center_changed” jest uruchamiane za każdym razem, gdy wywoływana jest funkcja fitBounds (), chociaż jest uruchamiane natychmiast i niekoniecznie po przesunięciu mapy.

W normalnych przypadkach „bezczynność” jest nadal najlepszym odbiornikiem zdarzeń, ale może to pomóc kilku osobom napotkać dziwne problemy z wywołaniami fitBounds ().

Zobacz Google Maps fitBounds oddzwonienia


0

Aby wdrożyć inne rozwiązanie - odkryłem, że podejście „nasłuchuj zdarzenia bounds_changed, a następnie ustaw nowe powiększenie” nie działało dla mnie niezawodnie. Myślę, że czasami fitBoundsdzwoniłem, zanim mapa została w pełni zainicjowana, a inicjalizacja spowodowała zdarzenie bounds_changed, które zużyłoby nasłuchiwacza, zanim fitBoundszmieniłem granice i poziom powiększenia. Skończyło się na tym kodzie, który wydaje się działać do tej pory:

// If there's only one marker, or if the markers are all super close together,
// `fitBounds` can zoom in too far. We want to limit the maximum zoom it can
// use.
//
// `fitBounds` is asynchronous, so we need to wait until the bounds have
// changed before we know what the new zoom is, using an event handler.
//
// Sometimes this handler gets triggered by a different event, before
// `fitBounds` takes effect; that particularly seems to happen if the map
// hasn't been fully initialized yet. So we don't immediately remove the
// listener; instead, we wait until the 'idle' event, and remove it then.
//
// But 'idle' might happen before 'bounds_changed', so we can't set up the
// removal handler immediately. Set it up in the first event handler.

var removeListener = null;
var listener = google.maps.event.addListener(map, 'bounds_changed', () => {
  console.log(map.getZoom());
  if (map.getZoom() > 15) {
    map.setZoom(15);
  }

  if (!removeListener) {
    removeListener = google.maps.event.addListenerOnce(map, 'idle', () => {
      console.log('remove');
      google.maps.event.removeListener(listener);
    });
  }
});

0

Dla mnie najłatwiejszym rozwiązaniem było:

map.fitBounds(bounds);

function set_zoom() {
    if(map.getZoom()) {map.setZoom(map.getZoom() - 1);}
    else {setTimeout(set_zoom, 5);}
}
setTimeout(set_zoom, 5);

-1
google.maps.event.addListener(marker, 'dblclick', function () {
    var oldZoom = map.getZoom(); 
    map.setCenter(this.getPosition());
    map.setZoom(parseInt(oldZoom) + 1);
});

-3

Wszystko co zrobiłem to:

map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));

I działa na API V3.


map.setCenter akceptuje tylko jeden atrybut, latlng, a mapa nie ma takiej funkcji jak getBoundsZoomLevel
Viktor,

Myślałem, że jest funkcja taka jak getBoundsZoomLevel po tym, jak zobaczyłem ten post, i jestem rozczarowany, gdy zdałem sobie sprawę, że nie ma.
sedran

Myślę, że getBoundsZoomLevel () był dostępny w API Google Maps V2, a nie w API V3
Kush
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.