Die internationale Datumsgrenze wird umbrochen


13

Mit OpenLayers habe ich einen WFS-Layer (auf GeoServer) mit einem Filter hinzugefügt, der alle Features (schwarz) zurückgibt, die mein Polygon (gelb) schneiden, das innerhalb bestimmter Daten über einigen lateinamerikanischen Ländern platziert wurde.

Bildbeschreibung hier eingeben

Das Feature, das sich horizontal über die Karte erstreckt, schneidet mein Polygon jedoch NICHT. Diese Funktion befindet sich irgendwo im Pazifischen Ozean zwischen Hawaii und Fidschi und NICHT in Lateinamerika. Das Problem ist, dass die internationale Datumsgrenze nicht überschritten, sondern auf der Karte gerendert wird, indem die ganze Welt umhüllt wird.

Das problamatische Merkmal ist definiert:

POLYGON ((- 179.700417 14.202717, -178.687422 13.992875,179.024138 8.24716, -179.98241 8.035567, -179.700417 14.202717))

Ich habe viele problematische Datumsangaben wie diese, habe sie jedoch für dieses Beispiel auf diese eingegrenzt. Ich kann es in meiner Bewerbung nicht einfach ignorieren, weil ich viele davon habe.

Ich habe versucht, "wrapDateLine: true" in der Basisebene und der WFS-Ebene mit den gleichen Ergebnissen zu verwenden.

Ich bin mir nicht sicher, ob dies ein GeoServer- oder ein OpenLayers-Problem ist.

Kennt jemand eine Lösung für mein internationales Terminproblem?


2
Ich weiß nicht, warum Software so ein Problem damit hat, die Welt ist flach, oder ?!
DavidF

Vielleicht sollte es einen Richtungsparameter geben.
CaptDragon

@CaptDragon Irgendeine Lösung für dieses Problem?
Anil

@ Anil Noch keine. Bitte lassen Sie mich wissen, wenn Sie eine finden.
CaptDragon

Antworten:


6

Leider ist dies ein bekanntes Problem. Das Problem ist, dass Geometrien, die die Datumsgrenze wie folgt überschreiten, nicht eindeutig sind. Die OL- und GeoServer-Renderer können nicht leicht erkennen, dass die Absicht darin besteht, den "kurzen" Weg um die Welt zu gehen. Sie interpretieren also beispielsweise 170 bis -170 den "normalen" Weg und gehen den langen Weg um die Welt.

Leider gibt es keine gute Lösung dafür, außer Ihre Geometrien, die über die Datenlinie liegen, aufzuteilen.


Danke +1, ich stimme zu, aber ich kann meine Geometrien nicht teilen. Mal sehen, ob noch jemand andere Ideen hat.
CaptDragon

Ich fand einen Weg, sie auf OpenLayers schön aufzuteilen.
CaptDragon

7

Projizieren Sie Ihre Karte neu, um eine Projektion zu verwenden, die am Greenwich-Meridian (oder an einer anderen Stelle) aufgeteilt ist, damit die Polygone, an denen Sie interessiert sind, die Diskontinuität in Ihrer Karte nicht überschreiten.


Die Polygone decken die Welt ziemlich gut ab. Es wird immer Polygone geben, die eine Linie kreuzen. Kennen Sie Projektionen, die nicht so aufgeteilt sind?
CaptDragon

1
Alle Projektionen müssen die Welt irgendwo aufteilen, es ist in der Mathematik implizit (schälen Sie eine Orange, wenn Sie mir nicht glauben :-)). Sie können nur die beste Projektion für Ihre Aufgabe auswählen.
Ian Turton

ja, du hast recht. Ich lasse dies ein paar Tage offen und schaue, ob andere Ideen auftauchen. Vielen Dank für Ihren Vorschlag. :-)
CaptDragon

2

