Cálculo de caixa delimitadora por centro e escala fornecidos no Android?

8

Dadas estas condições:

  • uma escala como 1:50000
  • o centro da janela é 100°E, 20°N
  • o tamanho da viewport é 400x600

Como posso calcular a caixa delimitadora da janela de exibição?


O sistema de coordenadas geográficas do mapa é EPSG: 4490.

Queremos exibi-los em uma projeção diferente, como Mercator ou latitude_longitude_projection (talvez este seja o chamado não-projetado).

O tamanho da janela de exibição está em pixels.

giser
fonte
Em que projeção está o seu mapa? Web Mercator? Ou você exibe um mapa não projetado em lat / lon? E o tamanho da sua janela de visualização é em pixels? A escala está em piels? Você usa alguma estrutura / biblioteca de mapeamento que possa ter uma função para isso?
Atlefren
Qual software e versão do GIS você está usando para tentar fazer isso?
PolyGeo
@atlefren: Eu atualizo o post.
giser 15/05
@ PolyGeo: Nenhum, tentamos exibir os dados do mapa no Android. E não encontramos nenhuma biblioteca.
Giser 15/05
1
Android, você está usando a API do Google Maps ou simplesmente exibe uma imagem? Eu também recomendaria não usar um sistema de coordenadas não projetado para exibir seu mapa, pois ele parecerá muito distorcido.
Atlefren

Respostas:

9

Ok, com alguns problemas iniciais resolvidos, a tarefa é relativamente simples.

Escala, representada como f.ex 1: 50000, significa que uma unidade no mapa corresponde a 50.000 unidades no mundo real.

Para um mapa em papel impresso em uma escala de 1: 50000, isso significa que 1 metro no mapa corresponde a 50.000 metros no mundo real, ou para facilitar: 1 cm no mapa corresponde a 50 metros no mundo real. Por enquanto, tudo bem.

Quando o computador (ou a tela do telefone) entra no programa, é muito mais difícil: a unidade de medida na tela é um pixel, que não é mapeado diretamente em centímetros. Os OpenLayers estão (ou pelo menos onde) usando os "Pontos por polegada" e assumem que uma polegada corresponde a 72 pixels (isso faz algum sentido em telas de 72 dpi, mas está errado em, por exemplo, Retina Displays. Mas, por enquanto, vamos nos ater ao 72 dpi (como é o que a maioria das bibliotecas de mapeamento faz (acho, as correções são bem-vindas)).

OpenLayers tem uma função OpenLayers.Util.getResolutionFromScale (consulte a fonte ):

OpenLayers.Util.getResolutionFromScale = function (scale, units) {
    var resolution;
    if (scale) {
        if (units == null) {
            units = "degrees";
        }
        var normScale = OpenLayers.Util.normalizeScale(scale);
        resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units]
                                        * OpenLayers.DOTS_PER_INCH);
    }
    return resolution;
};
  • Com units = "degrees" (que é EPSG: 4490, pelo que eu deduzo), obtemos inches_per unit = 4374754 (OpenLayers.INCHES_PER_UNIT ["degrees"])

  • uma escala de 1: 50000 (que corresponde a 1/50000 = 0,00002) (é o que penLayers.Util.normalizeScale calcula) fornece normScale = 0,00002

  • OpenLayers.DOTS_PER_INCH = 72

Podemos então calcular a resolução como

1 / (0.00002 * 4374754 * 72) = 0.00015873908440210453

Sabendo o ponto central (lon = 100, lat = 30), o tamanho de pixel da viewport (w = 400, h = 600) e a resolução, podemos usar a função calculBounds do OpenLayers.Map (consulte a fonte ):

calculateBounds: function(center, resolution) {

        var extent = null;

        if (center == null) {
            center = this.getCachedCenter();
        }
        if (resolution == null) {
            resolution = this.getResolution();
        }

        if ((center != null) && (resolution != null)) {
            var halfWDeg = (this.size.w * resolution) / 2;
            var halfHDeg = (this.size.h * resolution) / 2;

            extent = new OpenLayers.Bounds(center.lon - halfWDeg,
                                           center.lat - halfHDeg,
                                           center.lon + halfWDeg,
                                           center.lat + halfHDeg);
        }

        return extent;
    },

que podemos reduzir para:

function calculateBounds(center, resolution, size) {       
    var halfWDeg = (size.w * resolution) / 2;
    var halfHDeg = (size.h * resolution) / 2;
    return {
        "left": center.lon - halfWDeg,
        "bottom": center.lat - halfHDeg,
        "right": center.lon + halfWDeg,
        "top": center.lat + halfHDeg
    };
}

Chamar isso com nossos valores fornece:

calculateBounds({"lon": 100, "lat": 30}, 0.00015873908440210453, {"w": 400, "h":600});
{ 
    left: 99.96825218311957, 
    bottom: 29.95237827467937,
    right: 100.03174781688043,
    top: 30.04762172532063
}

Podemos então combinar tudo isso a uma função que funciona em graus com denominador de escala, dado:

function calculateBounds(center, scaleDenominator, size) {       
    var resolution = 1 / ((1 / scaleDenominator) * 4374754 * 72)
    var halfWDeg = (size.w * resolution) / 2;
    var halfHDeg = (size.h * resolution) / 2;
    return {
        "left": center.lon - halfWDeg,
        "bottom": center.lat - halfHDeg,
        "right": center.lon + halfWDeg,
        "top": center.lat + halfHDeg
    };
}
Atlefren
fonte
Resposta maravilhosa. De fato, parece que o ponto principal aqui é o dpivalor. Não é?
Giser # 15/13
Ah, e a projeção? Parece que os códigos acima são usados ​​para calcular o grau diretamente.
giser 15/05
Sim, está correto. Enquanto os usuários não espera usar o mapa da mesma forma como um mapa de papel (ou seja, medindo nele) qualquer valor pode ser usado (e eu acho que a maioria das bibliotecas de mapeamento usar 72 ou 96)
atlefren
a única coisa que você precisa saber sobre a projeção são suas unidades, o OpenLayers usa o array OpenLayers.INCHES_PER_UNIT [units] no Utils.js. web Mercator usos metros, como unidade, o que significa 39,37 em vez de 4.374.754 para a resolução de calcular
atlefren
@ atlerfren: Oi, parece que você é um especialista em openlayers, eu encontro alguns problemas relacionados a ele. Você pode poupar algum tempo para dar uma olhada neste post? gis.stackexchange.com/q/61256/12681
giser