在给定图片上将long / lat转换为像素x / y

我有一张莫斯科的城市地图。 我们用一些艺术元素修改了Google Maps图像,但是GPS坐标和像素之间的关系保持不变。

问题:如何将GPS坐标从我们拥有的各种数据点转换成图像中的像素坐标?

理想情况下,我可以在Javascript中做到这一点,但PHP会好的。


我知道,在小尺度上(例如在城市尺度上),它足够简单了(需要了解哪个地理坐标有一个图片边angular,然后学习坐标轴上的一个图片上的一个像素的“价格” OX和OY)。

但在大规模(国家规模)中,一个像素的“价格”将不是一个常数,而且会有很大的变化,上述方法不能适用。

如何解决国家尺度上的问题?


更新:

我不使用API​​谷歌地图,我只有:对象的地理坐标(他们是从谷歌地图),我仍然在我的网站有一个简单的图片*。 gif,其中我必须绘制一个相应的地理坐标点。

所有这一切的关键是了解地图投影 。 正如其他人已经指出的,失真的原因是球面(或更精确的椭球体)地球投射到一个平面的事实。

为了实现你的目标,你首先必须知道你的数据的两件事情:

  1. 您的地图所在的投影。如果它们纯粹是从Google地图派生的,那么他们很可能会使用球形的墨卡托投影 。
  2. 您的纬度/经度坐标正在使用的地理坐标系。 这可能会有所不同,因为在地球上定位经纬度有不同的方法。 WGS84是用于大多数networking地图应用和GPS的最常用的GCS。

我假设你的数据在这些坐标系中。

球形墨卡托投影定义了一个以米为单位的地球表面的坐标对。 这意味着,对于每个纬度/经度坐标都有一个匹配的米/米坐标。 这使您可以使用以下过程进行转换:

  1. find图像angular落的WGS84 lat / long。
  2. 将WGS lat / longs转换为球形墨卡托投影。 那里有转换工具,我最喜欢的是使用PROJ4项目中的cs2cs工具。
  3. 您可以安全地进行简单的线性变换,以在图像上的点之间转换,并在球面墨卡托投影中的地球上点,然后再返回。

为了从WGS84点到图像上的像素,现在的程序是:

  1. 项目经纬度到球形墨卡托。 这可以使用proj4js库来完成。
  2. 使用上面发现的线性关系将球形墨卡托坐标变换为图像像素坐标。

你可以像这样使用proj4js库:

// include the library <script src="lib/proj4js-combined.js"></script> //adjust the path for your server //or else use the compressed version // creating source and destination Proj4js objects // once initialized, these may be re-used as often as needed var source = new Proj4js.Proj('EPSG:4326'); //source coordinates will be in Longitude/Latitude, WGS84 var dest = new Proj4js.Proj('EPSG:3785'); //destination coordinates in meters, global spherical mercators projection, see http://spatialreference.org/ref/epsg/3785/ // transforming point coordinates var p = new Proj4js.Point(-76.0,45.0); //any object will do as long as it has 'x' and 'y' properties Proj4js.transform(source, dest, p); //do the transformation. x and y are modified in place //px and py are now EPSG:3785 in meters 

