【问题标题】:Detecting whether a GPS coordinate falls within a polygon on a map检测 GPS 坐标是否落在地图上的多边形内
【发布时间】:2011-05-16 07:48:06
【问题描述】:

如标题所述,目标是有一种方法来检测给定的 GPS 坐标是否落在多边形内。

多边形本身可以是凸的也可以是凹的。它被定义为一组边向量和该多边形内的一个已知点。每个边缘向量由四个坐标进一步定义,这些坐标是各个尖端的经纬度和相对于起点的方位。

在 StackOverflow 上有几个与此类似的问题,但它们仅以一般术语和 2D 平面描述解决方案,而我正在寻找支持由 @ 中的纬度/经度对定义的多边形的现有实现987654321@.

有哪些 API 或服务可用于进行此类碰撞测试?

【问题讨论】:

  • 你的多边形是由大圆线段定义的,还是由恒定纬度/经度点的线定义的?

标签: math geolocation gps coordinates geospatial


【解决方案1】:

JavaScript 版本 -

{
const PI = 3.14159265;
const TWOPI = 2*PI;
function isCoordinateInsidePitch(latitude, longitude, latArray, longArray)
{       
       let angle=0;
       let p1Lat;
       let p1Long;
       let p2Lat;
       let p2Long;
       let n = latArray.length;

       for (let i = 0; i < n; i++) {
          p1Lat = latArray[i] - latitude;
          p1Long = longArray[i] - longitude;
          p2Lat = latArray[(i+1)%n] - latitude;
          p2Long = longArray[(i+1)%n] - longitude;
          angle += angle2D(p1Lat,p1Long,p2Lat,p2Long);
       }

       return !(Math.abs(angle) < PI);
}

function angle2D(y1, x1, y2, x2)
{
   let dtheta,theta1,theta2;

   theta1 = Math.atan2(y1,x1);
   theta2 = Math.atan2(y2,x2);
   dtheta = theta2 - theta1;
   while (dtheta > PI)
      dtheta -= TWOPI;
   while (dtheta < -PI)
      dtheta += TWOPI;

   return dtheta;
}

function isValidCoordinate(latitude,longitude)
{
    return (
    latitude !== '' && longitude !== '' && !isNaN(latitude) 
     && !isNaN(longitude) && latitude > -90 &&
     latitude < 90 && longitude > -180 && longitude < 180
     )
}
let latArray = [32.10458, 32.10479, 32.1038, 32.10361];
let longArray = [34.86448, 34.86529, 34.86563, 34.86486];
// true
console.log(isCoordinateInsidePitch(32.104447, 34.865108,latArray, longArray));
// false
// isCoordinateInsidePitch(32.104974, 34.864576,latArray, longArray);
// true
// isValidCoordinate(0, 0)
// true
// isValidCoordinate(32.104974, 34.864576)
}

【讨论】:

  • 它不适用于多边形,可能只适用于矩形。测试数据... let latArray = [32.10458, 32.10479, 32.1038, 32.10361, 32.104447];让 longArray = [34.86448, 34.86529, 34.86563, 34.86486, 34.866];测试点:32.10456,34.86552在里面,但是你的代码返回false。
【解决方案2】:

我首先想到的是 shab(他的提议被称为 Ray-Casting Algorithm),但后来又像 Spacedman 一样思考:

...但是所有几何图形都必须在球坐标中重做...

我实现并测试了数学上正确的方法,即与大圆相交并确定两个相交点之一是否在两条弧上。 (注意:我按照here描述的步骤进行操作,但是我发现了几个错误:第6步结束时缺少sign函数(就在arcsin之前),最后的测试是数字垃圾(因为减法是条件差);使用相当L_1T &gt;= max(L_1a, L_1b) 来测试S1 是否在第一条弧线等)

这也是极其缓慢和数字噩梦(评估约 100 个三角函数等);事实证明它在我们的嵌入式系统中不可用。

不过有一个技巧:如果您考虑的区域足够小,只需进行标准制图投影,例如spherical Mercator projection,各点:

