geotools使用

项目中遇到要用经纬度计算长度,周长,面积,电子栅栏的情况,可以使用geotools

准备工作

首先我们引入jar依赖

        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-geometry</artifactId>
            <version>24.2</version>
        </dependency>
        <dependency>
            <groupId>org.geotools</groupId>
            <artifactId>gt-epsg-hsql</artifactId>
            <version>24.2</version>
        </dependency>

很多同学会说下载不下来,我也遇到了这种情况,是马云的仓库里没有,这里我们换个仓库下载,

要贴在repositories标签里

        <!--   下载 geotools用的     -->
        <repository>
            <id>osgeo</id>
            <name>OSGeo Release Repository</name>
            <url>https://repo.osgeo.org/repository/release/</url>
            <snapshots><enabled>false</enabled></snapshots>
            <releases><enabled>true</enabled></releases>
        </repository>
        <repository>
            <id>osgeo-snapshot</id>
            <name>OSGeo Snapshot Repository</name>
            <url>https://repo.osgeo.org/repository/snapshot/</url>
            <snapshots><enabled>true</enabled></snapshots>
            <releases><enabled>false</enabled></releases>
        </repository>
        <!--   下载 geotools用的     -->

依赖搞定了,我们试试API

场景1:判断点是否在电子围栏中

我这次所有API用到的import如下

值得注意的是我的代码大多数来自ChatGPT,但是他给的不一定完全正确,他告诉我要引入24.2版本的,但是24.2版本的org.locationtech.jts这个包名和旧版本的不一样了

import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.geotools.referencing.GeodeticCalculator;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.geom.impl.CoordinateArraySequence;
import org.locationtech.jts.io.WKTReader;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
    // 判断点是否在电子围栏中
    public static boolean isPointInFence(double lon, double lat, String fenceWkt) throws Exception {
        GeometryFactory gf = new GeometryFactory();
        WKTReader reader = new WKTReader(gf);

        // 解析电子围栏的边界点列表
        Coordinate[] coords = parseCoordinates(fenceWkt);
        LinearRing ring = new LinearRing(new CoordinateArraySequence(coords), gf);
        Polygon fence = new Polygon(ring, null, gf);

        // 创建目标点
        Point point = gf.createPoint(new Coordinate(lon, lat));

        // 判断点是否在围栏内
        return fence.contains(point);
    }

    // 解析WKT格式的坐标串
    private static Coordinate[] parseCoordinates(String wkt) throws Exception {
        WKTReader reader = new WKTReader();
        Geometry geom = reader.read(wkt);
        return geom.getCoordinates();
    }
// 测试--点是否在电子围栏中
        String fenceWkt = "POLYGON((116.412928 39.930636,116.413198 39.928658,116.417348 39.928713,116.417043 39.930678,116.412928 39.930636))";
        double lon = 116.414797;
        double lat = 39.929792;
        boolean isInFence = GeoToolsUtils.isPointInFence(lon, lat, fenceWkt);
        System.out.println(isInFence);

场景2:经纬度点与点之间的距离

    public static double geotoolsDistance(double lon1, double lat1, double lon2, double lat2) throws Exception {
        CoordinateReferenceSystem crs = org.geotools.referencing.crs.DefaultGeographicCRS.WGS84;
        GeodeticCalculator gc = new GeodeticCalculator(crs);
        JTSFactoryFinder.getGeometryFactory(null);
        Coordinate coord1 = new Coordinate(lon1, lat1);
        Coordinate coord2 = new Coordinate(lon2, lat2);
        gc.setStartingPosition(org.geotools.geometry.jts.JTS.toDirectPosition(coord1, crs));
        gc.setDestinationPosition(org.geotools.geometry.jts.JTS.toDirectPosition(coord2, crs));
        return gc.getOrthodromicDistance() / 1000; // 单位为千米
    }

一开始ChatGPT给的代码是下面的

    public static final double R = 6371.0; // 地球平均半径,单位为千米

    public static double haversine(double lon1, double lat1, double lon2, double lat2) {
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        double d = R * c;
        return d;
    }
        //经纬度点与点之间的距离
        double distance = GeoToolsUtils.geotoolsDistance(116.509442, 39.98279, 116.509442, 39.982556);
        System.out.println(distance);
        double distance2 = GeoToolsUtils.haversine(116.509442, 39.98279, 116.509442, 39.982556);
        System.out.println(distance2);
GeoTools计算的:0.025982026110380408
数学计算的:0.026019612834710798

场景三:判断点是否在圆内

就是给定圆心和半径

