Międzynarodowa linia daty jest zawijana

13

Korzystając z OpenLayers, dodałem warstwę WFS (na GeoServer) z filtrem, który zwraca wszystkie funkcje (czarne) przecinające mój wielokąt (żółty) umieszczony w niektórych krajach Ameryki Łacińskiej w określonych datach.

wprowadź opis zdjęcia tutaj

Jednak funkcja przechodząca poziomo przez mapę NIE przecina mojego wielokąta. Ta funkcja znajduje się gdzieś na Oceanie Spokojnym między Hawajami a Fidżi, a NIE w Ameryce Łacińskiej. Problem polega na tym, że zamiast przekraczać międzynarodową linię daty, jest ona renderowana na mapie, owijając się po całym świecie.

Funkcja problamatyczna jest zdefiniowana:

POLYGON ((- 179,700417 14.202717, -178,687422 13,992875, 179,024138 8,24716, -179,98241 8,035567, -179,700417 14.202717))

Mam wiele problematycznych funkcji linii daty, ale zawęziłem ją do tego w tym przykładzie. Nie mogę tego zignorować w mojej aplikacji, ponieważ mam ich wiele.

Próbowałem użyć „wrapDateLine: true” w warstwie podstawowej i warstwie WFS z tymi samymi wynikami.

Nie jestem pewien, czy byłby to problem GeoServer czy problem OpenLayers.

Czy ktoś zna rozwiązanie mojego problemu z międzynarodową linią dat?

CaptDragon
źródło
2
Nie wiem, dlaczego oprogramowanie ma taki problem, świat jest płaski, prawda ?!
DavidF,
Może powinien istnieć parametr kierunku.
CaptDragon,
@CaptDragon Wszelkie rozwiązania tego problemu?
Anil
@ Anil None Jeszcze. Daj mi znać, jeśli znajdziesz.
CaptDragon,

Odpowiedzi:

6

Niestety jest to znany problem. Problem polega na tym, że geometrie przecinające linię daty w ten sposób są niejednoznaczne. Rendery OL i GeoServer nie mają łatwego sposobu, aby dowiedzieć się, że intencją jest przejście „krótkiej” drogi dookoła świata, więc po prostu interpretują na przykład 170 do -170 „zwykłą” drogę i idą długą drogą dookoła świata.

Niestety nie ma na to dobrego rozwiązania poza podzieleniem geometrii leżących w poprzek linii danych.

jdeolive
źródło
Dzięki +1, zgadzam się, ale nie mogę podzielić moich geometrii. Zobaczmy, czy ktoś jeszcze ma jakieś inne pomysły.
CaptDragon,
Wymyśliłem sposób, aby ładnie je podzielić na OpenLayers.
CaptDragon
7

Ponownie rzutuj mapę, aby użyć rzutu podzielonego na południk Greenwich (lub w innym miejscu), aby wielokąty, którymi jesteś zainteresowany, nie przekraczały nieciągłości na mapie.

Ian Turton
źródło
wielokąty całkiem dobrze pokrywają świat, zawsze będą wielokąty, które przekroczą pewną linię. A czy znasz jakieś projekcje, które nie są podzielone w ten sposób?
CaptDragon,
1
Wszystkie projekcje muszą gdzieś podzielić świat, jest to ukryte w matematyce (obierz pomarańczę, jeśli mi nie wierzysz :-)). Wszystko, co możesz zrobić, to wybrać najlepszą projekcję do swojego zadania.
Ian Turton
tak, masz rację. Zostawię to otwarte na kilka dni i sprawdzę, czy pojawią się jakieś inne pomysły. Dzięki za sugestię. :-)
CaptDragon
2