// latitude, longitude in radians
x = longitude;
y = log(tan(pi/4 + latitude/2));

然后,您可以应用光线投射,通过此函数检查弧的交点:

public bool ArcsIntersecting(double x1, double y1, double x2, double y2, 
  double x3, double y3, double x4, double y4)
    {

    double vx1 = x2 - x1;
    double vy1 = y2 - y1;

    double vx2 = x4 - x3;
    double vy2 = y4 - y3;

    double denom = vx1 * vy2 - vx2 * vy1;

    if (denom == 0) { return false; } // edges are parallel

    double t1 = (vx2 * (y1 - y3) - vy2 * (x1 - x3)) / denom;

    double t2;

    if (vx2 != 0) { t2 = (x1 - x3 + t1 * vx1) / vx2; }
    else if (vy2 != 0) { t2 = (y1 - y3 + t1 * vy1) / vy2; }
    else { return false; } // edges are matching

    return min(t1, t2) >= 0 && max(t1, t2) <= 1;
}

【讨论】:

  • "// latitude, longitude in radians" 这是否意味着我需要将 lat/long 转换为弧度,然后应用这些公式,或者这些公式是转换它们的公式?
  • 在您链接的论文中找到公式:弧度 = 度 × 0.0174533
【解决方案3】:

这是一个 java 程序,它使用一个函数,如果在由纬度/经度列表定义的多边形内找到纬度/经度,该函数将返回 true,并演示佛罗里达州。

