GeoUtils.java 7.55 KB
package com.bsth.util.Geo;

import java.util.ArrayList;
import java.util.List;


public class GeoUtils {

	private final static double EARTH_RADIUS = 6378137;
	private final static double INLINE_THRESHOLD = Integer.parseInt("120");
	private final static double DRIFT_THRESHOLD = Integer.parseInt("2000");

	public static boolean isPointInRect(Point point, Bounds bounds) {
		Point sw = bounds.getSouthWest(); // 西南脚点
		Point ne = bounds.getNorthEast(); // 东北脚点
		return (point.getLon() >= sw.getLon() && point.getLon() <= ne.getLon()
				&& point.getLat() >= sw.getLat() && point.getLat() <= ne
				.getLat());
	}

	public static boolean isPointInCircle(Point point, Circle circle) {
		// point与圆心距离小于圆形半径,则点在圆内,否则在圆外
		Point c = circle.getCenter();
		double r = circle.getRadius();

		double dis = getDistance(point, c);
		if (dis <= r) {
			return true;
		} else {
			return false;
		}
	}

	public static boolean isPointInPolygon(Point point, Polygon polygon) {
		Bounds polygonBounds = polygon.getBounds();
		if (!isPointInRect(point, polygonBounds)) {
			return false;
		}

		List<Point> pts = polygon.getPoints();// 获取多边形点

		// 下述代码来源:http://paulbourke.net/geometry/insidepoly/,进行了部分修改
		// 基本思想是利用射线法,计算射线与多边形各边的交点,如果是偶数,则点在多边形外,否则
		// 在多边形内。还会考虑一些特殊情况,如点在多边形顶点上,点在多边形边上等特殊情况。

		int N = pts.size();
		boolean boundOrVertex = true; // 如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
		int intersectCount = 0;// cross points count of x
		double precision = 2e-10; // 浮点类型计算时候与0比较时候的容差
		Point p1, p2;// neighbour bound vertices
		Point p = point; // 测试点

		p1 = pts.get(0);// left vertex
		for (int i = 1; i <= N; ++i) {// check all rays
			if (p.equals(p1)) {
				return boundOrVertex;// p is an vertex
			}

			p2 = pts.get(i % N);// right vertex
			if (p.getLat() < Math.min(p1.getLat(), p2.getLat())
					|| p.getLat() > Math.max(p1.getLat(), p2.getLat())) {// ray
																			// is
																			// outside
																			// of
																			// our
																			// interests
				p1 = p2;
				continue;// next ray left point
			}

			if (p.getLat() > Math.min(p1.getLat(), p2.getLat())
					&& p.getLat() < Math.max(p1.getLat(), p2.getLat())) {// ray
																			// is
																			// crossing
																			// over
																			// by
																			// the
																			// algorithm
																			// (common
																			// part
																			// of)
				if (p.getLon() <= Math.max(p1.getLon(), p2.getLon())) {// x is
																		// before
																		// of
																		// ray
					if (p1.getLat() == p2.getLat()
							&& p.getLon() >= Math.min(p1.getLon(), p2.getLon())) {// overlies
																					// on
																					// a
																					// horizontal
																					// ray
						return boundOrVertex;
					}

					if (p1.getLon() == p2.getLon()) {// ray is vertical
						if (p1.getLon() == p.getLon()) {// overlies on a
														// vertical ray
							return boundOrVertex;
						} else {// before ray
							++intersectCount;
						}
					} else {// cross point on the left side
						double xinters = (p.getLat() - p1.getLat())
								* (p2.getLon() - p1.getLon())
								/ (p2.getLat() - p1.getLat()) + p1.getLon();// cross
																			// point
																			// of
																			// lng
						if (Math.abs(p.getLon() - xinters) < precision) {// overlies
																			// on
																			// a
																			// ray
							return boundOrVertex;
						}

						if (p.getLon() < xinters) {// before ray
							++intersectCount;
						}
					}
				}
			} else {// special case when ray is crossing through the vertex
				if (p.getLat() == p2.getLat() && p.getLon() <= p2.getLon()) {// p
																				// crossing
																				// over
																				// p2
					Point p3 = pts.get((i + 1) % N); // next vertex
					if (p.getLat() >= Math.min(p1.getLat(), p3.getLat())
							&& p.getLat() <= Math.max(p1.getLat(), p3.getLat())) {// p.lat
																					// lies
																					// between
																					// p1.lat
																					// &
																					// p3.lat
						++intersectCount;
					} else {
						intersectCount += 2;
					}
				}
			}
			p1 = p2;// next ray left point
		}

		if (intersectCount % 2 == 0) {// 偶数在多边形外
			return false;
		} else { // 奇数在多边形内
			return true;
		}
	}

