División de cadenas lineales en fecha con OpenLayers

9

Hace un par de años publiqué The International Date Line y @jdeolive sugirió que dividiera las funciones en dateLine. Entonces lo intenté.

Cuando trato de dividir mi vía satelital con splitWith en la fecha, regreso null. Sé que me estoy dividiendo correctamente porque cuando divido en la línea de Greenwich obtengo los resultados esperados.

¿Alguien sabe cómo puedo dividir correctamente una cadena de líneas mediante programación a lo largo de la línea de fecha con OpenLayers? Busco código de ejemplo si lo tienes.

Lo he intentado wrapDateLinepero no parece estar funcionando en capas vectoriales a pesar de que mi capa vectorial es así:

vectorLayer = new OpenLayers.Layer.Vector("GroundTracks", {
    renderers: ['Canvas', 'VML'],
    wrapDateLine: true}); // <-- shoud be wraping.

ingrese la descripción de la imagen aquí

Aquí está mi código:

var features = [];
var format = new OpenLayers.Format.WKT({
    'internalProjection': map.baseLayer.projection,
    'externalProjection': prjGeographic
});
var satTrack = format.read("LINESTRING (95.538611 13.286511, 94.730711 16.908947, 93.901095 20.528750, 93.043594 24.145177, 92.150978 27.757436, 91.214579 31.364666, 90.223791 34.965899, 89.165364 38.560019, 88.022401 42.145679, 86.772901 45.721205, 85.387568 49.284424, 83.826433 52.832413, 82.033480 56.361087, 79.927797 59.864504, 77.388419 63.333664, 74.227306 66.754285, 70.139140 70.102478, 64.605267 73.335774, 56.712904 76.373458, 44.881134 79.052803, 26.939886 81.047314, 02.704174 81.839241, -21.686285 81.101751, -39.887660 79.141947, -51.906937 76.480894, -59.912477 73.452897, -65.514482 70.225089, -69.645366 66.880243, -72.834535 63.461797, -75.393132 59.994131, -77.512464 56.491789, -79.315407 52.963919, -80.884039 49.416549, -82.275114 45.853820, -83.529088 42.278691, -84.675583 38.693355, -85.736827 35.099503, -86.729876 31.498490, -87.668095 27.891443, -88.562176 24.279331, -89.420849 20.663020, -90.251389 17.043303, -91.059999 13.420926, -91.852092 09.796602, -92.632515 06.171020, -93.405728 02.544857, -94.175960 -01.081217, -94.947343 -04.706542, -95.724045 -08.330456, -96.510402 -11.952298, -97.311065 -15.571400, -98.131162 -19.187081, -98.976502 -22.798638, -99.853829 -26.405335, -100.771148 -30.006378, -101.738172 -33.600889, -102.766925 -37.187866, -103.872602 -40.766117, -105.074803 -44.334175, -106.399366 -47.890158, -107.881153 -51.431559, -109.568417 -54.954914, -111.529886 -58.455253, -113.866668 -61.925160, -116.733085 -65.353081, -120.374635 -68.720132, -125.199754 -71.993642, -131.916790 -75.113368, -141.772276 -77.960803, -156.750096 -80.294831, -178.475596 -81.673196, 156.248392 -81.611421, 135.042323 -80.136505, 120.556535 -77.748172, 111.014840 -74.872356, 104.485504 -71.737081, 99.775637 -68.454400, 96.208126 -65.081545, 93.391438 -61.649716, 91.089380 -58.177038, 89.152970 -54.674643, 87.484294 -51.149703, 86.016609 -47.607042, 84.702947 -44.050030, 83.509299 -40.481112, 82.410411 -36.902133, 81.387093 -33.314533, 80.424442 -29.719485, 79.510644 -26.117981, 78.636145 -22.510889, 77.793053 -18.898997, 76.974710 -15.283040, 76.175371 -11.663718, 75.389950 -08.041709, 74.613831 -04.417680, 73.842693 -00.792294, 73.072378 02.833789, 72.298749 06.459907, 71.517566 10.085391, 70.724342 13.709564, 69.914194 17.331733, 69.081655 20.951185, 68.220447 24.567170, 67.323194 28.178891, 66.381031 31.785476, 65.383084 35.385943, 64.315735 38.979152, 63.161579 42.563725, 61.897893 46.137940, 60.494337 49.699551, 58.909396 53.245525, 57.084691 56.771602, 54.935577 60.271560, 52.334964 63.735923, 49.084320 67.149569, 44.859585 70.487030, 39.107498 73.702694, 30.852243 76.709182, 18.420695 79.329532, -00.339911 81.212453, -25.028018 81.831766)");

var featGreenwichLine = format.read("LINESTRING(0 -89, 0 89)");
var featDateLine = format.read("LINESTRING(180 -89, 180 89)");

features.push(featGreenwichLine);
features.push(featDateLine);
features.push(satTrack);

