Радиус точки слоя тепловой карты Google Maps

Я хочу иметь возможность эффективно указывать радиус точек в метрах. API сконфигурирован таким образом, что свойство radius остается постоянным для пикселей, так что увеличение масштаба приводит к размытию тепловой карты (я знаю, что вы можете сделать так, чтобы тепловая карта не размывалась с помощью свойства dissipating, но это поднимает другие проблемы, т.е. чтобы вручную возиться с радиусом, чтобы моя тепловая карта отображалась правильно. .

В частности, я пытаюсь отобразить распределение вероятностей на карте. У меня есть распределение в виде изображения, и я хочу сопоставить веса 0-1 со слоем тепловой карты. (Я могу и не хочу накладывать изображения).

Какие-либо предложения?


person eqzx    schedule 06.09.2012    source источник
comment
Я думаю, вам нужно будет использовать тот факт, что на каждом уровне масштабирования расстояния на пиксель удваиваются, а затем перерисовывать тепловую карту для каждого уровня масштабирования?   -  person Tina CG Hoehr    schedule 06.09.2012


Ответы (4)


Хорошо, я попробовал некоторые вещи:

Используя пример проекции Меркатора (проверьте источник) для извлечения x ,y пиксельные координаты любой точки от latLng, чтобы позже использовать библиотеку геометрии, в частности функцию calculateOffset получите еще одну широту на расстоянии «DM» (в метрах) справа от предыдущей, получите разницу (в пикселях) как абсолютное значение «DP», и оттуда вы получите соотношение «пикселей на метр» DP / DM.

Итак, если вы хотите, чтобы ваш радиус был 100 метров, вы просто устанавливаете свойства на {radius:Math.floor(desiredRadiusPerPointInMeters*pixelsPerMeter)}

И чтобы обработать изменение масштаба, просто используйте слушателя

 google.maps.event.addListener(map, 'zoom_changed', function () {
          heatmap.setOptions({radius:getNewRadius()});
      });

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

person lccarrasco    schedule 06.09.2012
comment
Я думаю, что у меня это почти работает, однако параметр TILE_SIZE сбивает меня с толку. Почему установлено значение 256, это произвольно? Карта по умолчанию имеет размер тайла (256x256)? Как я могу получить правильный размер плитки? - person eqzx; 06.09.2012
comment
Спасибо. Следует отметить, что когда вы слишком сильно уменьшаете масштаб, каждая точка больше не может уместиться в пикселе, а радиусы устанавливаются равными нулю. добавление этого кода в метод getNewRadius() делает так, что точки отображаются как нерассеивающие (неточные), но, по крайней мере, они отображаются (dissipating=false при инициализации) if(totalPixelSize == 0 && dissipating == true) { heatmap.setOptions({dissipating: false}); totalPixelSize=.01; dissipate=false;} else if(totalPixelSize > 0 && dissipate==false) {heatmap.setOptions({dissipating: true}); dissipate=true;} else if(totalPixelSize == 0) { totalPixelSize=.01;} - person eqzx; 06.09.2012
comment
@nrhine1 Число 256 взято из Because the basic Mercator Google Maps tile is 256 x 256 pixels, the usable world coordinate space is {0-256}, {0-256} на этой странице документации, спасибо за отзыв по поводу диссипации :) - person lccarrasco; 07.09.2012

Для тех, кто хотел бы иметь хорошо упакованную версию jsbin-файла @lccarrasco для кофескрипта, вы можете просмотреть суть Класс кофейного скрипта MercatorProjection, который я создал, используя его код.

После того, как вы загрузили класс, вы можете использовать его со следующим:

map = new google.maps.Map(...)
heatmap = new google.maps.visualization.HeatmapLayer({map: map})
google.maps.event.addListener(map, 'zoom_changed', () ->
  projection = new MercatorProjection()
  heatmap.setOptions(radius: projection.getNewRadius(map, 15))
)

Где «15» — это радиус в метрах, с которым вы можете поиграть или установить программно другими способами, чтобы ваша тепловая карта выглядела так, как вы этого хотите.

person schenkman    schedule 26.06.2014
comment
Обязательно включите библиотеку геометрии, например: ‹script src='карты .googleapis.com/maps/api/'› . Спасибо @schenkman за класс! работает отлично - person MrDerp; 09.06.2015

Я решил это, используя прослушиватель, который использовал @lccarrasco, но в моей функции getNewRadius() я сделал изменение радиуса относительно увеличения.

т.е. var radius = (somemultiplicationfactor)/(Math.pow(2,(20-zoom)));

Это работает, поскольку коэффициент масштабирования составляет 2: 1 для каждого масштабирования.

person junlop    schedule 05.04.2016
comment
как насчет return multiplicator * Math.pow(2,zoom); ? - person Melounek; 17.01.2018

На основе сообщения на форуме группы Google и этой ГИС post, я придумал простое, но полное решение в том же духе.

Во-первых, определите функцию для получения радиуса в метрах для данного уровня масштабирования: поскольку существуют различия в масштабировании для разных широт, вам нужно ввести someLatValue, например, центр карты, которую вы планируете использовать. Хотя это приближение, этого будет достаточно для получения точных результатов вплоть до размера небольшой страны. Вам также необходимо указать размер желаемого радиуса в метрах.

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

var someLatValue = 35.726332;
var desiredRadiusInMeters = 1500;

function getHeatmapRadius(zoomLevel){
  metersPerPx = 156543.03392 * Math.cos(someLatValue * Math.PI / 180) / Math.pow(2,theMap.getZoom());
  return desiredRadiusInMeters / metersPerPx;
};

Это возвращает (приблизительный) радиус в пикселях для желаемого количества метров и уровня масштабирования вокруг определенной широты. Значение 156543.03392 основано на приблизительном радиусе Земли, который Google фактически использует для Карт Google.

Итак, скажем, у вас есть такая тепловая карта:

fixedRadiusHeatmap = new google.maps.visualization.HeatmapLayer({
  data: myCoords,
  map: theMap
});

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

fixedRadiusHeatmap.setOptions({radius: getHeatmapRadius(theMap.getZoom())});
fixedRadiusHeatmap.setMap(theMap);

Затем нужно сбрасывать радиус каждый раз при масштабировании карты, что можно сделать так:

google.maps.event.addListener(theMap, 'zoom_changed', function () {
  fixedRadiusHeatmap.setOptions({radius: getHeatmapRadius(theMap.getZoom())});
});

В моем случае он немного отстает, поэтому показывает (тупо агрегированную и плохо продуманную) тепловую карту по умолчанию до того, как появится карта с фиксированным радиусом. Возможно, есть способ отложить рендеринг, чтобы сделать переход более плавным, но это другая проблема.

person Aaron Bramson    schedule 25.01.2019