Преобразование точки широты / долготы в пиксели (x, y) на проекции меркатора
Я пытаюсь преобразовать точку lat / long в точку 2d, чтобы я мог отображать ее на изображении мира – проекции меркатора.
Я видел различные способы сделать это и несколько вопросов о переполнении стека – я опробовал различные fragmentы кода, и хотя я получаю правильную долготу к пикселю, широта всегда выключена – кажется, становится все более разумной.
Мне нужна формула, чтобы принять во внимание размер изображения, ширину и т. Д.
- Каков алгоритм преобразования буквы столбца Excel в его номер?
- Округление BigDecimal на * всегда * имеет два десятичных знака
- Алгоритм для поиска чисел из списка размера n sum на другое число
- Как java вычисляет модуль с отрицательными числами?
- Невозможно передать значение через перенос
Я пробовал эту часть кода:
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);
В примере, который я пытаюсь сделать, широта кажется около 30 пикселей. Любая помощь или совет?
Обновить
Исходя из этого вопроса: Lat / lon to xy
Я пытался использовать предоставленный код, но у меня все еще есть проблемы с преобразованием широты, долгота в порядке.
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);
При использовании исходного кода, если значение широты положительное, оно возвратило отрицательную точку, поэтому я немного изменил ее и протестировал с экстремальными широтами, который должен быть точкой 0 и точкой 766, он отлично работает. Однако, когда я пробую другое значение широты ex: 58.07 (только к северу от Великобритании), он отображается как север Испании.
- log2 не найден в моей math.h?
- Почему gdb оценивает sqrt (3) на 0?
- Как получить целые и дробные части из двух в JSP / Java?
- Создание математической библиотеки с использованием Generics в C #
- Лучший алгоритм для оценки математического выражения?
- Как обрабатывать очень большие числа в Java без использования java.math.BigInteger
- Оценить математическое выражение из строки с помощью VB
- Самый точный способ выполнить комбинированную операцию умножения и деления в 64-битном режиме?
Проекция карты Меркатора является специальным предельным случаем проекции проектора Ламбертского конического конформного отображения с экватором в качестве единственной стандартной параллели. Все остальные параллели широты – прямые, а меридианы – также прямые линии под прямым углом к экватору, равномерно распределенные друг от друга. Он является основой для поперечных и наклонных форм проекции. Он мало используется для целей картографирования земли, но практически универсален для навигационных карт. Как и конформный, он обладает тем свойством, что прямые линии, нарисованные на нем, являются линиями постоянного опоры. Таким образом, навигаторы могут вывести свой курс с того угла, который прямая линия курса делает с меридианами. [1]
Формулы для вывода outlookируемых координат Восток и Нортинг из сферической широты φ и долготы λ:
E = FE + R (λ – λₒ) N = FN + R ln[tan(π/4 + φ/2)]
где λ O – долгота естественного происхождения, а FE и FN – ложные восточные и ложные северные. В сферическом Меркаторе эти значения фактически не используются, поэтому вы можете упростить формулу
Пример псевдокода, поэтому это можно адаптировать к каждому языку программирования.
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));
Источники:
- Комитет по геоматике OGP, Руководство № 7, часть 2: Преобразование координат и трансформация
- Вывод проекции Меркатора
- Национальный атлас: картографические outlookы
- Карта Меркатора
EDIT Создал рабочий пример в PHP (потому что я сосать на Java)
Вы не можете просто переносить от долготы / широты к х / у, как это, потому что мир не плоский. Вы смотрите на этот пост? Преобразование долготы / широты в координату X / Y
ОБНОВЛЕНИЕ – 1/18/13
Я решил дать ему удар, и вот как я это делаю:
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)); } }
РЕЗУЛЬТАТ: ширина изображения = 600 пикселей, высота изображения = 600 пикселей, заполнение изображения = 50 пикселей
РЕЗУЛЬТАТ: ширина изображения = 300 пикселей, высота изображения = 500 пикселей, заполнение изображения = 50 пикселей
Java-версия оригинального JavaScript-кода JavaScript API JavaScript API v3 выглядит следующим образом: он работает без проблем
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; } }
Я хотел бы указать, что код в границах процедуры должен читать
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<