Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Maps heatmap layer point radius

I want to be able to specify the radius of points effectively in meters. The API is configured so that the radius property is held to be constant for pixels, so that zooming in causes the heatmap to erode (I know you can make the heatmap not erode with the dissipating property, but this raises other issues, i.e., having to manually mess with the radius to get my heatmap to display properly. Here is the heatmaps reference.

Specifically, I'm trying to display a probability distribution on a map. I have the distribution in image form, and want to map the 0-1 weights to a heatmap layer. (I can, and don't want to, overlay the images).

Any suggestions?

like image 631
eqzx Avatar asked Sep 06 '12 00:09

eqzx


2 Answers

Based on a Google group forum post and this GIS post, I came up with a simple yet complete solution along the same lines.

First, define a function to get the radius in meters for a given zoom level: Because there are scaling differences for different latitudes, you need to feed in someLatValue, for example the center of the map your plan on using. Although an approximation, it will be good enough for accurate results up to the size of a small country. You also need to specify the size of the radius you want in meters.

You could change the function to read these values in as parameters if you prefer (e.g., getting the lat of the center of the current map view and/or a radius based on property of the data), but if they are static, this making them globals is easier.

var someLatValue = 35.726332;
var desiredRadiusInMeters = 1500;

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

This returns the (approximate) radius in pixels for a desired number of meters and zoom level around a particular latitude. The value 156543.03392 is based on the approximate radius of the Earth that google actually uses for Google Maps.

So, say you have a heatmap like this:

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

In order to set the initial view, just call the function before adding the heatmap to your map.

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

Then you need to reset the radius every time the map is zoomed, which can be done like this:

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

In my case it lags a bit, so it shows the (stupidly aggregated and poorly thought out) default heatmap before the fixed radius one appears. Maybe there is a way to delay the rendering to make the transition smoother, but that's a different problem.

like image 167
Aaron Bramson Avatar answered Oct 21 '22 16:10

Aaron Bramson


Ok, I tried some things:

Using the Mercator Projection example (check the source) to extract the x,y pixel coordinates of any point from a latLng, to later use the geometry library, specifically the computeOffset function get another latLng a distance "DM" (in meters) to the right of the previous one, get the difference (in pixels) as an absolute value "DP" and from there you get your "pixelsPerMeter" ratio DP/DM.

So then, if you want your radius to be 100 meters you just set the properties to {radius:Math.floor(desiredRadiusPerPointInMeters*pixelsPerMeter)}

And to handle the change in zoom just use a listener

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

I uploaded a small example (try zooming), you can check if the distance looks right with the button on the bottom.

like image 28
lccarrasco Avatar answered Oct 21 '22 17:10

lccarrasco