国际日期变更线环绕


13

使用OpenLayers,我在GeoServer上添加了一个WFS图层,并带有一个过滤器,该过滤器返回在某些日期内与某些拉丁美洲国家/地区的多边形(黄色)相交的所有要素(黑色)。

在此处输入图片说明

但是,在地图上水平交叉的要素实际上并未与我的多边形相交。此功能位于夏威夷和斐济之间的太平洋中,而不是拉丁美洲。问题在于,它没有跨越国际日期变更线,而是通过环绕整个世界呈现在地图上。

常见功能定义如下:

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

我有很多有问题的日期行功能,例如本示例。我不能在我的应用程序中忽略它,因为我有很多。

我尝试在基础层和WFS层中使用“ wrapDateLine:true”,结果相同。

不知道这是GeoServer问题还是OpenLayers问题。

有人知道我的国际约会安排问题的解决方案吗?


2
我不知道为什么软件对此有这样的问题,世界是平坦的,对吧?
DavidF 2011年

也许应该有一个方向参数。
CaptDragon 2011年

@CaptDragon这个问题有解决方案吗?
Anil 2013年

@Anil还没有。如果找到一个,请告诉我。
CaptDragon 2013年

Answers:


6

不幸的是,这是一个已知的问题。问题是像这样跨越日期线的几何形状是模棱两可的。OL和GeoServer渲染器没有容易的方法知道其意图是在世界范围内“走短路”,因此他们只是将“常规”方式解释为170至-170,并在世界范围内走长路。

不幸的是,除了拆分跨越日期线的几何形状之外,没有很好的解决方案。


谢谢+1,我同意,但是我无法分割几何。让我们看看是否还有其他想法。
CaptDragon 2011年

我想出了一种在OpenLayers上很好地拆分它们的方法。
CaptDragon 2014年

7

重新投影地图以使用在格林威治子午线(或其他位置)处分开的投影,这样您感兴趣的多边形就不会越过地图中的不连续点。


多边形可以很好地覆盖整个世界,总会有一些多边形跨越某些线。但是,您知道没有这样分割的投影吗?
CaptDragon 2011年

1
所有的预测都必须在某个地方分裂世界,这在数学中是隐含的(如果您不相信我,请剥橙色:-)。您所能做的就是为您的任务选择最佳的投影。
伊恩·特顿

是啊,你说得对。我将开放几天,看看是否有其他想法。感谢您的建议。:-)
CaptDragon 2011年

2

我已经研究了这个问题很长时间了,因为我开发了一个应用程序,该应用程序允许用户通过DragBox动作或绘制用户输入的范围点来生成“关注区域”矩形。当我开始这个冒险时,我对OpenLayers完全陌生。手动输入的范围点的问题是,如果AOI覆盖了国际日期变更线,则绘制的矩形将在世界范围内以错误的方式绘制。众多StackExchange用户问到这个问题,只是被OpenLayers响应者告知(并且我在这里释义):“ OpenLayers无法知道要绘制的点的方向意图,因此它默认为...”。嗯,由于我现在对OpenLayers的了解已经足够危险了,所以我必须在响应中举起BS标志,这个问题已经发生在我身上。我的响应有一个问题,就是我在一定程度上加载了坐标,根据定义,该范围指定了右上经度和纬度以及左下经度和纬度。如果右上经度位于IDL的西侧,而左下经度位于IDL的东侧,则很明显,用户想以哪种方式绘制多边形,而OpenLayers坚持交换纵向值并绘制多边形以错误的方式环绕世界。范围声明和有问题的OpenLayers方法调用的示例如下所示。如果右上经度位于IDL的西侧,而左下经度位于IDL的东侧,则很明显,用户想以哪种方式绘制多边形,而OpenLayers坚持交换纵向值并绘制多边形以错误的方式环绕世界。范围声明和有问题的OpenLayers方法调用的示例如下所示。如果右上经度位于IDL的西侧,而左下经度位于IDL的东侧,则很明显,用户想以哪种方式绘制多边形,而OpenLayers坚持交换纵向值并绘制多边形以错误的方式环绕世界。范围声明和有问题的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__: []

