La ligne de date internationale s'enroule


13

En utilisant OpenLayers, j'ai ajouté une couche WFS (sur GeoServer) avec un filtre qui renvoie toutes les entités (noires) qui croisent mon polygone (jaune) placées sur certains pays d'Amérique latine à certaines dates.

entrez la description de l'image ici

Cependant, l'entité qui traverse horizontalement sur la carte n'intersecte PAS réellement mon polygone. Cette fonctionnalité se situe quelque part dans l'océan Pacifique entre Hawaï et Fidji et NON en Amérique latine. Le problème est qu'au lieu de traverser la ligne de date internationale, il est rendu sur la carte en enroulant le monde entier.

La caractéristique problématique est définie:

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

J'ai de nombreuses fonctionnalités de ligne de date problématiques comme celle-ci, mais je les ai limitées à celle-ci pour cet exemple. Je ne peux pas simplement l'ignorer dans ma candidature car j'en ai beaucoup.

J'ai essayé d'utiliser "wrapDateLine: true" dans la couche de base et la couche WFS avec les mêmes résultats.

Je ne sais pas si ce serait un problème GeoServer ou un problème OpenLayers.

Quelqu'un connaît-il une solution à mon problème de ligne de date internationale?


2
Je ne sais pas pourquoi le logiciel a un tel problème avec ça, le monde est plat, non?!
DavidF

Peut-être qu'il devrait y avoir un paramètre de direction.
CaptDragon

@CaptDragon Une solution à ce problème?
Anil

@Anil Aucun pour l'instant. Faites-le moi savoir si vous en trouvez un.
CaptDragon

Réponses:


6

Malheureusement, c'est un problème connu. Le problème est que les géométries qui traversent la ligne de date comme celle-ci sont ambiguës. Les moteurs de rendu OL et GeoServer n'ont aucun moyen facile de savoir que l'intention est de parcourir le "court" chemin à travers le monde, ils interprètent simplement par exemple 170 à -170 le chemin "normal" et parcourent le long chemin autour du monde.

Malheureusement, il n'y a pas de bonne solution pour cela, sauf pour diviser vos géométries qui se trouvent sur la ligne de données.


Merci +1, je suis d'accord mais je ne peux pas diviser mes géométries. Voyons voir si quelqu'un d'autre a d'autres idées.
CaptDragon

J'ai trouvé un moyen de bien les diviser sur OpenLayers.
CaptDragon

7

Reprojetez votre carte pour utiliser une projection qui est divisée au méridien de Greenwich (ou ailleurs) afin que les polygones qui vous intéressent ne traversent pas la discontinuité de votre carte.


les polygones couvrent assez bien le monde, il y aura toujours des polygones qui traverseront une ligne. Cependant, connaissez-vous des projections qui ne sont pas divisées comme ça?
CaptDragon

1
Toutes les projections doivent diviser le monde quelque part, c'est implicite dans les maths (peler une orange si vous ne me croyez pas :-)). Tout ce que vous pouvez faire est de choisir la meilleure projection pour votre tâche.
Ian Turton

Oui vous avez raison. Je vais laisser cela ouvert quelques jours et voir si d'autres idées surgissent. Merci pour votre suggestion. :-)
CaptDragon

2

J'avais étudié ce problème pendant un bon moment car j'ai développé une application qui permet à l'utilisateur de générer un rectangle de zone d'intérêt via une action DragBox ou en traçant des points d'extension entrés par l'utilisateur. Quand j'ai commencé cette aventure, j'étais complètement nouveau sur OpenLayers. Le problème avec les points d'extension entrés manuellement était que si AOI couvrait la ligne de données internationale, le rectangle dessiné serait dessiné dans le mauvais sens à travers le monde. De nombreux utilisateurs de StackExchange ont posé des questions sur ce problème uniquement pour être informé par un répondeur OpenLayers que (et je paraphrase ici) "OpenLayers n'a aucun moyen de connaître l'intention directionnelle des points à dessiner, il est par défaut ...". Euh, je dois lever le drapeau BS sur cette réponse car j'ai maintenant suffisamment appris sur OpenLayers pour être dangereux et ce problème m'est arrivé. Le problème que j'ai avec leur réponse est que je charge les coordonnées dans une mesure qui, par définition, spécifie la longitude et la latitude supérieure droite ainsi que la longitude et la latitude inférieures gauche. Si la longitude en haut à droite se trouve du côté ouest de l'IDL et la longitude en bas à gauche se trouve du côté est de l'IDL, il est assez évident de quelle manière l'utilisateur veut tracer le polygone et pourtant OpenLayers insiste pour permuter les valeurs longitudinales et le dessin le polygone dans le mauvais sens autour du monde. Un exemple de déclaration d'étendue et d'appel de méthode OpenLayers problématique est présenté ci-dessous. Si la longitude en haut à droite se trouve du côté ouest de l'IDL et la longitude en bas à gauche se trouve du côté est de l'IDL, il est assez évident de quelle manière l'utilisateur veut tracer le polygone et pourtant OpenLayers insiste pour permuter les valeurs longitudinales et le dessin le polygone dans le mauvais sens autour du monde. Un exemple de déclaration d'étendue et d'appel de méthode OpenLayers problématique est présenté ci-dessous. Si la longitude en haut à droite se trouve du côté ouest de l'IDL et la longitude en bas à gauche se trouve du côté est de l'IDL, il est assez évident de quelle manière l'utilisateur veut tracer le polygone et pourtant OpenLayers insiste pour permuter les valeurs longitudinales et le dessin le polygone dans le mauvais sens autour du monde. Un exemple de déclaration d'étendue et d'appel de méthode OpenLayers problématique est présenté ci-dessous.

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