Badałem ten problem od dłuższego czasu, ponieważ opracowałem aplikację, która pozwala użytkownikowi generować prostokąt Obszaru zainteresowania za pomocą akcji DragBox lub kreślenia wprowadzonych przez użytkownika punktów zasięgu. Gdy zacząłem tę przygodę, byłem zupełnie nowy w OpenLayers. Problem z ręcznie wprowadzonymi punktami zasięgu był taki, że jeśli AOI pokryłaby międzynarodową linię danych, narysowany prostokąt zostałby narysowany w niewłaściwy sposób na całym świecie. Wielu użytkowników StackExchange zapytało o ten problem tylko po to, by odpowiedzieć OpenLayers, że (i parafrazuję tutaj) „OpenLayers nie ma możliwości poznania kierunkowego celu punktów, które mają być narysowane, więc domyślnie ...”. Uh, muszę podnieść flagę BS w tej odpowiedzi, ponieważ dowiedziałem się już wystarczająco dużo o OpenLayers, aby być niebezpiecznym i ten problem zdarza mi się. Problem, jaki mam z ich odpowiedzią, polega na tym, że ładuję współrzędne w zakresie, który z definicji określa górną i prawą długość i szerokość geograficzną, a także dolną lewą długość i szerokość. Jeśli górna prawa długość geograficzna leży po zachodniej stronie IDL, a dolna lewa długość geograficzna leży po wschodniej stronie IDL, jest dość oczywiste, w jaki sposób użytkownik chce wykreślić wielokąt, a mimo to OpenLayers nalega na zamianę wartości wzdłużnych i rysowanie wielokąt w niewłaściwy sposób na całym świecie. Przykład deklaracji zakresu i problematycznego wywołania metody OpenLayers pokazano poniżej. Jeśli górna prawa długość geograficzna leży po zachodniej stronie IDL, a dolna lewa długość geograficzna leży po wschodniej stronie IDL, jest dość oczywiste, w jaki sposób użytkownik chce wykreślić wielokąt, a mimo to OpenLayers nalega na zamianę wartości wzdłużnych i rysowanie wielokąt w niewłaściwy sposób na całym świecie. Przykład deklaracji zakresu i problematycznego wywołania metody OpenLayers pokazano poniżej. Jeśli górna prawa długość geograficzna leży po zachodniej stronie IDL, a dolna lewa długość geograficzna leży po wschodniej stronie IDL, jest dość oczywiste, w jaki sposób użytkownik chce wykreślić wielokąt, a mimo to OpenLayers nalega na zamianę wartości wzdłużnych i rysowanie wielokąt w niewłaściwy sposób na całym świecie. Przykład deklaracji zakresu i problematycznego wywołania metody OpenLayers pokazano poniżej.

// I would start out with the following entered values as an example
lonLL = 175.781; // minX
latLL = 13.992;  // minY
lonUR = -165.937;// maxX
latUR = 25.945;  // maxY

// I would then make the following call
var manCoordEntryExtent = ol.extent.boundingExtent([[lonLL,latLL], [lonUR, latUR]]);

// Looking at the resulting structure in the debugger I get:
0: -165.937   // minX
1: 13.992     // minY
2: 175.781    // maxX
3: 25.945     // maxY
length: 4
__proto__: []

Jak widać, współrzędne podłużne zostają odwrócone, a więc po utworzeniu pełnej struktury współrzędnych, wielokąta. Wielokąt Wykonaj tę funkcję, a następnie zastosuj tę cechę do wektora, a na koniec wykreśl go, aby stwierdzić, że wielokąt idzie w niewłaściwą stronę na całym świecie.

Musiałem dowiedzieć się, dlaczego tak się dzieje, więc zagłębiłem się w tę metodę ol.extent.boundingExtent w bibliotece OpenLayers 4.

/**
 * Build an extent that includes all given coordinates.
 *
 * @param {Array.<ol.Coordinate>} coordinates Coordinates.
 * @return {ol.Extent} Bounding extent.
 * @api
 */
ol.extent.boundingExtent = function(coordinates) {
  var extent = ol.extent.createEmpty();
  for (var i = 0, ii = coordinates.length; i < ii; ++i) {
    ol.extent.extendCoordinate(extent, coordinates[i]);
  }
  return extent;
};

It first calls ol.extent.createEmpty to initially create an extent structure

/**
 * Create an empty extent.
 * @return {ol.Extent} Empty extent.
 * @api
 */
ol.extent.createEmpty = function() {
  return [Infinity, Infinity, -Infinity, -Infinity];
};

// It then iterates thru the number of coordinates and fills in the extent   structure values, however...
// Here is where the problem is.  Notice the complete lack of any explanation as to what the hell this
// method is doing.  Why is it doing what it does?  All I know is that it cannot handle plots across 
// the IDL and it corrupts your extent structure if you try.

/**
 * @param {ol.Extent} extent Extent.
 * @param {ol.Coordinate} coordinate Coordinate.
 */
ol.extent.extendCoordinate = function(extent, coordinate) {
  if (coordinate[0] < extent[0]) {
    extent[0] = coordinate[0];
  }
  if (coordinate[0] > extent[2]) {
    extent[2] = coordinate[0];
  }
  if (coordinate[1] < extent[1]) {
    extent[1] = coordinate[1];
  }
  if (coordinate[1] > extent[3]) {
    extent[3] = coordinate[1];
  }
};

// The solution was for me to test for IDL myself and if found then create an empty extent and populate it myself manually.

