Eu estava pesquisando esse problema há algum tempo, pois desenvolvi um aplicativo que permite ao usuário gerar um retângulo da área de interesse por meio de uma ação DragBox ou plotando pontos de extensão inseridos pelo usuário. Quando comecei essa aventura, eu era completamente novo no OpenLayers. O problema com os pontos de extensão inseridos manualmente era que, se a AOI cobrisse a Linha de Dados Internacional, o retângulo desenhado seria traçado de maneira errada em todo o mundo. Vários usuários do StackExchange perguntaram sobre esse problema apenas para serem informados por um respondente do OpenLayers que (e estou parafraseando aqui) "O OpenLayers não tem como saber a intenção direcional dos pontos a serem desenhados, por padrão ...". Tenho que levantar a bandeira da BS nessa resposta, pois agora aprendi o suficiente sobre o OpenLayers para ser perigoso e esse problema está acontecendo comigo. O problema que tenho com a resposta deles é que carrego as coordenadas em uma extensão que, por definição, especifica a longitude e a latitude superiores direita, bem como a longitude e latitude inferior esquerda. Se a longitude superior direita fica no lado ocidental do IDL e a longitude inferior esquerda fica no lado oriental do IDL, é bastante óbvio de que maneira o usuário deseja plotar o polígono e ainda assim o OpenLayers insiste em trocar os valores longitudinais e desenhar o polígono da maneira errada ao redor do mundo. Uma amostra da declaração de extensão e da chamada problemática do método OpenLayers é mostrada abaixo. Se a longitude superior direita fica no lado ocidental do IDL e a longitude inferior esquerda fica no lado oriental do IDL, é bastante óbvio de que maneira o usuário deseja plotar o polígono e ainda assim o OpenLayers insiste em trocar os valores longitudinais e desenhar o polígono da maneira errada ao redor do mundo. Uma amostra da declaração de extensão e da chamada problemática do método OpenLayers é mostrada abaixo. Se a longitude superior direita fica no lado ocidental do IDL e a longitude inferior esquerda fica no lado oriental do IDL, é bastante óbvio de que maneira o usuário deseja plotar o polígono e ainda assim o OpenLayers insiste em trocar os valores longitudinais e desenhar o polígono da maneira errada ao redor do mundo. Uma amostra da declaração de extensão e da chamada problemática do método OpenLayers é mostrada abaixo.
// 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 você pode ver, as coordenadas longitudinais são invertidas e, depois, você cria toda a estrutura de coordenadas, um polígono. a polygonFeature e, em seguida, aplique esse recurso a um vetor e, finalmente, plote-o apenas para descobrir que o polígono segue o caminho errado em todo o mundo.
Eu precisava descobrir por que isso estava acontecendo, então procurei esse método ol.extent.boundingExtent na biblioteca do 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__: []
Meu código calcula a área dinamicamente para que eu possa determinar se o usuário criou um polígono AOI de tamanho válido. Quando estou processando uma seleção gerada pelo DragBox, solicito as coordenadas da estrutura geométrica resultante e para uma projeção EPSG: 4326 quando ela retorna coordenadas de um mundo empacotado, as coordenadas após os primeiros 180,0 graus continuam incrementando, portanto, o motivo para o cálculo da longitude de 360,0 - 165,937 = 194,063. Meu caminho de código de cálculo de área usa o seguinte teste IDL e, para usar o mesmo caminho de código para as coordenadas inseridas manualmente, eu precisava simular o valor da coordenada como se tivesse sido retornado da chamada dragBox getGeometry. Na verdade, estou testando uma estrutura de polígono GEOJSON que é uma matriz tridimensional com a 1ª dimensão sendo o número do anel,
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 esses testes forem aprovados nesse momento, o código usará o algoritmo que desenvolvi para calcular a área sobre o IDL, caso contrário, apenas o calculará como normal em qualquer outro lugar.
Em seguida, uso essa extensão para criar um polígono, depois um polygonFeature, depois aplico esse recurso a um vetor e, finalmente, plotei-o e, desta vez, plotamos corretamente. Portanto, a correção que surgiu para ajudar a resolver o problema de cálculo de área que eu estava tendo também corrigiu o problema de plotagem.
Talvez essa solução ajude outras pessoas ou as faça pensar em uma direção diferente. A solução veio a mim quando finalmente consegui dividir o problema da IDL em duas questões. O cálculo da área real era um problema, com o outro sendo a plotagem do polígono sobre o IDL.