Podział linii na linii danych za pomocą OpenLayers

9

Kilka lat temu opublikowałem zawijanie The International Date Line i @jdeolive zasugerowało podzielenie funkcji na dateLine. Więc próbowałem.

Kiedy próbuję podzielić swoją ścieżkę satelitarną za pomocą splitWith na linii danych, wracam null. Wiem, że dzielę się poprawnie, ponieważ po podzieleniu na linii Greenwich uzyskuję oczekiwane wyniki.

Czy ktoś wie, jak mogę poprawnie rozdzielić Linestring programowo wzdłuż linii daty w OpenLayers? Szukam przykładowego kodu, jeśli go masz.

Próbowałem, wrapDateLineale wydaje się, że nie działa na warstwach wektorowych, mimo że moja warstwa wektorowa jest taka:

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

wprowadź opis zdjęcia tutaj

Oto mój kod:

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

Moje pytanie nie jest duplikatem tego pytania, ponieważ chcą wiedzieć, jak to zrobić w ogrodach2

Aktualizacja:

Tak wygląda typowy zestaw danych, z którym pracuję (24-godzinna ścieżka satelitarna): Wkt Linestring można znaleźć TUTAJ .

wprowadź opis zdjęcia tutaj

CaptDragon
źródło
Jakiej wersji Openlayers używasz?
Plux
@Plux 2.13.1 (Lastest)
CaptDragon
1
Zgodnie z ich API, wrapDateLine należy stosować tylko na warstwie podstawowej, więc nic dziwnego, że nie działa na warstwie wektorowej. Nie mam jednak pojęcia, jak sprawić, by działał na warstwie wektorowej. Sam mam podobny problem z wielobokami, które przecinają linię danych.
Plux
1
@Plux sprawdź moje rozwiązanie
CaptDragon
Niezłe rozwiązanie. Niestety nie ma zastosowania do mojego problemu, uważam, że mój problem dotyczy bardziej strony geoservera, a mój wielokąt zawiera współrzędne, które znajdują się po „drugiej stronie” linii danych, na przykład -180.00000000000003 90.00000190734869, co, jak sądzę, powoduje pewne problemy. Nie mam problemy z wyświetlaniem tego na mojej mapie (mam WMS, który to robi), ale chcę używać jako filterboxes on a WFS-zapytanie do GeoServer :)
Plux

Odpowiedzi:

2

Problem polega na tym, że twoja funkcja nie przekracza linii daty z perspektywy OpenLayers, więc twoja linia podziału nie przecina twojej funkcji. Przykład z twoich danych:

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

Przechodzisz od -178 do 156, a to nie przekracza linii daty z perspektywy OpenLayers. Zamiast dzielić na linię daty, należy podzielić na minimalną wartość X.

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

Zbudowałem tutaj przykład, który z powodzeniem podzielił twoją ścieżkę satelitarną na 2 funkcje: http://jsfiddle.net/6XJ5A/

Teraz, aby użyć WKT z wieloma liniami w aktualizacji, zamiast linii prostej, musisz przejść przez cały zestaw danych i zbudować linię podziału ze wszystkimi współrzędnymi przechodzącymi przez linię danych. Budując małą linię wewnątrz multilinii, możesz podzielić wszystkie współrzędne, które powinny przechodzić przez linię danych. Oto zaktualizowany przykład: http://jsfiddle.net/Jc274/

I kod:

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

Spowoduje to zwrócenie linii podzielonej na wszystkich punktach „przecinających” linię danych

Zauważ, że również pętlę przez współrzędne, aby usunąć linię przechodzącą przez mapę, aby połączyć 2 współrzędne:

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

Aktualizacja: Zaktualizowałem pierwszy przykład, aby dodać tylko linię, która została podzielona. Zaktualizowałem również odpowiednio wyjaśnienie. To podejście nie jest kuloodporne w przypadku 24-godzinnej ścieżki satelitarnej, którą podałeś, ale pracuję nad tym.

Aktualizacja 2: Zaktualizowałem drugi przykład. Używając multilinii do dzielenia i zapętlania wyniku w celu usunięcia dodatkowych współrzędnych dodanych przez podział, otrzymujemy zestaw funkcji, które nigdy nie przechodzą przez linię danych.

