Suddivisione di Linestring sulla linea dati con OpenLayers


9

Un paio di anni fa ho pubblicato The International Date Line a capo e @jdeolive mi ha suggerito di dividere le funzionalità su dateLine. Quindi ci ho provato.

Quando provo a dividere la mia traccia satellitare con splitCon la linea dati torno indietro null. So che sto dividendo correttamente perché quando divido sulla linea di Greenwich ottengo i risultati attesi.

Qualcuno sa come posso dividere correttamente una Linestring programmaticamente lungo la linea della data con OpenLayers? Cerco il codice di esempio se ce l'hai.

Ho provato wrapDateLinema non sembra funzionare su livelli vettoriali nonostante il mio livello vettoriale sia così:

vectorLayer = new OpenLayers.Layer.Vector("GroundTracks", {
    renderers: ['Canvas', 'VML'],
    wrapDateLine: true}); // <-- shoud be wraping.

inserisci qui la descrizione dell'immagine

Ecco il mio codice:

var features = [];
var format = new OpenLayers.Format.WKT({
    'internalProjection': map.baseLayer.projection,
    'externalProjection': prjGeographic
});
var satTrack = format.read("LINESTRING (95.538611 13.286511, 94.730711 16.908947, 93.901095 20.528750, 93.043594 24.145177, 92.150978 27.757436, 91.214579 31.364666, 90.223791 34.965899, 89.165364 38.560019, 88.022401 42.145679, 86.772901 45.721205, 85.387568 49.284424, 83.826433 52.832413, 82.033480 56.361087, 79.927797 59.864504, 77.388419 63.333664, 74.227306 66.754285, 70.139140 70.102478, 64.605267 73.335774, 56.712904 76.373458, 44.881134 79.052803, 26.939886 81.047314, 02.704174 81.839241, -21.686285 81.101751, -39.887660 79.141947, -51.906937 76.480894, -59.912477 73.452897, -65.514482 70.225089, -69.645366 66.880243, -72.834535 63.461797, -75.393132 59.994131, -77.512464 56.491789, -79.315407 52.963919, -80.884039 49.416549, -82.275114 45.853820, -83.529088 42.278691, -84.675583 38.693355, -85.736827 35.099503, -86.729876 31.498490, -87.668095 27.891443, -88.562176 24.279331, -89.420849 20.663020, -90.251389 17.043303, -91.059999 13.420926, -91.852092 09.796602, -92.632515 06.171020, -93.405728 02.544857, -94.175960 -01.081217, -94.947343 -04.706542, -95.724045 -08.330456, -96.510402 -11.952298, -97.311065 -15.571400, -98.131162 -19.187081, -98.976502 -22.798638, -99.853829 -26.405335, -100.771148 -30.006378, -101.738172 -33.600889, -102.766925 -37.187866, -103.872602 -40.766117, -105.074803 -44.334175, -106.399366 -47.890158, -107.881153 -51.431559, -109.568417 -54.954914, -111.529886 -58.455253, -113.866668 -61.925160, -116.733085 -65.353081, -120.374635 -68.720132, -125.199754 -71.993642, -131.916790 -75.113368, -141.772276 -77.960803, -156.750096 -80.294831, -178.475596 -81.673196, 156.248392 -81.611421, 135.042323 -80.136505, 120.556535 -77.748172, 111.014840 -74.872356, 104.485504 -71.737081, 99.775637 -68.454400, 96.208126 -65.081545, 93.391438 -61.649716, 91.089380 -58.177038, 89.152970 -54.674643, 87.484294 -51.149703, 86.016609 -47.607042, 84.702947 -44.050030, 83.509299 -40.481112, 82.410411 -36.902133, 81.387093 -33.314533, 80.424442 -29.719485, 79.510644 -26.117981, 78.636145 -22.510889, 77.793053 -18.898997, 76.974710 -15.283040, 76.175371 -11.663718, 75.389950 -08.041709, 74.613831 -04.417680, 73.842693 -00.792294, 73.072378 02.833789, 72.298749 06.459907, 71.517566 10.085391, 70.724342 13.709564, 69.914194 17.331733, 69.081655 20.951185, 68.220447 24.567170, 67.323194 28.178891, 66.381031 31.785476, 65.383084 35.385943, 64.315735 38.979152, 63.161579 42.563725, 61.897893 46.137940, 60.494337 49.699551, 58.909396 53.245525, 57.084691 56.771602, 54.935577 60.271560, 52.334964 63.735923, 49.084320 67.149569, 44.859585 70.487030, 39.107498 73.702694, 30.852243 76.709182, 18.420695 79.329532, -00.339911 81.212453, -25.028018 81.831766)");

var featGreenwichLine = format.read("LINESTRING(0 -89, 0 89)");
var featDateLine = format.read("LINESTRING(180 -89, 180 89)");

features.push(featGreenwichLine);
features.push(featDateLine);
features.push(satTrack);