Comme vous pouvez le voir, les coordonnées longitudinales sont inversées et donc après avoir créé la structure de coordonnées complète, un polygone. a polygonFeature, puis appliquez cette entité à un vecteur et finalement tracez-la uniquement pour découvrir que le polygone va dans le mauvais sens dans le monde.

J'avais besoin de comprendre pourquoi cela se produisait alors j'ai creusé dans cette méthode ol.extent.boundingExtent dans la bibliothèque 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__: []

Mon code calcule la zone de manière dynamique afin que je puisse déterminer si l'utilisateur a créé un polygone AOI de taille valide. Lorsque je traite une sélection générée par DragBox, je demande les coordonnées à la structure géométrique résultante et pour une projection EPSG: 4326 lorsqu'elle renvoie les coordonnées d'un monde enveloppé, les coordonnées après les 180 premiers degrés continuent à augmenter, ce qui explique le calcul du lonUR de 360,0 - 165,937 = 194,063. Mon chemin de codage de calcul de zone utilise le test IDL suivant et afin d'utiliser le même chemin de codage pour les coordonnées saisies manuellement, j'avais besoin de simuler la valeur de coordonnées comme si elle avait été renvoyée depuis l'appel DragBox getGeometry. Je suis en train de tester une structure polygonale GEOJSON qui est un tableau à 3 dimensions avec la 1ère dimension étant le numéro d'anneau,

 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 ces tests réussissent à ce stade, le code utilise l'algorithme que j'ai développé pour calculer la zone sur l'IDL, sinon il la calcule comme normale partout ailleurs.

J'utilise ensuite cette étendue pour créer un polygone, puis un polygoneFeature, puis j'applique cette entité à un vecteur et finalement le trace et cette fois, il est tracé correctement. Donc, le correctif que j'ai trouvé pour aider à résoudre le problème de calcul de surface que j'avais rencontré a également corrigé le problème de traçage.

Peut-être que cette solution aidera quelqu'un d'autre ou le fera réfléchir dans une direction différente. La solution m'est venue lorsque j'ai finalement réussi à diviser le problème de l'IDL en deux problèmes. Le calcul de l'aire réelle était un problème, l'autre étant le tracé du polygone sur l'IDL.


1
OL utilise simplement l'opérateur> = pour savoir de quel côté se diriger lors du traçage. Si vous donnez 170 puis 190, ce sera le chemin le plus court; si vous donnez 170 puis -170, cela ira très loin. Si vous "normalisez" toujours la longitude entre -180 et 180, vous perdez des informations. Une façon de récupérer les informations est de dicter que les distances entre les points ne doivent pas être supérieures à 180
Rivenfall

1

Solution: exemple

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


J'utilise WFS, le lien que vous avez publié dit: "Vous pouvez le faire avec une couche 'Layer.WMS' ou 'Layer.MapServer'"
CaptDragon

Si les deux sont pris en charge et que vous n'avez pas spécifiquement besoin de Layer.MapServer, optez pour Layer.WMS (qui pourrait toujours être servi à partir de MapServer).
DavidF

@DavidF: Merci, mais je dois utiliser les capacités vectorielles de WFS.
CaptDragon

1

Deux ans plus tard, j'ai continué à avoir ce problème avec les fonctionnalités sur une couche vectorielle. J'ai trouvé ce fichier contenant un extrait de code qui montre comment retourner un point de terminaison s'il traversait la ligne de données:

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

Mise à jour:

En fait, ce qui précède n'a pas fonctionné pour plus d'une révolution dans le monde. J'ai fini par faire CECI .

entrez la description de l'image ici


1

J'ai trouvé une solution pour cela dans mes propres projets qui peuvent ou non fonctionner pour vous. Je sais pertinemment que cela fonctionne avec LineStrings mais je ne suis pas sûr des autres types de géométrie.

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 fonction dateLineFix traverse récursivement le LineString donné pour tous les segments qui traversent la ligne de date. Il les coupe ensuite en deux au niveau de la ligne de données et renvoie tous les segments résultants en tant que MultiLineString.

Cela a parfaitement fonctionné pour mon but (dessiner une grille polaire lat-lon).


0

J'ai eu quelques problèmes avec Dateline et j'ai réussi à les résoudre tous. Vous pouvez essayer de suivre.

  1. Mettez à jour manuellement les valeurs du cadre de délimitation de la couche GeoServer pour couvrir votre polygone sans le casser et voir s'il résout le problème.

  2. Un des correctifs que j'ai fait dans Openlayers est qu'il manque des tuiles lors du passage de la ligne de temps de + ve longitude à -ve. http://trac.osgeo.org/openlayers/ticket/2754 Je ne sais pas si cela s'applique à WFS. Vous pouvez obtenir la dernière version de développement openlayers et essayer.


0

J'ai rencontré ce problème avec LineStrings et créé une solution pour cela. Je ne sais pas si cela pourrait vous aider avec les polygones. Vous pouvez le voir sur mon repl.it ici: https://repl.it/@gpantic/OpenLayersSplitRouteOverPacific


1
S'il répond à la question, veuillez ajouter toutes les informations à votre réponse au lieu de fournir un lien.
BERA

0

EPSG: 3832 (WGS84 PDC) est une projection centrée sur l'océan Pacifique. Cela échangera des problèmes de passage IDL contre des problèmes de passage Prime Meridian. Cela peut ne pas être un problème selon ce que vous décrivez. J'ai également trouvé des problèmes près des cercles arctique et antarctique.

Le mérite revient à cet article.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.