var resultsGreenwich = satTrack.geometry.splitWith(featGreenwichLine.geometry);
var resultsDateLine = satTrack.geometry.splitWith(featDateLine.geometry);

console.log(resultsGreenwich); //<--RETURNS EXPECTED RESULTS.
console.log(resultsDateLine);//<--RETURNS NULL.

vectorLayer.addFeatures(features);

Mi pregunta no es un duplicado de esta pregunta porque quieren saber cómo hacerlo en ogr2ogr

Actualizar:

Así es como se ve un conjunto de datos típico con el que trabajo (seguimiento satelital de 24 horas): la línea de líneas se puede encontrar AQUÍ .

ingrese la descripción de la imagen aquí

CaptDragon
fuente
¿Qué versión de openlayers estás usando?
Plux
@Plux 2.13.1 (Último)
CaptDragon
1
Según su API, wrapDateLine solo debe usarse en la capa base, por lo que no es de extrañar que no funcione en la capa vectorial. Sin embargo, no tengo idea de cómo hacer que funcione en la capa vectorial. Estoy teniendo un problema similar con los multipolígonos que cruzan la línea de fecha.
Plux
1
@Plux mira mi solución
CaptDragon
Buena solución Desafortunadamente, no es aplicable a mi problema, creo que mi problema está más en el lado del geoservidor, y mi polígono contiene coordenadas que están en el "otro lado" de la línea de fecha, como -180.00000000000003 90.00000190734869 que crea algunos problemas en el geoservidor, creo. No tengo problemas para mostrar esto en mi mapa (tengo un wms que hace esto), pero quiero usarlos como cajas de filtro en una consulta wfs para geoserver :)
Plux

Respuestas:

2

El problema es que su entidad no cruza la línea de fecha desde la perspectiva de OpenLayers, por lo que su línea de división no intersecta su entidad. Ejemplo de sus datos:

..., -178.475596 -81.673196, 156.248392 -81.611421,...

Pasas de -178 a 156, y esto no cruza la línea de fecha desde la perspectiva OpenLayers. En lugar de dividir en la línea de fecha, debe dividir en su valor X mínimo.

// Build the splitting line based on the min and max coordinates of the vector to split
var minX = 999999999;
var minY = -20037508.34 // minimum value of the spherical mercator projection
var maxY = 20037508.34  // maximum value of the spherical mercator projection
//Extract the minimum X from the data as bounds seems to be rounded.
for(var i=0; i<satTrack.geometry.components.length; i++) {
    if(satTrack.geometry.components[i].x < minX)
        minX = satTrack.geometry.components[i].x;
}
var pointList = [
    new OpenLayers.Geometry.Point(minX, minY),
    new OpenLayers.Geometry.Point(minX, maxY)
];
var featDateLine = new OpenLayers.Feature.Vector(
    new OpenLayers.Geometry.LineString(pointList)
);

He creado un ejemplo aquí que dividió con éxito su ruta satelital en 2 características: http://jsfiddle.net/6XJ5A/

Ahora, para usar el WKT con varias líneas en su actualización, en lugar de usar una línea recta, debe recorrer todo el conjunto de datos y construir su línea dividida con todas las coordenadas que atraviesan la línea de fecha. Al construir una pequeña línea dentro de una multilínea, puede dividir todas las coordenadas que deben cruzar la línea de fecha. Aquí está el ejemplo actualizado: http://jsfiddle.net/Jc274/

Y el código:

// Build the splitting line based on the min and max coordinates of the vector to split
var pointList = [];
var lastPoint = satTrack.geometry.components[0];
//Extract the minimum X from the data as bounds seems to be rounded.
for (var i = 1; i < satTrack.geometry.components.length; i++) {
    if (Math.abs(satTrack.geometry.components[i].x - lastPoint.x) > 10000000) {
        pointList.push(satTrack.geometry.components[i]);
    }
    lastPoint = satTrack.geometry.components[i];
}

var lineList = [];
for(var i=0; i<pointList.length; i++) {
    lineList.push(new OpenLayers.Geometry.LineString([
        new OpenLayers.Geometry.Point(pointList[i].x, pointList[i].y-0.00001), 
        new OpenLayers.Geometry.Point(pointList[i].x, pointList[i].y+0.00001)
    ]));
}

var featDateLine = new OpenLayers.Feature.Vector(
new OpenLayers.Geometry.MultiLineString(lineList), null, split_style);

Esto le devolverá una línea dividida en todos los puntos que "cruzan" la línea de fecha

Tenga en cuenta que también recorro las coordenadas para eliminar la línea que cruza el mapa para conectar las 2 coordenadas:

