2015-10-16 119 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; 
}