我不确定它是否处理纬度/经度 GPS 系统不是 x/y 坐标平面的事实。对于我的使用,我已经证明它有效(我认为如果你在边界框中指定足够多的点,它会消除地球是球体的效果,地球上两点之间的直线不是箭头直线.

首先指定构成多边形角点的点,它可以有凹角和凸角。我在下面使用的坐标描绘了佛罗里达州的周长。

方法coordinate_is_inside_polygon 使用了一种我不太了解的算法。这是我得到它的来源的官方解释:

"... Philippe Reverdy 提出的解决方案是计算测试点与构成多边形的每对点之间的角度之和。如果该和为 2pi,则该点为内部点,如果为 0那么这个点就是一个外部点。这也适用于有孔的多边形,因为多边形是用一条由进出孔的重合边组成的路径定义的,这在许多 CAD 软件包中很常见。"

我的单元测试表明它确实可以可靠地工作,即使边界框是“C”形或什至像Torus 一样。 (我的单元测试测试了佛罗里达州内的许多点,并确保函数返回 true。我在世界其他任何地方选择了许多坐标并确保它返回 false。我选择了世界各地可能会混淆它的地方。

如果多边形边界框穿过赤道、本初子午线或坐标从 -180 -> 180、-90 -> 90 变化的任何区域,我不确定这是否有效。或者您的多边形环绕地球围绕北极/南极。对我来说,我只需要它在佛罗里达周边工作。如果您必须定义一个跨越地球或跨越这些线的多边形,您可以通过制作两个多边形来解决它,一个代表子午线一侧的区域,一个代表另一侧的区域并测试您的点位于其中任何一点。

我在这里找到了这个算法:Determining if a point lies on the interior of a polygon - Solution 2

自己运行以仔细检查。

将其放入名为 Runner.java 的文件中

import java.util.ArrayList;
public class Runner
{
    public static double PI = 3.14159265;
    public static double TWOPI = 2*PI;
    public static void main(String[] args) {
    ArrayList<Double> lat_array = new ArrayList<Double>();
    ArrayList<Double> long_array = new ArrayList<Double>();

    //This is the polygon bounding box, if you plot it, 
    //you'll notice it is a rough tracing of the parameter of 
    //the state of Florida starting at the upper left, moving 
    //clockwise, and finishing at the upper left corner of florida.

    ArrayList<String> polygon_lat_long_pairs = new ArrayList<String>();
    polygon_lat_long_pairs.add("31.000213,-87.584839");  
    //lat/long of upper left tip of florida.
    polygon_lat_long_pairs.add("31.009629,-85.003052");
    polygon_lat_long_pairs.add("30.726726,-84.838257");
    polygon_lat_long_pairs.add("30.584962,-82.168579");
    polygon_lat_long_pairs.add("30.73617,-81.476441");  
    //lat/long of upper right tip of florida.
    polygon_lat_long_pairs.add("29.002375,-80.795288");
    polygon_lat_long_pairs.add("26.896598,-79.938355");
    polygon_lat_long_pairs.add("25.813738,-80.059204");
    polygon_lat_long_pairs.add("24.93028,-80.454712");
    polygon_lat_long_pairs.add("24.401135,-81.817017");
    polygon_lat_long_pairs.add("24.700927,-81.959839");
    polygon_lat_long_pairs.add("24.950203,-81.124878");
    polygon_lat_long_pairs.add("26.0015,-82.014771");
    polygon_lat_long_pairs.add("27.833247,-83.014527");
    polygon_lat_long_pairs.add("28.8389,-82.871704");
    polygon_lat_long_pairs.add("29.987293,-84.091187");
    polygon_lat_long_pairs.add("29.539053,-85.134888");
    polygon_lat_long_pairs.add("30.272352,-86.47522");
    polygon_lat_long_pairs.add("30.281839,-87.628784");

    //Convert the strings to doubles.       
    for(String s : polygon_lat_long_pairs){
        lat_array.add(Double.parseDouble(s.split(",")[0]));
        long_array.add(Double.parseDouble(s.split(",")[1]));
    }

   //prints TRUE true because the lat/long passed in is
    //inside the bounding box.
    System.out.println(coordinate_is_inside_polygon(
            25.7814014D,-80.186969D,
            lat_array, long_array));

    //prints FALSE because the lat/long passed in 
    //is Not inside the bounding box.
    System.out.println(coordinate_is_inside_polygon(
            25.831538D,-1.069338D, 
            lat_array, long_array));

}
public static boolean coordinate_is_inside_polygon(
    double latitude, double longitude, 
    ArrayList<Double> lat_array, ArrayList<Double> long_array)
{       
       int i;
       double angle=0;
       double point1_lat;
       double point1_long;
       double point2_lat;
       double point2_long;
       int n = lat_array.size();

       for (i=0;i<n;i++) {
          point1_lat = lat_array.get(i) - latitude;
          point1_long = long_array.get(i) - longitude;
          point2_lat = lat_array.get((i+1)%n) - latitude; 
          //you should have paid more attention in high school geometry.
          point2_long = long_array.get((i+1)%n) - longitude;
          angle += Angle2D(point1_lat,point1_long,point2_lat,point2_long);
       }

       if (Math.abs(angle) < PI)
          return false;
       else
          return true;
}

public static double Angle2D(double y1, double x1, double y2, double x2)
{
   double dtheta,theta1,theta2;

   theta1 = Math.atan2(y1,x1);
   theta2 = Math.atan2(y2,x2);
   dtheta = theta2 - theta1;
   while (dtheta > PI)
      dtheta -= TWOPI;
   while (dtheta < -PI)
      dtheta += TWOPI;

   return(dtheta);
}

public static boolean is_valid_gps_coordinate(double latitude, 
    double longitude)
{
    //This is a bonus function, it's unused, to reject invalid lat/longs.
    if (latitude > -90 && latitude < 90 && 
            longitude > -180 && longitude < 180)
    {
        return true;
    }
    return false;
}
}

恶魔魔法需要进行单元测试。将其放入名为 MainTest.java 的文件中以验证它是否适合您

import java.util.ArrayList;
import org.junit.Test;
import static org.junit.Assert.*;

public class MainTest {
@Test
public void test_lat_long_in_bounds(){
    Runner r = new Runner();
    //These make sure the lat/long passed in is a valid gps 
    //lat/long coordinate.  These should be valid. 
    assertTrue(r.is_valid_gps_coordinate(25, -82));
    assertTrue(r.is_valid_gps_coordinate(-25, -82));
    assertTrue(r.is_valid_gps_coordinate(25, 82));
    assertTrue(r.is_valid_gps_coordinate(-25, 82));
    assertTrue(r.is_valid_gps_coordinate(0, 0));
    assertTrue(r.is_valid_gps_coordinate(89, 179));
    assertTrue(r.is_valid_gps_coordinate(-89, -179));
    assertTrue(r.is_valid_gps_coordinate(89.999, 179));
    //If your bounding box crosses the equator or prime meridian, 
    then you have to test for those situations still work.
}
@Test
public void realTest_for_points_inside()
{
    ArrayList<Double> lat_array = new ArrayList<Double>();
    ArrayList<Double> long_array = new ArrayList<Double>();
    ArrayList<String> polygon_lat_long_pairs = new ArrayList<String>();
    //upper left tip of florida.
    polygon_lat_long_pairs.add("31.000213,-87.584839");
    polygon_lat_long_pairs.add("31.009629,-85.003052");
    polygon_lat_long_pairs.add("30.726726,-84.838257");
    polygon_lat_long_pairs.add("30.584962,-82.168579");
    polygon_lat_long_pairs.add("30.73617,-81.476441");  
    //upper right tip of florida.
    polygon_lat_long_pairs.add("29.002375,-80.795288");
    polygon_lat_long_pairs.add("26.896598,-79.938355");
    polygon_lat_long_pairs.add("25.813738,-80.059204");
    polygon_lat_long_pairs.add("24.93028,-80.454712");
    polygon_lat_long_pairs.add("24.401135,-81.817017");
    polygon_lat_long_pairs.add("24.700927,-81.959839");
    polygon_lat_long_pairs.add("24.950203,-81.124878");
    polygon_lat_long_pairs.add("26.0015,-82.014771");
    polygon_lat_long_pairs.add("27.833247,-83.014527");
    polygon_lat_long_pairs.add("28.8389,-82.871704");
    polygon_lat_long_pairs.add("29.987293,-84.091187");
    polygon_lat_long_pairs.add("29.539053,-85.134888");
    polygon_lat_long_pairs.add("30.272352,-86.47522");
    polygon_lat_long_pairs.add("30.281839,-87.628784");

    for(String s : polygon_lat_long_pairs){
        lat_array.add(Double.parseDouble(s.split(",")[0]));
        long_array.add(Double.parseDouble(s.split(",")[1]));
    }

    Runner r = new Runner();
    ArrayList<String> pointsInside = new ArrayList<String>();
    pointsInside.add("30.82112,-87.255249");
    pointsInside.add("30.499804,-86.8927");
    pointsInside.add("29.96826,-85.036011");
    pointsInside.add("30.490338,-83.981323");
    pointsInside.add("29.825395,-83.344116");
    pointsInside.add("30.215406,-81.828003");
    pointsInside.add("29.299813,-82.728882");
    pointsInside.add("28.540135,-81.212769");
    pointsInside.add("27.92065,-82.619019");
    pointsInside.add("28.143691,-81.740113");
    pointsInside.add("27.473186,-80.718384");
    pointsInside.add("26.769154,-81.729126");
    pointsInside.add("25.853292,-80.223999");
    pointsInside.add("25.278477,-80.707398");
    pointsInside.add("24.571105,-81.762085");   //bottom tip of keywest
    pointsInside.add("24.900388,-80.663452");
    pointsInside.add("24.680963,-81.366577");

    for(String s : pointsInside)
    {
        assertTrue(r.coordinate_is_inside_polygon(
            Double.parseDouble(s.split(",")[0]), 
            Double.parseDouble(s.split(",")[1]), 
            lat_array, long_array));
    }
}

@Test
public void realTest_for_points_outside()
{
    ArrayList<Double> lat_array = new ArrayList<Double>();
    ArrayList<Double> long_array = new ArrayList<Double>();

    ArrayList<String> polygon_lat_long_pairs = new ArrayList<String>();
    //upper left tip, florida.
    polygon_lat_long_pairs.add("31.000213,-87.584839");
    polygon_lat_long_pairs.add("31.009629,-85.003052");
    polygon_lat_long_pairs.add("30.726726,-84.838257");
    polygon_lat_long_pairs.add("30.584962,-82.168579");
    polygon_lat_long_pairs.add("30.73617,-81.476441");
    //upper right tip, florida.
    polygon_lat_long_pairs.add("29.002375,-80.795288");
    polygon_lat_long_pairs.add("26.896598,-79.938355");
    polygon_lat_long_pairs.add("25.813738,-80.059204");
    polygon_lat_long_pairs.add("24.93028,-80.454712");
    polygon_lat_long_pairs.add("24.401135,-81.817017");
    polygon_lat_long_pairs.add("24.700927,-81.959839");
    polygon_lat_long_pairs.add("24.950203,-81.124878");
    polygon_lat_long_pairs.add("26.0015,-82.014771");
    polygon_lat_long_pairs.add("27.833247,-83.014527");
    polygon_lat_long_pairs.add("28.8389,-82.871704");
    polygon_lat_long_pairs.add("29.987293,-84.091187");
    polygon_lat_long_pairs.add("29.539053,-85.134888");
    polygon_lat_long_pairs.add("30.272352,-86.47522");
    polygon_lat_long_pairs.add("30.281839,-87.628784");

    for(String s : polygon_lat_long_pairs)
    {
        lat_array.add(Double.parseDouble(s.split(",")[0]));
        long_array.add(Double.parseDouble(s.split(",")[1]));
    }

    Runner r = new Runner();

    ArrayList<String> pointsOutside = new ArrayList<String>();
    pointsOutside.add("31.451159,-87.958374");
    pointsOutside.add("31.319856,-84.607544");
    pointsOutside.add("30.868282,-84.717407");
    pointsOutside.add("31.338624,-81.685181");
    pointsOutside.add("29.452991,-80.498657");
    pointsOutside.add("26.935783,-79.487915");
    pointsOutside.add("25.159207,-79.916382");
    pointsOutside.add("24.311058,-81.17981");
    pointsOutside.add("25.149263,-81.838989");
    pointsOutside.add("27.726326,-83.695679");
    pointsOutside.add("29.787263,-87.024536");
    pointsOutside.add("29.205877,-62.102052");
    pointsOutside.add("14.025751,-80.690919");
    pointsOutside.add("29.029276,-90.805666");
    pointsOutside.add("-12.606032,-70.151369");
    pointsOutside.add("-56.520716,-172.822269");
    pointsOutside.add("-75.89666,9.082024");
    pointsOutside.add("-24.078567,142.675774");
    pointsOutside.add("84.940737,177.480462");
    pointsOutside.add("47.374545,9.082024");
    pointsOutside.add("25.831538,-1.069338");
    pointsOutside.add("0,0");

    for(String s : pointsOutside){
        assertFalse(r.coordinate_is_inside_polygon(
            Double.parseDouble(s.split(",")[0]),
            Double.parseDouble(s.split(",")[1]), lat_array, long_array));
    }
}
}
//The list of lat/long inside florida bounding box all return true.
//The list of lat/long outside florida bounding box all return false.

我使用 eclipse IDE 让它使用 java 1.6.0 运行 java。对我来说,所有的单元测试都通过了。您需要在类路径中包含 junit 4 jar 文件或将其导入 Eclipse。

【讨论】:

  • 非常适合我!感谢演示和解释。
  • 有用的代码我已经翻译成 c# 并且运行良好,谢谢!
  • @FernandoAguilar 如果您发布 C# 版本会很有用。
  • 我认为stackoverflow.com/a/43822141/2234161 中的图片解释了为什么该公式有效。
  • 像魅力一样工作...感谢您的解决方案。 @埃里克
【解决方案4】:

VB.NET 中的 Runner.Java 代码

为了 .NET 人员的利益,将相同的代码放在 VB.NET 中。已经尝试过了,速度相当快。尝试了 350000 条记录,只需几分钟即可完成。 但正如作者所说,我还没有测试与赤道、多区域等相交的场景。

'用法

If coordinate_is_inside_polygon(CurLat, CurLong, Lat_Array, Long_Array) Then
    MsgBox("Location " & CurLat & "," & CurLong & " is within polygon boundary")
Else
    MsgBox("Location " & CurLat & "," & CurLong & " is NOT within polygon boundary")
End If

'函数

Public Function coordinate_is_inside_polygon(ByVal latitude As Double, ByVal longitude As Double, ByVal lat_array() As Double, ByVal long_array() As Double) As Boolean
    Dim i As Integer
    Dim angle As Double = 0
    Dim point1_lat As Double
    Dim point1_long As Double
    Dim point2_lat As Double
    Dim point2_long As Double
    Dim n As Integer = lat_array.Length()
    For i = 0 To n - 1
        point1_lat = lat_array(i) - latitude
        point1_long = long_array(i) - longitude
        point2_lat = lat_array((i + 1) Mod n) - latitude
        point2_long = long_array((i + 1) Mod n) - longitude
        angle += Angle2D(point1_lat, point1_long, point2_lat, point2_long)
    Next

    If Math.Abs(angle) < PI Then Return False Else Return True
End Function

Public Function Angle2D(ByVal y1 As Double, ByVal x1 As Double, ByVal y2 As Double, ByVal x2 As Double) As Double
    Dim dtheta, theta1, theta2 As Double
    theta1 = Math.Atan2(y1, x1)
    theta2 = Math.Atan2(y2, x2)
    dtheta = theta2 - theta1
    While dtheta > PI
         dtheta -= TWOPI
    End While

    While dtheta < -PI
        dtheta += TWOPI
    End While
    Return (dtheta)
End Function



 Public Function is_valid_gps_coordinate(ByVal latitude As Double, ByVal longitude As Double) As Boolean
        If latitude > -90 AndAlso latitude < 90 AndAlso longitude > -180 AndAlso longitude < 180 Then
            Return True
        End If

        Return False
End Function

【讨论】:

    【解决方案5】:

    这是用 Go 编写的算法: 它采用 [lat,long] 格式的点坐标和格式 [[lat,long],[lat,long]...] 的多边形。算法将连接多边形切片中的第一个和最后一个点

    import "math"
    
    // ContainsLocation determines whether the point is inside the polygon
    func ContainsLocation(point []float64, polygon [][]float64, geodesic 
       bool) bool {
        size := len(polygon)
        if size == 0 {
            return false
        }
    
        var (
            lat2, lng2, dLng3 float64
        )
    
        lat3 := toRadians(point[0])
        lng3 := toRadians(point[1])
        prev := polygon[size-1]
        lat1 := toRadians(prev[0])
        lng1 := toRadians(prev[1])
        nIntersect := 0
    
        for _, v := range polygon {
            dLng3 = wrap(lng3-lng1, -math.Pi, math.Pi)
            // Special case: point equal to vertex is inside.
            if lat3 == lat1 && dLng3 == 0 {
                return true
            }
            lat2 = toRadians(v[0])
            lng2 = toRadians(v[1])
            // Offset longitudes by -lng1.
            if intersects(lat1, lat2, wrap(lng2-lng1, -math.Pi, math.Pi), lat3, dLng3, geodesic) {
                nIntersect++
            }
            lat1 = lat2
            lng1 = lng2
        }
    
        return (nIntersect & 1) != 0
    }
    
    func toRadians(p float64) float64 {
        return p * (math.Pi / 180.0)
    }
    
    func wrap(n, min, max float64) float64 {
        if n >= min && n < max {
            return n
        }
        return mod(n-min, max-min) + min
    }
    
    func mod(x, m float64) float64 {
        return math.Remainder(math.Remainder(x, m)+m, m)
    }
    
    func intersects(lat1, lat2, lng2, lat3, lng3 float64, geodesic bool) bool {
        // Both ends on the same side of lng3.
        if (lng3 >= 0 && lng3 >= lng2) || (lng3 < 0 && lng3 < lng2) {
            return false
        }
        // Point is South Pole.
        if lat3 <= -math.Pi/2 {
            return false
        }
        // Any segment end is a pole.
        if lat1 <= -math.Pi/2 || lat2 <= -math.Pi/2 || lat1 >= math.Pi/2 || lat2 >= math.Pi/2 {
            return false
        }
        if lng2 <= -math.Pi {
            return false
        }
        linearLat := (lat1*(lng2-lng3) + lat2*lng3) / lng2
        // Northern hemisphere and point under lat-lng line.
        if lat1 >= 0 && lat2 >= 0 && lat3 < linearLat {
            return false
        }
        // Southern hemisphere and point above lat-lng line.
        if lat1 <= 0 && lat2 <= 0 && lat3 >= linearLat {
            return true
        }
        // North Pole.
        if lat3 >= math.Pi/2 {
            return true
        }
    
        // Compare lat3 with latitude on the GC/Rhumb segment corresponding to lng3.
        // Compare through a strictly-increasing function (tan() or mercator()) as convenient.
        if geodesic {
            return math.Tan(lat3) >= tanLatGC(lat1, lat2, lng2, lng3)
        }
        return mercator(lat3) >= mercatorLatRhumb(lat1, lat2, lng2, lng3)
    }
    
    func tanLatGC(lat1, lat2, lng2, lng3 float64) float64 {
        return (math.Tan(lat1)*math.Sin(lng2-lng3) + math.Tan(lat2)*math.Sin(lng3)) / math.Sin(lng2)
    }
    
    func mercator(lat float64) float64 {
        return math.Log(math.Tan(lat*0.5 + math.Pi/4))
    }
    
    func mercatorLatRhumb(lat1, lat2, lng2, lng3 float64) float64 {
        return (mercator(lat1)*(lng2-lng3) + mercator(lat2)*lng3) / lng2
    }
    

    【讨论】:

      【解决方案6】:

      根据记忆,确定一个点是否位于多边形内的方法是想象从该位置画一条线到某个远处的点。然后计算多边形的线和线段之间的交叉点数。如果计数是偶数,则它不在多边形内。如果它是假的,那么它确实位于多边形内。

      【讨论】:

      • 有没有一种方法可以展示如何使用这些要点来做到这一点? 41.21,-104.77(点检查)然后我有多边形点(39.39 -101.69 + 48.8335,-106.2435 + 38.803,-109.5781 + 39.4413,-111.043 + 45.6336,-113.7162 + 48.8335,-106.2435在伪代码中可以工作!
      【解决方案7】:

      假设您处理环绕子午线并穿过赤道的情况(通过添加偏移量) - 您不能将其视为多边形中的简单二维点吗?

      【讨论】:

      • 有点,但您不能将 (a,b) 到 (c,d) 之间的线视为直线(换句话说,它不通过 ((a+c)/ 2,(b+d)/2)) 所以欧几里得空间中通常的 2d pip 算法将不起作用。可以应用这些算法的方法,但必须在球坐标中重做所有几何图形(例如计算线交叉)。
      • @Spacedman:这取决于 Saul 使用的是大圆线段还是恒定的经纬线段。
      【解决方案8】:

      如果您在球体上有 WGS84 坐标,那么您的多边形会将球体分成两个区域 - 我们如何知道哪个区域在多边形“内部”和哪个区域在“外部”?这个问题本质上是没有意义的!

      例如,假设多边形形成了赤道线——北半球是“进”还是“出”?

      【讨论】:

      • 虽然这个答案应该是一个评论,但它确实是一个有效的观点。我更新了问题以解决歧义。您对 API 方面有什么建议吗?
      • 要测试一个点是否与另一个点在同一个多边形中,您只需要测试点之间的线是否与奇数或偶数个多边形段相交。但是,球体上的多边形段不是由两对经纬坐标唯一定义的,因为连接这些点的大圆弧可以走两种方式中的任何一种。通常,您希望使用最短路径,但如果您想要一个完全通用的解决方案,则不一定如此。无论如何,地理操作最好的东西可能是 PostGIS。
      • 确实如此。为每个多边形段定义一个方位角可能会起到作用。
      猜你喜欢
      • 2011-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 2011-06-17
      相关资源
      最近更新 更多