L'International Date Line si chiude


13

Usando OpenLayers, ho aggiunto un livello WFS (su GeoServer) con un filtro che restituisce tutte le funzioni (nere) che si intersecano con il mio poligono (giallo) collocate su alcuni paesi dell'America Latina entro determinate date.

inserisci qui la descrizione dell'immagine

Tuttavia, la funzione che attraversa orizzontalmente la mappa NON interseca effettivamente il mio poligono. Questa funzione si trova da qualche parte nell'Oceano Pacifico tra Hawaii e Figi e NON in America Latina. Il problema è che invece di attraversare la linea di data internazionale viene renderizzata sulla mappa avvolgendo tutto il mondo.

La funzione problamatica è definita:

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

Ho molte caratteristiche problematiche della linea di data come questa, ma l'ho ristretta a questa per questo esempio. Non posso semplicemente ignorarlo nella mia applicazione perché ne ho molti.

Ho provato a utilizzare "wrapDateLine: true" nel livello base e nel livello WFS con gli stessi risultati.

Non sono sicuro se questo sarebbe un problema GeoServer o un problema OpenLayers.

Qualcuno conosce una soluzione al mio problema con la data internazionale?


2
Non so perché il software abbia un problema del genere, il mondo è piatto, giusto ?!
DavidF

Forse dovrebbe esserci un parametro di direzione.
CaptDragon,

@CaptDragon Qualunque soluzione a questo problema?
Anil,

@Anil Nessuno ancora. Per favore fatemi sapere se ne trovate uno.
CaptDragon,

Risposte:


6

Purtroppo questo è un problema noto. Il problema è che le geometrie che attraversano la linea della data in questo modo sono ambigue. I renderizzatori OL e GeoServer non hanno un modo semplice per sapere che l'intenzione è quella di fare il "breve" giro del mondo, quindi interpretano ad esempio da 170 a -170 il modo "normale" e fanno il lungo giro del mondo.

Sfortunatamente non esiste una buona soluzione per questo se non quella di dividere le geometrie che si trovano lungo la linea di dati.


Grazie +1, sono d'accordo ma non riesco a dividere le mie geometrie. Vediamo se qualcun altro ha altre idee.
CaptDragon,

Ho trovato un modo per dividerli bene su OpenLayers.
CaptDragon,

7

Riprogetta la tua mappa per usare una proiezione che è divisa nel meridiano di Greenwich (o altrove) in modo che i poligoni a cui sei interessato non attraversino la discontinuità nella tua mappa.


i poligoni coprono abbastanza bene il mondo, ci saranno sempre poligoni che attraverseranno qualche linea. Tuttavia, conosci qualche proiezione che non è divisa in questo modo?
CaptDragon,

1
Tutte le proiezioni devono dividere il mondo da qualche parte, è implicito in matematica (sbucciare un'arancia se non mi credi :-)). Tutto quello che puoi fare è scegliere la migliore proiezione per il tuo compito.
Ian Turton

Si hai ragione. Lo lascerò aperto tra qualche giorno e vedrò se nascono altre idee. Grazie per il tuo suggerimento. :-)
CaptDragon

2

Ho studiato questo problema da un po 'di tempo poiché ho sviluppato un'applicazione che consente all'utente di generare un rettangolo di area di interesse tramite un'azione DragBox o tracciando punti di estensione immessi dall'utente. Quando ho iniziato questa avventura, ero completamente nuovo su OpenLayers. Il problema con i punti di estensione immessi manualmente era che se AOI avesse coperto la linea dati internazionale il rettangolo disegnato sarebbe stato disegnato nel modo sbagliato in tutto il mondo. Numerosi utenti StackExchange hanno chiesto di questo problema solo per essere detto da un risponditore di OpenLayers che (e sto parafrasando qui) "OpenLayers non ha modo di conoscere l'intento direzionale dei punti da tracciare, quindi per impostazione predefinita ...". Uh, devo alzare la bandiera BS su quella risposta dato che ora ho imparato abbastanza su OpenLayers per essere pericoloso e questo problema mi sta succedendo. Il problema che ho con la loro risposta è che carico le coordinate in una misura che, per definizione, specifica la longitudine e la latitudine in alto a destra, nonché la longitudine e la latitudine in basso a sinistra. Se la longitudine in alto a destra si trova sul lato occidentale dell'IDL e la longitudine in basso a sinistra si trova sul lato orientale dell'IDL, è abbastanza ovvio in che modo l'utente vuole tracciare il poligono e tuttavia OpenLayers insiste sullo scambio dei valori longitudinali e sul disegno il poligono nel modo sbagliato nel mondo. Di seguito è riportato un esempio della dichiarazione di estensione e della chiamata problematica del metodo OpenLayers. Se la longitudine in alto a destra si trova sul lato occidentale dell'IDL e la longitudine in basso a sinistra si trova sul lato orientale dell'IDL, è abbastanza ovvio in che modo l'utente vuole tracciare il poligono e tuttavia OpenLayers insiste sullo scambio dei valori longitudinali e sul disegno il poligono nel modo sbagliato nel mondo. Di seguito è riportato un esempio della dichiarazione di estensione e della chiamata problematica del metodo OpenLayers. Se la longitudine in alto a destra si trova sul lato occidentale dell'IDL e la longitudine in basso a sinistra si trova sul lato orientale dell'IDL, è abbastanza ovvio in che modo l'utente vuole tracciare il poligono e tuttavia OpenLayers insiste sullo scambio dei valori longitudinali e sul disegno il poligono nel modo sbagliato nel mondo. Di seguito è riportato un esempio della dichiarazione di estensione e della chiamata problematica del metodo OpenLayers.

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

