package com.ltgk.common.utils;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.awt.geom.Point2D;
import java.math.BigDecimal;
import java.util.List;

/**
 * @Author: wan kun
 * @Description: TODO
 * @Date: 2024/12/5 10:26
 * @Version: 1.0
 */
public class GeoUtils {
    private static final double EARTH_RADIUS = 6371.0; // 地球半径，单位为千米

    /**
     * 返回两个坐标点中间的坐标点 并返回相应的距离
     *
     * @param point1 第一个坐标点
     * @param point2 第二个坐标点
     * @return CenterAndDistance 返回坐标点以及距离 单位
     */
    public static CenterAndDistance calculateCenterAndDistance(CoordinatePoint point1, CoordinatePoint point2) {
        // 计算中心点
        BigDecimal latitude = point1.getLatitude().add(point2.getLatitude()).divide(BigDecimal.valueOf(2), 6, BigDecimal.ROUND_HALF_UP);
        BigDecimal longitude = point1.getLongitude().add(point2.getLongitude()).divide(BigDecimal.valueOf(2), 6, BigDecimal.ROUND_HALF_UP);
        CoordinatePoint centerPoint = new CoordinatePoint(longitude, latitude);
        double distance = calculateDistance(point1, centerPoint);
        // 返回结果
        return new CenterAndDistance(centerPoint, BigDecimal.valueOf(distance), "km");
    }

    /**
     * 计算两点之间的距离
     *
     * @param point1
     * @param point2
     * @return
     */
    public static double calculateDistance(CoordinatePoint point1, CoordinatePoint point2) {
        double phi1 = Math.toRadians(point1.getLatitude().doubleValue());
        double lambda1 = Math.toRadians(point1.getLongitude().doubleValue());
        double phi2 = Math.toRadians(point2.getLatitude().doubleValue());
        double lambda2 = Math.toRadians(point2.getLongitude().doubleValue());

        double deltaLambda = Math.abs(lambda1 - lambda2);
        double numerator = Math.sqrt(
                Math.pow(Math.cos(phi2) * Math.sin(deltaLambda), 2) +
                        Math.pow(Math.cos(phi1) * Math.sin(phi2) - Math.sin(phi1) * Math.cos(phi2) * Math.cos(deltaLambda), 2)
        );
        double denominator = Math.sin(phi1) * Math.sin(phi2) + Math.cos(phi1) * Math.cos(phi2) * Math.cos(deltaLambda);
        return EARTH_RADIUS * Math.atan2(numerator, denominator);
    }


    /**
     * 坐标点
     */
    @Data
    public static class CoordinatePoint {
        @JsonProperty(value = "longitude")
        @ApiModelProperty(value = "坐标点经度")
        private BigDecimal longitude;
        @JsonProperty(value = "latitude")
        @ApiModelProperty(value = "坐标点纬度")
        private BigDecimal latitude;

        public CoordinatePoint(BigDecimal longitude, BigDecimal latitude) {
            this.longitude = longitude;
            this.latitude = latitude;
        }
    }

    /**
     * 返回坐标点以及距离
     */
    @Data
    public static class CenterAndDistance {
        @JsonProperty(value = "centerPoint")
        @ApiModelProperty(value = "返回坐标点")
        private CoordinatePoint centerPoint;
        @JsonProperty(value = "distance")
        @ApiModelProperty(value = "距离")
        private BigDecimal distance;
        @JsonProperty(value = "unit")
        @ApiModelProperty(value = "单位")
        private String unit;

        public CenterAndDistance(CoordinatePoint centerPoint, BigDecimal distance, String unit) {
            this.centerPoint = centerPoint;
            this.distance = distance;
            this.unit = unit;
        }
    }

    /**
     * 判断点是否在多边形内
     *
     * @param point 测试点
     * @param pts   多边形的点
     * @return boolean
     * @Title: IsPointInPoly
     * @Description:
     */
    public static boolean isInPolygon(Point2D.Double point, List<Point2D.Double> pts) {
        int num = pts.size();
        // 交叉点数量
        int intersectCount = 0;
        // 浮点类型计算时候与0比较时候的容差
        double precision = 2e-10;
        // 临近顶点
        Point2D.Double p1, p2;
        p1 = pts.get(0);
        for (int i = 1; i <= num; ++i) {
            if (point.equals(p1)) {
                return true;
            }

            p2 = pts.get(i % num);
            if (point.x < Math.min(p1.x, p2.x) || point.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                continue;
            }

            //射线穿过算法
            if (point.x > Math.min(p1.x, p2.x) && point.x < Math.max(p1.x, p2.x)) {
                if (point.y <= Math.max(p1.y, p2.y)) {
                    if (p1.x == p2.x && point.y >= Math.min(p1.y, p2.y)) {
                        return true;
                    }

                    if (p1.y == p2.y) {
                        if (p1.y == point.y) {
                            return true;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = (point.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(point.y - xinters) < precision) {
                            return true;
                        }

                        if (point.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {
                if (point.x == p2.x && point.y <= p2.y) {
                    Point2D.Double p3 = pts.get((i + 1) % num);
                    if (point.x >= Math.min(p1.x, p3.x) && point.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }
        // 偶数在多边形外， 奇数在多边形内
        return intersectCount % 2 != 0;
    }
}
