モスクワの市内地図があります。 Googleマップの画像をいくつかの芸術的要素で修正しましたが、GPS座標とピクセルの関係は変わりません。
問題: GPS座標をさまざまなデータポイントから画像のピクセル座標に変換するにはどうすればよいですか?
理想的には、Javascriptでこれを行うことができますが、PHPは問題ありません。
私は、小さなスケール(都市スケールなど)で十分に簡単にできることを知っています(地理座標に画像コーナーの1つがあることを学習し、次に軸上の画像の地理座標の1ピクセルの「価格」を学習する必要があります) OXとOYは別々に)。
しかし、大きなスケール(国のスケール)では、1ピクセルの「価格」は一定ではなく、十分に大きく変化するため、上記の方法は適用できません。
国の規模で問題を解決するには?
更新:
私はAPI Google Mapsを使用せず、オブジェクトの地理座標(Googleマップからのもの)のみを所有しています。サイトにはまだ簡単な写真があります*。 gif。地理座標に対応するポイントを描画する必要があります。
このすべての鍵は、 地図投影法 を理解することです。他の人が指摘したように、歪みの原因は、球体(またはより正確には楕円体)の地球が平面に投影されるという事実です。
目標を達成するには、まずデータに関する2つのことを知っておく必要があります。
あなたのデータはこれらの座標系にあると仮定しています。
球体メルカトル図法は、地表のメートル単位の座標ペアを定義します。つまり、すべての緯度/経度座標に対して、一致するメーター/メーター座標が存在します。これにより、次の手順を使用して変換を行うことができます。
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
ご使用の言語で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クラスは、表示されたマップ上のピクセルと緯度/経度座標間の変換を提供します。
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)(left、top)に配置され、fromLatLngToContainerPixel()が緯度/経度がピクセル座標(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
gheatで使用されているコード をご覧になるかもしれません。jsからpythonに移植されています。
対処する翻訳は、 Map Projection に関係しています。これは、世界の球面を2次元レンダリングに変換する方法です。 2Dサーフェスに世界をレンダリングするには、複数の方法(投影)があります。
マップが特定の投影法のみを使用している場合( Mercator 人気があります)、方程式、サンプルコード、および/またはライブラリを見つけることができるはずです(たとえば、1つのメルカトールソリューション- 緯度/経度をX/Y座標に変換 。うまくいかない場合は、他のサンプルを見つけることができると確信しています- https://stackoverflow.com/search?q=mercator 。画像がメルカトル図法を使用してマップされていない場合、正しい変換式を見つけるために使用する投影法を決定する必要があります。
複数のマップ投影をサポートしようとしている場合(異なる投影を使用する多くの異なるマップをサポートする場合)、 PROJ.4 のようなライブラリを使用することは間違いありませんが、やはりわかりませんJavascriptまたはPHPで見つかるもの。
緯度と経度を直交座標に変換するには、数式が必要です。多数の選択肢があり、それぞれ異なる方法で地図を歪めます。 Wolfram MathWorldには良いコレクションがあります:
http://mathworld.wolfram.com/MapProjection.html
「関連項目」リンクに従ってください。
各ピクセルが同じエリアであると想定される場合、距離を経度/緯度座標に変換することに関する次の記事が役立つ場合があります。
http://www.johndcook.com/blog/2009/04/27/converting-miles-to-degrees-longitude-or-latitude/
考慮すべき重要なことの1つは、投影の「ズーム」レベルです(特に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
}
}
私のアプローチは、ライブラリなしでトリミングされたマップで機能します。メルカトル図の一部だけで機能することを意味します。多分それは誰かを助ける: https://stackoverflow.com/a/10401734/73082
これに苦労-オープンストリートマップとグーグルストリートマップの両方を持っており、外部のグラフィック画像を投影したかった
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());