Come puoi vedere, le coordinate longitudinali vengono invertite e quindi dopo aver creato l'intera struttura delle coordinate, un poligono. un poligono Funzionalità e quindi applicare quella funzione a un vettore e infine tracciarlo solo per scoprire che il poligono va nella direzione sbagliata in tutto il mondo.

Avevo bisogno di capire perché questo stesse accadendo, così ho scavato in questo metodo ol.extent.boundingExtent nella libreria 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__: []

Il mio codice calcola l'area in modo dinamico in modo da poter determinare se l'utente ha creato un poligono AOI di dimensioni valide. Quando sto elaborando una selezione generata da DragBox, sto richiedendo le coordinate dalla struttura della geometria risultante e per una proiezione EPSG: 4326 quando restituisce coordinate da un mondo avvolto, le coordinate oltre i primi 180,0 gradi continuano ad aumentare quindi la ragione del calcolo del 360,0 - 165,937 = 194,063. Il mio codepath per il calcolo dell'area utilizza il seguente test IDL e per utilizzare lo stesso codepath per le coordinate immesse manualmente avevo bisogno di simulare il valore delle coordinate come se fosse stato restituito dalla chiamata getGeometry di DragBox. Sto effettivamente testando una struttura poligonale GEOJSON che è un array tridimensionale con la prima dimensione che è il numero dell'anello,

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

Se questi test superano a questo punto il codice utilizza l'algoritmo che ho sviluppato per calcolare l'area sopra l'IDL, altrimenti la calcola normalmente come ovunque.

Quindi uso questa estensione per creare un poligono, quindi una funzione poligono, quindi applico quella funzione a un vettore e infine la tracciamo e questa volta è stata stampata correttamente. Quindi la correzione che mi è venuta per risolvere il problema di calcolo dell'area che avevo risolto anche il problema di stampa.

Forse questa soluzione aiuterà qualcun altro o li farà pensare in una direzione diversa. La soluzione mi è venuta quando sono finalmente riuscita a dividere il problema dell'IDL in due problemi. Il calcolo effettivo dell'area era un problema con l'altro era la stampa del poligono sull'IDL.


1
OL usa semplicemente l'operatore> = per sapere da che parte andare quando si stampa. Se dai 170 poi 190 andrà nel modo più breve; se dai 170 poi -170, farà molta strada. Se "normalizzi" sempre la longitudine tra -180 e 180, perdi informazioni. Un modo per recuperare le informazioni è dettare che le distanze tra i punti non possono essere> 180
Rivenfall,

1

Workround: esempio

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


Sto usando WFS il link che hai postato dice: "Puoi farlo con un livello 'Layer.WMS' o 'Layer.MapServer'"
CaptDragon

Se entrambi sono supportati e non hai un'esigenza specifica per Layer.MapServer, scegli Layer.WMS (che potrebbe ancora essere servito da MapServer).
DavidF

@DavidF: Grazie, ma devo usare le funzionalità vettoriali di WFS.
CaptDragon,

1

Due anni dopo, ho continuato a riscontrare questo problema con le funzionalità su un livello vettoriale. Ho trovato questo file contenente uno snippet di codice che mostra come invertire un endpoint se attraversava la linea di dati:

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

Aggiornare:

In realtà quanto sopra non ha funzionato per più di una rivoluzione in tutto il mondo. Ho finito per fare QUESTO .

inserisci qui la descrizione dell'immagine


1

Ho trovato una soluzione per questo nei miei progetti che potrebbe o meno funzionare per te. So per certo che funziona con LineStrings ma non sono sicuro di altri tipi di geometria.

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

La funzione dateLineFix attraversa ricorsivamente il LineString specificato per tutti i segmenti che attraversano la linea della data. Quindi li taglia in due sulla linea dati e restituisce tutti i segmenti risultanti come MultiLineString.

Ha funzionato perfettamente per il mio scopo (disegnare una griglia lat-lon polare).


0

Ho avuto alcuni problemi con la linea dati e sono riuscito a risolverli tutti. Potresti provare a seguire.

  1. Aggiorna manualmente i valori del riquadro di delimitazione del livello GeoServer per coprire il poligono senza interruzioni e vedere se risolve il problema.

  2. Una delle correzioni che ho fatto in Openlayers è la mancanza di riquadri quando si passa la linea dati da + ve longitudine a -ve. http://trac.osgeo.org/openlayers/ticket/2754 Non sono sicuro che sia applicabile a WFS. Potresti ottenere l'ultima versione di sviluppo di openlayer e provare.



0

EPSG: 3832 (WGS84 PDC) è una proiezione centrata sull'Oceano Pacifico. Ciò scambierà i problemi di attraversamento dell'IDL con problemi di attraversamento del Meridiano Prime. Questo potrebbe non essere un problema a seconda di ciò che stai raffigurando. Ho anche riscontrato problemi vicino ai circoli artici e antartici.

Il merito va a questo articolo.

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.