Como conectar cartões em uma projeção elipsóide, se isso não for fornecido?

Ou como ajustar os blocos de mapa Yandex à projeção do OpenStreetMaps?

Entrada


Toda vez que você abre algum tipo de mapa on-line, você não o baixa por completo. Para acelerar o carregamento do mapa, ele é dividido em pequenos pedaços (ladrilhos) para que apenas a área desejada possa ser baixada. O problema é que você pode cortar esses quadrados de várias maneiras.



A maioria dos cartões on-line “pensa” que a Terra está na forma de uma bola. Entre eles, por exemplo, mapas do Google e OpenStreetMaps. Alguns, mais meticulosos, levam em conta o fato de que o planeta não é a bola certa: pelo menos, é achatada nos pólos. Essa projeção elipsóide é usada, por exemplo, em mapas Yandex.



Como resultado, uma célula com o mesmo número em projeções diferentes mostrará lugares completamente diferentes. Por exemplo, aqui está um bloco com o número 10427 no eixo X, 5119 no eixo Y. Nível de escala 14. À esquerda - OSM, no Yandex direito. Em vez de uma cidade - algum tipo de floresta.



Embora a maioria dos mecanismos de mapas possa ajustar automaticamente os blocos à projeção desejada, às vezes você pode precisar fazer isso manualmente. Mas como A maneira mais fácil é simplesmente mudar as peças por um certo número de pixels. Como resultado, veremos a área desejada no mapa. Claro, se você olhar de perto, poderá ver algumas distorções. Mas acho, no entanto, que tarefas diárias de precisão semelhante serão mais que suficientes. Portanto, é hora de terminar a introdução e começar a fazer o conversor.



Metodologia


Para funcionar, precisamos de uma fórmula de conversão. Até onde eu entendi, ele foi extraído diretamente do código da página do Yandex Maps, naqueles dias, quando ainda era possível fazer isso. Não encontrarei o link para a fonte no momento, mas essa fórmula já foi publicada no hub. Eu praticamente não toquei: simplesmente reescrevi no Swift e dei às variáveis ​​incompreensíveis de uma letra mais nomes "faladores". Pelo menos aqueles que foram capazes de se identificar. (Obrigado a Erelen pela ajuda)

Bem, a tarefa é a seguinte. Precisamos fazer um conversor que considere o número do bloco na projeção padrão como entrada e o número do bloco na projeção elipsóide e o número de pixels pelos quais ele precisa ser deslocado para a entrada.

Então Por exemplo, pegue um bloco com o número X 10427, Y 5119, Z 14.

Vamos agir em duas etapas. Primeiro, você precisa encontrar as coordenadas (latitude e longitude) desse bloco. Por exemplo, as coordenadas do seu canto superior esquerdo.

func tileNumberToCoordinates(tileX: Int, tileY: Int, mapZoom: Int) -> (lat_deg: Double, lon_deg: Double) { let n : Double = pow(2.0, Double(mapZoom)) let lon = (Double(tileX) / n) * 360.0 - 180.0 let lat = atan( sinh (.pi - (Double(tileY) / n) * 2 * Double.pi)) * (180.0 / .pi) return (lat, lon) } 

Nós obtemos a saída (55.7889 49.1088). Agora substituímos os valores obtidos em nossa fórmula. O nível de zoom ainda é o mesmo: 14º.

 func getWGS84Position(latitude: Double, longitude: Double, zoom: Int) -> (x:Int, y:Int, offsetX:Int, offsetY:Int) { // Earth vertical and horisontal radiuses let radiusA = 6378137.0 let radiusB = 6356752.0 let latitudeInRadians = latitude * Double.pi / 180 let yCompressionOfEllipsoid = sqrt( pow(radiusA, 2.0) - pow(radiusB, 2.0)) / radiusA // I really don't know what the name of this variable mean =( let m2 = log((1 + sin(latitudeInRadians)) / (1 - sin(latitudeInRadians))) / 2 - yCompressionOfEllipsoid * log((1 + yCompressionOfEllipsoid * sin(latitudeInRadians)) / (1 - yCompressionOfEllipsoid * sin(latitudeInRadians))) / 2 // x count = y count let xTilesCountForThisZoom = Double(1 << zoom) //Tile numbers in WGS-84 proection let xTileNumber = floor((longitude + 180) / 360 * xTilesCountForThisZoom) let yTileNumber = floor(xTilesCountForThisZoom / 2 - m2 * xTilesCountForThisZoom / 2 / Double.pi) //Offset in pixels of the coordinate of the //left-top corner of the OSM tile //from the left-top corner of the WGS-84 tile let offsetX = floor(((longitude + 180) / 360 * xTilesCountForThisZoom - xTileNumber) * 256) let offsetY = floor(((xTilesCountForThisZoom / 2 - m2 * xTilesCountForThisZoom / 2 / Double.pi) - yTileNumber) * 256) return (Int(xTileNumber), Int(yTileNumber), Int(offsetX), Int(offsetY)) } 

Temos (10427, 5133, 0, 117). Isso significa que precisamos de um bloco Yandex com o número X 10427, Y 5133, Z 14. E se você o deslocar por 0 pixels para a esquerda e 117 pixels para cima, ele ficará no lugar certo.



E o que fazer sobre isso?


Se você escreve seu navegador e tem a oportunidade de influenciar a exibição do mapa, tudo é relativamente simples. Calcule o deslocamento para qualquer uma das peças na tela. E mova o elemento com o mapa por esse número de pixels de qualquer maneira conveniente.

Mas se você não tiver acesso ao código, será necessário inserir um link intermediário entre o servidor de mapas e o navegador. Por exemplo, eu criei um servidor simples para isso. Ele pega a entrada do número do bloco desejado, calcula o número do bloco na projeção elipsóide. Ele também baixa três peças vizinhas. Cole esses quatro blocos em um grande e, em seguida, recorte o fragmento desejado e o devolva ao navegador do usuário.



Resultado e Original:



Obviamente, todas essas operações exigem custos adicionais de tempo. Usando esses links, você pode avaliar a velocidade com que o servidor "photoshop" o cartão em tempo real:

https://anygis.ru/api/v1/Yandex_map/{xasket/{y}/{z}

https://anygis.ru/api/v1/Yandex_sat_clean/{xasket/{y}/{z}

Bem, espero que as informações apresentadas aqui sejam úteis para alguém. Boa sorte com seus experimentos.

Source: https://habr.com/ru/post/pt483120/


All Articles