空間搜索-射線法
在配送場景下,每個商戶都有自己的配送區域,底層數據是按照geojson格式保存對應的配送區域
比如下面的多邊形對應的存儲格式和地圖上表示的範圍
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[106.10595703125, 33.33970700424026],
[106.32568359375, 32.41706632846282],
[108.03955078125, 32.2313896627376],
[108.25927734375, 33.15594830078649],
[106.10595703125, 33.33970700424026]
]
]
}
}
在配送場景下每個商戶都有多個如上圖的配送區域,搜索乾的事就是判斷用戶定位點是不是在這個配送區域裏面,這不就是幾何判斷邏輯嘛!?
射線法
在幾何學中,PIP(Point in Polygon)問題即判斷一點在多邊形的內部或外部。
射線法(Ray casting algorithm)是一種判斷點是否在多邊形內部的一種簡單方法。即從該點做一條射線,計算它跟多邊形邊界的交點個數,如果交點個數爲奇數,那麼點在多邊形內部,否則點在多邊形外部。
如何理解呢?
其實,對於平面內任意閉合曲線,曲線都把平面分割成了內、外兩部分。對於平面內任意一條直線,在穿越多邊形邊界時,有且只有兩種情況:進入多邊形或穿出多邊形。即:
如果點在多邊形內部,射線第一次穿越邊界一定是穿出多邊形。
如果點在多邊形外部,射線第一次穿越邊界一定是進入多邊形。
由於直線可以無限延伸,而閉合曲線包圍的區域是有限的,因此最後一次穿越多邊形邊界,一定是穿出多邊形,到達外部。
由上可推斷,從一點做一條射線,計算它跟多邊形邊界的交點個數,如果交點個數爲奇數,那麼點在多邊形內部,否則點在多邊形外部。
這裏直接上代碼
import java.io.Serializable;
import java.util.List;
/**
* @author duson
*/
public class MyPoly {
private List<Corner> corner;
public List<Corner> getCorner() {
return corner;
}
public void setCorner(List<Corner> corner) {
this.corner = corner;
}
public boolean searchPoint(Point point) {
if (corner == null || corner.size() == 0) {
return false;
}
double x = point.origX();
double y = point.origY();
int points = corner.size();
int hits = 0;
double lastX = corner.get(points - 1).getPolyX();
double lastY = corner.get(points - 1).getPolyY();
double curX, curY;
for (int i = 0; i < points; lastX = curX, lastY = curY, i++) {
curX = corner.get(i).getPolyX();
curY = corner.get(i).getPolyY();
if (x == curX && y == curY) {
return true;
}
if (curY == lastY) {
continue;
}
double leftX;
if (curX < lastX) {
if (x >= lastX) {
continue;
}
leftX = curX;
} else {
if (x >= curX) {
continue;
}
leftX = lastX;
}
double test1, test2;
if (curY < lastY) {
if (y < curY || y >= lastY) {
continue;
}
if (x < leftX) {
hits++;
continue;
}
test1 = x - curX;
test2 = y - curY;
} else {
if (y < lastY || y >= curY) {
continue;
}
if (x < leftX) {
hits++;
continue;
}
test1 = x - lastX;
test2 = y - lastY;
}
if (test1 < (test2 / (lastY - curY) * (lastX - curX))) {
hits++;
}
}
return (hits & 1) != 0;
}
public static final class Point {
private final double origX;
private final double origY;
private Point(double x, double y) {
this.origX = x;
this.origY = y;
}
public double origX() {
return this.origX;
}
public double origY() {
return this.origY;
}
}
public static class Corner implements Serializable {
private int polyX;
private int polyY;
public Corner(double polyX, double polyY) {
this.polyX = (int) (polyX * 1000000d);
this.polyY = (int) (polyY * 1000000d);
}
public double getPolyX() {
return polyX / 1000000d;
}
public void setPolyX(int polyX) {
this.polyX = polyX;
}
public double getPolyY() {
return polyY / 1000000d;
}
public void setPolyY(int polyY) {
this.polyY = polyY;
}
}
}