La línea de fecha internacional envuelve


13

Usando OpenLayers, agregué una capa WFS (en GeoServer) con un filtro que devuelve todas las características (negro) que intersecan mi polígono (amarillo) colocado sobre algunos países latinoamericanos dentro de ciertas fechas.

ingrese la descripción de la imagen aquí

Sin embargo, la característica que se cruza horizontalmente a través del mapa NO se cruza realmente con mi polígono. Esta característica se encuentra en algún lugar del océano Pacífico entre Hawai y Fiji y NO en América Latina. El problema es que, en lugar de cruzar la línea de fecha internacional, se representa en el mapa envolviendo todo el mundo.

La característica problamática se define:

POLÍGONO ((- 179.700417 14.202717, -178.687422 13.992875,179.024138 8.24716, -179.98241 8.035567, -179.700417 14.202717))

Tengo muchas características problemáticas de línea de fecha como esta, pero la he reducido a esta para este ejemplo. No puedo ignorarlo en mi aplicación porque tengo muchos de ellos.

Intenté usar "wrapDateLine: true" en la capa base y la capa WFS con los mismos resultados.

No estoy seguro si esto sería un problema de GeoServer o un problema de OpenLayers.

¿Alguien sabe una solución a mi problema de línea de fecha internacional?


2
No sé por qué el software tiene un problema con esto, el mundo es plano, ¿verdad?
DavidF

Tal vez debería haber un parámetro de dirección.
CaptDragon

@CaptDragon ¿Alguna solución a este problema?
Anil

@Anil Ninguno todavía. Avísame si encuentras uno.
CaptDragon

Respuestas:


6

Lamentablemente, este es un problema conocido. El problema es que las geometrías que cruzan la línea de fecha como esta son ambiguas. Los procesadores OL y GeoServer no tienen una manera fácil de saber que la intención es dar la vuelta "corta" alrededor del mundo, por lo que simplemente interpretan, por ejemplo, 170 a -170 de la manera "normal" y dan la vuelta al mundo.

Desafortunadamente, no hay una buena solución para esto, excepto dividir las geometrías que se encuentran en la línea de fecha.


Gracias +1, estoy de acuerdo pero no puedo dividir mis geometrías. Veamos si alguien más tiene otras ideas.
CaptDragon

Descubrí una forma de dividirlos bien en OpenLayers.
CaptDragon

7

Vuelva a proyectar su mapa para usar una proyección que se divide en el meridiano de Greenwich (o en otro lugar) para que los polígonos que le interesan no crucen la discontinuidad en su mapa.


los polígonos cubren el mundo bastante bien, siempre habrá polígonos que cruzarán alguna línea. Sin embargo, ¿conoce alguna proyección que no esté dividida de esta manera?
CaptDragon

1
Todas las proyecciones deben dividir el mundo en algún lugar, está implícito en las matemáticas (pelar una naranja si no me crees :-)). Todo lo que puede hacer es elegir la mejor proyección para su tarea.
Ian Turton

Si, tienes razón. Dejaré esto abierto unos días y veré si surgen otras ideas. Gracias por su sugerencia. :-)
CaptDragon

2

He estado investigando este problema durante bastante tiempo, ya que he desarrollado una aplicación que permite al usuario generar un rectángulo de Área de interés, ya sea a través de una acción de DragBox o trazando puntos de extensión ingresados ​​por el Usuario. Cuando comencé esta aventura, era completamente nuevo en OpenLayers. El problema con los puntos de extensión ingresados ​​manualmente era que si AOI cubría la línea de fecha internacional, el rectángulo dibujado se dibujaría de la manera incorrecta en todo el mundo. Numerosos usuarios de StackExchange han preguntado sobre este problema solo para que un respondedor de OpenLayers les diga que (y estoy parafraseando aquí) "OpenLayers no tiene forma de conocer la intención direccional de los puntos que se dibujarán, por lo que por defecto ...". Tengo que levantar la bandera de BS en esa respuesta, ya que he aprendido lo suficiente sobre OpenLayers como para ser peligroso y este problema me ha estado sucediendo. El problema que tengo con su respuesta es que cargo las coordenadas en una medida que, por definición, especifica la longitud y latitud superior derecha, así como la longitud y latitud inferior izquierda. Si la longitud superior derecha se encuentra en el lado occidental de la IDL y la longitud inferior izquierda se encuentra en el lado oriental de la IDL, es bastante obvio de qué manera el usuario desea trazar el polígono y, sin embargo, OpenLayers insiste en intercambiar los valores longitudinales y el dibujo el polígono al revés del mundo. A continuación se muestra una muestra de la declaración de extensión y la llamada problemática al método OpenLayers. Si la longitud superior derecha se encuentra en el lado occidental de la IDL y la longitud inferior izquierda se encuentra en el lado oriental de la IDL, es bastante obvio de qué manera el usuario desea trazar el polígono y, sin embargo, OpenLayers insiste en intercambiar los valores longitudinales y el dibujo el polígono al revés del mundo. A continuación se muestra una muestra de la declaración de extensión y la llamada problemática al método OpenLayers. Si la longitud superior derecha se encuentra en el lado occidental de la IDL y la longitud inferior izquierda se encuentra en el lado oriental de la IDL, es bastante obvio de qué manera el usuario desea trazar el polígono y, sin embargo, OpenLayers insiste en intercambiar los valores longitudinales y el dibujo el polígono al revés del mundo. A continuación se muestra una muestra de la declaración de extensión y la llamada problemática al método 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__: []

