2015-10-16 76 views
0

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

回答

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; 
}