Converti il ​​punto di latitudine / longitudine in pixel (x, y) sulla proiezione di mercatore

Sto provando a convertire un punto lat / long in un punto 2d in modo che possa visualizzarlo su un’immagine del mondo, che è una proiezione di mercatore.

Ho visto vari modi per farlo e alcune domande sull’overflow dello stack: ho provato i diversi frammenti di codice e, sebbene ottenga la corretta longitudine del pixel, la latitudine è sempre distriggersta, sembra essere comunque più ragionevole.

Ho bisogno che la formula tenga in considerazione la dimensione dell’immagine, la larghezza, ecc.

Ho provato questo pezzo di codice:

double minLat = -85.05112878; double minLong = -180; double maxLat = 85.05112878; double maxLong = 180; // Map image size (in points) double mapHeight = 768.0; double mapWidth = 991.0; // Determine the map scale (points per degree) double xScale = mapWidth/ (maxLong - minLong); double yScale = mapHeight / (maxLat - minLat); // position of map image for point double x = (lon - minLong) * xScale; double y = - (lat + minLat) * yScale; System.out.println("final coords: " + x + " " + y); 

La latitudine sembra essere spenta di circa 30px nell’esempio che sto provando. Qualche aiuto o consiglio?

Aggiornare

Basato su questa domanda: Lat / lon a xy

Ho provato a utilizzare il codice fornito ma sto ancora avendo alcuni problemi con la conversione della latitudine, la longitudine va bene.

 int mapWidth = 991; int mapHeight = 768; double mapLonLeft = -180; double mapLonRight = 180; double mapLonDelta = mapLonRight - mapLonLeft; double mapLatBottom = -85.05112878; double mapLatBottomDegree = mapLatBottom * Math.PI / 180; double worldMapWidth = ((mapWidth / mapLonDelta) * 360) / (2 * Math.PI); double mapOffsetY = (worldMapWidth / 2 * Math.log((1 + Math.sin(mapLatBottomDegree)) / (1 - Math.sin(mapLatBottomDegree)))); double x = (lon - mapLonLeft) * (mapWidth / mapLonDelta); double y = 0.1; if (lat  0) { lat = lat * Math.PI / 180; lat = lat * -1; y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(lat)) / (1 - Math.sin(lat)))) - mapOffsetY); System.out.println("y before minus: " + y); y = mapHeight - y; } else { y = mapHeight / 2; } System.out.println(x); System.out.println(y); 

Quando si utilizza il codice originale se il valore di latitudine è positivo, ha restituito un punto negativo, quindi l’ho leggermente modificato e testato con le latitudini estreme, che dovrebbe essere il punto 0 e il punto 766, funziona perfettamente. Tuttavia quando provo un valore di latitudine diverso ex: 58,07 (appena a nord del Regno Unito) viene visualizzato a nord della Spagna.

La proiezione della mappa di Mercator è un caso limite specifico della proiezione della mappa conforms di Lambert Conic con l’equatore come singolo standard parallelo. Tutti gli altri paralleli di latitudine sono linee rette ei meridiani sono anche linee rette ad angolo retto rispetto all’equatore, equidistanti. È la base per le forms trasversali e oblique della proiezione. È poco utilizzato per scopi di mapping del territorio, ma è quasi universale per le carte di navigazione. Oltre ad essere conforms, ha la particolare proprietà che le linee rette disegnate su di esso sono linee di costante portata. Così i navigatori possono derivare il loro percorso dall’angolo che la linea retta fa con i meridiani. [1]

Proiezione di Mercatore

Le formule per ricavare le coordinate proiettate di Est e Nord da latitudine sferica φ e longitudine λ sono:

 E = FE + R (λ – λₒ) N = FN + R ln[tan(π/4 + φ/2)] 

dove λ O è la longitudine di origine naturale e FE e FN sono falso est e falso nord. Nel Mercator sferico questi valori non vengono effettivamente utilizzati, quindi è ansible semplificare la formula

derivazione della proiezione di mercatore (wikipedia)

