如何获取已返回的谷歌静态地图的范围,例如,跟随请求
http://maps.googleapis.com/maps/api/staticmap?center=0.0,0.0&zoom=10&size=640x640&sensor=false
据我所知,完整的地球地图是256x256图像。这意味着n个垂直像素包含x度,但n个水平像素包含2x度。正确?
谷歌says center定义地图的中心,与地图的所有边缘等距。据我所知,像素(或度数?)的等距离。并且每个后续缩放级别使水平和垂直尺寸的精度都加倍。 因此,我可以为每个缩放值找到地图经度的delta值:
dLongitude = (HorizontalMapSizeInPixels / 256 ) * ( 360 / pow(2, zoom) );
纬度的相同计算:
dLatitude = (VerticalMapSizeInPixels / 256 ) * ( 180 / pow(2, zoom) );
VerticalMapSizeInPixels和HorizontalMapSizeInPixels是URL中地图大小的参数。
计算经度的delta值是好的,但对于Latitude它是错误的。我找不到Latitude的delta值,有一些 delta 错误。
答案 0 :(得分:33)
据我所知,完整的地球地图是256x256图像。
是
这意味着n个垂直像素包含x度,但是n个水平 像素包含2x度。正确?
没有。一个像素将表示取决于纬度的不同纬度量。赤道上的一个像素表示纬度低于极点附近的一个像素。
地图的角落将取决于中心,缩放级别和地图大小,您需要使用墨卡托投影来计算它们。 如果您不想加载完整的API,那么这是一个MercatorProjection对象:
var MERCATOR_RANGE = 256;
function bound(value, opt_min, opt_max) {
if (opt_min != null) value = Math.max(value, opt_min);
if (opt_max != null) value = Math.min(value, opt_max);
return value;
}
function degreesToRadians(deg) {
return deg * (Math.PI / 180);
}
function radiansToDegrees(rad) {
return rad / (Math.PI / 180);
}
function MercatorProjection() {
this.pixelOrigin_ = new google.maps.Point( MERCATOR_RANGE / 2, MERCATOR_RANGE / 2);
this.pixelsPerLonDegree_ = MERCATOR_RANGE / 360;
this.pixelsPerLonRadian_ = MERCATOR_RANGE / (2 * Math.PI);
};
MercatorProjection.prototype.fromLatLngToPoint = function(latLng, opt_point) {
var me = this;
var point = opt_point || new google.maps.Point(0, 0);
var origin = me.pixelOrigin_;
point.x = origin.x + latLng.lng() * me.pixelsPerLonDegree_;
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
// 89.189. This is about a third of a tile past the edge of the world tile.
var siny = bound(Math.sin(degreesToRadians(latLng.lat())), -0.9999, 0.9999);
point.y = origin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) * -me.pixelsPerLonRadian_;
return point;
};
MercatorProjection.prototype.fromPointToLatLng = function(point) {
var me = this;
var origin = me.pixelOrigin_;
var lng = (point.x - origin.x) / me.pixelsPerLonDegree_;
var latRadians = (point.y - origin.y) / -me.pixelsPerLonRadian_;
var lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2);
return new google.maps.LatLng(lat, lng);
};
//pixelCoordinate = worldCoordinate * Math.pow(2,zoomLevel)
您可以将其保存到单独的文件中,例如“MercatorProjection.js”,然后将其包含在您的应用程序中。
<script src="MercatorProjection.js"></script>
加载上述文件后,以下函数计算给定大小和给定缩放的地图的SW和NE角。
function getCorners(center,zoom,mapWidth,mapHeight){
var scale = Math.pow(2,zoom);
var centerPx = proj.fromLatLngToPoint(center);
var SWPoint = {x: (centerPx.x -(mapWidth/2)/ scale) , y: (centerPx.y + (mapHeight/2)/ scale)};
var SWLatLon = proj.fromPointToLatLng(SWPoint);
alert('SW: ' + SWLatLon);
var NEPoint = {x: (centerPx.x +(mapWidth/2)/ scale) , y: (centerPx.y - (mapHeight/2)/ scale)};
var NELatLon = proj.fromPointToLatLng(NEPoint);
alert(' NE: '+ NELatLon);
}
你会这样称呼它:
var proj = new MercatorProjection();
var G = google.maps;
var centerPoint = new G.LatLng(49.141404, -121.960988);
var zoom = 10;
getCorners(centerPoint,zoom,640,640);
答案 1 :(得分:15)
感谢Marcelo的回答。这非常有帮助。万一有人会感兴趣,这里是代码的Python版本(PHP代码的粗略翻译,可能不像pythonic那样):
from __future__ import division
import math
MERCATOR_RANGE = 256
def bound(value, opt_min, opt_max):
if (opt_min != None):
value = max(value, opt_min)
if (opt_max != None):
value = min(value, opt_max)
return value
def degreesToRadians(deg) :
return deg * (math.pi / 180)
def radiansToDegrees(rad) :
return rad / (math.pi / 180)
class G_Point :
def __init__(self,x=0, y=0):
self.x = x
self.y = y
class G_LatLng :
def __init__(self,lt, ln):
self.lat = lt
self.lng = ln
class MercatorProjection :
def __init__(self) :
self.pixelOrigin_ = G_Point( MERCATOR_RANGE / 2, MERCATOR_RANGE / 2)
self.pixelsPerLonDegree_ = MERCATOR_RANGE / 360
self.pixelsPerLonRadian_ = MERCATOR_RANGE / (2 * math.pi)
def fromLatLngToPoint(self, latLng, opt_point=None) :
point = opt_point if opt_point is not None else G_Point(0,0)
origin = self.pixelOrigin_
point.x = origin.x + latLng.lng * self.pixelsPerLonDegree_
# NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
# 89.189. This is about a third of a tile past the edge of the world tile.
siny = bound(math.sin(degreesToRadians(latLng.lat)), -0.9999, 0.9999)
point.y = origin.y + 0.5 * math.log((1 + siny) / (1 - siny)) * - self.pixelsPerLonRadian_
return point
def fromPointToLatLng(self,point) :
origin = self.pixelOrigin_
lng = (point.x - origin.x) / self.pixelsPerLonDegree_
latRadians = (point.y - origin.y) / -self.pixelsPerLonRadian_
lat = radiansToDegrees(2 * math.atan(math.exp(latRadians)) - math.pi / 2)
return G_LatLng(lat, lng)
#pixelCoordinate = worldCoordinate * pow(2,zoomLevel)
def getCorners(center, zoom, mapWidth, mapHeight):
scale = 2**zoom
proj = MercatorProjection()
centerPx = proj.fromLatLngToPoint(center)
SWPoint = G_Point(centerPx.x-(mapWidth/2)/scale, centerPx.y+(mapHeight/2)/scale)
SWLatLon = proj.fromPointToLatLng(SWPoint)
NEPoint = G_Point(centerPx.x+(mapWidth/2)/scale, centerPx.y-(mapHeight/2)/scale)
NELatLon = proj.fromPointToLatLng(NEPoint)
return {
'N' : NELatLon.lat,
'E' : NELatLon.lng,
'S' : SWLatLon.lat,
'W' : SWLatLon.lng,
}
用法:
>>> import MercatorProjection
>>> centerLat = 49.141404
>>> centerLon = -121.960988
>>> zoom = 10
>>> mapWidth = 640
>>> mapHeight = 640
>>> centerPoint = MercatorProjection.G_LatLng(centerLat, centerLon)
>>> corners = MercatorProjection.getCorners(centerPoint, zoom, mapWidth, mapHeight)
>>> corners
{'E': -65.710988,
'N': 74.11120692972199,
'S': 0.333879313530149,
'W': -178.210988}
>>> mapURL = "http://maps.googleapis.com/maps/api/staticmap?center=%f,%f&zoom=%d&size=%dx%d&scale=2&maptype=roadmap&sensor=false"%(centerLat,centerLon,zoom,mapWidth,mapHeight)
>>> mapURL
http://maps.googleapis.com/maps/api/staticmap?center=49.141404,-121.960988&zoom=10&size=640x640&scale=2&maptype=roadmap&sensor=false'
答案 2 :(得分:2)
这是Marcelo在PHP中的代码的逐行翻译,可能会被清理一下。效果很好!感谢Marcelo做了很多努力。
define("MERCATOR_RANGE", 256);
function degreesToRadians($deg) {
return $deg * (M_PI / 180);
}
function radiansToDegrees($rad) {
return $rad / (M_PI / 180);
}
function bound($value, $opt_min, $opt_max) {
if ($opt_min != null) $value = max($value, $opt_min);
if ($opt_max != null) $value = min($value, $opt_max);
return $value;
}
class G_Point {
public $x,$y;
function G_Point($x=0, $y=0){
$this->x = $x;
$this->y = $y;
}
}
class G_LatLng {
public $lat,$lng;
function G_LatLng($lt, $ln){
$this->lat = $lt;
$this->lng = $ln;
}
}
class MercatorProjection {
private $pixelOrigin_, $pixelsPerLonDegree_, $pixelsPerLonRadian_;
function MercatorProjection() {
$this->pixelOrigin_ = new G_Point( MERCATOR_RANGE / 2, MERCATOR_RANGE / 2);
$this->pixelsPerLonDegree_ = MERCATOR_RANGE / 360;
$this->pixelsPerLonRadian_ = MERCATOR_RANGE / (2 * M_PI);
}
public function fromLatLngToPoint($latLng, $opt_point=null) {
$me = $this;
$point = $opt_point ? $opt_point : new G_Point(0,0);
$origin = $me->pixelOrigin_;
$point->x = $origin->x + $latLng->lng * $me->pixelsPerLonDegree_;
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
// 89.189. This is about a third of a tile past the edge of the world tile.
$siny = bound(sin(degreesToRadians($latLng->lat)), -0.9999, 0.9999);
$point->y = $origin->y + 0.5 * log((1 + $siny) / (1 - $siny)) * -$me->pixelsPerLonRadian_;
return $point;
}
public function fromPointToLatLng($point) {
$me = $this;
$origin = $me->pixelOrigin_;
$lng = ($point->x - $origin->x) / $me->pixelsPerLonDegree_;
$latRadians = ($point->y - $origin->y) / -$me->pixelsPerLonRadian_;
$lat = radiansToDegrees(2 * atan(exp($latRadians)) - M_PI / 2);
return new G_LatLng($lat, $lng);
}
//pixelCoordinate = worldCoordinate * pow(2,zoomLevel)
}
function getCorners($center, $zoom, $mapWidth, $mapHeight){
$scale = pow(2, $zoom);
$proj = new MercatorProjection();
$centerPx = $proj->fromLatLngToPoint($center);
$SWPoint = new G_Point($centerPx->x-($mapWidth/2)/$scale, $centerPx->y+($mapHeight/2)/$scale);
$SWLatLon = $proj->fromPointToLatLng($SWPoint);
$NEPoint = new G_Point($centerPx->x+($mapWidth/2)/$scale, $centerPx->y-($mapHeight/2)/$scale);
$NELatLon = $proj->fromPointToLatLng($NEPoint);
return array(
'N' => $NELatLon->lat,
'E' => $NELatLon->lng,
'S' => $SWLatLon->lat,
'W' => $SWLatLon->lng,
);
}
用法:
$centerLat = 49.141404;
$centerLon = -121.960988;
$zoom = 10;
$mapWidth = 640;
$mapHeight = 640;
$centerPoint = new G_LatLng($centerLat, $centerLon);
$corners = getCorners($centerPoint, $zoom, $mapWidth, $mapHeight);
$mapURL = "http://maps.googleapis.com/maps/api/staticmap?center={$centerLat},{$centerLon}&zoom={$zoom}&size={$mapWidth}x{$mapHeight}&scale=2&maptype=roadmap&sensor=false";
答案 3 :(得分:1)
对于大的缩放因子(&gt; = 8),y轴上的地图比例的不均匀性可以忽略不计,有更容易的方法,我们只需要考虑像素的1 / cos(纬度)校正/ (纬度)分辨率。 zoom = 0的初始分辨率是每360度256像素,x和y均为0度。
def get_static_map_bounds(lat, lng, zoom, sx, sy):
# lat, lng - center
# sx, sy - map size in pixels
# 256 pixels - initial map size for zoom factor 0
sz = 256 * 2 ** zoom
#resolution in degrees per pixel
res_lat = cos(lat * pi / 180.) * 360. / sz
res_lng = 360./sz
d_lat = res_lat * sy / 2
d_lng = res_lng * sx / 2
return ((lat-d_lat, lng-d_lng), (lat+d_lat, lng+d_lng))
答案 4 :(得分:1)
简单的 Python 版本
在类似问题上花费了很长时间并使用此线程寻求帮助,这里是 Marcelo(和 Jmague)代码的简化 Python 版本:
import math
import requests
def latLngToPoint(mapWidth, mapHeight, lat, lng):
x = (lng + 180) * (mapWidth/360)
y = ((1 - math.log(math.tan(lat * math.pi / 180) + 1 / math.cos(lat * math.pi / 180)) / math.pi) / 2) * mapHeight
return(x, y)
def pointToLatLng(mapWidth, mapHeight, x, y):
lng = x / mapWidth * 360 - 180
n = math.pi - 2 * math.pi * y / mapHeight
lat = (180 / math.pi * math. atan(0.5 * (math.exp(n) - math.exp(-n))))
return(lat, lng)
def getImageBounds(mapWidth, mapHeight, xScale, yScale, lat, lng):
centreX, centreY = latLngToPoint(mapWidth, mapHeight, lat, lng)
southWestX = centreX - (mapWidth/2)/ xScale
southWestY = centreY + (mapHeight/2)/ yScale
SWlat, SWlng = pointToLatLng(mapWidth, mapHeight, southWestX, southWestY)
northEastX = centreX + (mapWidth/2)/ xScale
northEastY = centreY - (mapHeight/2)/ yScale
NElat, NElng = pointToLatLng(mapWidth, mapHeight, northEastX, northEastY)
return[SWlat, SWlng, NElat, NElng]
lat = 37.806716
lng = -122.477702
zoom = 16
picHeight = 640 #Resulting image height in pixels (x2 if scale parameter is set to 2)
picWidth = 640
mapHeight = 256 #Original map size - specific to Google Maps
mapWidth = 256
xScale = math.pow(2, zoom) / (picWidth/mapWidth)
yScale = math.pow(2, zoom) / (picHeight/mapWidth)
corners = getImageBounds(mapWidth, mapHeight, xScale, yScale, lat, lng)
这里我用 x 和 y 来表示像素值,用 lat lng 表示纬度和经度。 lat、lng、zoom、picHeight 和 picWidth 都可以更改为您的特定用例。更改比例尺/地图类型等不会影响此计算。
我使用此代码平铺静态地图图像,没有间隙/重叠。如果您想了解它的更多使用情况/它在这个意义上的工作原理,请参阅我的 GitHub 上的更多信息。
答案 5 :(得分:0)
以下是对Delphi / Pascal的翻译,并进行了一些优化,以对应更严格的Pascal语言和内存管理。
unit Mercator.Google.Maps;
interface
uses System.Math;
type TG_Point = class(TObject)
private
Fx: integer;
Fy: integer;
public
property x: integer read Fx write Fx;
property y: integer read Fy write Fy;
constructor Create(Ax: integer = 0; Ay: integer = 0);
end;
type TG_LatLng = class(TObject)
private
FLat: double;
FLng: double;
public
property Lat: double read FLat write FLat;
property Lng: double read FLng write FLng;
constructor Create(ALat: double; ALng: double);
end;
type TMercatorProjection = class(TObject)
private
pixelOrigin_: TG_Point;
pixelsPerLonDegree_, pixelsPerLonRadian_: double;
function degreesToRadians(deg: double): double;
function radiansToDegrees(rad: double): double;
function bound(value: double; opt_min: double; opt_max: double): double;
public
constructor Create;
procedure fromLatLngToPoint(latLng: TG_LatLng; var point: TG_Point);
procedure fromPointToLatLng(point: TG_point; var latLng: TG_LatLng);
procedure getCorners(center: TG_LatLng; zoom: integer; mapWidth: integer; mapHeight: integer;
var NELatLon: TG_LatLng; var SWLatLon: TG_LatLng);
end;
implementation
const MERCATOR_RANGE = 256;
constructor TG_Point.Create(Ax: Integer = 0; Ay: Integer = 0);
begin
inherited Create;
Fx := Ax;
Fy := Ay
end;
// **************
constructor TG_LatLng.Create(ALat: double; ALng: double);
begin
inherited Create;
FLat := ALat;
FLng := ALng
end;
// **************
constructor TMercatorProjection.Create;
begin
inherited Create;
pixelOrigin_ := TG_Point.Create( Round(MERCATOR_RANGE / 2), Round(MERCATOR_RANGE / 2));
pixelsPerLonDegree_ := MERCATOR_RANGE / 360;
pixelsPerLonRadian_ := MERCATOR_RANGE / (2 * PI);
end;
// Translate degrees to radians
function TMercatorProjection.degreesToRadians(deg: double): double;
begin
Result := deg * (PI / 180);
end;
// Translate radians to degrees
function TMercatorProjection.radiansToDegrees(rad: double): double;
begin
Result := rad / (PI / 180);
end;
// keep value insid defined bounds
function TMercatorProjection.bound(value: double; opt_min: double; opt_max: double): double;
begin
if Value < opt_min then Result := opt_min
else if Value > opt_max then Result := opt_max
else Result := Value;
end;
procedure TMercatorProjection.fromLatLngToPoint(latLng: TG_LatLng; var point: TG_Point);
var
siny: double;
begin
if Assigned(point) then
begin
point.x := Round(pixelOrigin_.x + latLng.lng * pixelsPerLonDegree_);
// NOTE(appleton): Truncating to 0.9999 effectively limits latitude to
// 89.189. This is about a third of a tile past the edge of the world tile.
siny := bound(sin(degreesToRadians(latLng.lat)), -0.9999, 0.9999);
point.y := Round(pixelOrigin_.y + 0.5 * ln((1 + siny) / (1 - siny)) * -pixelsPerLonRadian_);
end;
end;
procedure TMercatorProjection.fromPointToLatLng(point: TG_point; var latLng: TG_LatLng);
var
latRadians: double;
begin
if Assigned(latLng) then
begin
latLng.lng := (point.x - pixelOrigin_.x) / pixelsPerLonDegree_;
latRadians := (point.y - pixelOrigin_.y) / -pixelsPerLonRadian_;
latLng.lat := radiansToDegrees(2 * arctan(exp(latRadians)) - PI / 2);
end;
end;
//pixelCoordinate = worldCoordinate * pow(2,zoomLevel)
procedure TMercatorProjection.getCorners(center: TG_LatLng; zoom: integer; mapWidth: integer; mapHeight: integer;
var NELatLon: TG_LatLng; var SWLatLon: TG_LatLng);
var
scale: double;
centerPx, SWPoint, NEPoint: TG_Point;
begin
scale := power(2, zoom);
centerPx := TG_Point.Create(0, 0);
try
fromLatLngToPoint(center, centerPx);
SWPoint := TG_Point.Create(Round(centerPx.x-(mapWidth/2)/scale), Round(centerPx.y+(mapHeight/2)/scale));
NEPoint := TG_Point.Create(Round(centerPx.x+(mapWidth/2)/scale), Round(centerPx.y-(mapHeight/2)/scale));
try
fromPointToLatLng(SWPoint, SWLatLon);
fromPointToLatLng(NEPoint, NELatLon);
finally
SWPoint.Free;
NEPoint.Free;
end;
finally
centerPx.Free;
end;
end;
end.
用法示例:
with TMercatorProjection.Create do
try
CLatLon := TG_LatLng.Create(Latitude, Longitude);
SWLatLon := TG_LatLng.Create(0,0);
NELatLon := TG_LatLng.Create(0,0);
try
getCorners(CLatLon, Zoom,
MapWidth, MapHeight,
SWLatLon, NELatLon);
finally
ShowMessage('SWLat='+FloatToStr(SWLatLon.Lat)+' | SWLon='+FloatToStr(SWLatLon.Lng));
ShowMessage('NELat='+FloatToStr(NELatLon.Lat)+' | NELon='+FloatToStr(NELatLon.Lng));
SWLatLon.Free;
NELatLon.Free;
CLatLon.Free;
end;
finally
Free;
end;