Ich habe dieses Problem bereits seit einiger Zeit untersucht, da ich eine Anwendung entwickelt habe, mit der der Benutzer ein Rechteck für den Interessenbereich entweder über eine DragBox-Aktion oder durch Zeichnen von vom Benutzer eingegebenen Ausdehnungspunkten erstellen kann. Als ich mit diesem Abenteuer anfing, war ich für OpenLayers völlig neu. Das Problem mit den manuell eingegebenen Ausdehnungspunkten war, dass das gezeichnete Rechteck um die Welt herum falsch gezeichnet würde, wenn AOI die internationale Datenlinie abdeckte. Zahlreiche StackExchange-Benutzer haben nach diesem Problem gefragt, nur um von einem OpenLayers-Antwortenden zu erfahren, dass (und ich umschreibe es hier) "OpenLayers keine Möglichkeit hat, die Richtungsabsicht der zu zeichnenden Punkte zu kennen, so dass sie standardmäßig ...". Ähm, ich muss das BS-Flag für diese Antwort setzen, da ich jetzt genug über OpenLayers gelernt habe, um gefährlich zu sein, und dieses Problem ist mir passiert. Das Problem, das ich mit ihrer Antwort habe, ist, dass ich die Koordinaten für eine Ausdehnung lade, die per Definition die Längen- und Breitengrade oben rechts sowie die Längen- und Breitengrade unten links angibt. Liegt der obere rechte Längengrad auf der Westseite der IDL und der untere linke Längengrad auf der Ostseite der IDL, ist es ziemlich offensichtlich, auf welche Weise der Benutzer das Polygon zeichnen möchte, und dennoch besteht OpenLayers darauf, die Längswerte und das Zeichnen zu vertauschen das Polygon den falschen Weg um die Welt. Ein Beispiel für die Extent-Deklaration und den problematischen OpenLayers-Methodenaufruf ist unten dargestellt. Liegt der obere rechte Längengrad auf der Westseite der IDL und der untere linke Längengrad auf der Ostseite der IDL, ist es ziemlich offensichtlich, auf welche Weise der Benutzer das Polygon zeichnen möchte, und dennoch besteht OpenLayers darauf, die Längswerte und das Zeichnen zu vertauschen das Polygon den falschen Weg um die Welt. Ein Beispiel für die Extent-Deklaration und den problematischen OpenLayers-Methodenaufruf ist unten dargestellt. Liegt der obere rechte Längengrad auf der westlichen Seite der IDL und der untere linke Längengrad auf der östlichen Seite der IDL, ist es ziemlich offensichtlich, auf welche Weise der Benutzer das Polygon zeichnen möchte, und dennoch besteht OpenLayers darauf, die Längenwerte und das Zeichnen zu vertauschen das Polygon den falschen Weg um die Welt. Ein Beispiel für die Extent-Deklaration und den problematischen OpenLayers-Methodenaufruf ist unten dargestellt.

// 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__: []

Wie Sie sehen können, werden die Längenkoordinaten umgekehrt und so entsteht nach dem Erstellen der vollständigen Koordinatenstruktur ein Polygon. ein polygonFeature und wenden Sie dieses Feature dann auf einen Vektor an und zeichnen Sie es schließlich nur, um festzustellen, dass das Polygon um die Welt in die falsche Richtung verläuft.

Ich musste herausfinden, warum dies geschah, also habe ich mich mit dieser ol.extent.boundingExtent-Methode in der OpenLayers 4-Bibliothek befasst.