Esempio di pseudo codice, quindi questo può essere adattato ad ogni linguaggio di programmazione.

 latitude = 41.145556; // (φ) longitude = -73.995; // (λ) mapWidth = 200; mapHeight = 100; // get x value x = (longitude+180)*(mapWidth/360) // convert from degrees to radians latRad = latitude*PI/180; // get y value mercN = log(tan((PI/4)+(latRad/2))); y = (mapHeight/2)-(mapWidth*mercN/(2*PI)); 

fonti:

  1. OGP Geomatics Committee, Guidance Note Number 7, part 2: Coordinate Conversions and Transformation
  2. Derivazione della proiezione di Mercatore
  3. Atlante nazionale: proiezioni cartografiche
  4. Proiezione della mappa di Mercatore

EDIT Creato un esempio funzionante in PHP (perché faccio schifo su Java)

https://github.com/mfeldheim/mapStuff.git

Non puoi semplicemente trasporre da longitudine / latitudine a x / y come quello perché il mondo non è piatto. Hai visto questo post? Conversione di longitudine / latitudine in coordinate X / Y

AGGIORNAMENTO – 18/1/13

Ho deciso di fare una pugnalata, ed ecco come lo faccio: –

 public class MapService { // CHANGE THIS: the output path of the image to be created private static final String IMAGE_FILE_PATH = "/some/user/path/map.png"; // CHANGE THIS: image width in pixel private static final int IMAGE_WIDTH_IN_PX = 300; // CHANGE THIS: image height in pixel private static final int IMAGE_HEIGHT_IN_PX = 500; // CHANGE THIS: minimum padding in pixel private static final int MINIMUM_IMAGE_PADDING_IN_PX = 50; // formula for quarter PI private final static double QUARTERPI = Math.PI / 4.0; // some service that provides the county boundaries data in longitude and latitude private CountyService countyService; public void run() throws Exception { // configuring the buffered image and graphics to draw the map BufferedImage bufferedImage = new BufferedImage(IMAGE_WIDTH_IN_PX, IMAGE_HEIGHT_IN_PX, BufferedImage.TYPE_INT_RGB); Graphics2D g = bufferedImage.createGraphics(); Map map = new HashMap(); map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); RenderingHints renderHints = new RenderingHints(map); g.setRenderingHints(renderHints); // min and max coordinates, used in the computation below Point2D.Double minXY = new Point2D.Double(-1, -1); Point2D.Double maxXY = new Point2D.Double(-1, -1); // a list of counties where each county contains a list of coordinates that form the county boundary Collection> countyBoundaries = new ArrayList>(); // for every county, convert the longitude/latitude to X/Y using Mercator projection formula for (County county : countyService.getAllCounties()) { Collection lonLat = new ArrayList(); for (CountyBoundary countyBoundary : county.getCountyBoundaries()) { // convert to radian double longitude = countyBoundary.getLongitude() * Math.PI / 180; double latitude = countyBoundary.getLatitude() * Math.PI / 180; Point2D.Double xy = new Point2D.Double(); xy.x = longitude; xy.y = Math.log(Math.tan(QUARTERPI + 0.5 * latitude)); // The reason we need to determine the min X and Y values is because in order to draw the map, // we need to offset the position so that there will be no negative X and Y values minXY.x = (minXY.x == -1) ? xy.x : Math.min(minXY.x, xy.x); minXY.y = (minXY.y == -1) ? xy.y : Math.min(minXY.y, xy.y); lonLat.add(xy); } countyBoundaries.add(lonLat); } // readjust coordinate to ensure there are no negative values for (Collection points : countyBoundaries) { for (Point2D.Double point : points) { point.x = point.x - minXY.x; point.y = point.y - minXY.y; // now, we need to keep track the max X and Y values maxXY.x = (maxXY.x == -1) ? point.x : Math.max(maxXY.x, point.x); maxXY.y = (maxXY.y == -1) ? point.y : Math.max(maxXY.y, point.y); } } int paddingBothSides = MINIMUM_IMAGE_PADDING_IN_PX * 2; // the actual drawing space for the map on the image int mapWidth = IMAGE_WIDTH_IN_PX - paddingBothSides; int mapHeight = IMAGE_HEIGHT_IN_PX - paddingBothSides; // determine the width and height ratio because we need to magnify the map to fit into the given image dimension double mapWidthRatio = mapWidth / maxXY.x; double mapHeightRatio = mapHeight / maxXY.y; // using different ratios for width and height will cause the map to be stretched. So, we have to determine // the global ratio that will perfectly fit into the given image dimension double globalRatio = Math.min(mapWidthRatio, mapHeightRatio); // now we need to readjust the padding to ensure the map is always drawn on the center of the given image dimension double heightPadding = (IMAGE_HEIGHT_IN_PX - (globalRatio * maxXY.y)) / 2; double widthPadding = (IMAGE_WIDTH_IN_PX - (globalRatio * maxXY.x)) / 2; // for each country, draw the boundary using polygon for (Collection points : countyBoundaries) { Polygon polygon = new Polygon(); for (Point2D.Double point : points) { int adjustedX = (int) (widthPadding + (point.getX() * globalRatio)); // need to invert the Y since 0,0 starts at top left int adjustedY = (int) (IMAGE_HEIGHT_IN_PX - heightPadding - (point.getY() * globalRatio)); polygon.addPoint(adjustedX, adjustedY); } g.drawPolygon(polygon); } // create the image file ImageIO.write(bufferedImage, "PNG", new File(IMAGE_FILE_PATH)); } } 

RISULTATO: Larghezza immagine = 600 px, Altezza immagine = 600 px, Immagine riempimento = 50 px

inserisci la descrizione dell'immagine qui

RISULTATO: Larghezza immagine = 300 px, Altezza immagine = 500 px, Immagine riempimento = 50 px

inserisci la descrizione dell'immagine qui

La versione Java del codice JavaScript java dell’API JavaScript v3 di JavaScript di Google Maps è la seguente, funziona senza problemi

 public final class GoogleMapsProjection2 { private final int TILE_SIZE = 256; private PointF _pixelOrigin; private double _pixelsPerLonDegree; private double _pixelsPerLonRadian; public GoogleMapsProjection2() { this._pixelOrigin = new PointF(TILE_SIZE / 2.0,TILE_SIZE / 2.0); this._pixelsPerLonDegree = TILE_SIZE / 360.0; this._pixelsPerLonRadian = TILE_SIZE / (2 * Math.PI); } double bound(double val, double valMin, double valMax) { double res; res = Math.max(val, valMin); res = Math.min(res, valMax); return res; } double degreesToRadians(double deg) { return deg * (Math.PI / 180); } double radiansToDegrees(double rad) { return rad / (Math.PI / 180); } PointF fromLatLngToPoint(double lat, double lng, int zoom) { PointF point = new PointF(0, 0); point.x = _pixelOrigin.x + lng * _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. double siny = bound(Math.sin(degreesToRadians(lat)), -0.9999,0.9999); point.y = _pixelOrigin.y + 0.5 * Math.log((1 + siny) / (1 - siny)) *- _pixelsPerLonRadian; int numTiles = 1 << zoom; point.x = point.x * numTiles; point.y = point.y * numTiles; return point; } PointF fromPointToLatLng(PointF point, int zoom) { int numTiles = 1 << zoom; point.x = point.x / numTiles; point.y = point.y / numTiles; double lng = (point.x - _pixelOrigin.x) / _pixelsPerLonDegree; double latRadians = (point.y - _pixelOrigin.y) / - _pixelsPerLonRadian; double lat = radiansToDegrees(2 * Math.atan(Math.exp(latRadians)) - Math.PI / 2); return new PointF(lat, lng); } public static void main(String []args) { GoogleMapsProjection2 gmap2 = new GoogleMapsProjection2(); PointF point1 = gmap2.fromLatLngToPoint(41.850033, -87.6500523, 15); System.out.println(point1.x+" "+point1.y); PointF point2 = gmap2.fromPointToLatLng(point1,15); System.out.println(point2.x+" "+point2.y); } } public final class PointF { public double x; public double y; public PointF(double x, double y) { this.x = x; this.y = y; } } 

Vorrei sottolineare che il codice nei limiti della procedura dovrebbe essere letto

 double bound(double val, double valMin, double valMax) { double res; res = Math.max(val, valMin); res = Math.min(res, valMax); return res; } 
  public static String getTileNumber(final double lat, final double lon, final int zoom) { int xtile = (int)Math.floor( (lon + 180) / 360 * (1<= (1<= (1<