2012-04-11 11 views
16

Desidero essere in grado di dire se il rubinetto si trova all'interno di un MKPolygon.rileva se un punto si trova all'interno di una sovrapposizione MKPolygon

ho un MKPolygon:

CLLocationCoordinate2D points[4]; 

points[0] = CLLocationCoordinate2DMake(41.000512, -109.050116); 
points[1] = CLLocationCoordinate2DMake(41.002371, -102.052066); 
points[2] = CLLocationCoordinate2DMake(36.993076, -102.041981); 
points[3] = CLLocationCoordinate2DMake(36.99892, -109.045267); 

MKPolygon* poly = [MKPolygon polygonWithCoordinates:points count:4]; 

[self.mapView addOverlay:poly]; 

//create UIGestureRecognizer to detect a tap 
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(foundTap:)]; 
tapRecognizer.numberOfTapsRequired = 1; 
tapRecognizer.numberOfTouchesRequired = 1; 
[self.mapView addGestureRecognizer:tapRecognizer]; 

il suo solo una struttura di base dello stato del Colorado.

ho avuto il rubinetto per Lat Long conversione/configurare:

-(IBAction)foundTap:(UITapGestureRecognizer *)recognizer 
{ 
    CGPoint point = [recognizer locationInView:self.mapView]; 

    CLLocationCoordinate2D tapPoint = [self.mapView convertPoint:point toCoordinateFromView:self.view]; 
} 

ma sono sicuri di come tecnologia se il mio punto di rubinetto è all'interno del MKPolygon. non sembra esserci un metodo per effettuare questo controllo, quindi suppongo di dover convertire MKPolygon in un CGRect e utilizzare CGRectContainsPoint.

MKPolygon ha una proprietà .points ma non riesco a farli tornare indietro.

qualche suggerimento?

EDIT:

Entrambe le soluzioni indicati lavoro in iOS 6 o inferiore, ma interruzioni IOS 7. In iOS 7 proprietà polygon.path restituisce allways NULL. La signora Anna è stata così gentile da fornire a solution in another SO question here. Si tratta di creare il tuo percorso dai punti poligono per passare a CGPathContainsPoint().

immagine del mio poligono:

enter image description here

risposta

11

Ho creato questa categoria MKPolygon nel caso in cui qualcuno volesse usarlo. Sembra funzionare bene. Si deve tenere conto di poligoni interni (cioè fori nel poligono):

@interface MKPolygon (PointInPolygon) 
    -(BOOL) pointInPolygon:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView; 
@end 

@implementation MKPolygon (PointInPolygon) 

-(BOOL) pointInPolygon:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView { 
    MKMapPoint mapPoint = MKMapPointForCoordinate(point); 
    MKPolygonView * polygonView = (MKPolygonView*)[mapView viewForOverlay:self]; 
    CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint]; 
    return CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO) && 
     ![self pointInInteriorPolygons:point mapView:mapView]; 
} 

-(BOOL) pointInInteriorPolygons:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView { 
    return [self pointInInteriorPolygonIndex:0 point:point mapView:mapView]; 
} 

-(BOOL) pointInInteriorPolygonIndex:(int) index point:(CLLocationCoordinate2D) point mapView: (MKMapView*) mapView { 
    if(index >= [self.interiorPolygons count]) 
     return NO; 
    return [[self.interiorPolygons objectAtIndex:index] pointInPolygon:point mapView:mapView] || [self pointInInteriorPolygonIndex:(index+1) point:point mapView:mapView]; 
} 

@end 
+0

Ho appena implementato la categoria e lavoro sui miei poligoni semplici (senza fori). Dici che devi tenerne conto? vuol dire che non rileverà se il rubinetto è dentro un buco? – Padin215

+0

Ok, questo codice si rompe in iOS7. Qualcosa che ha a che fare con 'CGPathContainsPoint (polygonView.path, NULL, polygonViewPoint, NO)' restituisce sempre 'FALSE' ora. – Padin215

4