您将不得不以您的语言实施Google Maps API投影。 我有这个C#源代码:

 public class GoogleMapsAPIProjection { private readonly double PixelTileSize = 256d; private readonly double DegreesToRadiansRatio = 180d / Math.PI; private readonly double RadiansToDegreesRatio = Math.PI / 180d; private readonly PointF PixelGlobeCenter; private readonly double XPixelsToDegreesRatio; private readonly double YPixelsToRadiansRatio; public GoogleMapsAPIProjection(double zoomLevel) { var pixelGlobeSize = this.PixelTileSize * Math.Pow(2d, zoomLevel); this.XPixelsToDegreesRatio = pixelGlobeSize / 360d; this.YPixelsToRadiansRatio = pixelGlobeSize / (2d * Math.PI); var halfPixelGlobeSize = Convert.ToSingle(pixelGlobeSize / 2d); this.PixelGlobeCenter = new PointF( halfPixelGlobeSize, halfPixelGlobeSize); } public PointF FromCoordinatesToPixel(PointF coordinates) { var x = Math.Round(this.PixelGlobeCenter.X + (coordinates.X * this.XPixelsToDegreesRatio)); var f = Math.Min( Math.Max( Math.Sin(coordinates.Y * RadiansToDegreesRatio), -0.9999d), 0.9999d); var y = Math.Round(this.PixelGlobeCenter.Y + .5d * Math.Log((1d + f) / (1d - f)) * -this.YPixelsToRadiansRatio); return new PointF(Convert.ToSingle(x), Convert.ToSingle(y)); } public PointF FromPixelToCoordinates(PointF pixel) { var longitude = (pixel.X - this.PixelGlobeCenter.X) / this.XPixelsToDegreesRatio; var latitude = (2 * Math.Atan(Math.Exp( (pixel.Y - this.PixelGlobeCenter.Y) / -this.YPixelsToRadiansRatio)) - Math.PI / 2) * DegreesToRadiansRatio; return new PointF( Convert.ToSingle(latitude), Convert.ToSingle(longitude)); } } 

资源:

http://code.google.com/p/geographical-dot-net/source/browse/trunk/GeographicalDotNet/GeographicalDotNet/Projection/GoogleMapsAPIProjection.cs

那么你想获取纬度/经度坐标并找出你的图像上的像素坐标?

主GMap2类提供了在显示的地图上的一个像素和一个纬度/经度坐标之间的转换:

 Gmap2.fromLatLngToContainerPixel(latlng) 

例如:

 var gmap2 = new GMap2(document.getElementById("map_canvas")); var geocoder = new GClientGeocoder(); geocoder.getLatLng( "1600 Pennsylvania Avenue NW Washington, DC 20500", function( latlng ) { var pixel_coords = gmap2.fromLatLngToContainerPixel(latlng); window.alert( "The White House is at pixel coordinates (" + pixel_coodrs.x + ", " + pixel_coords.y + ") on the " + "map image shown on this page." ); } ); 

因此,假设您的地图图像是Google Map显示屏幕的屏幕抓图,那么这将会为您提供经纬度坐标图像上的正确的像素坐标。

如果您抓取平铺图像并将它们拼接在一起,事情就会变得复杂起来,因为完整平铺集的区域将位于显示的地图区域之外。

在这种情况下,您需要使用左上angular的图像拼贴的左侧和顶部的值作为fromLatLngToContainerPixel(latlng:GLatLng)所给出的坐标的偏移量,从x坐标减去左侧坐标,从y坐标。 因此,如果左上angular的图像位于(-50,-122)(左,顶部),并且fromLatLngToContainerPixel()告诉您纬度/长度位于像素坐标(150,320)处,则将图像拼接在一起(150,(-50),320 – ( – 122)),这是(200,442)。

GMap2坐标转换函数也有可能是这样的:

 GMap2.fromLatLngToDivPixel(latlng:GLatLng) 

会给你正确的经纬度像素翻译拼接瓷砖的情况下 – 我没有testing过这个,也不是从API文档100%清楚。

详情请参阅: http : //code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations

你可以看看在gheat上使用的代码 ,它从js移植到python。

您正在处理的翻译与地图投影有关 ,这就是我们世界的球面如何被翻译成二维渲染。 在二维表面上渲染世界有多种方式(投影)。

