【问题标题】:Find closest point on line from a particular point从特定点在线查找最近的点
【发布时间】:2020-09-28 16:21:45
【问题描述】:

我正在使用 Google Maps iOS 在建筑群周围设置地理围栏。我在复合体周围创建了一条折线,如果用户在折线之外点击,它会将标记移动到折线上的最近点,否则它只会放置标记。使用this method 似乎效果相对较好。

但是我注意到,这种方法似乎只在所讨论的点垂直于线上的点时才有效,否则会产生奇怪的结果。我在下面发布了我的代码和一些屏幕截图。

-(CLLocationCoordinate2D) findClosestPointWithinFence:(CLLocationCoordinate2D) pointToTest {
    CLLocationDistance smallestDistance = 0;
    CLLocationCoordinate2D closestPoint = pointToTest;

    for(int i = 0; i < [geoFencePoints count] - 1; i++) {
        CGPoint point = [[geoFencePoints objectAtIndex:i] CGPointValue];
        CGPoint point2 = [[geoFencePoints objectAtIndex:i + 1] CGPointValue];
        CLLocationCoordinate2D locationA = CLLocationCoordinate2DMake(point.x, point.y);
        CLLocationCoordinate2D locationB = CLLocationCoordinate2DMake(point2.x, point2.y);
        CLLocationCoordinate2D myLoc = [self findClosestPointOnLine:locationA secondPoint:locationB fromPoint:pointToTest];

        if(GMSGeometryIsLocationOnPath(myLoc, dealershipParameters.path, YES)) {
            if(smallestDistance == 0) {
                smallestDistance = GMSGeometryDistance(myLoc, pointToTest);
                closestPoint = myLoc;
            } else {
                if(smallestDistance > GMSGeometryDistance(myLoc, pointToTest)) {
                    smallestDistance = GMSGeometryDistance(myLoc, pointToTest);
                    closestPoint = myLoc;
                }
            }
        }
    }
    return closestPoint;
}

-(CLLocationCoordinate2D) findClosestPointOnLine:(CLLocationCoordinate2D)locationA secondPoint:(CLLocationCoordinate2D)locationB fromPoint:(CLLocationCoordinate2D) pointToTest {
    CGPoint aToP = CGPointMake(pointToTest.latitude - locationA.latitude, pointToTest.longitude - locationA.longitude);
    CGPoint aToB = CGPointMake(locationB.latitude - locationA.latitude, locationB.longitude - locationA.longitude);

    float atb2 = (aToB.x * aToB.x) + (aToB.y * aToB.y);

    float atp_dot_atb = (aToP.x  * aToB.x) + (aToP.y * aToB.y);

    float t = atp_dot_atb / atb2;

    CLLocationCoordinate2D myLoc = CLLocationCoordinate2DMake(locationA.latitude + aToB.x * t, locationA.longitude + aToB.y * t);
    return myLoc;
}

-(BOOL)testIfInsideGeoFence:(CLLocationCoordinate2D) pointToTest {
    return GMSGeometryContainsLocation(pointToTest, dealershipParameters.path, YES) || GMSGeometryIsLocationOnPath(pointToTest, dealershipParameters.path, YES);
}

在第一个屏幕截图下方显示了标记成功找到最近的点,蓝线外的标记是我最初点击的位置,蓝线上的标记是它找到的点。第二个显示标记未能找到最近的点。屏幕上的标记是我最初点击的位置,因为它无法找到合适的解决方案,所以没有放置第二个标记。

Screenshot 1 Screenshot 2

【问题讨论】:

    标签: ios google-maps


    【解决方案1】:

    我遇到了类似的问题。我认为正在发生的事情是您将线段视为一条线。由于线段没有延伸到垂直于该点的点,因此线段上最近的点将是其中一个端点,而不是线段的延伸。

    这是我正在使用的一种方法。它采用线段的端点并返回一个结构,其中包含线段上最近的点以及到给定点的距离。关键区别在于 if-else 语句检查解决方案是否在段上。为了您的目的,您可能需要修改一些东西。

    要注意的另一件事是,我在 MKMapPoints 而不是 CLLocationCoordinate2D 对象上执行数学运算得到了更准确的结果。我认为这与地球是圆的或诸如此类的废话有关。

    + (struct TGShortestDistanceAndNearestCoordinate)distanceFromPoint:(CLLocationCoordinate2D)p
                       toLineSegmentBetween:(CLLocationCoordinate2D)l1
                                        and:(CLLocationCoordinate2D)l2 {
        return [[self class] distanceFromMapPoint:MKMapPointForCoordinate(p)
                             toLineSegmentBetween:MKMapPointForCoordinate(l1)
                                              and:MKMapPointForCoordinate(l2)];
    }
    
    + (struct TGShortestDistanceAndNearestCoordinate)distanceFromMapPoint:(MKMapPoint)p
                          toLineSegmentBetween:(MKMapPoint)l1
                                           and:(MKMapPoint)l2 {
        double A = p.x - l1.x;
        double B = p.y - l1.y;
        double C = l2.x - l1.x;
        double D = l2.y - l1.y;
    
        double dot = A * C + B * D;
        double len_sq = C * C + D * D;
        double param = dot / len_sq;
    
        double xx, yy;
    
        if (param < 0 || (l1.x == l2.x && l1.y == l2.y)) {
            xx = l1.x;
            yy = l1.y;
        }
        else if (param > 1) {
            xx = l2.x;
            yy = l2.y;
        }
        else {
            xx = l1.x + param * C;
            yy = l1.y + param * D;
        }
    
        struct TGShortestDistanceAndNearestCoordinate result;
        MKMapPoint nearestPoint = MKMapPointMake(xx, yy);
        result.shortestDistance = MKMetersBetweenMapPoints(p, nearestPoint);
        result.nearestCoordinate = MKCoordinateForMapPoint(nearestPoint);
    
        return result;
    }
    

    【讨论】:

      【解决方案2】:

      一个非常优雅的解决方案。但是我不确定您在“if param

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-17
        • 2022-10-24
        • 1970-01-01
        • 2022-11-26
        相关资源
        最近更新 更多