Badałem ten problem od dłuższego czasu, ponieważ opracowałem aplikację, która pozwala użytkownikowi generować prostokąt Obszaru zainteresowania za pomocą akcji DragBox lub kreślenia wprowadzonych przez użytkownika punktów zasięgu. Gdy zacząłem tę przygodę, byłem zupełnie nowy w OpenLayers. Problem z ręcznie wprowadzonymi punktami zasięgu był taki, że jeśli AOI pokryłaby międzynarodową linię danych, narysowany prostokąt zostałby narysowany w niewłaściwy sposób na całym świecie. Wielu użytkowników StackExchange zapytało o ten problem tylko po to, by odpowiedzieć OpenLayers, że (i parafrazuję tutaj) „OpenLayers nie ma możliwości poznania kierunkowego celu punktów, które mają być narysowane, więc domyślnie ...”. Uh, muszę podnieść flagę BS w tej odpowiedzi, ponieważ dowiedziałem się już wystarczająco dużo o OpenLayers, aby być niebezpiecznym i ten problem zdarza mi się. Problem, jaki mam z ich odpowiedzią, polega na tym, że ładuję współrzędne w zakresie, który z definicji określa górną i prawą długość i szerokość geograficzną, a także dolną lewą długość i szerokość. Jeśli górna prawa długość geograficzna leży po zachodniej stronie IDL, a dolna lewa długość geograficzna leży po wschodniej stronie IDL, jest dość oczywiste, w jaki sposób użytkownik chce wykreślić wielokąt, a mimo to OpenLayers nalega na zamianę wartości wzdłużnych i rysowanie wielokąt w niewłaściwy sposób na całym świecie. Przykład deklaracji zakresu i problematycznego wywołania metody OpenLayers pokazano poniżej. Jeśli górna prawa długość geograficzna leży po zachodniej stronie IDL, a dolna lewa długość geograficzna leży po wschodniej stronie IDL, jest dość oczywiste, w jaki sposób użytkownik chce wykreślić wielokąt, a mimo to OpenLayers nalega na zamianę wartości wzdłużnych i rysowanie wielokąt w niewłaściwy sposób na całym świecie. Przykład deklaracji zakresu i problematycznego wywołania metody OpenLayers pokazano poniżej. Jeśli górna prawa długość geograficzna leży po zachodniej stronie IDL, a dolna lewa długość geograficzna leży po wschodniej stronie IDL, jest dość oczywiste, w jaki sposób użytkownik chce wykreślić wielokąt, a mimo to OpenLayers nalega na zamianę wartości wzdłużnych i rysowanie wielokąt w niewłaściwy sposób na całym świecie. Przykład deklaracji zakresu i problematycznego wywołania metody OpenLayers pokazano poniżej.
// 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__: []
Jak widać, współrzędne podłużne zostają odwrócone, a więc po utworzeniu pełnej struktury współrzędnych, wielokąta. Wielokąt Wykonaj tę funkcję, a następnie zastosuj tę cechę do wektora, a na koniec wykreśl go, aby stwierdzić, że wielokąt idzie w niewłaściwą stronę na całym świecie.
Musiałem dowiedzieć się, dlaczego tak się dzieje, więc zagłębiłem się w tę metodę ol.extent.boundingExtent w bibliotece 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__: []
Mój kod oblicza obszar dynamicznie, dzięki czemu mogę ustalić, czy użytkownik utworzył wielokąt AOI o prawidłowej wielkości. Kiedy przetwarzam zaznaczenie wygenerowane przez DragBox, żądam współrzędnych z wynikowej struktury geometrycznej, a dla projekcji EPSG: 4326, gdy zwraca współrzędne z zawiniętego świata, współrzędne powyżej pierwszych 180,0 stopni kontynuują zwiększanie, dlatego powód obliczenia lonUR dla 360,0 - 165,937 = 194,063. Moja ścieżka kodowa obliczania obszaru korzysta z następującego testu IDL i aby użyć tej samej ścieżki kodowej dla ręcznie wprowadzonych współrzędnych, musiałem zasymulować wartość współrzędnych, tak jakby została zwrócona z wywołania DragGox getGeometry. W rzeczywistości testuję strukturę wielokąta GEOJSON, która jest trójwymiarową tablicą, a pierwszy wymiar to liczba pierścienia,
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))) {
.
.
.
Jeśli testy te przejdą w tym momencie, kod korzysta z opracowanego przeze mnie algorytmu, aby obliczyć obszar nad IDL, w przeciwnym razie po prostu oblicza go normalnie wszędzie indziej.
Następnie używam tego zasięgu do utworzenia wielokąta, a następnie polygonFeature, a następnie zastosuję tę cechę do wektora i na koniec narysuję go, tym razem wykreślając poprawnie. Więc poprawka, którą wymyśliłem, aby pomóc rozwiązać problem obliczania obszaru, miałem również naprawiony problem z wykreślaniem.
Może to rozwiązanie pomoże komuś innemu lub zmusi go do myślenia w innym kierunku. Rozwiązanie przyszło mi do głowy, kiedy w końcu udało mi się rozdzielić problem IDL na dwa problemy. Faktyczne obliczanie powierzchni było jednym problemem, a drugim było wykreślanie wielokąta nad IDL.