Divisão de cadeias de linhas na linha de dados com o OpenLayers

9

Há alguns anos, publiquei o The International Date Line e o @jdeolive sugeriu que eu dividisse os recursos no dateLine. Então eu tentei.

Quando tento dividir minha faixa de satélite com splitWith na linha de dados, volto null. Sei que estou me dividindo corretamente porque, quando me divido na linha de Greenwich, obtenho os resultados esperados.

Alguém sabe como eu posso dividir corretamente uma Linestring programaticamente ao longo da linha da data com o OpenLayers? Eu procuro um código de exemplo, se você o tiver.

Eu tentei, wrapDateLinemas ele não parece estar trabalhando em camadas vetoriais, apesar de minha camada vetorial ser assim:

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

insira a descrição da imagem aqui

Aqui está o meu 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);

Minha pergunta não é uma duplicata desta pergunta, porque eles querem saber como fazê-lo no ogr2ogr

Atualizar:

É assim que parece um conjunto de dados típico com o qual trabalho (faixa de satélite de 24 horas): O Linestring wkt pode ser encontrado AQUI .

insira a descrição da imagem aqui

CaptDragon
fonte
Qual versão do openlayers você está usando?
Plux 21/01
@Plux 2.13.1 (Lastest)
CaptDragon
11
De acordo com a API, wrapDateLine deve ser usado apenas na camada base, portanto, não é de admirar que não funcione na camada vetorial. No entanto, não tenho idéia de como fazê-lo funcionar na camada vetorial. Estou tendo um problema semelhante com multipolígonos que cruzam a linha de dados.
Plux 21/01
11
@Plux confira minha solução
CaptDragon
Ótima solução. Infelizmente, isso não se aplica ao meu problema, acredito que meu problema está mais no lado do servidor geográfico e meu polígono contém coordenadas que estão no "outro lado" da linha de dados, como -180.00000000000003 90.00000190734869, o que cria alguns problemas no geoserver. Eu não estou tendo problemas exibindo isso em meu mapa (eu tenho um WMS que faz isso), mas eu quero usá-los como filterboxes em um wfs-consulta para geoserver :)
Plux

Respostas:

2

O problema é que seu recurso não cruza a linha da data da perspectiva do OpenLayers; portanto, sua linha de divisão não cruza seu recurso. Exemplo de seus dados:

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

Você vai de -178 a 156, e isso não cruza a linha da data da perspectiva do OpenLayers. Em vez de dividir na linha da data, você deve dividir no seu valor mínimo de X.

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

Criei um exemplo aqui que dividiu com êxito sua trilha de satélite em 2 recursos: http://jsfiddle.net/6XJ5A/

Agora, para usar o WKT com várias linhas na sua atualização, em vez de usar uma linha reta, você deve percorrer todo o conjunto de dados e criar sua linha de divisão com todas as coordenadas que atravessam a linha de dados. Ao criar uma linha pequena dentro de uma multilinha, você pode dividir todas as coordenadas que devem atravessar a linha de dados. Aqui está o exemplo atualizado: http://jsfiddle.net/Jc274/

E o 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);

Isso retornará uma linha dividida em todos os pontos que "cruzam" a linha de dados

Observe que eu também percorro as coordenadas para remover a linha que atravessa o mapa para conectar as 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]));
}

Atualização: atualizei o primeiro exemplo para adicionar apenas a linha que foi dividida. Também atualizei a explicação de acordo. Essa abordagem não é à prova de balas com a faixa de satélite de 24 horas que você forneceu, mas estou trabalhando nela.

Atualização 2: atualizei o segundo exemplo. Ao usar uma multilinha para dividir e repetir o resultado para remover coordenadas extras adicionadas pela divisão, obtemos um conjunto de recursos que nunca passam pela linha de dados.

Julien-Samuel Lacroix
fonte
+1 por esforço, mas seus exemplos de mapas não parecem resolver o problema. As trilhas de satélite em seu mapa de exemplo ainda atravessam o mundo inteiro, em vez de seguir o caminho natural da trilha de satélite. Eu devo estar esquecendo alguma coisa?
precisa saber é o seguinte
Você pode estar entendendo mal o problema porque ..., -178.475596 -81.673196, 156.248392 -81.611421,...atravessa absolutamente a linha de dados. Veja aqui
CaptDragon
Deixe-me atualizar o código e minha explicação. Eu sei que ele deve cruzar a linha de dados, mas o OpenLayers não suporta isso. Do ponto de vista de OL, não está cruzando a linha de dados.
Julien-Samuel Lacroix
Está certo. Esse é o problema. Vou precisar enganar o OpenLayers e dividir a linha para que ela vá até a borda e continue do outro lado onde deveria.
CaptDragon
2

Encontrei uma ótima solução no github do @Dane. Chama-se Arc.js e serve para calcular rotas de círculo grande. Além disso, ele também dividirá a linha na linha de dados e fornecerá duas cadeias de linhas que se encontram na linha de dados, que os OpenLayers podem facilmente mapear. Espero que ele se apresente para reivindicar a recompensa.

Aqui estão os meus resultados:

insira a descrição da imagem aqui insira a descrição da imagem aqui

CaptDragon
fonte
1

A função splitWith não sabe sobre a forma tridimensional da Terra. Só opera em um mundo bidimensional. No seu caso, todas as suas LINESTRINGcoordenadas X estão entre -180 e 180. Portanto, da perspectiva bidimensional do OpenLayers, a sequência de linhas nunca cruza sua geometria dividida (a linha da data) e informa isso retornando null.

Acredito que você precisará escrever um código personalizado para fazer a divisão. Estou imaginando um algoritmo que circula seus vértices, construindo seqüências de linhas de saída como esta:

  • Para cada par adjacente de vértices, decida se o segmento entre eles cruza a linha da data.
  • Caso contrário, mantenha esse segmento como ele é e adicione-o à string de linha de saída "atual".
  • Se ele faz , dividir o segmento em duas partes. Adicione uma parte à sequência de linhas "atual", inicie uma nova sequência de linhas "atual" e adicione a outra parte a essa nova.

Uma heurística razoável para determinar se um par de vértices cruza a linha da data é verificar se a diferença entre as coordenadas X é superior a 180 graus. (Embora isso possa dar errado, por exemplo, nas regiões polares. Talvez você tenha sorte o suficiente para não ter latitudes realmente altas.)

A operação de dividir um segmento em duas partes pode ser tão simples quanto a interpolação linear (se você não se importa muito com a precisão da pista). Quando você detecta que o segmento cruza a linha da data, copia o segundo vértice e move sua coordenada X (adicionando ou subtraindo 360 graus) e interpola a coordenada Y.

EDIT : Aqui está um JSFiddle que demonstra o algoritmo acima em seus dados: http://jsfiddle.net/85vjS/

csd
fonte
0

Se funcionar com Greenwich, é porque você está dentro dos limites do seu CRS. Então, primeiro sugiro a mesma solução alternativa que você está apontando para a postagem:

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

e talvez

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

para o outro lado.

Outra solução é trabalhar em um CRS que não esteja "fora do limite" na linha de dados. Você deve poder dividir seus dados sem problemas.

radouxju
fonte
Eu já tentei os dois LINESTRING(179.99 -89, 179.99 89)e LINESTRING(-179.99 -89, -179.99 89)sem sucesso. Com relação ao CRS, infelizmente, isso não funcionará para o meu propósito, porque estou mapeando trilhas de satélites que percorrem o mundo muitas vezes. Portanto, todos os CRSs estão divididos em algum lugar e terei o mesmo problema em qualquer lugar. Obrigado pela sua contribuição.
precisa saber é o seguinte