API Google Maps v3: чи можна встановитиZoom після fitBounds?


201

У мене є набір точок, які я хочу побудувати на вбудованій карті Google (API v3). Мені хотілося б, щоб межі містили всі точки, якщо рівень масштабування не занадто низький (тобто занадто сильно зменшений). Мій підхід був такий:

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

// extend bounds with each point

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

Це не працює. Останній рядок "gmap.setZoom ()" не змінює рівень масштабування карти, якщо викликається безпосередньо після fitBounds.

Чи є спосіб отримати рівень масштабування меж, не застосовуючи його до карти? Інші ідеї для вирішення цього питання?


Відповіді:


337

Редагувати : Дивіться коментар Метта Діаманта нижче.

Зрозумів! Спробуйте це:

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

Змініть свої потреби.


76
Прекрасне рішення, повністю врятувало мені багато неприємностей. Просто хотілося додати, що ви можете додатково спростити його, використовуючи метод addListenerOnce ... таким чином, вам не потрібно зберегти слухача та видалити його вручну, оскільки метод піклується про це за вас.
Метт Діамант

9
Така поведінка бачить, як карта "пропускається". Замість цього слухайте подію "zoom_change" та налаштовуйте її перед викликом fitBounds (). Дивіться мою відповідь нижче
Бенджамін Суссман

Дякую! google.maps.event.addListenerOnce (карта, "простоювати", функція () {} -> чудово працював для мого пакету карт для SugarCRM.
jjwdesign

Слухач мене не спрацьовує. Я спробував це в document.ready і window.load. Будь-які ідеї? об’єкт карти в порядку і спробував addListener також: `var map = $ (" # js-main-map-canvas "); var listener = google.maps.event.addListenerOnce (map, "idle", function () {alert ('hello');}); `
Henrik Erlandsson

Генрік, спробуйте $ ("# js-main-map-canvas") [0] або $ ("# js-main-map-canvas"). Get (0);
Рафаель

68

Я вирішив подібну проблему в одному зі своїх додатків. Я трохи збентежився вашим описом проблеми, але я думаю, ви маєте ту саму мету, що і я ...

У своєму додатку я хотів побудувати один або кілька маркерів і переконатися, що карта показує їх усі. Проблема полягала в тому, що якби я покладався виключно на метод fitBounds, то рівень масштабування буде максимізовано, коли залишилася єдина точка - це було б не добре.

Рішенням було використання fitBounds, коли було багато точок, і setCenter + setZoom, коли було лише одне очко.

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

5
Ваша відповідь вирішила мою проблему з Google Maps V3. Дякую!
FR6

1
У мене була точно така ж проблема, і ваше рішення спрацювало! Дякую!
manubkk

Я сам намагався такий же підхід, але це не спрацювало, тому що я не викликав setCenter () перед встановленням масштабу ... Мені все ще цікаво, чому цей крок потрібен, але все одно він працює ... дякую багато Джима!
daveoncode

Map Center вирішив проблему, яку я мав! Хороший +1
Piotr Kula

1
Дякуємо за Вашу добру відповідь +1
Бхарат Чодвадія

44

Я неодноразово приходив на цю сторінку, щоб отримати відповідь, і хоча всі існуючі відповіді були дуже корисними, вони точно не вирішили мою проблему.

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

В основному я виявив, що подія "zoom_changed" перешкоджає користувальницькому інтерфейсу на карті "пропускати", що сталося, коли я чекав події "простою".

Сподіваюся, це комусь допоможе!


4
Чудово, я також мав поведінку "пропускання / автоматичного масштабування". Як зауваження для інших читачів: обов'язково використовуйте "addListenerOnce", як робить Бенджамін вище, а не "addListener", або ламайте голову над тим, чому ваш браузер весь час виходить з ладу ;-)
Geert-Jan

6
Також зауважте, що слухач повинен бути доданий перед викликом fitBounds()при використанніzoom_changed
gapple

1
Дякую за це, здається, що v4 повинен мати fitBounds (межі, minZoomLevel) :)
Річард