	private static double degreeToRad(double degree) {
		return Math.PI * degree / 180;
	}

	private static double radToDegree(double rad) {
		return (180 * rad) / Math.PI;
	}

	private static double getRange(double v, double a, double b) {
		v = Math.min(Math.max(v, a), b);
		return v;
	}

	private static double getLoop(double v, double a, double b) {
		while (v > b) {
			v -= b - a;
		}
		while (v < a) {
			v += b - a;
		}
		return v;
	}
	
	/**
	 * 判断点是否在某个路段
	 * @param ps
	 * @param p
	 * @return
	 */
	public static boolean isInSection(List<Point> ps, Point p) {
		for (int i = 0, len = ps.size();i < len - 1;i++) {
			if (isInSection(ps.get(i), ps.get(i + 1), p)) return true;
		}
		return false;
	}
	
	/**
	 * 判断点是否在某个线段上
	 * @param lp1
	 * @param lp2
	 * @param p
	 * @return
	 */
	private static boolean isInSection(Point lp1, Point lp2, Point p) {
		double a = lp1.getLat() - lp2.getLat(), b = lp2.getLon() - lp1.getLon(), c = lp1.getLon()*lp2.getLat() - lp2.getLon()*lp1.getLat();
		double lon = (Math.pow(b, 2)*p.getLon() - a*b*p.getLat() - a*c)/(Math.pow(a, 2) + Math.pow(b, 2));
		double lat = (Math.pow(a, 2)*p.getLat() - a*b*p.getLon() - b*c)/(Math.pow(a, 2) + Math.pow(b, 2));
		Point vp = new Point(lon, lat);
		return getDistance(p, vp) < INLINE_THRESHOLD && isBetween(lp1, lp2, vp);
	}
	
	private static boolean isBetween(Point lp1, Point lp2, Point p) {
		double lon1 = lp1.getLon(), lat1 = lp1.getLat();
		double lon2 = lp2.getLon(), lat2 = lp2.getLat();
		double lon = p.getLon(), lat = p.getLat();
		return (lon > lon1 && lon < lon2 || lon > lon2 && lon < lon1) || (lat > lat1 && lat < lat2 || lat > lat2 && lat < lat1);
	}
	
	public static boolean isDrift(Point p1, Point p2) {
		return getDistance(p1, p2) > DRIFT_THRESHOLD;
	}

	public static double getDistance(Point p1, Point p2) {
		double lng1 = getLoop(p1.getLon(), -180, 180), lat1 = getRange(
				p1.getLat(), -74, 74);
		double lng2 = getLoop(p2.getLon(), -180, 180), lat2 = getRange(
				p2.getLat(), -74, 74);

		double x1, x2, y1, y2;
		x1 = degreeToRad(lng1);
		y1 = degreeToRad(lat1);
		x2 = degreeToRad(lng2);
		y2 = degreeToRad(lat2);
		return EARTH_RADIUS
				* Math.acos((Math.sin(y1) * Math.sin(y2) + Math.cos(y1)
						* Math.cos(y2) * Math.cos(x2 - x1)));
	}
	
	public static void main(String args[]) {
		String a = "121.644416 31.195991, 121.643949 31.195928, 121.64364 31.195886";
		List<Point> points = new ArrayList<>();
		for(String t1 : a.split(", ")){
			String[] arr = t1.split(" ");
			Point p = new Point(Double.parseDouble(arr[0]), Double.parseDouble(arr[1]));
			points.add(p);
		}
		System.out.println(isInSection(points, new Point(121.644416, 31.195991)));
	}
}