如您所见,纵向坐标反转了,因此在创建完整的坐标结构后即为多边形。将polygonFeature应用于特征,然后将该特征应用于矢量,最后对其进行绘制,以发现多边形在世界范围内走错了方向。

我需要弄清楚为什么会这样,所以我在OpenLayers 4库中钻研了这个ol.extent.boundingExtent方法。

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

我的代码动态地计算面积,以便我可以确定用户是否创建了有效尺寸的AOI多边形。当我处理DragBox生成的选择时,我要从生成的几何结构中请求坐标,对于EPSG:4326投影,当它从环绕的世界返回坐标时,超过第一个180.0度的坐标将继续递增,因此计算lonUR的原因360.0-165.937 = 194.063。我的面积计算代码路径使用以下IDL测试,并且为了对手动输入的坐标使用相同的代码路径,我需要模拟坐标值,就好像它是从DragBox getGeometry调用返回的一样。我实际上正在测试GEOJSON多边形结构,该结构是3维数组,第1维是Ring编号,

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

如果这些测试在此时通过,则代码将使用我开发的算法来计算IDL上的面积,否则它将在其他所有地方正常进行计算。

然后,我使用此范围创建一个多边形,然后创建一个polygonFeature,然后将该特征应用于矢量,最后对其进行绘制,这次将其正确绘制。因此,我想出的解决方案可以帮助解决面积计算问题,而我也解决了绘图问题。

也许此解决方案将帮助其他人或使他们朝着不同的方向思考。当我终于能够将IDL的问题分解为两个问题时,解决方案就来了。实际面积计算是一个问题,另一个问题是在IDL上绘制多边形。


1
OL仅使用> =运算符来知道绘图时要走哪一边。如果您给170,然后再给190,它将走很短的路。如果您给170然后-170,它将大有帮助。如果始终将经度“标准化”为-180到180之间,则会丢失信息。获取信息的一种方法是规定点之间的距离不得大于180
Rivenfall '19

1

解决方法:示例

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


我正在使用WFS,您发布的链接说:“您可以使用'Layer.WMS'或'Layer.MapServer'图层来实现”
CaptDragon 2011年

如果两者均受支持,并且您对Layer.MapServer没有特定的需求,请使用Layer.WMS(仍可以从MapServer进行服务)。
DavidF 2011年

@DavidF:谢谢,但是我需要使用WFS的向量功能。
CaptDragon 2011年

1

两年后,我一直在矢量层上遇到有关此问题的问题。我发现此文件包含一段代码,该代码段显示了如果它越过日期线如何翻转端点:

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

更新:

实际上,以上方法在全球范围内不止一次革命。我最终做了这个

在此处输入图片说明


1

我在自己的项目中为此提出了一个解决方案,该解决方案可能对您不起作用。我知道它可以与LineStrings一起使用,但是我不确定其他几何类型。

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

dateLineFix函数以递归方式遍历给定的LineString,以跨越日期线。然后,将它们在日期行处切成两部分,并将所有结果段作为MultiLineString返回。

它非常适合我的目的(绘制极坐标纬度网格)。


0

我在日期变更方面遇到了一些问题,并设法解决了所有问题。您可以尝试关注。

  1. 手动更新GeoServer图层边界框值以覆盖您的多边形而不会损坏,并查看其是否解决了问题。

  2. 我在Openlayers中完成的一项修复工作是在将日期线从+ ve经度传递到-ve时缺少磁贴。 http://trac.osgeo.org/openlayers/ticket/2754 不确定是否适用于WFS。您可以获得最新的openlayers开发版本并尝试。


0

我在LineStrings中遇到了此问题,并为此创建了解决方案。我不确定是否对多边形有帮助。您可以在我的repl.it上看到它:https ://repl.it/@gpantic/OpenLayersSplitRouteOverPacific


1
如果它回答了问题,请在回答中添加所有信息,而不提供链接。
BERA

0

EPSG:3832(WGS84 PDC)是一个以太平洋为中心的投影。这将用IDL穿越问题换取本初子午线穿越问题。根据您所描绘的内容,这可能不是问题。我还在北极圈和南极圈附近发现了问题。

幸得文章。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.