将经度/纬度转换为X / Y坐标

limc:

我使用Google Maps API创建了一张地图,该地图突出显示了明尼苏达州的所有县。基本上,我使用一组经度/纬度坐标创建了县多边形。这是生成的地图的屏幕截图:-

在此处输入图片说明

用户需求之一是能够拥有与图像相似的地图,以便他们可以将其嵌入到PowerPoint /主题幻灯片中。我找不到任何有用的Google Maps API,可以让我按原样保存自定义地图(如果您知道,请告诉我),因此我认为我应该使用Java中的Graphics2D进行绘制。

阅读有关将经度/纬度转换为X / Y坐标的公式后,我得到以下代码:-

private static final int    EARTH_RADIUS    = 6371;
private static final double FOCAL_LENGTH    = 500;

...

BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();

for (Coordinate coordinate : coordinates) {
    double latitude = Double.valueOf(coordinate.getLatitude());
    double longitude = Double.valueOf(coordinate.getLongitude());

    latitude = latitude * Math.PI / 180;
    longitude = longitude * Math.PI / 180;

    double x = EARTH_RADIUS * Math.sin(latitude) * Math.cos(longitude);
    double y = EARTH_RADIUS * Math.sin(latitude) * Math.sin(longitude);
    double z = EARTH_RADIUS * Math.cos(latitude);

    double projectedX = x * FOCAL_LENGTH / (FOCAL_LENGTH + z);
    double projectedY = y * FOCAL_LENGTH / (FOCAL_LENGTH + z);

    // scale the map bigger
    int magnifiedX = (int) Math.round(projectedX * 5);
    int magnifiedY = (int) Math.round(projectedY * 5);

    ...
    g.drawPolygon(...);
    ...
}

The generated map is similar the one generated by Google Maps API using the same set of longitudes/latitudes. However, it seems a little bit tilted and it looks a little off, and I'm not sure how to fix this.

在此处输入图片说明

How do I make the shape of the counties to look just like the one generated by Google Maps API above?

Thanks much.

FINAL SOLUTION

I finally found the solution thanks to @QuantumMechanic and @Anon.

The Mercator projection really does the trick here. I'm using Java Map Projection Library to perform the calculation for Mercator projection.

private static final int    IMAGE_WIDTH     = 1000;
private static final int    IMAGE_HEIGHT    = 1000;
private static final int    IMAGE_PADDING   = 50;

...

private List<Point2D.Double> convertToXY(List<Coordinate> coordinates) {
    List<Point2D.Double> xys = new ArrayList<Point2D.Double>();

    MercatorProjection projection = new MercatorProjection();

    for (Coordinate coordinate : coordinates) {
        double latitude = Double.valueOf(coordinate.getLatitude());
        double longitude = Double.valueOf(coordinate.getLongitude());

        // convert to radian
        latitude = latitude * Math.PI / 180;
        longitude = longitude * Math.PI / 180;

        Point2D.Double d = projection.project(longitude, latitude, new Point2D.Double());

        // shift by 10 to remove negative Xs and Ys
        // scaling by 6000 to make the map bigger
        int magnifiedX = (int) Math.round((10 + d.x) * 6000);
        int magnifiedY = (int) Math.round((10 + d.y) * 6000);

        minX = (minX == -1) ? magnifiedX : Math.min(minX, magnifiedX);
        minY = (minY == -1) ? magnifiedY : Math.min(minY, magnifiedY);

        xys.add(new Point2D.Double(magnifiedX, magnifiedY));
    }

    return xys;
}

...

By using the generated XY coordinate, the map seems inverted, and that's because I believe the graphics2D's 0,0 starts at top left. So, I need to invert the Y by subtracting the value from the image height, something like this:-

...

Polygon polygon = new Polygon();

for (Point2D.Double point : xys) {
    int adjustedX = (int) (IMAGE_PADDING + (point.getX() - minX));

    // need to invert the Y since 0,0 starts at top left
    int adjustedY = (int) (IMAGE_HEIGHT - IMAGE_PADDING - (point.getY() - minY));

    polygon.addPoint(adjustedX, adjustedY);
}

...

Here's the generated map:-

在此处输入图片说明

IT IS PERFECT!

UPDATE 01-25-2013

Here's the code to create the image map based on the width and height (in pixel). In this case, I'm not relying on the Java Map Project Library, instead, I extracted out the pertinent formula and embed it in my code. This gives you a greater control of the map generation, compared to the above code example that relies on an arbitrary scaling value (the example above uses 6000).

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

RESULT: Image width = 600px, Image height = 600px, Image padding = 50px

在此处输入图片说明

RESULT: Image width = 300px, Image height = 500px, Image padding = 50px

在此处输入图片说明

Anon :

The big issue with plotting maps is that the spherical surface of the Earth cannot be conveniently converted into a flat representation. There are a bunch of different projections that attempt to resolve this.

墨卡托(Mercator)是最简单的一种:它假定纬度相等的线是平行的水平线,而经度相等的线是平行的垂直线。这对纬度有效(无论您在何处,1纬度大约等于111 km),但对经度无效(经度的表面距离与纬度的余弦成正比)。

但是,只要您在大约45度以下(明尼苏达州的大部分地区),墨卡托投影就可以很好地工作,并且可以创建大多数人会从他们的小学地图中识别的形式。这非常简单:只需将这些点视为绝对坐标,然后缩放到要在其上绘制它们的任何空间即可。无需进行trig。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

以正确的方式将经度和纬度坐标转换为地图像素(X和Y)

将(旋转的)ImageOverlay中的X和Y坐标转换为纬度和经度

将经度和纬度转换为Alber投影上的图像X,Y像素坐标

将英国网格坐标(X,Y)转换为经度和纬度

如何将向量(X,Y)位置转换为纬度/经度坐标?Java脚本

将地图坐标转换为纬度/经度

将 X 和 Y 转换为纬度和经度

在边界框内将纬度/经度转换为X,Y位置

将X,Y像素转换为经度和纬度

将经度和纬度坐标转换为地图像素X和Y坐标的图像Java

Cartopy:将点从轴坐标转换为纬度/经度坐标

将图块的xyz坐标转换为经度/纬度

将纬度和经度转换为图像上的坐标

使用python将经度和纬度转换为x和y网格系统

将GPS纬度和经度跟踪数据转换为足球场上的位置x和位置y?

如何使用空间参考wkid:102100将经纬度坐标转换为x和y

如何将经度,纬度,高程转换为笛卡尔坐标?

将纬度/经度转换为MGRS坐标,反之亦然的Java库?

如何在Swift中绘制地址,将地址转换为经度和纬度坐标?

如何将度,分,秒和方向转换为经度和纬度坐标?

如何将十进制的经度和纬度转换为WGS84坐标?

将纬度/经度坐标转换为R中的位置时,NA返回不正确

如何将 WGS84 纬度经度转换为 UWP MapControl 的 XY Tile 坐标

将经度/纬度坐标转换为Lambert保形圆锥投影

将opengl中的纬度和经度转换为右手世界坐标

如何使用 Xamarin.Forms.GoogleMaps 将地图纬度和经度转换为像素坐标?

无法将屏幕 x,y 坐标转换为 svg 坐标

将GPS坐标转换为X,Y,Z坐标

在pyspark中将纬度和经度转换为UTM坐标