for (var i = 0; i < resultsDateLine.length; i++) {
    // Remove the first (or last) point of the line, the one that cross the dateline
    if (Math.abs(resultsDateLine[i].components[0].x - resultsDateLine[i].components[1].x) > 10000000) {
        resultsDateLine[i].removeComponent(resultsDateLine[i].components[0]);
    }
    if (Math.abs(resultsDateLine[i].components[resultsDateLine[i].components.length - 1].x - resultsDateLine[i].components[resultsDateLine[i].components.length - 2].x) > 10000000) {
        resultsDateLine[i].removeComponent(resultsDateLine[i].components[resultsDateLine[i].components.length - 1]);
    }
    features.push(new OpenLayers.Feature.Vector(resultsDateLine[i], null, style_array[i]));
}

Actualización: actualicé el primer ejemplo para agregar solo la línea que se dividió. También actualicé la explicación en consecuencia. Este enfoque no es a prueba de balas con la pista satelital de 24 horas que proporcionó, pero estoy trabajando en ello.

Actualización 2: actualicé el segundo ejemplo. Al usar una línea múltiple para dividir y recorrer el resultado para eliminar las coordenadas adicionales agregadas por la división, obtenemos un conjunto de características que nunca pasan por la línea de fecha.

Julien-Samuel Lacroix
fuente
+1 por esfuerzo, pero sus ejemplos de mapas no parecen resolver el problema. Las pistas de satélite en su mapa de ejemplo todavía cruzan todo el mundo en lugar de seguir el camino natural de la pista de satélite. ¿Debo estar perdiendo algo?
CaptDragon
Puede estar malinterpretando el problema porque ..., -178.475596 -81.673196, 156.248392 -81.611421,...cruza absolutamente la fecha. Ver aquí
CaptDragon
Déjame actualizar el código y mi explicación. Sé que debería cruzar la línea de datos, pero OpenLayers no lo admite. Desde el punto de vista OL, no está cruzando la línea de fecha.
Julien-Samuel Lacroix
Así es. Ese es el problema. Tendré que engañar a OpenLayers y dividir la línea para que vaya hasta el borde y luego continúe en el otro lado donde se supone que debe hacerlo.
CaptDragon
2

Encontré una gran solución en el github de @Dane. Se llama Arc.js y es para calcular rutas de gran círculo. No solo eso, también dividirá la línea en la línea de fecha y le proporcionará dos cadenas de líneas que se encuentran en la línea de fecha, que OpenLayers puede asignar fácilmente. Espero que se presente para reclamar la recompensa.

Aquí están mis resultados:

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

CaptDragon
fuente
1

La función splitWith no conoce la forma tridimensional de la tierra. Solo opera en un mundo bidimensional. En su caso, todas sus LINESTRINGcoordenadas X están entre -180 y 180. Entonces, desde la perspectiva bidimensional de OpenLayers, la cadena de línea nunca cruza su geometría dividida (la línea de fecha) y se lo dice al regresar null.

Creo que tendrá que escribir un código personalizado para hacer la división. Estoy imaginando un algoritmo que recorre tus vértices, creando cadenas de línea de salida como esta:

  • Para cada par de vértices adyacentes, decida si el segmento entre ellos cruza la línea de fecha.
  • Si no es así, mantenga ese segmento como está y agréguelo a la cadena de línea de salida "actual".
  • Si lo hace , dividir el segmento en dos partes. Agregue una parte a la cadena de línea "actual", comience una nueva cadena de línea "actual" y agregue la otra parte a esta nueva.

Una heurística razonable para determinar si un par de vértices cruza la línea de fecha es ver si la diferencia entre las coordenadas X es más de 180 grados. (Aunque esto puede equivocarse, por ejemplo, en las regiones polares. Quizás tenga la suerte de no tener latitudes realmente altas).

La operación de dividir un segmento en dos partes podría ser tan simple como la interpolación lineal (si no le importa demasiado la precisión de la pista). Cuando detecta que el segmento cruza la línea de fecha, hace una copia del segundo vértice y mueve su coordenada X (sumando o restando 360 grados) y luego interpola la coordenada Y.

EDITAR : Aquí hay un JSFiddle que muestra el algoritmo anterior en sus datos: http://jsfiddle.net/85vjS/

csd
fuente
0

Si funciona con Greenwich, esto se debe a que está dentro de los límites de su CRS. Así que primero sugeriría la misma solución alternativa que en la publicación a la que apunta:

var featDateLine = format.read("LINESTRING(179.99 -89, 179.99 89)");

y tal vez

var featDateLine = format.read("LINESTRING(-179.99 -89, -179.99 89)");

por el otro lado

Otra solución es trabajar en un CRS que no está "fuera de límite" en la fecha. Entonces debería poder dividir sus datos sin problema.

radouxju
fuente
Ya he probado ambos LINESTRING(179.99 -89, 179.99 89)y fue LINESTRING(-179.99 -89, -179.99 89)en vano Con respecto al CRS, desafortunadamente, esto no funcionará para mi propósito porque estoy mapeando pistas satelitales que dan la vuelta al mundo muchas veces. Entonces, todos los CRS se dividen en algún lugar y tendré el mismo problema en cualquier lugar donde lo divida. Gracias por tu contribución.
CaptDragon