简体   繁体   中英

Google maps static pixels to latitude longitude relative to zoom level

So , i use google maps static api v2 to get a 256x256 image from a lat,lng @ zoom 9 (for instance). I do some calculation on the image pixels and i get an array of x,y points. For which i want to plot layer on as a Polyline.

I would like to convert that array of x,y pixel points to lat,lng.

my pseudo code was something like:

var lat = 35.000,lng=33.000;
var TILE_SIZE = 256;
// google code
function degreesToRadians(deg) {
  return deg * (Math.PI / 180);
}

function radiansToDegrees(rad) {
  return rad / (Math.PI / 180);
}

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;
}
/** @constructor */
function MercatorProjection() {
  this.pixelOrigin_ = new google.maps.Point(TILE_SIZE / 2,
      TILE_SIZE / 2);
  this.pixelsPerLonDegree_ = TILE_SIZE / 360;
  this.pixelsPerLonRadian_ = TILE_SIZE / (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_;

  // 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);
};
// google code

getLatLngFromXY = function(x,y,zoom){
var scale = Math.pow(2,zoom );

      var projection = new MercatorProjection();
      var worldCoordinateCenter = projection.fromLatLngToPoint(new google.maps.LatLng(lat,lng));
      var pixelOffset = new google.maps.Point((x/scale) || 0,(y/scale) ||0);
      var offest_x = (x > 128) ? worldCoordinateCenter.x + pixelOffset.x: worldCoordinateCenter.x - pixelOffset.x;
      var offest_y = (y > 128) ? worldCoordinateCenter.y + pixelOffset.y :
          worldCoordinateCenter.y - pixelOffset.y;
       var worldCoordinateNewCenter = new google.maps.Point(
          offest_x,
          offest_y
      );
       var newCenter = projection.fromPointToLatLng(worldCoordinateNewCenter);

      var ideal_lat = newCenter.lat(),
          ideal_lng = newCenter.lng();
return {lat: ideal_lat, lng: ideal_lng};
}

var some_list = [{x: 129, y: 124},{x: 118, y: 52},...],
    polyline = [];

for(var i=0; i < some_list.length; i++){
    temp_latlng = getLatLngFromXY(some_list[i].x,some_list[i].y,9);
    polyline.push(new google.maps.LatLng(temp_latlng.lat,temp_latlng.lng);
}

new google.maps.Polyline({
  map: map,
  strokeColor: '#FF0000',
  strokeOpacity: 1.0,
  strokeWeight: 10,
  path: polyline
});

I hope this makes sense. The problem is: because i'm offsetting (i guess), i get markers all over the place, unlike how it looks on the static image. So the conversion between pixels->mercator i've did, is wrong.

Thank you

I was able to solve this problem... it was quite hard understanding what is wrong and what needs to be done, basically we want to be ZOOM dependant (scale above)

that means our code should change (the "google code" section) as follows:

MercatorProjection.prototype.fromLatLngToPoint = function(latLng, zoom, 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_;

  // 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_;
  var tiles_no = 1 << zoom;
  point.x *= tiles_no;
  point.y *= tiles_no;

  return point;
};

MercatorProjection.prototype.fromPointToLatLng = function(point, zoom) {
  var me         = this;
  var origin     = me.pixelOrigin_;

  var tiles_no   = 1 << zoom;
  point.x        /= tiles_no;
  point.y        /= tiles_no;
  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);
};

Afterwards when we want to convert from lat,lng to point:

var worldCoordinateCenter = projection.fromLatLngToPoint(new google.maps.LatLng(lat,lng),zoom);
and also the offset changes (just for comfort and logic, its almost the same)
var offset_x = (x > (TILE_SIZE / 2)) ?  x - (TILE_SIZE / 2):  -Math.abs(x - (TILE_SIZE / 2));
          var offset_y = (y > (TILE_SIZE / 2)) ?  y - (TILE_SIZE / 2):  -Math.abs(y - (TILE_SIZE / 2));

var newCenter =  projection.fromPointToLatLng({x: worldCoordinateCenter.x + offset_x,y:worldCoordinateCenter.y + offset_y },zoom);

that's it.

Notice that "1 << zoom" is just like Math.pow(2,zoom); and also that in fromLatLngToPoint we multiply by it (it's the number of tiles for each zoom level) and in fromPointToLatLng we divide by it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM