Algoritmo per la ricerca del centroide poligonale irregolare (punto dell'etichetta)


13

Devo trovare un centroide (o punto dell'etichetta) per i poligoni di forma irregolare su Google Maps. Sto mostrando InfoWindows per i pacchi e ho bisogno di un posto per ancorare l'InfoWindow che è garantito per essere in superficie. Vedi le immagini qui sotto.

testo alternativo testo alternativo

In realtà non ho bisogno di nulla di specifico su Google Maps, sto solo cercando un'idea su come trovare automaticamente questo punto.

La mia prima idea è stata quella di trovare il centroide "falso" prendendo il lat medio e gli lng e il posizionamento casuale di punti da lì fino a quando non ne trovo uno che interseca il poligono. Ho già il codice point-in-poligono. Questo mi sembra terribilmente "confuso".

Dovrei notare che non ho accesso a nessuno dei codici lato server che emettono la geometria, quindi non posso fare nulla come ST_PointOnSurface (the_geom).

Risposte:


6

Veloce e sporco: se il centroide "falso" non è nel poligono, usa il vertice più vicino a quel punto.


Non ci avevo pensato. Idealmente mi piacerebbe questo punto nel poligono e non sul bordo, ma questo potrebbe essere ciò a cui mi riferisco.
Jason,

Una volta trovato un punto limite, puoi intersecare un piccolo quadrato centrato in quel punto con il poligono e quindi scegliere il centroide dell'intersezione. Quando il quadrato è abbastanza piccolo, questo è garantito per essere un punto interno (anche se ovviamente sarà molto vicino a un bordo).
whuber

@Jason Se usi il centroide reale potresti avere meno probabilità di riscontrare questo problema. Non dovrebbe essere troppo difficile tradurre rapidamente qualcosa in JavaScript: github.com/cartesiananalytics/Pipra/blob/master/src/…
Dandy

Mentre la mia soluzione (raggi dal falso centroide) funzionerà per la maggior parte del tempo, penso che questa soluzione probabilmente funzionerebbe meglio a causa della sua semplicità e del fatto che sei sicuro di trovare un punto almeno sul bordo e potrebbe facilmente spostarsi essere all'interno del poligono con il minimo sforzo.
Jason,

3

Potresti voler guardare questo: http://github.com/tparkin/Google-Maps-Point-in-Polygon

Sembra utilizzare un algoritmo Ray Casting che dovrebbe corrispondere al caso presentato.

C'è un post sul blog qui. http://appdelegateinc.com/blog/2010/05/16/point-in-polygon-checking/


Se si desidera implementarlo sul lato server, sia JTS (Java) che Geos (C) implementano questa funzionalità.
DavidF,

Sì, probabilmente avrei dovuto aggiungere che ho già il codice per determinare se il mio centroide "calcolato" si trova all'interno del poligono o meno. Quello che voglio veramente è un modo per creare un centroide all'interno del poligono.
Jason,

3

Un algoritmo ESRI (più vecchio) calcola il centro di massa e, dopo averlo testato per l'inclusione nel poligono, lo sposta orizzontalmente, se necessario, fino a quando si trova all'interno del poligono. (Ciò potrebbe essere fatto in molti modi a seconda delle operazioni fondamentali disponibili nel proprio ambiente di programmazione.) Questo tende a produrre punti etichetta abbastanza vicini al centro visivo del poligono: provalo sull'illustrazione.


1

Ho risolto il mio problema estendendo il popolare codice epoly da http://econym.org.uk/gmap . Fondamentalmente quello che ho finito per fare è stato:

  • Crea una serie di raggi che iniziano dal "falso centroide" e si estendono ad ogni angolo e lato (8 in totale)
  • Crea in modo incrementale un punto del 10,20,30 ... percento in giù di ogni raggio e vedi se questo punto è nel nostro poligono originale

Codice epoly esteso di seguito:

google.maps.Polygon.prototype.Centroid = function() {
var p = this;
var b = this.Bounds();
var c = new google.maps.LatLng((b.getSouthWest().lat()+b.getNorthEast().lat())/2,(b.getSouthWest().lng()+b.getNorthEast().lng())/2);
if (!p.Contains(c)){
    var fc = c; //False Centroid
    var percentages = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]; //We'll check every 10% down each ray and see if we're inside our polygon
    var rays = [
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getSouthWest().lng())]}),
        new google.maps.Polyline({path:[fc,b.getNorthEast()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,b.getSouthWest()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),b.getSouthWest().lng())]})
    ];
    var lp;
    for (var i=0;i<percentages.length;i++){
        var percent = percentages[i];
        for (var j=0;j<rays.length;j++){
            var ray = rays[j];
            var tp = ray.GetPointAtDistance(percent*ray.Distance()); //Test Point i% down the ray
            if (p.Contains(tp)){
                lp = tp; //It worked, store it
                break;
            }
        }
        if (lp){
            c = lp;
            break;
        }
    }
}
return c;}