determinare se un punto è in un poligono arbitrario non è banale e non sorprende che Apple non fornisce come parte di MKPolygon. Puoi accedere ai punti, che ti permettono di scorrere oltre i bordi.

Per determinare se un punto p si trova all'interno di un poligono s, considerare ciascun bordo come un segmento di linea diretto in s. Se un raggio da p in qualsiasi direzione fissa (in genere parallelo all'asse X o Y) interseca il segmento, prendi il segno della componente Z del prodotto incrociato del raggio con quel segmento di linea diretto. Se il componente Z è> 0, aggiungi 1 a un contatore. Se è < 0, sottrarre 1. Il trucco nell'implementazione di questo è evitare problemi quando il bordo è quasi parallelo al raggio, o quando il raggio passa attraverso un vertice (deve contare solo una volta, non una volta per ogni spigolo) .

Quando hai fatto questo per tutti i bordi in s, avrai contato il numero di volte in cui il raggio interseca il contorno del poligono, dove se il bordo andava da sinistra a destra hai aggiunto, e se stava andando da destra a sinistra, ti sei sottratto. Se la somma risultante è zero, sei fuori dal poligono. Altrimenti sei dentro.

Sono possibili numerose ottimizzazioni. Uno di questi è fare un rapido test di bounding box prima del test più completo. Un altro è avere una struttura dati con limiti su tutti i bordi per scartare banalmente i bordi che non intersecano il raggio.

Edit: la componente Z del AXB (il prodotto vettoriale di A con B) è data da:

a.x * b.y - a.y * b.x 

poiché tutto ciò che interessa è il segno, è possibile controllare

a.x * b.y > a.y * b.x 
+0

ciò che è il componente Z? – Padin215

+0

aggiunta definizione per rispondere. – DRVic

+0

ok, gotcha. lavorando su di esso ora, solo cercando di capire come rilevare se un raggio si interseca con un segmento di linea. – Padin215

9

il tuo metodo foundTap:

-(IBAction)foundTap:(UITapGestureRecognizer *)recognizer 
{ 
    CGPoint point = [recognizer locationInView:self.mapView]; 

    CLLocationCoordinate2D tapPoint = [self.mapView convertPoint:point toCoordinateFromView:self.view]; 

    [self pointInsideOverlay:tapPoint]; 

    if (isInside) 
    { 
     .... 
    } 
} 

Ecco un metodo da chiamare dal precedente per verificare se il punto è all'interno dell'overlay:

-(void)pointInsideOverlay:(CLLocationCoordinate2D)tapPoint 
{ 
    isInside = FALSE; 

    MKPolygonView *polygonView = (MKPolygonView *)[mapView viewForOverlay:polygonOverlay]; 

    MKMapPoint mapPoint = MKMapPointForCoordinate(tapPoint); 

    CGPoint polygonViewPoint = [polygonView pointForMapPoint:mapPoint]; 

    BOOL mapCoordinateIsInPolygon = CGPathContainsPoint(polygonView.path, NULL, polygonViewPoint, NO); 

     if (!mapCoordinateIsInPolygon) 

      //we are finding points that are inside the overlay 
     { 
      isInside = TRUE; 
     } 
} 
+0

Questo è un buon modo per eseguire un controllo di base per verificare se il punto di tocco si trova all'interno del rettangolo del poligono. non è preciso per rilevare i tocchi all'interno dell'area disegnata di un poligono. – Padin215

+2

@ Log139 Perché dici così? 'polygonView' è il' MKPolygonView', 'path' è il suo' CGPathRef', non il suo rettangolo di delimitazione, e 'CGPathContainsPoint' esegue il test di contenimento del percorso/punto, no? Cosa mi manca qui? Penso che se usi '[polygonView pointInside: polygonViewPoint withEvent: nil]', potresti avere ragione. Ma se stai ricevendo l'effettivo 'percorso' e chiamando' CGPathContainsPoint', penso che tu stia bene. – Rob

+1