var resultsGreenwich = satTrack.geometry.splitWith(featGreenwichLine.geometry);
var resultsDateLine = satTrack.geometry.splitWith(featDateLine.geometry);

console.log(resultsGreenwich); //<--RETURNS EXPECTED RESULTS.
console.log(resultsDateLine);//<--RETURNS NULL.

vectorLayer.addFeatures(features);

La mia domanda non è un duplicato di questa domanda perché vogliono sapere come farlo in ogr2ogr

Aggiornare:

Ecco come appare un tipico set di dati con cui lavoro (traccia satellitare 24 ore): QUI di Linestring si trova QUI .

inserisci qui la descrizione dell'immagine


Quale versione di openlayer stai usando?
Plux,

@Plux 2.13.1 (Lastest)
CaptDragon,

1
Secondo la loro API, wrapDateLine dovrebbe essere usato solo sul livello base, quindi non c'è da meravigliarsi se non funziona sul livello vettoriale. Tuttavia, non ho idea di come farlo funzionare sul livello vettoriale. Sto riscontrando un problema simile con i multipoligoni che attraversano la linea di dati.
Plux,

1
@Plux controlla la mia soluzione
CaptDragon

Bella soluzione. Sfortunatamente non è applicabile al mio problema, credo che il mio problema sia più sul lato geoserver e il mio poligono contiene coordinate che si trovano "sull'altro lato" della linea di dati, come -180.00000000000003 90.00000190734869 che crea alcuni problemi nel geoserver. Non ho problemi a visualizzarlo sulla mia mappa (ho un wms che lo fa), ma voglio usarli come filtri box su una query wfs per geoserver :)
Plux,

Risposte:


2

Il problema è che la tua funzione non attraversa la linea della data dalla prospettiva OpenLayers, quindi la tua linea di divisione non interseca la tua funzione. Esempio dai tuoi dati:

..., -178.475596 -81.673196, 156.248392 -81.611421,...

Si passa da -178 a 156 e questo non attraversa la riga della data dalla prospettiva OpenLayers. Invece di dividere sulla riga della data, dovresti dividere il valore X minimo.

// Build the splitting line based on the min and max coordinates of the vector to split
var minX = 999999999;
var minY = -20037508.34 // minimum value of the spherical mercator projection
var maxY = 20037508.34  // maximum value of the spherical mercator projection
//Extract the minimum X from the data as bounds seems to be rounded.
for(var i=0; i<satTrack.geometry.components.length; i++) {
    if(satTrack.geometry.components[i].x < minX)
        minX = satTrack.geometry.components[i].x;
}
var pointList = [
    new OpenLayers.Geometry.Point(minX, minY),
    new OpenLayers.Geometry.Point(minX, maxY)
];
var featDateLine = new OpenLayers.Feature.Vector(
    new OpenLayers.Geometry.LineString(pointList)
);

Ho creato un esempio qui che ha diviso con successo la tua traccia satellitare in 2 funzioni: http://jsfiddle.net/6XJ5A/

Ora per utilizzare il WKT con più righe nell'aggiornamento, anziché utilizzare una linea retta, è necessario passare attraverso l'intero set di dati e creare la linea di divisione con tutte le coordinate che attraversano la linea dati. Costruendo una piccola linea all'interno di una multilinea, puoi dividere tutte le coordinate che dovrebbero attraversare la linea di dati. Ecco l'esempio aggiornato: http://jsfiddle.net/Jc274/

E il codice:

// Build the splitting line based on the min and max coordinates of the vector to split
var pointList = [];
var lastPoint = satTrack.geometry.components[0];
//Extract the minimum X from the data as bounds seems to be rounded.
for (var i = 1; i < satTrack.geometry.components.length; i++) {
    if (Math.abs(satTrack.geometry.components[i].x - lastPoint.x) > 10000000) {
        pointList.push(satTrack.geometry.components[i]);
    }
    lastPoint = satTrack.geometry.components[i];
}

var lineList = [];
for(var i=0; i<pointList.length; i++) {
    lineList.push(new OpenLayers.Geometry.LineString([
        new OpenLayers.Geometry.Point(pointList[i].x, pointList[i].y-0.00001), 
        new OpenLayers.Geometry.Point(pointList[i].x, pointList[i].y+0.00001)
    ]));
}

var featDateLine = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.MultiLineString(lineList), null, split_style);

Questo ti restituirà una linea divisa su tutti i punti che "attraversano" la linea dati

Nota che cerco anche le coordinate per rimuovere la linea che attraversa la mappa per collegare le 2 coordinate:

for (var i = 0; i < resultsDateLine.length; i++) {
    // Remove the first (or last) point of the line, the one that cross the dateline
    if (Math.abs(resultsDateLine[i].components[0].x - resultsDateLine[i].components[1].x) > 10000000) {
        resultsDateLine[i].removeComponent(resultsDateLine[i].components[0]);
    }
    if (Math.abs(resultsDateLine[i].components[resultsDateLine[i].components.length - 1].x - resultsDateLine[i].components[resultsDateLine[i].components.length - 2].x) > 10000000) {
        resultsDateLine[i].removeComponent(resultsDateLine[i].components[resultsDateLine[i].components.length - 1]);
    }
    features.push(new OpenLayers.Feature.Vector(resultsDateLine[i], null, style_array[i]));
}