Ancora un po 'confuso ma sembra funzionare.


Questo metodo fallirà per alcuni poligoni tortuosi. Ad esempio, bufferizzare la polilinea {{0, 9}, {10, 20}, {9, 9}, {20, 10}, {9, 0}, {20, -10}, {9, -9} , {10, -20}, {0, -9}, {-10, -20}, {-9, -9}, {-20, -10}, {-9, 0}, {-20, 10}, {-9, 9}, {-10, 20}, {0,9}} di meno di 1/2. È anche inefficiente rispetto al metodo QAD di Dandy, per esempio.
whuber

1

Un altro algoritmo 'sporco' per farlo:

  • Prendi il rettangolo di selezione della geometria (Xmax, Ymax, Xmin, Ymin)

  • Ripeti finché non ( Xmin+rand*(Xmax-Xmin), Ymin+rand*(Ymax-Ymin) ) viene trovato un punto casuale all'interno della geometria (utilizzando Google-Maps-Point-in-Polygon )


+1 perché questa potrebbe avere una buona probabilità di colpire la seconda volta. Finché il tuo "random" è riproducibile ogni volta per non infastidire l'utente, questa è anche una soluzione valida. Le probabilità che non raggiunga presto un punto valido sono ridotte, specialmente se inizi con un buon punto di ipotesi.
Dandy,

1
@Dandy: In realtà, in alcuni casi questo può essere un algoritmo davvero scadente. Si consideri ad esempio un nastro diagonale stretto. Esistono in pratica (ad es. Pacchi lunghi di facciata stradale) e possono facilmente occupare meno dello 0,1% del riquadro di delimitazione (a volte molto meno). Per essere ragionevolmente sicuri (del 95% di fiducia) di colpire un tale poligono con questa tecnica richiederebbe circa 3000 iterazioni.
whuber

@Whuber: se scegli una posizione di partenza sbagliata, potrebbe volerci un po 'di tempo per completarla. Se si considera anche che ipoteticamente il 95% dei clic verrà eseguito su geometrie più desiderabili, questo potrebbe essere solo un problema il 5% delle volte. Inoltre, come per un'altra domanda GIS.se se le prestazioni sono l'obiettivo non esiste mai un'unica soluzione, è meglio cambiare tattiche basate sull'euristica. Non c'è motivo di esaurirlo per 3000 iterazioni. Puoi sempre salvare il mio QAD dopo il 10. Penso che varrebbe la pena provare questo per alcune iterazioni poiché la posizione potrebbe essere più desiderabile.
Dandy,

@Dandy: Ma qual è il problema con la tua soluzione QAD? Potresti anche modificarlo un po 'spostando dal labelpoint di prova originale al vertice più vicino in un buffer interno del poligono: ancora QAD ma ora garantito per atterrare su una posizione interna della funzione originale. A proposito, la tua strategia di salvataggio presto è buona. Ogni volta che codifico una sonda casuale come questa, precomputo il rapporto tra l'area della funzione e quella del suo riquadro di selezione, lo uso per trovare il tempo atteso per il successo e avverto immediatamente l'utente se potrebbe essere lungo.
whuber

@Whuber l'euristic ratio dell'area è una grande idea perché si calcola semplicemente il centroide quando si calcola l'area. Per quanto riguarda il problema con la mia soluzione QAD: è al limite. Se scelgo quel punto e lo bufferizzo come dici tu, quel raggio "piccolo" potrebbe essere più grande della lunghezza attraverso quella sezione stretta. C'è sempre un caso d'angolo. Tanto da considerare, solo per creare un palloncino che ingombrerà l'interfaccia utente e oscurerà comunque la geometria. Probabilmente è meglio scegliere il vertice più alto o più basso.
Dandy,

1

Alla luce del recente chiarimento che preferiresti un luogo strettamente interno, puoi selezionare qualsiasi punto della Trasformazione dell'asse mediale che non si trovi anche sul confine del poligono. (Se non hai un codice per un MAT, puoi approssimarlo tamponando negativamente il poligono. Una ricerca binaria o secante produrrà rapidamente un piccolo poligono interno che approssima una parte del MAT; usa qualsiasi punto sul suo confine.)