Ok, ritiro il mio commento precedente, questo funziona. Bene, funziona in iOS6 o meno, si rompe in iOS7. – Padin215

5

sto ottenendo punti MKPolygon dati da file xml nella stringa. Ho analizzare la stringa di dati per array di punti e l'uso approccio cedere http://alienryderflex.com/polygon/

Funziona per me ....

-(BOOL)isPoint:(CLLocationCoordinate2D)findLocation inPloygon:(NSArray*)polygon{ 

    NSMutableArray *tempPolygon=[NSMutableArray arrayWithArray:polygon]; 
    int i, j=(int)tempPolygon.count-1 ; 
    bool oddNodes=NO; 
    double x=findLocation.latitude; 
    double y=findLocation.longitude; 

    for (i=0; i<tempPolygon.count; i++) { 
     NSString*coordString=[tempPolygon objectAtIndex:i]; 
     NSArray*pointsOfCoordString=[coordString componentsSeparatedByString:@","]; 
     CLLocationCoordinate2D point=CLLocationCoordinate2DMake([[pointsOfCoordString objectAtIndex:1] doubleValue], [[pointsOfCoordString objectAtIndex:0] doubleValue]); 
     NSString*nextCoordString=[tempPolygon objectAtIndex:j]; 
     NSArray*nextPointsOfCoordString=[nextCoordString componentsSeparatedByString:@","]; 
     CLLocationCoordinate2D nextPoint=CLLocationCoordinate2DMake([[nextPointsOfCoordString objectAtIndex:1] doubleValue], [[nextPointsOfCoordString objectAtIndex:0] doubleValue]); 


     if ((point.longitude<y && nextPoint.longitude>=y) 
      || (nextPoint.longitude<y && point.longitude>=y)) { 
      if (point.latitude+(y-point.longitude)/(nextPoint.longitude-point.longitude)*(nextPoint.latitude-point.latitude)<x) { 
       oddNodes=!oddNodes; }} 
     j=i; } 


    return oddNodes; 

} 

mio poligono (NSArray) gli oggetti sono in stringa per esempio @"-89.860021,44.944266,0"

+0

Potresti per favore elaborare più la tua risposta aggiungendo un po 'più di descrizione della soluzione che fornisci? – abarisone

+1

questo metodo controlla la tua posizione data si trova all'interno del poligono fatto da punti dati (i punti dati sono array di posizione geografica). Per l'algoritmo completo, consultare http://alienryderflex.com/polygon/. –

+0

Ha funzionato benissimo per me, ma ho dovuto modificare l'ordine dei componenti del poligono: @ "longitudine, latitudine, 0" grazie! –

2

questo ha lavorato per me in Swift:

extension MKPolygon { 

    func isCoordinateInsidePolyon(coordinate: CLLocationCoordinate2D) -> Bool { 

     var inside = false 

     let polygonRenderer = MKPolygonRenderer(polygon: self) 
     let currentMapPoint: MKMapPoint = MKMapPointForCoordinate(coordinate) 
     let polygonViewPoint: CGPoint = polygonRenderer.pointForMapPoint(currentMapPoint) 

     if CGPathContainsPoint(polygonRenderer.path, nil, polygonViewPoint, true) { 
      inside = true 
     } 

     return inside 
    } 
} 
+0

Questo funziona e ha il vantaggio di non richiedere un MKMapView come gli approcci MKPolygonView descritti in altre risposte. – oh7lzb

3

qui è rapido 3 versione aggiornata grazie al @Steve Stomp

extension MKPolygon { 

    func contains(coordinate: CLLocationCoordinate2D) -> Bool { 

     let polygonRenderer = MKPolygonRenderer(polygon: self) 
     let currentMapPoint: MKMapPoint = MKMapPointForCoordinate(coordinate) 
     let polygonViewPoint: CGPoint = polygonRenderer.point(for: currentMapPoint) 

     return polygonRenderer.path.contains(polygonViewPoint) 
    } 
} 
Problemi correlati