【问题标题】:How to get bounds of a google static map?如何获得谷歌静态地图的边界?
【发布时间】:2012-09-12 12:05:05
【问题描述】:

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

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

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

纬度的相同计算:

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

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

计算经度的增量值很好,但纬度是错误的。我找不到纬度的增量值,有一些 delta 错误。

【问题讨论】:

标签: google-maps map latitude-longitude bounds


【解决方案1】:

据我所知,完整的地球地图是 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);

【讨论】:

  • 这有多精确?我将它移植到 java,我必须说我真的很高兴!我只有几米。我想知道这是算法还是我犯了错误。出于某种原因,我不得不将 getCorners 中的比例与 2.0 相乘,否则范围会变大。
  • 知道了,我有一个大小为 1024x1024 的请求。它给了我一张我不知道的 1080x1080 的图像!所以我对一个小区域进行了边界检查。导致一个很小的区域......
  • 嘿@marcelo,你的代码翻译成C#有问题。有关问题的更多信息here
  • 我如何使用它从视图中的纬度到像素 x,y?
【解决方案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】:

    感谢马塞洛的回答。这很有帮助。如果有人感兴趣,这里是 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'
    

    【讨论】:

    • Buggy : 缩放应该定义为 2**scale,而不是 2^scale,它是 2 和 scale 之间的按位异或
    【解决方案4】:

    这是对 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;
    

    【讨论】:

      【解决方案5】:

      对于大缩放系数 (>=8),可以忽略 y 轴上地图比例的不均匀性,有更简单的方法,我们只需对像素/(纬度)分辨率。对于 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))
      

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-11-30
          • 2019-04-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多