Linestrings auf Dateline mit OpenLayers teilen

9

Vor ein paar Jahren habe ich The International Date Line Wrap Around gepostet und @jdeolive hat vorgeschlagen, die Funktionen in der DateLine aufzuteilen. Also habe ich es versucht.

Wenn ich versuche, meine Satellitenspur mit splitWith auf der Datenlinie zu teilen, erhalte ich zurück null. Ich weiß, dass ich richtig teile, weil ich beim Teilen auf der Greenwich-Linie die erwarteten Ergebnisse erhalte.

Weiß jemand, wie ich einen Linestring mit OpenLayers programmgesteuert entlang der Datumsgrenze richtig aufteilen kann ? Ich suche Beispielcode, wenn Sie ihn haben.

Ich habe es versucht, wrapDateLineaber es scheint nicht auf Vektorebenen zu funktionieren, obwohl meine Vektorebene so aussieht:

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

Geben Sie hier die Bildbeschreibung ein

Hier ist mein Code:

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

Meine Frage ist kein Duplikat dieser Frage, weil sie wissen wollen, wie es in ogr2ogr geht

Aktualisieren:

So sieht ein typischer Datensatz aus, mit dem ich arbeite (24-Stunden-Satellitenspur): Den Linestring wkt finden Sie HIER .

Geben Sie hier die Bildbeschreibung ein

CaptDragon
quelle
Welche Version von Openlayern verwenden Sie?
Plux
@Plux 2.13.1 (Lastest)
CaptDragon
1
Laut API sollte wrapDateLine nur auf der Basisebene verwendet werden, daher ist es kein Wunder, dass es auf der Vektorebene nicht funktioniert. Ich habe jedoch keine Ahnung, wie es auf der Vektorebene funktioniert. Ich habe selbst ein ähnliches Problem mit Multipolygonen, die die Datenlinie überschreiten.
Plux
1
@Plux überprüfen Sie meine Lösung
CaptDragon
Schöne Lösung. Es ist leider nicht auf mein Problem anwendbar. Ich glaube, mein Problem liegt eher auf der Geoserver-Seite, und mein Polygon enthält Koordinaten, die sich auf der "anderen Seite" der Datenlinie befinden, z. B. -180.00000000000003 90.00000190734869, was meiner Meinung nach einige Probleme im Geoserver verursacht. Ich habe keine Probleme, dies auf meiner Karte anzuzeigen (ich habe ein wms, das dies tut), aber ich möchte diese als Filterboxen für eine wfs-Abfrage an den Geoserver verwenden :)
Plux

Antworten:

2

Das Problem ist, dass Ihr Feature aus Sicht von OpenLayers die Datumsgrenze nicht überschreitet, sodass Ihre geteilte Linie Ihr Feature nicht schneidet. Beispiel aus Ihren Daten:

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

Sie gehen von -178 auf 156, und dies überschreitet aus Sicht von OpenLayers nicht die Datumsgrenze. Anstatt in der Datumszeile zu teilen, sollten Sie Ihren minimalen X-Wert teilen.

// 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)
);

Ich habe hier ein Beispiel erstellt, das Ihre Satellitenspur erfolgreich in zwei Funktionen aufteilt : http://jsfiddle.net/6XJ5A/

Um nun die WKT mit mehreren Linien in Ihrem Update zu verwenden, müssen Sie anstelle einer geraden Linie den gesamten Datensatz durchlaufen und Ihre geteilte Linie mit allen Koordinaten erstellen, die über die Datenlinie verlaufen. Durch Erstellen einer kleinen Linie innerhalb einer mehrzeiligen Linie können Sie alle Koordinaten aufteilen, die über die Datenlinie verlaufen sollen. Hier ist das aktualisierte Beispiel: http://jsfiddle.net/Jc274/

Und der Code:

// 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);

Dadurch erhalten Sie eine geteilte Linie für alle Punkte, die die Datenlinie "kreuzen"

Beachten Sie, dass ich auch die Koordinaten durchlaufe, um die Linie zu entfernen, die über die Karte verläuft, um die beiden Koordinaten zu verbinden:

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

Update: Ich habe das erste Beispiel aktualisiert, um nur die geteilte Zeile hinzuzufügen. Ich habe auch die Erklärung entsprechend aktualisiert. Dieser Ansatz ist mit der von Ihnen bereitgestellten 24-Stunden-Satellitenspur nicht kugelsicher, aber ich arbeite daran.

Update 2: Ich habe das zweite Beispiel aktualisiert. Durch die Verwendung einer Mehrzeiligkeit zum Teilen und Durchlaufen des Ergebnisses zum Entfernen zusätzlicher Koordinaten, die durch die Teilung hinzugefügt wurden, erhalten wir eine Reihe von Funktionen, die niemals die Datenlinie überschreiten.

