Google Maps API 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 (map, "idle", function () {} -> отлично работал для моего пакета карт для SugarCRM.
jjwdesign

Слушатель не срабатывает для меня. Я попробовал это в document.ready и window.load. Любые идеи? Объект карты в порядке и пробовал addListener также: `var map = $ (" # js-main-map-canvas "); var listener = google.maps.event.addListenerOnce (map, "idle", function () {alert ('hello');}); `
Хенрик Эрландссон

Хенрик, попробуйте $ ("# 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
У меня была точно такая же проблема, и ваше решение сработало! Спасибо!
Манубкк

Я пробовал тот же самый подход самостоятельно, но он не работал, потому что я не вызывал setCenter () до установки масштаба ... Я все еще задаюсь вопросом, зачем нужен этот шаг, но в любом случае теперь он работает ... спасибо много Джим!
Daveoncode

Карт Центр решил проблему, которая у меня была! Хороший один +1
Петр Кула

1
Спасибо за ваш Хороший ответ +1
Бхарат Ходвадия

44

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

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

По сути, я обнаружил, что событие zoom_changed предотвращает «пропуск» пользовательского интерфейса карты, что произошло, когда я ждал события «idle».

Надеюсь, это кому-нибудь поможет!


4
Отлично, у меня также было поведение «пропуск / автоматическое увеличение». Так же, как примечание для других читателей: обязательно используйте «addListenerOnce», как Бенджамин делает выше, вместо «addListener», или ломайте голову над тем, почему ваш браузер все время падает,
Geert-Jan

6
Также обратите внимание, что слушатель должен быть добавлен перед вызовом fitBounds()при использованииzoom_changed
gapple

1
Спасибо за это, похоже, что v4 должен иметь fitBounds (bounds, 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

Это действительно лучший обход.
Даунхиллски

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()событие listener ( ) будет запущено только один раз, сразу после 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. Используйте схему, предоставленную Koderoid (для каждого набора маркеров 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 маркера.
Майкер

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 есть метод extend (). Это было бы намного проще.
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 (), хотя оно запускается сразу и не обязательно после перемещения карты.

В обычных случаях 'idle' по-прежнему лучший слушатель событий, но это может помочь паре людей, сталкивающихся со странными проблемами с их вызовами fitBounds ().

Смотрите Google Maps FitBounds обратный вызов


0

Чтобы присоединиться к другому решению - я обнаружил, что подход «прослушать событие bounds_changed и затем установить новый масштаб» не работает для меня надежно. Я думаю, что я иногда звонил 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
Виктор

Я думал, что есть функция вроде getBoundsZoomLevel после того, как я вижу этот пост, и я разочарован после того, как понял, что нет.
Седран

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