Capisco cosa stavi dicendo sull'utilizzo del bordo di una geometria in modo tale che il bordo si trovi all'interno del poligono di interesse. Non capisco come faresti per creare questo bordo / vertice. L'unica cosa che mi viene in mente è di creare un triangolo virtuale intersecando un raggio perpendicolare dal punto di interesse al segmento opposto al segmento del punto selezionato. Il punto medio tra questi due punti potrebbe essere la cima di quel triangolo virtuale.
Dandy,

@Dandy: Questo arriva al cuore. Esistono molti modi per farlo a seconda di cosa fa il GIS in modo nativo. Ad esempio, una volta trovato un raggio che interseca la feature originale in un insieme di lunghezza positiva, quell'intersezione sarà un'unione disgiunta di segmenti di linea. Usa il centro di uno di questi segmenti. Un altro modo è quello di iniziare con qualsiasi punto della funzione (preferibilmente vicino al suo centro, che è ciò che il tuo metodo QED ha realizzato), creare un piccolo poligono semplice (ad esempio, quadrato) centrato lì, intersecarlo con la funzione originale, scegliere l'unicità connessa componente ...
whuber

(seguito) ... contenente il punto iniziale e scegliere ricorsivamente un centro per quel componente. Ci saranno molti metodi disponibili quando il tuo GIS ti consente di scorrere le sequenze di vertici che descrivono il confine della funzione. Se sono supportati buffer negativi, è possibile trovare in modo iterativo una serie di punti interni a massima distanza (lo "scheletro", che è un sottoinsieme del MAT). Questo è un po 'costoso ma è abbastanza facile da programmare e produce ottimi punti etichetta.
whuber

0

Perché non usare il centroide solo per la posizione verticale (latitudine)? Quindi, puoi posizionare l'etichetta in orizzontale selezionando la longitudine media a quella latitudine . (Per questo dovresti trovare il valore di longitudine per un bordo poligonale a una latitudine specifica, che non dovrebbe darti alcun problema).

Inoltre, fai attenzione alle forme a U e a quelle più complesse. :) Forse per quelli, scegli la media della coppia di longitudinali più a destra (ogni coppia corrisponderebbe a una porzione del poligono), poiché la finestra informativa è orientata in quel modo?

Questo ti dà anche un po 'più di controllo sul posizionamento; ad esempio, potrebbe essere utile posizionare la finestra informativa al 66 o al 75% in verticale, in modo da lasciare più parte del poligono visibile. (O forse no! Ma hai la manopola per modificare.)


0

Che ne dici di usare solo il punto su cui l'utente ha fatto clic per selezionarlo, se è selezionato dall'utente che lo è.


Può essere selezionato con un clic del mouse o una query non spaziale, quindi non sempre funzionerebbe.
Jason,

0

Sto cercando di risolvere anche questo. Ho imposto una condizione ai miei poligoni che non possono avere linee di incrocio che entrano in quello che descriverò.

Quindi, il mio approccio usa la triangolazione. Prendi un vertice casuale (eventualmente prendi un vertice all'estremo N, E, W o S può semplificare le cose).

Da questo vertice, traccia le linee al vertice a un vertice di distanza, ovvero se il tuo vertice è vertice 3, osserva il vertice 3 + 2.

Costruisci una linea dal tuo vertice originale a questo vertice. Se la linea costruita:

  1. non incrocia altre linee e
  2. il suo punto medio non è esterno al poligono

Quindi hai costruito un triangolo all'interno del poligono. Se il vertice di successo era n + 2, il triangolo è {n, n + 1, n + 2}, che chiameremo {v, v1, v2}. In caso contrario, prova il vertice successivo e continua fino a quando tutti i vertici sono stati provati.

Quando trovi un triangolo, trova il centro di quello prendendo una linea dal vertice v al punto medio di v1 e v2. Il punto medio di quella linea è garantito all'interno del triangolo e all'interno del poligono.

Non l'ho ancora codificato, ma vedo come penso che un poligono con linee incrociate causerà effettivamente alcune condizioni esotiche in cui ciò non funziona. Se questo è il tipo di poligoni che hai, dovresti testare ogni segmento di linea sul poligono e assicurarti che non sia stato attraversato. Salta i segmenti di linea che sono incrociati e penso che funzionerà.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.