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.