ios - 从特定点查找线上最近的点

标签 ios google-maps

我正在使用 iOS 版 Google map 在建筑群周围设置地理围栏。我在复合体周围创建了一条多段线,如果用户点击多段线外部,它会将标记移动到多段线上最近的点,否则它只会放置标记。这似乎使用 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

最佳答案

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

这是我正在使用的一种方法。它获取线段的端点并返回一个结构,其中包含线段上最近的点以及距给定点的距离。关键区别在于 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;
}

关于ios - 从特定点查找线上最近的点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33172856/

相关文章:

ios - 创建新的构建配置导致 header 未找到错误

google-maps - 传单位置过滤器示例和可拖动可调整大小的矩形区域 - 在 map 上选择

javascript - 如何使谷歌地图不脱离世界界限?

javascript - gmaps4rails - 在不刷新页面的情况下动态更新 map 上数据库中的标记

javascript - 将 string[] 传递给 JavaScript

iOS : Collection view doesn't show

iphone - 在 iOS4 中,让 NSTimer 与 NSRunLoop 一起运行会禁用触摸/手势输入

ios - Segue 后 UILabel 不更新

iphone - 根据自定义 URL 架构显示来自 appDelegate 的 View Controller

security - 在开源项目中包含 Google Maps API Key?