12

Using OpenLayers, I added a WFS layer (on GeoServer) with a filter that returns all features (black) that intersect my polygon (yellow) placed over some Latin American countries within certain dates.

enter image description here

However, the feature that crosses horizontally across the map does NOT actually intersect my polygon. This feature is somewhere in the pacific ocean between Hawaii and Fiji and NOT in Latin America. The problem is that instead of crossing the International Date Line it is being rendered on the map by wrapping around the whole world.

The problamatic feature is defined:

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

I have many of problematic date line features like this but have narrowed it down to this one for this example. I can't just ignore it in my application because I have many of them.

I've tried using "wrapDateLine: true" in base layer and WFS layer with same results.

Not sure if this would be a GeoServer problem or an OpenLayers problem.

Does anyone know a solution to my international date line problem?

  • 1
    I don't know why software has such a problem with this, the world is flat, right?! – DavidF Mar 10 '11 at 18:03
  • Maybe there should be a direction parameter. – CaptDragon Mar 10 '11 at 18:08
  • @CaptDragon Any solution to this Problem ? – Anil Dec 11 '13 at 12:46
  • @Anil None yet. Please let me know if you find one. – CaptDragon Dec 11 '13 at 15:15
5

Unfortunately this is a known problem. The issue is that geometries that cross the date line like this are ambiguous. The OL and GeoServer renderers have no easy way of knowing that the intention is to go the "short" way around the world so they just interpret for instance 170 to -170 the "regular" way and go the long way around the world.

Unfortunately there is no good solution for this except to split up your geometries that lie across the dateline.

  • Thanks +1, i agree but i can't split my geometries. Let's see if anyone else has any other ideas. – CaptDragon Mar 10 '11 at 18:03
  • I figured out a way to split them nicely on OpenLayers. – CaptDragon Jan 21 '14 at 21:13
5

Reproject your map to use a projection which is split at the Greenwich meridian (or elsewhere) so that the polygons you are interested in don't cross the discontinuity in your map.

  • the polygons cover the world pretty well there will always be polygons that will cross some line. Though, do you know of any projections that are not split like this? – CaptDragon Mar 10 '11 at 18:53
  • All projections must split the world somewhere, it is implicit in the maths (peel an orange if you don't believe me :-)). All you can do is pick the best projection for your task. – Ian Turton Mar 10 '11 at 19:09
  • yeah you're right. I'll leave this open a few days and see if any other ideas come up. Thanks for your suggestion.:-) – CaptDragon Mar 10 '11 at 19:51
2

I had been researching this issue for quite a while as I have developed an application that allows the user to generate an Area of Interest rectangle either via a DragBox action or plotting User entered extent points. When I started this adventure I was completely new to OpenLayers. The problem with the manually entered extent points was that if AOI covered the International Dateline the rectangle drawn would be drawn the wrong way around the world. Numerous StackExchange users have asked about this problem only to be told by an OpenLayers responder that (and I'm paraphrasing here) "OpenLayers has no way of knowing the directional intent of the points to be drawn so it defaults...". Uh, I have to raise the BS flag on that response as I have now learned enough about OpenLayers to be dangerous and this issue has been happening to me. The problem I have with their response is that I load the coordinates for an extent which, by definition, specifies the Upper Right Longitude and Latitude as well as the Lower left Longitude and Latitude. If the Upper Right Longitude lies on the Western side of the IDL and the Lower Left Longitude lies on the Eastern side of the IDL it is pretty obvious which way the user wants to plot the polygon and yet OpenLayers insists on swapping the Longitudinal values and drawing the polygon the wrong way around the world. A sample of the extent declaration and problematic OpenLayers method call is shown below.

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

As you can see the Longitudinal coordinates get reversed and so after you then create the full coordinate structure, a polygon. a polygonFeature and then apply that feature to a vector and finally plot it only to find that the polygon goes the wrong way around the world.

I needed to figure out why this was happening so I dug into this ol.extent.boundingExtent method in the OpenLayers 4 library.

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

My code calculates the area dynamically so that I can determine if the User has created a valid sized AOI polygon. When I am processing a DragBox generated selection I am requesting the coordinates from the resulting geometry structure and for an EPSG:4326 projection when it returns coordinates from a wrapped world the coordinates past the first 180.0 degrees continue incrementing thus the reason for the lonUR calculation of 360.0 - 165.937 = 194.063. My area calculation codepath uses the following IDL test and in order to use the same codepath for the manually entered coordinates I needed to simulate the coordinate value as if it had been returned from the DragBox getGeometry call. I'm actually testing a GEOJSON polygon structure which is a 3 dimensional array with the 1st dimension being the Ring number, the 2nd the X index and 3rd the Y index.

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

If these tests pass at this point the code uses the algorithm I developed to calculate the area over the IDL otherwise it just calculates it as normal everywhere else.

I then use this extent to create a polygon, then a polygonFeature, then apply that feature to a vector and finally plot it and this time it plotted correctly. So the fix I came up with to help solve the area calculation problem I was having also fixed the plotting problem.

Maybe this solution will help someone else or get them thinking in a different direction. The solution came to me when I was finally able to break the problem of the IDL into two issues. The actual area calculation was one issue with the other being the plotting of the polygon over the IDL.

  • OL just uses the >= operator to know which side to go when plotting. If you give 170 then 190 it will go the short way; if you give 170 then -170, it will go the long way. If you always "normalize" the longitude to be between -180 and 180, you lose information. One way to get the information back is by dictating that distances between points are not allowed to be > 180 – Rivenfall Jul 23 at 10:05
1

Workround: Example

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

  • I'm using WFS the link you posted says: "You can do it with a 'Layer.WMS' or a 'Layer.MapServer' layer" – CaptDragon Mar 10 '11 at 18:06
  • If both are supported and you don't have a specific need for Layer.MapServer, go with Layer.WMS (which could still be served from MapServer). – DavidF Mar 10 '11 at 18:50
  • @DavidF: Thanks, but I need to use the vector capabilities of WFS. – CaptDragon Mar 10 '11 at 19:53
1

Two years later, I kept having this issue with features on a vector layer. I found this file containing a snippet of code that shows how to flip an endpoint if it crossed the dateline:

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

Update:

Actually the above did not work for more than one revolution around the world. I ended up doing THIS.

enter image description here

0

I had few issues with dateline and managed to fix all of them. You could try following.

  1. Update the GeoServer layer bounding box values manually to cover your polygon without breaking and see whether it solves the issue.

  2. One of the fix i have done in Openlayers is missing tiles when passing dateline from +ve longitude to -ve. http://trac.osgeo.org/openlayers/ticket/2754 Not sure whether it applicable for WFS. You could get the latest openlayers development version and try.

0

I've come up with a solution for this in my own projects that may or may not work for you. I know for a fact that it works with LineStrings but I'm not sure about other geometry types.

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);
};

The dateLineFix function recursively traverses the given LineString for any segments that cross the date line. It then cuts them in two at the dateline and returns all of the resulting segments as a MultiLineString.

It worked perfectly for my purpose (drawing a polar lat-lon grid).

0

I have encountered this problem with LineStrings and created a solution for it. I'm not sure if it would help you with Polygons. You can see it on my repl.it here: https://repl.it/@gpantic/OpenLayersSplitRouteOverPacific

  • If it answers the question please add all info to your answer instead of providing a link. – BERA Nov 17 '17 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.