3
Бенджамін, я думаю, що це краще використовувати bounds_changed, тому що zoom_changedце не повинно спрацьовувати у випадку, коли fitBoundsзберігається рівень збільшення. Моя спроба jsfiddle.net/rHeMp/10 цього не підтверджує, але не слід покладатися на невизначену поведінку.
TMS

10

Я тільки що це виправив, встановивши maxZoom заздалегідь, потім видаливши його. Наприклад:

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

На сьогодні найкраще рішення для запобігання надмірного збільшення. Простий та інтуїтивний. Немає малювання та повторного малювання карти.
user2605793

Це справді найкраща розбіжність поки що.
Downhillski

2
Я виявив, що це не працює, якщо скинути maxZoom відразу після виклику fitBounds, оскільки зум відбувається асинхронно (тому maxZoom повертається до нуля до моменту його збільшення). Якщо чекати, поки "в режимі очікування" скинути це, виправить це, хоча я думаю.
user987356

4

Якщо я не помиляюся, я припускаю, що ви хочете, щоб всі ваші очки були видні на карті з максимально можливим рівнем збільшення. Я досяг цього, ініціалізуючи рівень масштабування карти до 16 (не впевнений, чи це найвищий рівень масштабування на V3).

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

Потім після цього я робив межі:

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

Результат: Успіх!


3

Я використовую:

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

Це використовує менший з 24 і необхідний рівень масштабування відповідно до вашого коду, однак, ймовірно, все одно змінить масштаб і не хвилює, скільки ви зменшили масштаб.


Я ненавиджу це говорити, але наскільки дивно, як виглядала ця відповідь, це єдине, що працювало до цього часу, і я шукав 4-5 годин на 3-денний період. Але все одно буде шукати кращого рішення.
Аллов

3

Спробуйте:

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

У мене була така ж проблема, і я зміг її вирішити за допомогою наступного коду. Ця google.maps.addListenerOnce()подія слухача ( ) буде звільнена лише один раз, відразу після map.fitBounds()виконання. Отже, не потрібно

  1. Відстежуйте і вручну видаляйте слухача, або
  2. Зачекайте, поки карта idle.

Він встановлює відповідний рівень збільшення спочатку і дозволяє користувачеві збільшувати та зменшувати попередній рівень масштабування, оскільки у слухача подій минув термін дії. Наприклад, якби тільки google.maps.addListener()було викликано, то користувач ніколи не зможе збільшити масштаб минулого зазначеного рівня збільшення (у випадку 4). Оскільки ми реалізували google.maps.addListenerOnce(), користувач зможе збільшувати масштаб на будь-який рівень, який він обере.

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

Була така ж проблема, необхідна для розміщення багатьох маркерів на карті. Це вирішило мою справу:

  1. Об'явіть межі
  2. Використовуйте схему, надану кодероїдом (для кожного набору маркерів bounds.extend(objLatLng))
  3. Виконати прив'язку ПІСЛЯ картки завершено:

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

2

Я знайшов рішення, яке робить перевірку перед викликом, fitBoundsщоб ви не збільшували масштаб і раптово зменшували масштаб

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

Вам доведеться трохи пограти зі змінною minLatSpan, щоб отримати її там, де ви хочете. Він буде змінюватися залежно від рівня масштабу та розмірів полотна карти.

Ви також можете використовувати довготу замість широти


Для мене працює, але я не використовую minLatSpan - мені потрібно встановити масштабування лише тоді, коли на карті є 1 маркер. Тому я лише перевіряю, чи більше 1 маркера.
Micer

2

Я бачив багато неправильних або занадто складних рішень, тому вирішив опублікувати робоче, елегантне рішення .

Причина setZoom()не працює так, як ви очікували, fitBounds()це асинхронність, тому вона не дає гарантії, що вона негайно оновить масштаб, але ваш заклик setZoom()покладатися на це.

Що ви можете врахувати, це встановити параметр minZoomкарти перед викликом, fitBounds()а потім очистити її після її завершення (щоб користувачі могли все-таки зменшити масштаб вручну, якщо хочуть):

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

