17

如何获取已返回的谷歌静态地图的度数范围,例如,对于以下请求

http://maps.googleapis.com/maps/api/staticmap?center=0.0,0.0&zoom=10&size=640x640&sensor=false

据我所知,完整的地球地图是 256x256 图像。这意味着 n 个垂直像素包含 x 度,但 n 个水平像素包含 2x 度。正确的?

正如谷歌所说, 中心定义了地图的中心,与地图的所有边缘等距。据我了解,像素等距(或度数?)。每个后续的缩放级别都会使水平和垂直维度的精度翻倍。因此,我可以为每个缩放值找到地图经度的增量值:

dLongitude = (HorizontalMapSizeInPixels / 256 ) * ( 360 / pow(2, zoom) );

纬度的相同计算:

dLatitude = (VerticalMapSizeInPixels / 256 ) * ( 180 / pow(2, zoom) );

VerticalMapSizeInPixels 和 Horizo​​ntalMapSizeInPixels 是 URL 中地图大小的参数。

计算经度的增量值很好,但对于纬度,它是错误的。我找不到纬度的增量值,存在一些增量错误。

4

6 回答 6

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);
于 2012-09-20T11:38:44.477 回答
15

感谢马塞洛的回答。这很有帮助。如果有人感兴趣,这里是代码的 Python 版本(PHP 代码的粗略翻译,可能不像 Python 那样):

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'
于 2014-01-14T14:49:20.133 回答
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";
于 2013-10-31T17:01:50.237 回答
2

简单的 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上有更多信息。

于 2021-02-06T14:21:11.290 回答
1

对于大缩放因子(> = 8),可以忽略 y 轴上地图比例的不均匀性,有更简单的方法,我们只需对像素/(纬度度数)进行 accout 1/cos(latitude) 校正) 解析度。对于 0 纬度的 x 和 y,zoom=0 的初始分辨率为每 360 度 256 个像素。

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))
于 2017-11-30T17:32:15.113 回答
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;
于 2016-05-11T14:17:02.703 回答