RGeo投影缓冲区多边形太小

时间:2012-11-30 19:36:49

标签: ruby-on-rails gis postgis rgeo

我有一个使用rgeo 0.3.19的rails应用程序,proj4支持使用rgeo-activerecord 0.4.5 gem连接到PostGIS 1.5数据库。

我的应用程序有一个名为Region的模型,其中包含地理点,半径和多边形形状。当一个新区域即将保存时,它使用区域的geofactory的缓冲区函数来使用半径和地理点创建多边形。

以下是用于区域模型

的地理信息
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)

我使用的projection_srid是Apple和Google地图的mercator投影3857。 问题是正在创建的缓冲区与我在苹果地图或谷歌地图中绘制的缓冲区大小不同。例如,如果我使用内置的MapKit函数MKCircle

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

圆圈会像这样绘制和叠加。 iOS Drawing Radius

但是如果我从构成缓冲区函数的坐标中获取构成数据库中多边形的坐标并在谷歌地图上绘制它我得到了这个。

GoogleMaps Radius

如您所见,使用相同投影系统创建的多边形小于应有的多边形。该问题基于所定义的半径的大小呈指数地失控。我也尝试使用RGeo中定义的simple_mercator工厂,它产生了相同的结果。

希望有人能够深入了解为什么当经度,纬度投影点被缓冲时,会产生不正确大小的多边形。

1 个答案:

答案 0 :(得分:10)

你在这里观察的是墨卡托变形。除非你在赤道上,否则墨卡托投影中“50”的距离与真实行星表面上的50米不对应。

iOS地图绘制的圆圈是正确的:即50米半径。我怀疑你创建第二张图片的目的是将点投影到墨卡托投影(根据你提供的Proj4)。然后,您继续在投影坐标系中创建半径为50的缓冲区。然而,北纬40.61处的50个墨卡托单位对应于地球距离表面仅约37.96米。因此,当您将该多边形投影回纬度和经度并绘制它时,您所看到的就是:一个38米长的圆圈。

可视化的一种方法是在Google地图上查看完整的世界地图。在赤道上画一个半径为50像素的圆。然后在格陵兰岛上绘制另一个半径为50像素的圆圈。在地图上(在墨卡托坐标系中),这些圆圈的大小相同。但是,如果你知道你的墨卡托投影,你知道它会扭曲格陵兰岛,因为格陵兰岛远离赤道,所以你在格陵兰岛的圈子实际上比赤道上方的圈子小得多。在40度纬度,失真不是那么严重,但它仍然存在。

如果你想纠正这个问题,那就很容易了。墨卡托投影引起的尺寸失真与纬度的正割成正比。也就是说,赤道上的50个墨卡托单位等于50米,但纬度x(弧度)的50个墨卡托单位对应于50 /秒(x)米。因此,如果您想要半径为50米,则乘以50乘以秒(纬度),并将该数字用作墨卡托坐标中的半径。在RGeo中说:

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)