Крім того, якщо ви також хочете обмежити максимальне збільшення, ви можете застосувати той самий трюк із maxZoomвластивістю.

Див. Документи MapOptions .


1

Я використовую це для того, щоб рівень масштабування не перевищував заданий рівень, так що я знаю, що будуть доступні супутникові знімки.

Додайте слухача до zoom_changedподії. Це має додаткову перевагу і в управлінні масштабуванням на інтерфейсі користувача.

Виконайте лише виконання, setZoomякщо вам потрібно, тому ifзаява є кращою для Math.maxабоMath.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);

Щоб запобігти занадто далеко зменшити масштаб:

   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

У цій функції потрібно динамічно додавати метадані, щоб зберігати тип геометрії лише тому, що функція приймає будь-яку геометрію.

"fitGeometries" - це функція JSON, що розширює об'єкт карти.

"геометрії" - це загальний масив JavaScript, а не 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);
},

По черзі я робив всю роботу, не знаючи, що на об’єкт LatLngBounds існує метод exte (). Це було б набагато простіше.
CrazyEnigma

1

ця робота для мене з API v3, але з встановленням фіксованого масштабування:

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

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

1

Як і я, якщо ви не бажаєте грати зі слухачами, це просте рішення, яке я придумав: Додати метод на карті, який працює строго відповідно до ваших вимог, як цей:

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

то ви можете зателефонувати map.fitLmtdBounds(bounds)замість map.fitBounds(bounds)встановлення меж у визначеному діапазоні масштабування ... або map.fitLmtdBounds(bounds,3,5)змінити діапазон масштабування ..


0

Спробуйте це.

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

Якщо це вам допоможе.

Всього найкращого


0

Після обчислення меж можна перевірити відстань між лівим верхнім та нижнім правим кутом; тоді ви можете зрозуміти рівень масштабування, випробувавши відстань (якщо відстань занадто далека, рівень масштабування буде низьким), тоді ви можете вибрати колесо за допомогою методу setbound або setZoom ..


0

Я не люблю це пропонувати, але якщо потрібно спробувати - спочатку зателефонуйте

gmap.fitBounds(bounds);

Потім створіть нову Thread / AsyncTask, увімкніть її 20-50 мс або близько того, а потім зателефонуйте

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

з потоку користувальницького інтерфейсу (використовуйте обробник або onPostExecuteметод для AsyncTask).

Я не знаю, чи працює, просто пропозиція. Крім того, вам доведеться якось самостійно обчислити рівень масштабування з точки, перевірте, чи він занадто низький, виправте його, а потім просто зателефонуйтеgmap.setZoom(correctedZoom)


0

Якщо "bounds_changed" не працює належним чином (іноді Google, схоже, не приймає координати ідеально), тоді замість цього скористайтеся "center_changed".

Подія "center_changed" запускається щоразу, коли викликається fitBounds (), хоча вона запускається негайно, а не обов'язково після переміщення карти.

У звичайних випадках "простоювати" все ще є найкращим слухачем подій, але це може допомогти парі людей, які стикаються з дивними проблемами зі своїми дзвінками fitBounds ().

Див. Зворотний виклик Google Maps fitBounds


0

Захоплюватися іншим рішенням - я виявив, що підхід "слухати події" та змінити масштаб ", а потім встановити нове масштабування, для мене не працює надійно. Я думаю, що мені іноді дзвонили fitBoundsдо того, як карта була повністю ініціалізована, і ініціалізація викликала подію bounds_changed, яка використала б слухача, перш ніж fitBoundsзмінити межі та рівень масштабування. Я закінчив цей код, який, здається, працює досі:

// 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

Для мене найпростішим рішенням було таке:

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

Все, що я зробив:

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

І він працює на V3 API.


map.setCenter приймає лише один атрибут, latlng, і карта не має такої функції, як getBoundsZoomLevel
Viktor

Я подумав, що після такої публікації є така функція, як getBoundsZoomLevel, і я розчарувався, коли зрозумів, що її немає.
седран

я думаю, що getBoundsZoomLevel () був доступний в API Карт Google V2, а не в API V3
Куш
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.