Como puede ver, las coordenadas longitudinales se invierten y, después, crea la estructura de coordenadas completa, un polígono. una característica de polígono y luego aplique esa característica a un vector y finalmente trazarla solo para descubrir que el polígono va en la dirección incorrecta en todo el mundo.

Necesitaba entender por qué estaba sucediendo esto, así que busqué en este método ol.extent.boundingExtent en la biblioteca 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__: []

Mi código calcula el área dinámicamente para poder determinar si el usuario ha creado un polígono AOI de tamaño válido. Cuando estoy procesando una selección generada por DragBox, solicito las coordenadas de la estructura de geometría resultante y para una proyección EPSG: 4326 cuando devuelve coordenadas de un mundo envuelto, las coordenadas más allá de los primeros 180.0 grados continúan incrementándose, por lo tanto, la razón para el cálculo prolongado de 360.0 - 165.937 = 194.063. Mi ruta de código de cálculo de área usa la siguiente prueba IDL y para usar la misma ruta de código para las coordenadas ingresadas manualmente, necesitaba simular el valor de coordenadas como si hubiera sido devuelto por la llamada getBoeometry de DragBox. De hecho, estoy probando una estructura de polígono GEOJSON que es una matriz tridimensional con la primera dimensión como el número de anillo,

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

Si estas pruebas pasan en este punto, el código usa el algoritmo que desarrollé para calcular el área sobre el IDL; de lo contrario, solo lo calcula como normal en cualquier otro lugar.

Luego uso esta extensión para crear un polígono, luego una función de polígono, luego aplico esa característica a un vector y finalmente lo trazo y esta vez se trazó correctamente. Entonces, la solución que se me ocurrió para ayudar a resolver el problema de cálculo del área que tenía también solucionó el problema de trazado.

Quizás esta solución ayude a alguien más o haga que piensen en una dirección diferente. La solución se me ocurrió cuando finalmente pude dividir el problema de la IDL en dos cuestiones. El cálculo del área real fue un problema y el otro fue el trazado del polígono sobre el IDL.


1
OL solo usa el operador> = para saber a qué lado ir al trazar. Si das 170, entonces 190 irá por el camino corto; si das 170 y luego -170, será de gran ayuda. Si siempre "normaliza" la longitud entre -180 y 180, perderá información. Una forma de recuperar la información es dictando que las distancias entre puntos no pueden ser> 180
Rivenfall

1

Workround: Ejemplo

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


Estoy usando WFS, el enlace que publicó dice: "Puede hacerlo con una capa 'Layer.WMS' o 'Layer.MapServer'"
CaptDragon

Si ambos son compatibles y no tiene una necesidad específica de Layer.MapServer, vaya con Layer.WMS (que aún se puede servir desde MapServer).
DavidF

@DavidF: Gracias, pero necesito usar las capacidades vectoriales de WFS.
CaptDragon

1

Dos años después, seguía teniendo este problema con las características en una capa vectorial. Encontré este archivo que contiene un fragmento de código que muestra cómo voltear un punto final si cruza la línea de fecha:

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

Actualizar:

En realidad, lo anterior no funcionó para más de una revolución en todo el mundo. Terminé haciendo ESTO .

ingrese la descripción de la imagen aquí


1

Se me ocurrió una solución para esto en mis propios proyectos que pueden o no funcionar para usted. Sé a ciencia cierta que funciona con LineStrings, pero no estoy seguro acerca de otros tipos de geometría.

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 función dateLineFix atraviesa recursivamente el LineString dado para cualquier segmento que cruza la línea de fecha. Luego los corta en dos en la línea de fecha y devuelve todos los segmentos resultantes como MultiLineString.

Funcionó perfectamente para mi propósito (dibujar una cuadrícula polar lat-lon).


0

Tuve algunos problemas con dateline y logré solucionarlos todos. Podrías intentar seguir.

  1. Actualice los valores del cuadro delimitador de capa de GeoServer manualmente para cubrir su polígono sin romperse y vea si resuelve el problema.

  2. Una de las soluciones que he hecho en Openlayers es la falta de mosaicos al pasar la línea de fecha de + ve longitud a -ve. http://trac.osgeo.org/openlayers/ticket/2754 No estoy seguro de si es aplicable para WFS. Podrías obtener la última versión de desarrollo de Openlayers e intentarlo.



0

EPSG: 3832 (WGS84 PDC) es una proyección centrada en el Océano Pacífico. Esto cambiará los problemas de cruce de IDL por los problemas de cruce de Prime Meridian. Esto puede no ser un problema dependiendo de lo que esté representando. También encontré problemas cerca de los círculos ártico y antártico.

El crédito va a este artículo.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.