package com.sdtool.common.tool;

import com.sdtool.common.entity.geo.GeoPoiLocation;

import java.math.BigDecimal;
import java.util.List;

/**
 * Created by jlutt on 2022-02-17
 *
 * @author jlutt
 */
public class GeoUtils {

    /**
     * 判断点是否在多边形内
     * https://cbxg.icu/?p=10
     * poi:点对象
     * pois:点集合
     */
    public static boolean pointIsInBlock(GeoPoiLocation poi, List<GeoPoiLocation> pois) {

        int N = pois.size();
        boolean boundOrVertex = true;
        int intersectCount = 0;//交叉点数量
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差

        GeoPoiLocation poi1 = new GeoPoiLocation();
        GeoPoiLocation poi2 = new GeoPoiLocation();

        poi1 = pois.get(0);
        for (int i = 1; i <= N; ++i) {
            if (poi.equals(poi1)) {
                return boundOrVertex;
            }

            poi2 = pois.get(i % N);
            if (poi.getLongitude() < Math.min(poi1.getLongitude(), poi2.getLongitude()) || poi.getLongitude() > Math.max(poi1.getLongitude(), poi2.getLongitude())) {
                poi1 = poi2;
                continue;
            }

            //射线穿过算法
            if (poi.getLongitude() > Math.min(poi1.getLongitude(), poi2.getLongitude()) && poi.getLongitude() < Math.max(poi1.getLongitude(), poi2.getLongitude())) {
                if (poi.getLatitude() <= Math.max(poi1.getLatitude(), poi2.getLatitude())) {
                    if (poi1.getLongitude() == poi2.getLongitude() && poi.getLatitude() >= Math.min(poi1.getLatitude(), poi2.getLatitude())) {
                        return boundOrVertex;
                    }

                    if (poi1.getLatitude() == poi2.getLatitude()) {
                        if (poi1.getLatitude() == poi.getLatitude()) {
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }
                    } else {
                        double xinters = (poi.getLongitude() - poi1.getLongitude()) * (poi2.getLatitude() - poi1.getLatitude()) / (poi2.getLongitude() - poi1.getLongitude()) + poi1.getLatitude();
                        if (Math.abs(poi.getLatitude() - xinters) < precision) {
                            return boundOrVertex;
                        }

                        if (poi.getLatitude() < xinters) {
                            ++intersectCount;
                        }
                    }
                }
            } else {
                if (poi.getLongitude() == poi2.getLongitude() && poi.getLatitude() <= poi2.getLatitude()) {
                    GeoPoiLocation p3 = pois.get((i + 1) % N);
                    if (poi.getLongitude() >= Math.min(poi1.getLongitude(), p3.getLongitude()) && poi.getLongitude() <= Math.max(poi1.getLongitude(), p3.getLongitude())) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            poi1 = poi2;
        }
        if (intersectCount % 2 == 0) {//偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    /**
     * 计算电子围栏面积
     */
    public static BigDecimal getArea(List<GeoPoiLocation> ring) {
        double sJ = 6378137;
        double Hq = 0.017453292519943295;
        double c = sJ * Hq;
        double d = 0;

        if (3 > ring.size()) {
            return new BigDecimal(0);
        }

        for (int i = 0; i < ring.size() - 1; i++) {
            GeoPoiLocation h = ring.get(i);
            GeoPoiLocation k = ring.get(i + 1);
            double u = h.getLongitude() * c * Math.cos(h.getLatitude() * Hq);
            double hhh = h.getLatitude() * c;
            double v = k.getLongitude() * c * Math.cos(k.getLatitude() * Hq);
            d = d + (u * k.getLatitude() * c - v * hhh);
        }
        GeoPoiLocation g1 = ring.get(ring.size() - 1);
        GeoPoiLocation point = ring.get(0);
        double eee = g1.getLongitude() * c * Math.cos(g1.getLatitude() * Hq);
        double g2 = g1.getLatitude() * c;
        double k = point.getLongitude() * c * Math.cos(point.getLatitude() * Hq);
        d += eee * point.getLatitude() * c - k * g2;
        return BigDecimal.valueOf(0.5 * Math.abs(d)).divide(BigDecimal.ONE);
    }
}
