2012-09-20 17 views
12

Come ottenere limiti in gradi di mappa statica Google, che è stato restituito, ad esempio, per la seguente richiestaCome ottenere i limiti di una mappa statica google?

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

Per quanto ne so, completa mappa della Terra è l'immagine 256x256. Ciò significa che n pixel verticali contengono x gradi, ma n pixel orizzontali contengono 2x gradi. Destra?

Come google says centro definisce il centro della mappa, equidistante da tutti i bordi della mappa. Come ho capito equidistante in pixel (o in gradi?). E ogni livello di zoom successivo raddoppia la precisione in entrambe le dimensioni orizzontali e verticali. Quindi, posso trovare il valore delta Longitudine di mappa per ogni valore di zoom come:

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

stessi calcoli per Latitude:

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

VerticalMapSizeInPixels e HorizontalMapSizeInPixels sono parametri di dimensioni mappa in URL.

È utile calcolare il valore delta di Longitudine, ma per Latitudine è sbagliato. Non riesco a trovare il valore delta di Latitude, c'è qualche errore delta.

+0

Eventuali duplicati di http://stackoverflow.com/questions/4730885/how-do-i-get-the-bounds-of-a-static-google-map?rq=1 – j0nes

risposta

30

Come noto, la mappa completa della Terra è l'immagine 256x256.

Sì.

Ciò significa che n pixel verticali contengono x gradi, ma n orizzontali pixel contengono 2x gradi. Destra?

No. Un pixel rappresenterà una quantità variabile di latitudine in base alla latitudine. Un pixel all'equatore rappresenta meno la latitudine di un pixel vicino ai poli.

Gli angoli della mappa dipenderanno dal centro, dal livello di zoom e dalle dimensioni della mappa e sarà necessario utilizzare la proiezione di Mercatore per calcolarli. Se non si desidera caricare il pieno API, ecco un oggetto Proiezione cilindrica centrografica modificata di Mercatore:

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) 

È possibile salvare che in un file separato, ad esempio "MercatorProjection.js", e poi includerlo nella vostra applicazione.

<script src="MercatorProjection.js"></script> 

Con il file sopra caricato, la seguente funzione calcola gli angoli SW e NE di una mappa di una data dimensione e in un particolare zoom.

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); 
} 

e si definirebbe così:

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); 
+0

Come precisa è Questo? L'ho portato su Java, devo dire che ne sono davvero felice! Sono a pochi metri da. Mi chiedo se questo sia l'algoritmo o che io commetta un errore. Per qualche ragione ho dovuto moltiplicare la scala in getCorners con 2.0 altrimenti i limiti dove andare al grande. – clankill3r

+0

GOT IT Ho ricevuto una richiesta con dimensione = 1024x1024. Mi ha dato un'immagine di 1080x1080 di cui non ero a conoscenza! Quindi ho controllato i limiti su una piccola regione. Risultante in una regione piccola ... – clankill3r

+0

Ehi @marcelo, ho un problema con il tuo codice tradotto in C#. Maggiori informazioni sul problema [qui] (http://stackoverflow.com/questions/37008037/get-sw-ne-corners-of-google-static-maps-api) – Leniaal

2

Ecco una linea da traduzione riga di codice di Marcelo in PHP, che può probabilmente essere ripulito un po '. Funziona alla grande! Grazie a Marcelo per aver fatto la parte difficile.

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, 
    ); 
} 

Usage:

$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"; 
12

Grazie Marcelo per la risposta. È stato abbastanza utile Nel caso qualcuno fosse interessato, ecco la versione di Python del codice (una traduzione approssimativa del codice PHP, probabilmente non come divinatorio come potrebbe essere):

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, 
    } 

Usage:

>>> 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

Buggy: lo zoom deve essere definito come 2 ** scala, non 2^scala che è uno XOR bit a bit tra 2 e scala – jmague

0

Qui è la traduzione in Delphi/Pascal con alcune ottimizzazioni per corrispondere al linguaggio Pascal più rigido e alla gestione della memoria.

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. 

Esempio di utilizzo:

  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; 
0

Per i grandi fattori di zoom (> = 8), in cui non uniformità della scala della mappa sull'asse y può essere trascurata, non v'è il metodo più semplice, dove abbiamo appena prende in considerazione la correzione 1/cos (latitudine) per la risoluzione dei pixel/(gradi di latitudine). La risoluzione iniziale per lo zoom = 0 è 256 pixel per 360 gradi sia per x che y a 0 latitudine.

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))