Javascript 在给定图片上将 long/lat 转换为像素 x/y
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2651099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Convert long/lat to pixel x/y on a given picture
提问by Kalinin
I have a city map of Moscow. We modified a Google Maps image with some artistic elements, but the relation between GPS coordinates and pixels remains the same.
我有一张莫斯科的城市地图。我们用一些艺术元素修改了一张谷歌地图图像,但 GPS 坐标和像素之间的关系保持不变。
Problem:How do I convert GPS coordinates from various data points we have into pixel coordinates in the image?
问题:如何将 GPS 坐标从我们拥有的各种数据点转换为图像中的像素坐标?
Ideally I can do this in Javascript, but PHP would be OK.
理想情况下,我可以在 Javascript 中执行此操作,但是 PHP 就可以了。
I know that on small scales (for example on city scales) it to make simply enough (it is necessary to learn what geographic coordinates has one of picture corners, then to learn "price" of one pixel in geographic coordinates on a picture on axes OX and OY separately).
我知道在小尺度上(例如在城市尺度上)它可以制作得足够简单(有必要了解哪些地理坐标具有图片角之一,然后在轴上的图片上了解地理坐标中一个像素的“价格” OX 和 OY 分开)。
But on the big scales (country scale) "price" of one pixel will be not a constant, and will vary strongly enough and the method described above cannot be applied.
但是在大尺度(国家尺度)上,一个像素的“价格”将不是一个常数,并且变化很大,上述方法无法应用。
How to solve a problem on country scales?
如何解决国家层面的问题?
Update:
更新:
I do not use API Google Maps, I have only: geographic coordinates of the object (they are from google maps), I still have at my site a simple picture *. gif, in which I must draw a point corresponding geographic coordinates.
我不使用API Google Maps,我只有:对象的地理坐标(它们来自谷歌地图),我的网站上还有一张简单的图片*。gif,我必须在其中绘制一个点对应的地理坐标。
回答by fmark
The key to all of this is understanding map projections. As others have pointed out, the cause of the distortion is the fact that the spherical (or more accurately ellipsoidal) earth is projected onto a plane.
所有这一切的关键是理解地图投影。正如其他人指出的那样,失真的原因是球形(或更准确地说是椭圆体)地球被投影到一个平面上。
In order to achieve your goal, you first must know two things about your data:
为了实现您的目标,您首先必须了解有关数据的两件事:
- The projection your maps are in. If they are purely derived from Google Maps, then chances are they are using a spherical Mercator projection.
- The geographic coordinate system your latitude/longitude coordinates are using. This can vary, because there are different ways of locating lat/longs on the globe. The most common GCS, used in most web-mapping applications and for GPS's, is WGS84.
- 您的地图所在的投影。如果它们纯粹来自Google 地图,那么它们很可能使用的是球形墨卡托投影。
- 您的纬度/经度坐标使用的地理坐标系。这可能会有所不同,因为在地球上定位经纬度有不同的方法。最常见的 GCS 是WGS84,用于大多数网络地图应用程序和 GPS 。
I'm assuming your data is in these coordinate systems.
我假设您的数据在这些坐标系中。
The spherical Mercator projection defines a coordinate pair in meters, for the surface of the earth. This means, for every lat/long coordinate there is a matching meter/meter coordinate. This enables you to do the conversion using the following procedure:
球形墨卡托投影为地球表面定义了一个以米为单位的坐标对。这意味着,对于每个纬度/经度坐标,都有一个匹配的米/米坐标。这使您可以使用以下过程进行转换:
- Find the WGS84 lat/long of the corners of the image.
- Convert the WGS lat/longs to the spherical Mercator projection. There conversion tools out there, my favorite is to use the cs2cs tool that is part of the PROJ4 project.
- You can safely do a simple linear transform to convert between points on the image, and points on the earth in the spherical Mercator projection, and back again.
- 找到图像角的 WGS84 纬度/经度。
- 将 WGS 纬度/经度转换为球形墨卡托投影。那里有转换工具,我最喜欢的是使用PROJ4 项目中的 cs2cs 工具。
- 您可以安全地进行简单的线性变换,以在图像上的点与球形墨卡托投影中地球上的点之间进行转换,然后再返回。
In order to go from a WGS84 point to a pixel on the image, the procedure is now:
为了从 WGS84 点到图像上的像素,现在的过程是:
- Project lat/lon to spherical Mercator. This can be done using the proj4js library.
- Transform spherical Mercator coordinate into image pixel coordinate using the linear relationship discovered above.
You can use the proj4js library like this:
您可以像这样使用 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
回答by Jader Dias
You will have to implement the Google Maps API projection in your language. I have the C# source code for this:
您必须以您的语言实现 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));
}
}
Source:
来源:
回答by Alan Donnelly
So you want to take latitude/longitude coordinates and find out the pixel coordinates on your image of that location?
因此,您想获取纬度/经度坐标并找出该位置图像上的像素坐标吗?
The main GMap2 class provides transformation to/from a pixel on the displayed map and a lat/long coordinate:
主要的 GMap2 类提供到/从显示地图上的像素和纬度/经度坐标的转换:
Gmap2.fromLatLngToContainerPixel(latlng)
For example:
例如:
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." );
}
);
So assuming that your map image is a screen grab of the Google Map display, then this will give you the correct pixel coordinate on that image of a lat/long coordinate.
因此,假设您的地图图像是 Google 地图显示的屏幕抓取,那么这将为您提供经纬度坐标图像上的正确像素坐标。
Things are trickier if you're grabbing tile images and stitching them together yourself since the area of the complete tile set will lie outside the area of the displayed map.
如果您自己抓取图块图像并将它们拼接在一起,事情就会变得棘手,因为完整图块集的区域将位于显示地图的区域之外。
In this case, you'll need to use the left and top values of the top-left image tile as an offset from the coordinates that fromLatLngToContainerPixel(latlng:GLatLng) gives you, subtracting the left coordinate from the x coordinate and top from the y coordinate. So if the top-left image is positioned at (-50, -122) (left, top), and fromLatLngToContainerPixel() tells you a lat/long is at pixel coordinate (150, 320), then on the image stitched together from tiles, the true position of the coordinate is at (150 - (-50), 320 - (-122)) which is (200, 442).
在这种情况下,您需要使用左上角图像图块的左值和上值作为 fromLatLngToContainerPixel(latlng:GLatLng) 为您提供的坐标的偏移量,从 x 坐标中减去左坐标,从y 坐标。因此,如果左上角的图像位于 (-50, -122) (左,上),并且 fromLatLngToContainerPixel() 告诉您纬度/经度位于像素坐标 (150, 320),那么在从瓷砖,坐标的真实位置在 (150 - (-50), 320 - (-122)) 即 (200, 442)。
It's also possible that a similar GMap2 coordinate translation function:
也有可能是类似的 GMap2 坐标平移函数:
GMap2.fromLatLngToDivPixel(latlng:GLatLng)
will give you the correct lat/long to pixel translation for the stitched-tiles case - I've not tested this, nor is it 100% clear from the API docs.
将为您提供拼接瓷砖案例的正确纬度/经度到像素的转换 - 我没有测试过这个,也不是 100% 从 API 文档中清楚。
See here for more: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations
有关更多信息,请参见此处:http: //code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations
回答by Ahmet AYGüN
You may have a look at code that used on gheat, it's ported from js to python.
您可能会查看在gheat 上使用的代码,它是从 js 移植到 python 的。
回答by Bert F
The translation you are addressing has to do with Map Projection, which is how the spherical surface of our world is translated into a 2 dimensional rendering. There are multiple ways (projections) to render the world on a 2-D surface.
您要解决的转换与Map Projection 相关,这就是我们世界的球面如何转换为二维渲染的方式。有多种方式(投影)可以在二维表面上渲染世界。
If your maps are using just a specific projection (Mercatorbeing popular), you should be able to find the equations, some sample code, and/or some library (e.g. one Mercator solution - Convert Lat/Longs to X/Y Co-ordinates. If that doesn't do it, I'm sure you can find other samples - https://stackoverflow.com/search?q=mercator. If your images aren't map(s) using a Mercator projection, you'll need to determine what projection it does use to find the right translation equations.
如果您的地图仅使用特定投影(墨卡托很受欢迎),您应该能够找到方程、一些示例代码和/或一些库(例如,墨卡托解决方案 -将纬度/经度转换为 X/Y 坐标. 如果那不行,我相信你可以找到其他示例 - https://stackoverflow.com/search?q=mercator。如果你的图像不是使用墨卡托投影的地图,你需要确定它使用什么投影来找到正确的平移方程。
If you are trying to support multiple map projections (you want to support many different maps that use different projections), then you definitely want to use a library like PROJ.4, but again I'm not sure what you'll find for Javascript or PHP.
如果您尝试支持多个地图投影(您想支持许多使用不同投影的不同地图),那么您肯定希望使用像PROJ.4这样的库,但我不确定您会为 Javascript 找到什么或 PHP。
回答by zaf
If each pixel is assumed to be of the same area then the following article about converting distances to longitude/latitude co-ordinates may be of some help to you:
如果假设每个像素都属于同一区域,那么以下有关将距离转换为经度/纬度坐标的文章可能对您有所帮助:
http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/
http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/
回答by Amos Newcombe
You need formulas to convert latitude and longitude to rectangular coordinates. There are a great number to choose from and each will distort the map in a different way. Wolfram MathWorld has a good collection:
您需要将纬度和经度转换为直角坐标的公式。有很多可供选择的数字,每个都会以不同的方式扭曲地图。Wolfram MathWorld 有一个很好的收藏:
http://mathworld.wolfram.com/MapProjection.html
http://mathworld.wolfram.com/MapProjection.html
Follow the "See Also" links.
按照“另请参阅”链接。
回答by Louis LC
One of the important things to take into account is the "zoom" level of your projection (for Google Maps in particular).
要考虑的重要事项之一是投影的“缩放”级别(特别是对于 Google 地图)。
As Google explains it:
正如谷歌解释的那样:
At zoom level 1, the map consists of 4 256x256 pixels tiles, resulting in a pixel space from 512x512. At zoom level 19, each x and y pixel on the map can be referenced using a value between 0 and 256 * 2^19
在缩放级别 1 时,地图由 4 个 256x256 像素的图块组成,从而产生一个 512x512 的像素空间。在缩放级别 19 时,可以使用 0 到 256 * 2^19 之间的值来引用地图上的每个 x 和 y 像素
( See https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates)
(见https://developers.google.com/maps/documentation/javascript/maptypes?hl=en#MapCoordinates)
To factor in the "zoom" value, I recommend the simple and effective deltaLonPerDeltaXand deltaLatPerDeltaYfunctions below. While x-pixels and longitudes are strictly proportional, this is not the case for y-pixels and latitudes, for which the formula requires the initial latitude.
为了考虑“缩放”值,我建议使用下面简单有效的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
}
}
回答by Raphael
my approach works without a library and with cropped maps. Means it works with just parts from a Mercator image. Maybe it helps somebody: https://stackoverflow.com/a/10401734/730823
我的方法在没有图书馆和裁剪地图的情况下工作。意味着它只适用于墨卡托图像的部分。也许它对某人有帮助:https: //stackoverflow.com/a/10401734/730823
回答by Alex Punnen
Struggled with this - Have both openstreet map and google street map and wanted to project an external graphic image
苦苦挣扎 - 同时拥有 openstreet map 和 google street map 并想投影一个外部图形图像
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());

