Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RGeo Projected Buffer Polygon too small

I have a rails app that is using rgeo 0.3.19 with proj4 support connecting to a PostGIS 1.5 database with the rgeo-activerecord 0.4.5 gem.

My app has a model called Region which contains a geographic point, a radius, and a polygon shape. When a new region is about to save it uses the region's geofactory's buffer function to create a polygon using the radius and the geographic point.

Here is the geofactory that is being used for the region model

GEOFACTORY = RGeo::Geographic.projected_factory(:buffer_resolution => 8, :projection_proj4 => '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', :projection_srid => 3857)

The projection_srid I am using is that of Apple and Google maps mercator projection 3857. The problem is that the buffer that is being created is not the same size as the one I am drawing in either apple maps or google maps. For example, if I use the built in MapKit function MKCircle

[MKCircle circleWithCenterCoordinate:self.coordinate radius:50];

The circle will draw and overlay like this. iOS Drawing Radius

But if I take the coordinates that were created form the buffer function that make up the polygon shape in the database and plot them on google maps I get this.

GoogleMaps Radius

As you can see, the polygon that was created using the same projection system is smaller than it should be. This problem exponentially grows out of control based on the size of the radius defined. I have also tried to use the simple_mercator factory as defined in RGeo which yielded the same results.

Hopefully somebody has some insight into why, when a longitude,latitude projected point is buffered it creates an incorrectly sized polygon.

like image 299
MobileOverlord Avatar asked Nov 30 '12 19:11

MobileOverlord


1 Answers

What you're observing here is Mercator distortion. A distance of "50" in a mercator projection doesn't correspond to 50 meters on the real planet surface, unless you're at the Equator.

The circle drawn by your iOS map is correct: that's the 50 meter radius. What I suspect you did to create your second image was to project the point into a Mercator projection (according to the Proj4 you provided). Then you proceeded to create a buffer with radius 50 in the projected coordinate system. However, 50 Mercator units at latitude 40.61 corresponds to only about 37.96 meters in surface of the earth distance. So when you project that polygon back into latitude and longitude and plot it, that's what you see: a 38-meter circle.

One way to visualize this is to look at the full world map on Google Maps. Draw a circle of radius 50 pixels at the equator. And then draw another circle of radius 50 pixels over Greenland. On the map (in Mercator coordinates), those circles are the same size. But, if you know your Mercator projection, you know it distorts Greenland because Greenland is far away from the Equator, so your circle over Greenland is actually much smaller in real life than your circle above the equator. At 40 degrees latitude, the distortion isn't as severe, but it still is there.

If you want to correct this, it's pretty easy. The size distortion caused by the Mercator projection is proportional to the secant of the latitude. That is, 50 mercator units on the equator equals 50 meters, but 50 mercator units at latitude x (in radians), corresponds to 50 / sec(x) meters. So if you want a radius of 50 meters, multiply 50 by sec(latitude), and use that number as the radius in mercator coordinates. In RGeo-speak:

p_lonlat = GEOFACTORY.point(40.610355377197266, -75.38220214843749)
p_proj = p_lonlat.projection
buf_proj = p_proj.buffer(50.0 * (1 / Math.cos(p_lonlat.y / 180.0 * Math::PI)))
buf_lonlat = GEOFACTORY.unproject(buf_proj)
like image 127
Daniel Azuma Avatar answered Sep 28 '22 11:09

Daniel Azuma