如果你的地图只使用一个特定的投影( 墨卡托stream行),你应该能够find方程,一些示例代码和/或一些库(例如一个墨卡托投影解决scheme – 将纬/长转换为X / Y坐标 。如果这样做没有,我相信你可以find其他样本 – https://stackoverflow.com/search?q=mercator 。如果你的图像不是使用墨卡托投影的地图,我们需要确定它使用什么投影来find正确的平移方程。

如果你试图支持多个地图投影(你想支持使用不同投影的许多不同的地图),那么你一定要使用像PROJ.4这样的库,但我不知道你会发现什么样的Javascript或PHP。

您需要公式将经度和纬度转换为直angular坐标。 有很多select,每个都会以不同的方式扭曲地图。 Wolfram MathWorld有一个很好的集合:

http://mathworld.wolfram.com/MapProjection.html

按照“另请参阅”链接。

如果每个像素被认为是相同的区域,那么以下关于将距离转换成经度/纬度坐标的文章可能对您有所帮助:

http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/

要考虑的重要事项之一是投影的“缩放”级别(特别是针对Google地图)。

正如Google解释的那样:

在缩放级别1时,地图由4个256×256像素的图块组成,从而产生512×512的像素空间。 在缩放级别19时,地图上的每个x和y像素都可以使用介于0和256 * 2 ^ 19之间的值进行引用

(请参阅https://developers.google.com/maps/documentation/javascript/maptypes?hl=zh-CN#MapCoordinates

为了考虑“缩放”值,我推荐下面简单有效的deltaLonPerDeltaXdeltaLatPerDeltaY函数。 尽pipex像素和经度严格地成比例,但y像素和纬度不是这种情况,对于该像素和公式要求初始的纬度。

 // Adapted from : http://blog.cppse.nl/xy-to-lat-lon-for-google-maps window.geo = { glOffset: Math.pow(2,28), //268435456, glRadius: Math.pow(2,28) / Math.PI, a: Math.pow(2,28), b: 85445659.4471, c: 0.017453292519943, d: 0.0000006705522537, e: Math.E, //2.7182818284590452353602875, p: Math.PI / 180, lonToX: function(lon) { return Math.round(this.glOffset + this.glRadius * lon * this.p); }, XtoLon: function(x) { return -180 + this.d * x; }, latToY: function(lat) { return Math.round(this.glOffset - this.glRadius * Math.log((1 + Math.sin(lat * this.p)) / (1 - Math.sin(lat * this.p))) / 2); }, YtoLat: function(y) { return Math.asin(Math.pow(this.e,(2*this.a/this.b - 2*y/this.b)) / (Math.pow(this.e, (2*this.a/this.b - 2*y/this.b))+1) - 1/(Math.pow(this.e, (2*this.a/this.b - 2*y/this.b))+1) ) / this.c; }, deltaLonPerDeltaX: function(deltaX, zoom) { // 2^(7+zoom) pixels <---> 180 degrees return deltaX * 180 / Math.pow(2, 7+zoom); }, deltaLatPerDeltaY: function(deltaY, zoom, startLat) { // more complex because of the curvature, we calculte it by difference var startY = this.latToY(startLat), endY = startY + deltaY * Math.pow(2, 28-7-zoom), endLat = this.YtoLat(endY); return ( endLat - startLat ); // = deltaLat } } 

我的方法没有图书馆和裁剪地图。 意味着它只与来自墨卡托图像的部分一起工作。 也许它有助于某人: https : //stackoverflow.com/a/10401734/730823

与此同时奋斗 – 拥有露天地图和谷歌街道地图,并希望投影外部graphics图像

 var map = new OpenLayers.Map({ div:"map-id", allOverlays: true }); var osm = new OpenLayers.Layer.OSM("OpenStreeMao"); var gmap = new OpenLayers.Layer.Google("Google Streets", {visibility: false}); map.addLayers([osm,gmap]); var vectorLayer = new OpenLayers.Layer.Vector("IconLayer"); var lonlatObject = new OpenLayers.LonLat(24.938622,60.170421).transform( new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject() ); console.log(lonlatObject); var point = new OpenLayers.Geometry.Point(lonlatObject.lon, lonlatObject.lat); console.log(point); var point2 = new OpenLayers.Geometry.Point(lonlatObject.x, lonlatObject.y); console.log(point2); var feature = new OpenLayers.Feature.Vector(point, null, { externalGraphic: "data/icons/SUPERVISTA/networking/png/72/antenna.png", graphicWidth: 72, graphicHeight: 72, fillOpacity: 1 }); vectorLayer.addFeatures(feature); map.addLayer(vectorLayer); map.setCenter( new OpenLayers.LonLat(24.938622,60.170421).transform( new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject() ), 12); map.addControl(new OpenLayers.Control.LayerSwitcher()); 

http://jsfiddle.net/alexcpn/N9dLN/8/