Aggiornamento: ho aggiornato il primo esempio per aggiungere solo la linea che era stata divisa. Ho anche aggiornato la spiegazione di conseguenza. Questo approccio non è a prova di proiettile con la traccia satellitare 24 ore che hai fornito, ma ci sto lavorando.

Aggiornamento 2: ho aggiornato il secondo esempio. Utilizzando una multilinea per suddividere e scorrere il risultato in modo da rimuovere le coordinate aggiuntive aggiunte dalla suddivisione, otteniamo un insieme di funzionalità che non attraversano mai la linea di dati.


+1 per sforzo, ma i tuoi esempi di mappe non sembrano risolvere il problema. Le tracce satellitari sulla tua mappa di esempio attraversano ancora l'intero globo invece di seguire il percorso naturale della traccia satellitare. Mi manca qualcosa?
CaptDragon,

Potresti fraintendere il problema perché ..., -178.475596 -81.673196, 156.248392 -81.611421,...attraversa assolutamente la linea di dati. Vedi qui
CaptDragon,

Vorrei aggiornare il codice e la mia spiegazione. So che dovrebbe attraversare la linea di dati, ma OpenLayers non supporta questo. Dal punto di vista OL, non sta attraversando la linea di dati.
Julien-Samuel Lacroix,

Giusto. Questo è il problema. Dovrò ingannare gli OpenLayer e dividere la linea in modo che vada fino al bordo e poi continui dall'altra parte dove dovrebbe.
CaptDragon,

2

Ho trovato un'ottima soluzione sul github di @Dane. Si chiama Arc.js ed è per il calcolo delle rotte Great Circle. Non solo, dividerà anche la linea sulla linea dati e ti fornirà due linee lineari che si incontrano sulla linea dati, che OpenLayers può facilmente mappare. Spero che si faccia avanti per rivendicare la generosità.

Ecco i miei risultati:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


1

La funzione splitWith non conosce la forma tridimensionale della terra. Funziona solo in un mondo bidimensionale. Nel tuo caso, tutte le tue LINESTRINGcoordinate X sono comprese tra -180 e 180. Quindi, dalla prospettiva bidimensionale di OpenLayers, la stringa di linea non attraversa mai realmente la tua geometria divisa (la linea della data) e te lo dice ritornando null.

Credo che dovrai scrivere del codice personalizzato per eseguire la divisione. Sto immaginando un algoritmo che gira sui tuoi vertici, costruendo stringhe di linea di output in questo modo:

  • Per ogni coppia adiacente di vertici decidere se il segmento tra loro attraversa la linea della data.
  • In caso contrario, mantieni quel segmento così com'è e aggiungilo alla stringa della linea di output "corrente".
  • Se si fa , dividere il segmento in due parti. Aggiungi una parte alla stringa di linea "corrente", avvia una nuova stringa di linea "corrente" e aggiungi l'altra parte a questa nuova.

Una euristica ragionevole per determinare se una coppia di vertici attraversa la linea della data è vedere se la differenza tra le coordinate X è superiore a 180 gradi. (Anche se questo può sbagliare, ad esempio, nelle regioni polari. Forse sei abbastanza fortunato da non avere latitudini molto alte.)

L'operazione di divisione di un segmento in due parti potrebbe essere semplice come l'interpolazione lineare (se non ti interessa troppo l'accuratezza della traccia). Quando si rileva che il segmento attraversa la riga della data, si crea una copia del secondo vertice e si sposta la sua coordinata X (aggiungendo o sottraendo 360 gradi), quindi interpolare la coordinata Y.

EDIT : Ecco un JSFiddle che dimostra l'algoritmo sopra riportato sui tuoi dati: http://jsfiddle.net/85vjS/


0

Se funziona con Greenwich, questo è perché sei all'interno dei limiti del tuo CRS. Quindi suggerirei prima la stessa soluzione alternativa nel post a cui stai indicando:

var featDateLine = format.read("LINESTRING(179.99 -89, 179.99 89)");

e forse

var featDateLine = format.read("LINESTRING(-179.99 -89, -179.99 89)");

per l'altro lato.

Un'altra soluzione è quella di lavorare in un CRS che non sia "fuori limite" sulla linea dati. Dovresti quindi essere in grado di dividere i dati senza problemi.


Ho già provato entrambi LINESTRING(179.99 -89, 179.99 89)e LINESTRING(-179.99 -89, -179.99 89)senza risultati. Per quanto riguarda il CRS, sfortunatamente, questo non funzionerà per il mio scopo perché sto mappando tracce satellitari che vanno in giro per il mondo molte volte. Quindi tutti i CRS sono divisi da qualche parte e avrò lo stesso problema ovunque lo divida. Grazie per il tuo contributo.
CaptDragon,
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.