Преобразование long/lat в пиксель x / y на заданном изображении
У меня есть карта города Москвы. Мы изменили изображение Google Maps с некоторыми художественными элементами, но связь между координатами GPS и пикселями остается прежней.
: как преобразовать координаты GPS из различных точек данных, которые у нас есть, в пиксельные координаты на изображении?
В идеале я могу сделать это на Javascript, но PHP будет в порядке.
Я знаю, что на малых масштабах (например, на городских масштабах) это сделать просто достаточно (нужно узнать, какие географические координаты имеет один из углов изображения, затем узнать "цену" одного пикселя в географических координатах на изображении по осям OX и OY отдельно).
но на больших масштабах (в масштабе страны)" цена " одного пикселя не будет постоянной и будет достаточно сильно варьироваться, и описанный выше метод не может быть применен.
Как решить проблему в стране весы?
обновление:
Я не использую API Google Maps, у меня есть только: географические координаты объекта (они из google maps), у меня все еще есть на моем сайте простая картинка *. gif, в котором я должен нарисовать точку, соответствующую географическим координатам.
10 ответов
ключ ко всему этому-понимание прогнозы карте. Как указывали другие, причиной искажения является тот факт, что сферическая (или, точнее, эллипсоидальная) земля проецируется на плоскость.
чтобы достичь своей цели, вы сначала должны знать две вещи о своих данных:
- проекция вашей карты. Если они являются чисто производными от Google Maps, тогда, скорее всего, они используют сферическая проекция Меркатора.
- географическая система координат используются координаты широты / долготы. Это может варьироваться, потому что есть разные способы размещения широта/долгота на глобусе. Наиболее распространенным GCS, используемым в большинстве веб-картографических приложений и для GPS, является от WGS84.
Я предполагаю, что ваши данные в этих системах координат.
сферическая проекция Меркатора определяет координатную пару в метров, на поверхности Земли. Это означает, что для каждой координаты lat/long существует соответствующая координата метр/метр. Это позволяет выполнить преобразование, используя следующую процедуру:
- найдите WGS84 lat / long углов изображения.
- преобразовать WGS lat / longs в сферическую проекцию Меркатора. Там инструменты преобразования там, мой любимый, чтобы использовать инструмент cs2cs, который является частью проект виде proj4.
- вы можете безопасно выполнить простое линейное преобразование для преобразования между точками на изображении и точками на земле в сферической проекции Меркатора и обратно.
чтобы перейти от точки WGS84 к пикселю на изображении, процедура теперь:
- проект lat / lon для сферического Меркатора. Это можно сделать с помощью библиотека proj4js.
- преобразование сферической координаты Меркатора в пиксель изображения координата с использованием линейной зависимости, обнаруженной выше.
вы можете использовать библиотеку 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
вам придется реализовать проекцию 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));
}
}
источник:
Итак, вы хотите взять координаты широты/долготы и узнать координаты пикселей на вашем изображении этого местоположения?
основной класс GMap2 обеспечивает преобразование в / из пикселя на отображаемой карте и координаты lat / long:
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, тогда это даст вам правильную координату пикселя на этом изображении lat/long координировать.
вещи сложнее, если вы захватываете изображения плитки и сшивать их вместе себя, так как область полного набора плитки будет лежать за пределами области отображаемой карты.
в этом случае вам нужно будет использовать левое и верхнее значения верхней левой плитки изображения в качестве смещения от координат, которые дает fromLatLngToContainerPixel(latlng:GLatLng), вычитая левую координату из координаты x и верхнюю из координаты y. Так если верхнее левое изображение расположено на (-50, -122) (слева, вверху), и fromLatLngToContainerPixel () сообщает вам, что lat/long находится в координате пикселя (150, 320), затем на изображении, сшитом из плиток, истинное положение координаты находится в (150 - (-50), 320 - (-122)) то есть (200, 442).
также возможно, что аналогичная функция перевода координат GMap2:
GMap2.fromLatLngToDivPixel(latlng:GLatLng)
даст вам правильный перевод lat / long на пиксель для случая сшитых плиток - я не проверял это, и это не 100% ясно из документации по API.
Подробнее см. здесь: http://code.google.com/apis/maps/documentation/reference.html#GMap2.Methods.Coordinate-Transformations
вы можете посмотреть код, который использовался на gheat, Он портирован с js на python.
перевод вы обращаетесь с Проекция, именно так сферическая поверхность нашего мира переводится в 2-мерный рендеринг. Существует несколько способов (проекций) визуализации мира на 2-D поверхности.
Если ваши карты используют только определенную проекцию (Меркатор будучи популярным), вы должны иметь возможность найти уравнения, некоторый пример кода и / или некоторую библиотеку (например, одно решение Меркатора - преобразовать Lat / Longs к координатам X/Y. Если этого не сделать, я уверен, что вы можете найти другие образцы -https://stackoverflow.com/search?q=mercator. Если ваши изображения не являются картами, использующими проекцию Меркатора, вам нужно определить, какую проекцию он использует, чтобы найти правильные уравнения перевода.
Если вы пытаетесь поддерживать несколько проекций карт (вы хотите поддерживать много разных карт, которые используют разные проекции), то вы определенно хотите использовать библиотека как PROJ.4, но опять же я не уверен, что вы найдете для Javascript или PHP.
Если предполагается, что каждый пиксель имеет одинаковую площадь, то следующая статья о преобразовании расстояний в координаты долготы / широты может вам помочь:
http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/
вам нужны формулы для преобразования широты и долготы в прямоугольные координаты. Есть большое количество на выбор, и каждый будет искажать карту по-разному. Wolfram MathWorld имеет хорошую коллекцию:
http://mathworld.wolfram.com/MapProjection.html
следовать "см. Также".
одной из важных вещей, которые следует учитывать, является уровень "масштабирования" вашей проекции (в частности, для Google Maps).
Как объясняет 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
}
}
мой подход работает без библиотеки и с обрезанными картами. Означает, что он работает только с частями изображения Mercator. Может быть, это кому-то помогает:https://stackoverflow.com/a/10401734/730823
боролся с этим-у 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());