// Using the same extent coordinates as before
lonLL = 175.781; // minX
latLL = 13.992;  // minY
lonUR = -165.937;// maxX
latUR = 25.945;  // maxY

// I test for Dateline instance (Dont have to worry about the potential of there being a polygon covering both Meridian 
// and Anti-meridian as a valid polygon is limited to a maximum size of just over 12 million square kilometers.)
if ((lonLL > 0.0) && (lonUR < 0.0)) {
    // Manually build the coordinates for the Area calculation as the boundingExtent 
    // codepath corrupts an extent to be plotted across the Dateline
    var manCoordEntryExtent = ol.extent.createEmpty();
    manCoordEntryExtent[0] = lonLL;
    manCoordEntryExtent[1] = latLL;
    manCoordEntryExtent[2] = lonUR + 360.0;
    manCoordEntryExtent[3] = latUR;
} else {
    var manCoordEntryExtent = ol.extent.boundingExtent([[lonLL,latLL], [lonUR, latUR]]);
}

// Looking at the resulting structure in the debugger I get:
0: 175.781 // minX
1: 13.992  // minY
2: 194.063 // maxX
3: 25.945  // maxY
length: 4
__proto__: []

Mój kod oblicza obszar dynamicznie, dzięki czemu mogę ustalić, czy użytkownik utworzył wielokąt AOI o prawidłowej wielkości. Kiedy przetwarzam zaznaczenie wygenerowane przez DragBox, żądam współrzędnych z wynikowej struktury geometrycznej, a dla projekcji EPSG: 4326, gdy zwraca współrzędne z zawiniętego świata, współrzędne powyżej pierwszych 180,0 stopni kontynuują zwiększanie, dlatego powód obliczenia lonUR dla 360,0 - 165,937 = 194,063. Moja ścieżka kodowa obliczania obszaru korzysta z następującego testu IDL i aby użyć tej samej ścieżki kodowej dla ręcznie wprowadzonych współrzędnych, musiałem zasymulować wartość współrzędnych, tak jakby została zwrócona z wywołania DragGox getGeometry. W rzeczywistości testuję strukturę wielokąta GEOJSON, która jest trójwymiarową tablicą, a pierwszy wymiar to liczba pierścienia,

 function getArea(coords, extent) {

  // Test for Western side of Dateline instance
  if (((coords[0][0][0] <= -180.0) && (coords[0][2][0] > -180.0)) ||
      // Test for Eastern side of Dateline instance
      ((coords[0][0][0] < 180.0) && (coords[0][2][0] >= 180.0))) {
 .
 .
 .

Jeśli testy te przejdą w tym momencie, kod korzysta z opracowanego przeze mnie algorytmu, aby obliczyć obszar nad IDL, w przeciwnym razie po prostu oblicza go normalnie wszędzie indziej.

Następnie używam tego zasięgu do utworzenia wielokąta, a następnie polygonFeature, a następnie zastosuję tę cechę do wektora i na koniec narysuję go, tym razem wykreślając poprawnie. Więc poprawka, którą wymyśliłem, aby pomóc rozwiązać problem obliczania obszaru, miałem również naprawiony problem z wykreślaniem.

Może to rozwiązanie pomoże komuś innemu lub zmusi go do myślenia w innym kierunku. Rozwiązanie przyszło mi do głowy, kiedy w końcu udało mi się rozdzielić problem IDL na dwa problemy. Faktyczne obliczanie powierzchni było jednym problemem, a drugim było wykreślanie wielokąta nad IDL.

użytkownik1593611
źródło
1
OL po prostu używa operatora> =, aby wiedzieć, którą stronę wybrać podczas drukowania. Jeśli dasz 170, a następnie 190, pójdzie to krótko; jeśli dasz 170, a następnie -170, przejdzie długą drogę. Jeśli zawsze „normalizujesz” długość geograficzną w zakresie od -180 do 180, tracisz informacje. Jednym ze sposobów odzyskania informacji jest narzucenie, że odległości między punktami nie mogą przekraczać> 180
Rivenfall,
1

Workround: Przykład

var mapserv = new OpenLayers.Layer.MapServer( "OpenLayers Basic",
                "http://vmap0.tiles.osgeo.org/wms/vmap0",
                {layers: 'basic'},
                {wrapDateLine: true} );

http://openlayers.org/dev/examples/wrapDateLine.html

Mapperz
źródło
Korzystam z WFS. Link, który opublikowałeś mówi: „Możesz to zrobić z warstwą„ Layer.WMS ”lub„ Layer.MapServer ”
CaptDragon,
Jeśli oba są obsługiwane i nie potrzebujesz konkretnie Layer.MapServer, przejdź do Layer.WMS (który nadal może być obsługiwany z MapServer).
DavidF
@DavidF: Dzięki, ale muszę użyć wektorowych możliwości WFS.
CaptDragon,
1

Dwa lata później nadal miałem ten problem z funkcjami na warstwie wektorowej. Znalazłem ten plik zawierający fragment kodu, który pokazuje, jak odwrócić punkt końcowy, jeśli przekroczył linię danych:

if(Math.abs(startPoint.x-endPoint.x) > 180) {
  if(startPoint.x < endPoint.x) {
    endPoint.x -= 360;
  } else {
    endPoint.x += 360;
  }
}

Aktualizacja:

W rzeczywistości powyższe nie działało przez więcej niż jedną rewolucję na całym świecie. Skończyłem robić TO .

wprowadź opis zdjęcia tutaj

CaptDragon
źródło
1

W moich własnych projektach znalazłem rozwiązanie, które może, ale nie musi, działać. Wiem na pewno, że działa z LineStrings, ale nie jestem pewien co do innych typów geometrii.

OpenLayers.Geometry.prototype.crossesDateLine = function() {
    var lastX = this.components[0];
    for (var i=0; i < this.components.length; i++) {
        if (Math.abs(this.components[i].x - lastX) > 180) return i;
        lastX = this.components[i].x;
    }
    return false;
};
OpenLayers.Geometry.prototype.dateLineFix = function() {
    var linestrings = [];
    if (this.crossesDateLine()) {
        var string1 = [];
        for (var i = 0; i < this.crossesDateLine(); i++)
            string1.push(this.components[i]);
        var ls1 = new OpenLayers.Geometry.LineString(string1);
        var string2 = [];
        for (var i = this.crossesDateLine(); i < this.components.length; i++)
            string2.push(this.components[i]);
        var ls2 = new OpenLayers.Geometry.LineString(string2);

        if (!ls1.crossesDateLine()) {
            linestrings.push(ls1);
        } else {
            var split = ls1.dateLineFix();
            for (var i = 0; i < split.components.length; i++)
                linestrings.push(split.components[i]);
        }
        if (!ls2.crossesDateLine()) {
            linestrings.push(ls2);
        } else {
            var split = ls2.dateLineFix();
            for (var i = 0; i < split.components.length; i++)
                linestrings.push(split.components[i]);
        }
    } else {
        linestrings.push(this);
    }
    return new OpenLayers.Geometry.MultiLineString(linestrings);
};

Funkcja dateLineFix rekurencyjnie przechodzi przez podany LineString dla wszystkich segmentów przekraczających linię daty. Następnie przecina je na dwie części na linii danych i zwraca wszystkie wynikowe segmenty jako MultiLineString.

Działa idealnie dla mojego celu (rysowanie polarnej siatki lat-lon).

Scott Odle
źródło
0

Miałem kilka problemów z linią danych i udało mi się je wszystkie naprawić. Możesz spróbować śledzić.

  1. Zaktualizuj ręcznie wartości obwiedni warstwy GeoServer, aby zakryć wielokąt bez rozbijania i sprawdź, czy to rozwiązuje problem.

  2. Jedną z poprawek, które zrobiłem w Openlayers, jest brak kafelków podczas przekazywania linii danych od + ve długości geograficznej do -ve. http://trac.osgeo.org/openlayers/ticket/2754 Nie wiem, czy dotyczy WFS. Możesz pobrać najnowszą wersję rozwojową Openlayers i spróbować.

Senthil
źródło
0

Napotkałem ten problem z LineStrings i stworzyłem dla niego rozwiązanie. Nie jestem pewien, czy pomogłoby ci to z wielokątami. Możesz to zobaczyć na moim repl.it tutaj: https://repl.it/@gpantic/OpenLayersSplitRouteOverPacific

olbrzymi
źródło
1
Jeśli odpowiada na pytanie, dodaj wszystkie informacje do swojej odpowiedzi zamiast podać link.
BERA
0

EPSG: 3832 (WGS84 PDC) to projekcja skoncentrowana na Oceanie Spokojnym. Spowoduje to wymianę problemów z przejściem IDL na problemy ze skrzyżowaniem Prime Meridian. To może nie stanowić problemu w zależności od tego, co przedstawisz. Znalazłem również problemy w pobliżu kół arktycznych i antarktycznych.

Kredyt trafia do tego artykułu.

Będzie
źródło