robinson proyecciones proyeccion peters homolográfica gerardus conica cartograficas biografia java math maps 2d mercator

java - proyecciones - Convierta el punto de latitud/longitud en píxeles(x, y) en la proyección de Mercator



proyeccion mercator y peters (5)

La proyección de mapa de Mercator es un caso límite especial de la proyección de mapa Conformal de Lambert Conic con el ecuador como el paralelo estándar único. Todos los demás paralelos de latitud son líneas rectas y los meridianos también son líneas rectas en ángulo recto con respecto al ecuador, igualmente espaciadas. Es la base de las formas transversales y oblicuas de la proyección. Se usa poco para fines de mapeo terrestre, pero es de uso casi universal para las cartas de navegación. Además de ser conforme, tiene la particular propiedad de que las líneas rectas dibujadas en él son líneas de constante porte. Por lo tanto, los navegantes pueden derivar su curso desde el ángulo que hace la línea recta con los meridianos. [1.]

Las fórmulas para derivar las coordenadas proyectadas Este y Norte desde la latitud esférica φ y la longitud λ son:

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

donde λ O es la longitud del origen natural y FE y FN son falsos al este y falso norte. En Mercator esférico esos valores en realidad no se utilizan, por lo que puede simplificar la fórmula para

Ejemplo de pseudocódigo, por lo que puede adaptarse a todos los lenguajes de programación.

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

Fuentes:

  1. OGP Geomatics Committee, Nota de orientación número 7, parte 2: Conversiones de coordenadas y transformación
  2. Derivación de la proyección de Mercator
  3. Atlas nacional: proyecciones de mapas
  4. Proyección del mapa de Mercator

EDITAR creó un ejemplo de trabajo en PHP (porque aspiro en Java)

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

Intento convertir un punto lat / largo en un punto 2d para poder mostrarlo en una imagen del mundo, que es una proyección mercator.

He visto varias maneras de hacer esto y algunas preguntas sobre el desbordamiento de la pila -he probado los diferentes fragmentos de código y aunque obtengo la longitud correcta en píxeles, la latitud siempre está desactivada- parece ser cada vez más razonable.

Necesito la fórmula para tener en cuenta el tamaño de la imagen, el ancho, etc.

He intentado este fragmento de código:

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 latitud parece estar fuera de alrededor de 30px en el ejemplo que intento. Cualquier ayuda o consejo?

Actualizar

Basado en esta pregunta: Lat / lon a xy

Intenté usar el código provisto, pero sigo teniendo problemas con la conversión de latitud, la longitud está bien.

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; y = mapHeight - ((worldMapWidth / 2 * Math.log((1 + Math.sin(lat)) / (1 - Math.sin(lat)))) - mapOffsetY); } else 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);

Al usar el código original si el valor de latitud es positivo, devuelve un punto negativo, por lo que lo modifiqué ligeramente y probé con las latitudes extremas, que deberían ser el punto 0 y el punto 766, funciona bien. Sin embargo, cuando intento un valor de latitud diferente, por ejemplo: 58.07 (justo al norte del Reino Unido), aparece como norte de España.


La versión de Java del código original de JavaScript de la API de JavaScript de Google Maps v3 es la siguiente, funciona sin problemas

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


Me gustaría señalar que el código en los límites del procedimiento debería leer

double bound(double val, double valMin, double valMax) { double res; res = Math.max(val, valMin); res = Math.min(res, valMax); return res; }


No puedes simplemente transponer de longitud / latitud a x / y de esa manera porque el mundo no es plano. ¿Has mirado esta publicación? Conversión de longitud / latitud a coordenadas X / Y

ACTUALIZACIÓN - 1/18/13

Decidí darle una puñalada, y así es como lo hago:

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<RenderingHints.Key, Object> map = new HashMap<RenderingHints.Key, Object>(); 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<Collection<Point2D.Double>> countyBoundaries = new ArrayList<Collection<Point2D.Double>>(); // for every county, convert the longitude/latitude to X/Y using Mercator projection formula for (County county : countyService.getAllCounties()) { Collection<Point2D.Double> lonLat = new ArrayList<Point2D.Double>(); 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<Point2D.Double> 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<Point2D.Double> 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)); } }

RESULTADO: ancho de la imagen = 600px, altura de la imagen = 600px, relleno de la imagen = 50px

RESULTADO: ancho de la imagen = 300 px, altura de la imagen = 500 px, relleno de la imagen = 50 px


public static String getTileNumber(final double lat, final double lon, final int zoom) { int xtile = (int)Math.floor( (lon + 180) / 360 * (1<<zoom) ) ; int ytile = (int)Math.floor( (1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1<<zoom) ) ; if (xtile < 0) xtile=0; if (xtile >= (1<<zoom)) xtile=((1<<zoom)-1); if (ytile < 0) ytile=0; if (ytile >= (1<<zoom)) ytile=((1<<zoom)-1); return("" + zoom + "/" + xtile + "/" + ytile); } }