多边形外的点到多边形边界最近点的坐标

多边形外的点到多边形边界最近点的坐标

package com.ruoyi.system.utils;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.system.domain.TbRail;
import com.ruoyi.system.domain.TbRailPoint;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;

import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class RailUtil {


    /**
     * 找到多边形边界上一个点,使得这个点与多边形外面的指定点距离最短
     * @param railPointList
     * @param point
     * @return
     */
    public static TbRailPoint findClosestPointOnPolygonBoundary(List<Point2D.Double> railPointList, TbRailPoint point){
        TbRailPoint rtn = new TbRailPoint();
        rtn.setLongitude(BigDecimal.ZERO);
        rtn.setLatitude(BigDecimal.ZERO);
        Point2D.Double closestPointOnPolygonBoundary = findClosestPointOnPolygonBoundary(railPointList, new Point2D.Double(point.getLongitude().doubleValue(), point.getLatitude().doubleValue()));
        if (Objects.isNull(closestPointOnPolygonBoundary)){
            return rtn;
        }
        rtn.setLongitude(BigDecimal.valueOf(closestPointOnPolygonBoundary.x));
        rtn.setLatitude(BigDecimal.valueOf(closestPointOnPolygonBoundary.y));
        return rtn;
    }

    public static Point2D.Double findClosestPointOnPolygonBoundary(List<Point2D.Double> polygonVertices, Point2D.Double externalPoint) {
        double minDistance = Double.MAX_VALUE;
        Point2D.Double closestPoint = null;

        // 遍历多边形的每条边
        for (int i = 0; i < polygonVertices.size(); i++) {
            Point2D.Double p1 = polygonVertices.get(i);
            Point2D.Double p2 = polygonVertices.get((i + 1) % polygonVertices.size()); // 循环到下一个顶点

            // 计算边与externalPoint的最近点
            Point2D.Double nearestOnEdge = findNearestPointOnLineSegment(p1, p2, externalPoint);

            // 计算距离
            double distance = distance(externalPoint, nearestOnEdge);

            // 更新最小距离和对应的点
            if (distance < minDistance) {
                minDistance = distance;
                closestPoint = nearestOnEdge;
            }
        }

        return closestPoint;
    }

    // 计算两点之间的距离
    public static double distance(Point2D.Double p1, Point2D.Double p2) {
        return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
    }

    // 找到线段上与点P最近的点
    public static Point2D.Double findNearestPointOnLineSegment(Point2D.Double p, Point2D.Double q, Point2D.Double r) {
        double l2 = distance(p, q) * distance(p, q);

        if (l2 == 0.0) return p;

        double t = ((r.x - p.x) * (q.x - p.x) + (r.y - p.y) * (q.y - p.y)) / l2;

        t = Math.max(0, Math.min(1, t));

        Point2D.Double projection = new Point2D.Double();
        projection.x = p.x + t * (q.x - p.x);
        projection.y = p.y + t * (q.y - p.y);

        return projection;
    }


    /**
     * 点 p(我们要找到它到线段 ab 的最近点的点),以及线段的两个端点 a 和 end。它首先计算点 p 到线段 ab 的垂足位置,然后根据这个位置是否在线段上来确定最近的点。如果垂足不在线段上,则最近的点将是线段的起点或终点,具体取决于哪个点距离 point 更近。
     * @param point
     * @return
     */
    public static Point2D.Double perpendicularIntersection(Point2D.Double point, Point2D.Double lineStart, Point2D.Double lineEnd) {
        // 计算点到线段两个端点的向量
        double dx1 = lineEnd.x - lineStart.x;
        double dy1 = lineEnd.y - lineStart.y;
        double dx2 = point.x - lineStart.x;
        double dy2 = point.y - lineStart.y;

        // 计算点到线段垂直向量的点积
        double dotProduct = dx1 * dx2 + dy1 * dy2;

        // 如果点积为0,则点垂直于线段,即点在线段上或其延长线上
        if (dotProduct <= 0) {
            return lineStart; // 最近点就是线段的起点
        }

        // 计算线段长度
        double lineLengthSquared = dx1 * dx1 + dy1 * dy1;

        // 如果点积大于线段长度的平方,则点垂直于线段延长线,最近点是线段的终点
        if (dotProduct >= lineLengthSquared) {
            return lineEnd; // 最近点就是线段的终点
        }

        // 计算投影长度
        double projLength = dotProduct / lineLengthSquared;

        // 计算最近点的坐标
        double closestX = lineStart.x + dx1 * projLength;
        double closestY = lineStart.y + dy1 * projLength;

        // 返回最近点
        return new Point2D.Double(closestX,closestY);
    }

    public static TbRailPoint findClosestPointOnPolygonBoundary2(List<Point2D.Double> polygonVertices, TbRailPoint point) {
        TbRailPoint rtn = new TbRailPoint();
        rtn.setLongitude(BigDecimal.ZERO);
        rtn.setLatitude(BigDecimal.ZERO);
        Point2D.Double externalPoint = new Point2D.Double(point.getLongitude().doubleValue(), point.getLatitude().doubleValue());
        if (CollUtil.isEmpty(polygonVertices) || polygonVertices.size()!=4){
            log.info("【3D围栏维护】边界异常 {} ", JSON.toJSONString(polygonVertices));
            return rtn;
        }
        // 界外的点,对每一条边界做垂线,找到最近的点
        Point2D.Double p1 = perpendicularIntersection(externalPoint, polygonVertices.get(0), polygonVertices.get(1));
        Point2D.Double p2 = perpendicularIntersection(externalPoint, polygonVertices.get(1), polygonVertices.get(2));
        Point2D.Double p3 = perpendicularIntersection(externalPoint, polygonVertices.get(2), polygonVertices.get(3));
        Point2D.Double p4 = perpendicularIntersection(externalPoint, polygonVertices.get(3), polygonVertices.get(0));
        ArrayList<Point2D.Double> nearPts = CollUtil.newArrayList(p1, p2, p3, p4);
        nearPts.addAll(polygonVertices);
        double minDis = distance(externalPoint, p1);
        Point2D.Double nearest = p1;
        for (Point2D.Double nearPt : nearPts) {
            double distance = distance(externalPoint, p2);
            if (distance<minDis){
                minDis = distance;
                nearest = nearPt;
            }
        }
        rtn.setLongitude(BigDecimal.valueOf(nearest.x));
        rtn.setLatitude(BigDecimal.valueOf(nearest.y));
        return rtn;
    }


    public static void main(String[] args) {
//        
//        // 定义多边形的坐标
//        List<Point2D.Double> polygonCoords = new ArrayList<>();
//        polygonCoords.add(new Point2D.Double(1, 1));
//        polygonCoords.add(new Point2D.Double(3, 1));
//        polygonCoords.add(new Point2D.Double(3, 3));
//        polygonCoords.add(new Point2D.Double(1, 3));
//
//        // 定义外部点的坐标
//        Point2D.Double outsidePoint = new Point2D.Double(0, 2);
//
//        // 找到外部点到多边形边界最近的点
//        Point2D.Double nearestPoint = findClosestPointOnPolygonBoundary(polygonCoords, outsidePoint);
//
//        // 输出结果
//        System.out.println("Nearest Point to Polygon Boundary: " + nearestPoint);
        Point2D.Double point = new Point2D.Double(-1, -1);
        Point2D.Double intersection = perpendicularIntersection(point, new Point2D.Double(0.0, 0.0), new Point2D.Double(2.0, 2.0));
        System.out.println("Intersection point: (" + intersection.x + ", " + intersection.y + ")");
    }
}

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Geotools判断一个是否在一个多边形内,你可以按照以下步骤进行操作: 1. 创建一个CoordinateReferenceSystem对象,表示GCJ02坐标系。 ```java CoordinateReferenceSystem crs = CRS.decode("EPSG:4326"); // GCJ02 ``` 2. 创建一个GeometryFactory对象,并使用CoordinateReferenceSystem初始化它。 ```java GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null); ``` 3. 创建一个多边形的几何对象。你可以使用Coordinate数组来表示多边形的顶坐标。 ```java List<Coordinate> polygonCoordinates = new ArrayList<>(); polygonCoordinates.add(new Coordinate(lon1, lat1)); polygonCoordinates.add(new Coordinate(lon2, lat2)); // 添加更多的顶坐标... polygonCoordinates.add(new Coordinate(lonN, latN)); LinearRing linearRing = geometryFactory.createLinearRing(polygonCoordinates.toArray(new Coordinate[0])); Polygon polygon = geometryFactory.createPolygon(linearRing, null); ``` 4. 创建一个的几何对象。 ```java Point point = geometryFactory.createPoint(new Coordinate(lon, lat)); ``` 5. 使用`contains()`方法判断是否在多边形内。 ```java boolean isInside = polygon.contains(point); ``` `isInside`变量将包含判断结果,如果多边形内,则为true,否则为false。 请注意,以上示例假设输入的多边形边界均为GCJ02坐标。如果你的数据不是GCJ02坐标,你可能需要在进行判断之前进行坐标转换。可以使用前面提到的Geotools方法来实现坐标转换。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值