我有莫斯科的城市地图。我们使用一些艺术元素修改了Google地图图片,但GPS坐标和像素之间的关系保持不变。
问题:如何将GPS坐标从我们拥有的各种数据点转换为图像中的像素坐标?
理想情况下,我可以在Javascript中执行此操作,但PHP可以。
我知道在小尺度上(例如在城市尺度上)它可以做得足够简单(有必要了解哪个地理坐标有一个图像角落,然后在地理坐标中学习一个像素的“价格”)分别在OX和OY轴上的图片。
但是在大尺度(国家规模)上,一个像素的“价格”将不是一个常数,并且会变得足够强烈,并且上述方法无法应用。
如何在国家范围内解决问题?
更新
我不使用API谷歌地图,我只有:对象的地理坐标(它们来自谷歌地图),我仍然在我的网站上有一个简单的图片*。 gif,我必须在其中绘制一个相应的地理坐标点。
答案 0 :(得分:66)
所有这一切的关键是理解map projections。正如其他人所指出的那样,失真的原因是球形(或更准确地说是椭球形)地球被投射到一个平面上。
为了实现您的目标,您首先必须了解有关数据的两件事:
我假设你的数据在这些坐标系中。
球形墨卡托投影定义了一个以米为单位的坐标对,用于地球表面。这意味着,对于每个纬度/经度坐标,存在匹配的仪表/仪表坐标。这使您可以使用以下过程进行转换:
为了从WGS84点转到图像上的像素,现在的步骤是:
您可以像这样使用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
//p.x and p.y are now EPSG:3785 in meters
答案 1 :(得分:16)
您必须以您的语言实施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));
}
}
来源:
答案 2 :(得分:8)
您想要获取纬度/经度坐标并找出该位置图像上的像素坐标吗?
主GMap2类提供与显示地图上像素的转换和纬度/经度坐标:
Gmap2.fromLatLngToContainerPixel(latlng)
例如:
var gmap2 = new GMap2(document.getElementById("map_canvas"));
var geocoder = new GClientGeocoder();
geocoder.getLatLng( "1600 Pennsylvania Avenue NW Washington, D.C. 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地图显示的屏幕抓取,那么这将为您提供纬度/经度坐标图像上的正确像素坐标。
如果您正在抓取平铺图像并将它们自己拼接在一起,那么事情会变得更加棘手,因为完整平铺集的区域将位于所显示地图的区域之外。
在这种情况下,您需要使用左上角图像拼贴的左侧和顶部值作为fromLatLngToContainerPixel(latlng:GLatLng)给出的坐标的偏移量,从x坐标减去左侧坐标,从y坐标顶部。因此,如果左上角的图像位于(-50,-122)(左侧,顶部),而且fromLatLngToContainerPixel()告诉您lat / long位于像素坐标(150,320),则将图像拼接在一起瓷砖,坐标的真实位置是(150 - ( - 50),320 - ( - 122)),即(200,442)。
也可能有类似的GMap2坐标转换功能:
GMap2.fromLatLngToDivPixel(latlng:GLatLng)
将为拼接方案例提供正确的纬度/长到像素转换 - 我没有对此进行过测试,也没有从API文档中100%清楚。
请点击此处了解更多信息: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations
答案 3 :(得分:7)
您可以查看code that used on gheat,它从js移植到python。
答案 4 :(得分:5)
您要解决的翻译与Map Projection有关,这就是我们世界的球面被翻译成二维渲染的方式。有两种方式(投影)可以在二维表面上渲染世界。
如果你的地图只使用特定的投影(Mercator很受欢迎),你应该能够找到方程,一些示例代码和/或一些库(例如一个墨卡托解决方案 - {{3}如果不这样做,我相信你可以找到其他样本 - Convert Lat/Longs to X/Y Co-ordinates。如果你的图像不是使用墨卡托投影的地图,你需要确定它是什么投影确实用来找到正确的平移方程式。
如果您尝试支持多个地图投影(您希望支持使用不同投影的许多不同地图),那么您肯定希望使用像https://stackoverflow.com/search?q=mercator这样的库,但我又不知道你是什么找到Javascript或PHP。
答案 5 :(得分:3)
如果假设每个像素属于同一区域,那么以下关于将距离转换为经度/纬度坐标的文章可能对您有所帮助:
http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/
答案 6 :(得分:3)
您需要公式将纬度和经度转换为直角坐标。有很多可供选择,每个都会以不同的方式扭曲地图。 Wolfram MathWorld有一个很好的收藏:
http://mathworld.wolfram.com/MapProjection.html
按照“另请参阅”链接。
答案 7 :(得分:3)
需要考虑的重要事项之一是投影的“缩放”级别(特别针对Google地图)。
正如Google解释的那样:
在缩放级别1,地图由4个256x256像素的图块组成 在512x512的像素空间中。在缩放级别19,每个x和y像素打开 可以使用0到256 * 2 ^ 19
之间的值来引用地图
(见https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates)
要考虑“缩放”值,我建议使用下面简单有效的 deltaLonPerDeltaX 和 deltaLatPerDeltaY 功能。虽然x像素和经度严格成比例,但对于y像素和纬度不是这种情况,公式需要初始纬度。
// Adapted from : http://blog.cppse.nl/x-y-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
}
}
答案 8 :(得分:2)
我的方法在没有库和裁剪地图的情况下工作。意味着它只适用于墨卡托图像中的部分。也许它有助于某人:https://stackoverflow.com/a/10401734/730823
答案 9 :(得分:0)
挣扎于此 - 同时拥有openstreetmap和google街道地图,并希望投影外部图片
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: "http://cdn1.iconfinder.com/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());