public static boolean isInCircle(double lon1, double lat1, double lon2, double lat2, double radius) {
    double distance = haversine(lon1, lat1, lon2, lat2); // 计算两点之间的距离
    return distance <= radius;
}

场景四:计算多边形面积

这个是结合地球半径,把多边形拆成多个三角形计算得出结果

    // 地球半径
    private static final double EARTH_RADIUS = 6378137.0; // meters

    // 计算多边形面积
    public static double calculatePolygonArea(List<Coordinate> coordinates) {
        double area = 0.0;
        int numPoints = coordinates.size();

        if (numPoints > 2) {
            for (int i = 0; i < numPoints; i++) {
                Coordinate p1 = coordinates.get(i);
                Coordinate p2 = coordinates.get((i + 1) % numPoints);
                area += Math.toRadians(p2.getX() - p1.getX()) *
                        (2 + Math.sin(Math.toRadians(p1.getY())) +
                                Math.sin(Math.toRadians(p2.getY())));
            }
            area = area * EARTH_RADIUS * EARTH_RADIUS / 2.0;
        }

        return Math.abs(area);
    }

测试

        List<Coordinate> polygonCoordinates = new ArrayList<>();
        polygonCoordinates.add(new Coordinate(116.412928, 39.930636)); // 左上角经纬度坐标
        polygonCoordinates.add(new Coordinate(116.413198, 39.928658)); // 左下角经纬度坐标
        polygonCoordinates.add(new Coordinate(116.417348, 39.928713)); // 右下角经纬度坐标
        polygonCoordinates.add(new Coordinate(116.417043, 39.930678)); // 右上角经纬度坐标
        polygonCoordinates.add(new Coordinate(116.412928, 39.930636)); // 右上角经纬度坐标

        double v = GeoToolsUtils.calculatePolygonArea(polygonCoordinates);
        System.out.println("多面积为:" + v);

"Mercator投影"。Mercator投影是一种圆柱投影,这个算法和通过球表面积计算的结果有很大出入,推荐用上的的解法。

    public static double calculateArea(List<Coordinate> coordinates) throws Exception {
        // 定义源坐标系和目标坐标系
        // 创建多边形对象
        GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();
        LinearRing linearRing = geometryFactory.createLinearRing(coordinates.toArray(new Coordinate[0]));
        Polygon polygon = geometryFactory.createPolygon(linearRing, null);

        // 创建坐标参考系对象
        CoordinateReferenceSystem sourceCRS = DefaultGeographicCRS.WGS84;
        CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:3857");

        // 创建坐标转换对象
        MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS);

        // 对多边形进行坐标转换
        Geometry geometry = JTS.transform(polygon, transform);
        System.out.println(geometry.getLength());
        // 计算多边形的面积
        return geometry.getArea();
    }

判断点是否在多边形内其他解法

    // 检查点是否位于多边形内
    public static boolean isPointInPolygon(Point2D.Double point, List<Point2D.Double> polygonCoords) {
        Path2D.Double polygon = new Path2D.Double();
        for (int i = 0; i < polygonCoords.size(); i++) {
            Point2D.Double coord = polygonCoords.get(i);
            if (i == 0) {
                polygon.moveTo(coord.getX(), coord.getY());
            } else {
                polygon.lineTo(coord.getX(), coord.getY());
            }
        }
        polygon.closePath();
        return polygon.contains(point);
    }
    public static void main(String[] args) {
        // 电子栅栏多边形的经纬度坐标列表
        List<Point2D.Double> polygonCoords = new ArrayList<>();
        polygonCoords.add(new Point2D.Double(39.930636, 116.412928));
        polygonCoords.add(new Point2D.Double(39.928658, 116.413198));
        polygonCoords.add(new Point2D.Double(39.928713, 116.417348));
        polygonCoords.add(new Point2D.Double(39.930678, 116.417043));
        polygonCoords.add(new Point2D.Double(39.930636, 116.412928));

        // 点的经纬度坐标列表
        List<Point2D.Double> pointCoords = new ArrayList<>();
        pointCoords.add(new Point2D.Double(39.929792, 116.414797));
        pointCoords.add(new Point2D.Double(39.929798, 116.414797));
        pointCoords.add(new Point2D.Double(37.400, -122.700));

        for (Point2D.Double point : pointCoords) {
            if (isPointInPolygon(point, polygonCoords)) {
                System.out.println("Point (" + point.getX() + ", " + point.getY() + ") is inside the polygon.");
            } else {
                System.out.println("Point (" + point.getX() + ", " + point.getY() + ") is outside the polygon.");
            }
        }
    }

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值