/**
 * 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__: []

Mein Code berechnet die Fläche dynamisch, damit ich feststellen kann, ob der Benutzer ein AOI-Polygon mit gültiger Größe erstellt hat. Wenn ich eine DragBox-generierte Auswahl bearbeite, fordere ich die Koordinaten der resultierenden Geometriestruktur an und für eine EPSG: 4326-Projektion, wenn Koordinaten aus einer umhüllten Welt zurückgegeben werden, erhöhen sich die Koordinaten nach den ersten 180,0 Grad weiter 360,0 - 165,937 = 194,063. Mein Flächenberechnungs-Codepfad verwendet den folgenden IDL-Test. Um denselben Codepfad für die manuell eingegebenen Koordinaten zu verwenden, musste ich den Koordinatenwert so simulieren, als ob er vom DragBox-Aufruf getGeometry zurückgegeben worden wäre. Ich teste gerade eine GEOJSON-Polygonstruktur, die ein dreidimensionales Array ist, wobei die erste Dimension die Ringzahl ist.

 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))) {
 .
 .
 .

Wenn diese Tests zu diesem Zeitpunkt erfolgreich sind, verwendet der Code den von mir entwickelten Algorithmus, um die Fläche über der IDL zu berechnen.

In dieser Ausdehnung erstelle ich dann ein Polygon, dann ein polygonFeature, wende dieses Feature auf einen Vektor an und zeichne es schließlich und dieses Mal richtig. Also das Update, das ich mir ausgedacht habe, um das Problem mit der Flächenberechnung zu lösen, das ich auch beim Plotten hatte.

Vielleicht hilft diese Lösung jemand anderem oder bringt ihn dazu, in eine andere Richtung zu denken. Die Lösung kam zu mir, als ich das Problem der IDL in zwei Punkte aufteilen konnte. Die tatsächliche Flächenberechnung war ein Problem, das andere war das Zeichnen des Polygons über der IDL.


1
OL verwendet nur den Operator> =, um zu wissen, welche Seite beim Plotten verwendet werden soll. Wenn Sie 170 dann 190 geben, wird es den kurzen Weg gehen; Wenn Sie 170 dann -170 geben, wird es den langen Weg gehen. Wenn Sie die Länge immer zwischen -180 und 180 "normalisieren", verlieren Sie Informationen. Eine Möglichkeit, die Informationen zurückzugewinnen, besteht darin,
festzulegen

1

Workround: Beispiel

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


Ich verwende WFS. Der Link, den Sie gepostet haben, lautet: "Sie können es mit einer Ebene 'Layer.WMS' oder 'Layer.MapServer' tun."
CaptDragon

Wenn beide unterstützt werden und Sie keinen speziellen Bedarf an Layer.MapServer haben, wählen Sie Layer.WMS (das weiterhin von MapServer bereitgestellt werden kann).
DavidF

@DavidF: Danke, aber ich muss die Vektorfunktionen von WFS nutzen.
CaptDragon

1

Zwei Jahre später hatte ich dieses Problem immer wieder mit Features auf einer Vektorebene. Ich habe diese Datei gefunden, die einen Codeausschnitt enthält, der zeigt, wie ein Endpunkt umgedreht wird, wenn er die Datumsgrenze überschreitet:

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

Aktualisieren:

Tatsächlich hat das oben Genannte nicht für mehr als eine Revolution auf der ganzen Welt funktioniert. Am Ende habe ich DAS getan .

Bildbeschreibung hier eingeben


1

Ich habe in meinen eigenen Projekten eine Lösung dafür gefunden, die möglicherweise für Sie funktioniert oder nicht. Ich weiß, dass es mit LineStrings funktioniert, bin mir aber bei anderen Geometrietypen nicht sicher.

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

Die Funktion dateLineFix durchläuft den angegebenen LineString rekursiv für alle Segmente, die die Datumsgrenze überschreiten. Anschließend werden sie an der Datumsgrenze in zwei Teile geteilt und alle resultierenden Segmente als MultiLineString zurückgegeben.

Es funktionierte perfekt für meinen Zweck (Zeichnen eines polaren Lat-Lon-Gitters).


0

Ich hatte nur wenige Probleme mit der Datenlinie und konnte sie alle beheben. Sie könnten versuchen zu folgen.

  1. Aktualisieren Sie die Werte des GeoServer-Layer-Begrenzungsrahmens manuell, um Ihr Polygon ohne Unterbrechung abzudecken, und prüfen Sie, ob das Problem dadurch behoben wird.

  2. Eine der Korrekturen, die ich in Openlayers vorgenommen habe, ist das Fehlen von Kacheln beim Übergeben der Datumsgrenze von + ve Länge bis -ve. http://trac.osgeo.org/openlayers/ticket/2754 Nicht sicher, ob es für WFS gilt. Sie können die neueste OpenLayer-Entwicklungsversion herunterladen und ausprobieren.


0

Ich bin auf dieses Problem mit LineStrings gestoßen und habe eine Lösung dafür erstellt. Ich bin mir nicht sicher, ob es Ihnen bei Polygonen helfen würde. Sie können es auf meiner repl.it hier sehen: https://repl.it/@gpantic/OpenLayersSplitRouteOverPacific


1
Wenn die Frage beantwortet wird, fügen Sie bitte alle Informationen zu Ihrer Antwort hinzu, anstatt einen Link anzugeben.
BERA

0

EPSG: 3832 (WGS84 PDC) ist eine auf den Pazifischen Ozean zentrierte Projektion. Dadurch werden IDL-Kreuzungsprobleme gegen Prime Meridian-Kreuzungsprobleme getauscht. Dies ist möglicherweise kein Problem, je nachdem, was Sie darstellen. Ich habe auch Probleme in der Nähe der Arktischen und Antarktischen Kreise gefunden.

Gutschrift geht zu diesem Artikel.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.