Julien-Samuel Lacroix
quelle
+1 für Mühe, aber Ihre Kartenbeispiele scheinen das Problem nicht zu lösen. Die Satellitenspuren auf Ihrer Beispielkarte durchqueren immer noch den gesamten Globus, anstatt dem natürlichen Pfad der Satellitenspur zu folgen. Mir muss etwas fehlen?
CaptDragon
Möglicherweise verstehen Sie das Problem falsch, da es ..., -178.475596 -81.673196, 156.248392 -81.611421,...die Datenlinie absolut überschreitet. Siehe hier
CaptDragon
Lassen Sie mich den Code und meine Erklärung aktualisieren. Ich weiß, dass es die Datenleitung überschreiten sollte, aber OpenLayers unterstützt dies nicht. Aus OL-Sicht überschreitet es nicht die Datenlinie.
Julien-Samuel Lacroix
Stimmt. Das ist das Problem. Ich muss OpenLayers austricksen und die Linie so teilen, dass sie bis zum Rand reicht und dann auf der anderen Seite weitergeht, wo sie soll.
CaptDragon
2

Ich habe eine großartige Lösung für @Danes Github gefunden. Es heißt Arc.js und dient zur Berechnung von Großkreisrouten. Darüber hinaus wird die Linie auf der Datenlinie aufgeteilt und Sie erhalten zwei Linestrings, die sich an der Datenlinie treffen und die OpenLayers problemlos zuordnen kann. Ich hoffe, er meldet sich, um das Kopfgeld zu fordern.

Hier sind meine Ergebnisse:

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

CaptDragon
quelle
1

Die Funktion splitWith kennt die dreidimensionale Form der Erde nicht. Es funktioniert nur in einer zweidimensionalen Welt. In Ihrem Fall liegen alle Ihre LINESTRINGX-Koordinaten zwischen -180 und 180. Aus der zweidimensionalen Perspektive von OpenLayers kreuzt die Linienzeichenfolge also niemals Ihre geteilte Geometrie (die Datumslinie) und teilt dies Ihnen mit, indem Sie zurückkehren null.

Ich glaube, dass Sie einen benutzerdefinierten Code schreiben müssen, um die Aufteilung durchzuführen. Ich stelle mir einen Algorithmus vor, der Ihre Scheitelpunkte durchläuft und folgende Ausgabezeilenzeichenfolgen erstellt:

  • Entscheiden Sie für jedes benachbarte Scheitelpunktpaar, ob das Segment zwischen ihnen die Datumsgrenze überschreitet.
  • Wenn dies nicht der Fall ist, behalten Sie das Segment bei und fügen Sie es der "aktuellen" Ausgabezeilenzeichenfolge hinzu.
  • Wenn dies der Fall ist , teilen Sie das Segment in zwei Teile. Fügen Sie einen Teil zur "aktuellen" Zeilenfolge hinzu, starten Sie eine neue "aktuelle" Zeilenfolge und fügen Sie den anderen Teil zu dieser neuen hinzu.

Eine vernünftige Heuristik zur Bestimmung, ob ein Paar von Eckpunkten die Datumsgrenze kreuzt, besteht darin, festzustellen, ob der Unterschied zwischen den X-Koordinaten mehr als 180 Grad beträgt. (Obwohl dies zum Beispiel in den Polarregionen falsch sein kann. Vielleicht haben Sie das Glück, keine wirklich hohen Breiten zu haben.)

Das Aufteilen eines Segments in zwei Teile kann so einfach wie eine lineare Interpolation sein (wenn Sie sich nicht zu sehr um die Genauigkeit der Spur kümmern). Wenn Sie feststellen, dass das Segment die Datumsgrenze überschreitet, erstellen Sie eine Kopie des zweiten Scheitelpunkts, verschieben seine X-Koordinate (durch Addieren oder Subtrahieren von 360 Grad) und interpolieren dann die Y-Koordinate.

BEARBEITEN : Hier ist eine JSFiddle, die den obigen Algorithmus für Ihre Daten demonstriert: http://jsfiddle.net/85vjS/

csd
quelle
0

Wenn es mit Greenwich funktioniert, liegt dies daran, dass Sie sich innerhalb der Grenzen Ihres CRS befinden. Daher würde ich zunächst dieselbe Problemumgehung vorschlagen wie in dem Beitrag, auf den Sie verweisen:

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

und vielleicht

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

für die andere Seite.

Eine andere Lösung besteht darin, in einem CRS zu arbeiten, das an der Datenlinie nicht "ungebunden" ist. Sie sollten dann in der Lage sein, Ihre Daten problemlos aufzuteilen.

Radouxju
quelle
Ich habe bereits beide versucht LINESTRING(179.99 -89, 179.99 89)und LINESTRING(-179.99 -89, -179.99 89)ohne Erfolg. In Bezug auf das CRS funktioniert dies leider nicht für meinen Zweck, da ich Satellitenspuren kartiere, die viele Male um die Welt gehen. Alle CRS sind also irgendwo aufgeteilt, und ich habe überall dort, wo ich sie aufteile, das gleiche Problem. Danke für deinen Beitrag.
CaptDragon