Julien-Samuel Lacroix
źródło
+1 za wysiłek, ale twoje przykłady map nie wydają się rozwiązać problemu. Ślady satelitarne na przykładowej mapie nadal przecinają cały glob zamiast podążać naturalną ścieżką śladu satelitarnego. Coś mi brakuje?
CaptDragon
Być może nie rozumiesz problemu, ponieważ ..., -178.475596 -81.673196, 156.248392 -81.611421,...absolutnie przecina linię danych. Zobacz tutaj
CaptDragon
Pozwól mi zaktualizować kod i moje wyjaśnienie. Wiem, że powinien przekroczyć linię danych, ale OpenLayers tego nie obsługuje. Z punktu widzenia OL nie przekracza linii danych.
Julien-Samuel Lacroix
Zgadza się. To jest problem. Będę musiał oszukać OpenLayers i podzielić linię tak, aby biegła aż do krawędzi, a następnie kontynuowała po drugiej stronie, gdzie powinna.
CaptDragon
2

Znalazłem świetne rozwiązanie na githubie @Dane. Nazywa się Arc.js i służy do obliczania tras Wielkiego Kręgu. Oprócz tego podzieli linię na linii danych i zapewni dwa linie łączące się na linii danych, które OpenLayers może łatwo zmapować. Mam nadzieję, że wystąpi, by odebrać nagrodę.

Oto moje wyniki:

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

CaptDragon
źródło
1

Funkcja splitWith nie wie o trójwymiarowym kształcie ziemi. Działa tylko w dwuwymiarowym świecie. W twoim przypadku wszystkie twoje LINESTRINGwspółrzędne X są w zakresie od -180 do 180. Tak więc z dwuwymiarowej perspektywy OpenLayers ciąg linii nigdy tak naprawdę nie przecina twojej podzielonej geometrii (linii daty) i mówi to przez powrót null.

Uważam, że będziesz musiał napisać niestandardowy kod, aby wykonać podział. Wyobrażam sobie algorytm, który zapętla twoje wierzchołki, budując ciągi linii wyjściowych w następujący sposób:

  • Dla każdej sąsiedniej pary wierzchołków zdecyduj, czy odcinek między nimi przecina linię daty.
  • Jeśli nie, zachowaj ten segment tak, jak jest i dodaj go do „bieżącego” ciągu linii wyjściowej.
  • Jeśli to robi , podzielić odcinek na dwie części. Dodaj jedną część do „bieżącego” ciągu linii, rozpocznij nowy „bieżący” ciąg linii i dodaj drugą część do tego nowego.

Jedną rozsądną heurystyką dla ustalenia, czy para wierzchołków przecina linię daty, jest sprawdzenie, czy różnica między współrzędnymi X jest większa niż 180 stopni. (Chociaż może to źle zrozumieć, na przykład w regionach polarnych. Może masz szczęście, że nie masz naprawdę dużych szerokości geograficznych).

Operacja dzielenia segmentu na dwie części może być tak prosta jak interpolacja liniowa (jeśli nie zależy ci zbytnio na dokładności ścieżki). Po wykryciu, że segment przecina linię daty, tworzysz kopię drugiego wierzchołka i przesuwasz jego współrzędną X (dodając lub odejmując o 360 stopni), a następnie interpolujesz współrzędną Y.

EDYCJA : Oto JSFiddle, który demonstruje powyższy algorytm na twoich danych: http://jsfiddle.net/85vjS/

csd
źródło
0

Jeśli to działa z Greenwich, dzieje się tak, ponieważ znajdujesz się w granicach swojego CRS. Chciałbym więc najpierw zasugerować to samo obejście jak we wpisie, na który wskazujesz:

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

I może

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

z drugiej strony.

Innym rozwiązaniem jest praca w CRS, który nie jest „niezwiązany” na linii danych. Powinieneś być w stanie podzielić swoje dane bez problemu.

radouxju
źródło
Próbowałem już obu LINESTRING(179.99 -89, 179.99 89)i LINESTRING(-179.99 -89, -179.99 89)bezskutecznie. Jeśli chodzi o CRS, niestety to nie zadziała, ponieważ mapuję ślady satelitów, które krążą po całym świecie wiele razy. Więc wszystkie CRS są gdzieś podzielone i będę miał ten sam problem gdziekolwiek to podzielę. Dzięki za wkład.
CaptDragon