Ione Souza Junior

Aplicativo com mapa offline usando Ionic Framework

18/01/2016 | 8 minutos de leitura | Tradu√ß√Ķes: en | #misc

Vamos l√°, esse post foi inspirado em uma d√ļvida postada no Slack do Ionic Brazil. A pergunta foi mais ou menos assim: ‚ÄúAlgu√©m j√° fez algo relacionado a mapas, como baixar o mapa pro celular do cara, sem precisar ficar carregando o mapa online?‚ÄĚ

Tendo em vista que isso tamb√©m pode ser d√ļvida de outros desenvolvedores, aqui vai um breve post que tem como objetivo deixar as coisas um pouco mais claras em rela√ß√£o ao assunto.

O projeto exemplo pode ser baixado no Github.

Neste projeto exemplo, foi utilizado o template maps do wizard do Ionic Framework. Ent√£o, a partir de agora vejamos as altera√ß√Ķes que realizei no projeto.

Alterando mapa para utilizar engine Leaflet

No projeto existe uma diretiva personalizada chamada map, existente no arquivo directives.js. O que vamos fazer é customizá-la para carregar os mapas utilizando Leaflet ao invés do Google Maps.

Ent√£o, vamos adicionar o Leaflet nas depend√™ncias do projeto. Poder√≠amos simplesmente utilizar bower install leaflet ‚Äďsave-dev mas isso iria trazer algumas depend√™ncias desnecess√°rias do AngularJS, pois o Ionic Framework j√° incorpora todas elas, ent√£o, vamos adicionar o Leaflet manualmente no projeto. Na p√°gina de download do Leaflet voc√™ encontra o pacote para baixar. Neste projeto de exemplo, utilizei a √ļltima vers√£o est√°vel, 0.7.7.

Ap√≥s baixar, voc√™ pode mover o conte√ļdo para a pasta www/lib do projeto e em seguida referenciar os arquivos javascript e CSS da biblioteca no arquivo index.html do aplicativo.

Neste projeto, o elemento que está renderizando o mapa é a diretiva map, localizada no arquivo index.html. O que precisamos fazer é alterar a engine de criação do mapa para o Leaflet ao invés do Google Maps. Começamos alterando a diretiva no arquivo directives.js:

link: function ($scope, $element, $attr) {
    function initialize() {
        var mapOptions = {
            center: [43.07493, -89.381388],
            zoom: 16
        };
        var map = L.map($element[0], mapOptions);
        L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        $scope.onCreate({map: map});
    }

    if (document.readyState === "complete") {
        initialize();
    } else {
        window.addEventListener('load', initialize);
    }
}

Mas apenas isso não basta, no controller MapCtrl também precisamos alterar o método que centraliza o mapa em uma determinada latitude e longitude, pois como trocamos a engine para o Leaflet, agora não iremos mais usar os objetos do Google Maps para interagir com o mapa. Alterei o arquivo controllers.js na linha destacada:

$scope.centerOnMe = function () {
    console.log("Centering");
    if (!$scope.map) {
        return;
    }

    $scope.loading = $ionicLoading.show({
        content: 'Getting current location...',
        showBackdrop: false
    });

    navigator.geolocation.getCurrentPosition(function (pos) {
        console.log('Got pos', pos);
        $scope.map.setView([pos.coords.latitude, pos.coords.longitude]);
        $scope.loading.hide();
    }, function (error) {
        alert('Unable to get location: ' + error.message);
    });
};

Por fim, excluí a inserção da biblioteca do Google Maps, presente no arquivo www/index.html. Com isso, já temos o aplicativo funcionando com o mapa utilizando a engine do Leaflet.

Baixando mapas utilizando MOBAC

MOBAC, Mobile Atlas Creator √© um programa open source que cria atlas offline para dispositivos port√°teis de GPS e aplicativos de telefone celular como TrekBuddy, AndNav e outras aplica√ß√Ķes Android e Windows CE. Bom, isso √© o que diz a descri√ß√£o dele no site, mas como podemos tirar proveito disso?

Com MOBAC, conseguimos selecionar uma parte do mapa, baixar os map tiles, adiciona-los no nosso aplicativo e, por fim, fazer a engine do Leaflet carregar os map tiles contidos no aplicativo. Ent√£o, vamos baixa-lo para iniciar as opera√ß√Ķes. Atualmente o MOBAC est√° na vers√£o 2.0.0 beta 1.

MOBAC é um aplicativo multiplataforma, desenvolvido em Java, então, você não deverá ter problemas para executa-lo em seu sistema operacional, sendo Windows, Linux, Mac, etc… ).

Ao abrir o MOBAC, vamos clicar no menu Atlas -> Convert Atlas Format e selecionar a opção Osmdroid ZIP.

Em seguida, na aba Map Source, selecione o OpenStreetMap MapQuest (1), ent√£o, iremos visualizar o mapa (2). Com o mapa aberto, √© poss√≠vel controlar o zoom do mesmo (3) para visualizar as regi√Ķes do globo.

Tela do MOBAC.
Tela do MOBAC.

No exemplo ilustrado, selecionei uma área da cidade de São Paulo para fins demonstrativos. Para baixar uma área do mapa, basta utilizar o cursor para selecionar a área desejada. Você irá visualizar a área selecionada hachurada em vermelho (4). Após selecionar uma área, você deve informar na aba Zoom Levels quais os zooms que deseja baixar os map tiles (5). Observe que, quanto maior a área selecionada e maior o zoom, mais imagens serão baixadas. Após selecionar os zooms desejados, clique em Add selection (6), ainda na aba Zoom Levels.

Tela do MOBAC.
Tela do MOBAC.

Após isso, basta baixar o mapa clicando em Atlas -> Create Atlas. Agora aguarde, mas fique atento caso existirem mensagens de erro. Talvez algumas imagens estejam indisponíveis para download devido a sobrecarga que este processo causa nos servidores.

Um detalhe muito importante aqui é que devemos utilizar isso com moderação, pois isso sobrecarrega os servidores de mapas e prejudica a utilização de outros usuários. Na página da wiki contém alguns conselhos aos usuários.

Bom, ap√≥s baixar o mapa selecionado, podemos clicar em Open Atlas Folder e visualizar o nosso mapa rec√©m baixado. Nosso mapa est√° dispon√≠vel em formato ZIP. Ao descompactar, uma pasta MapQuest aparecer√° e dentro dela outras pastas com as numera√ß√Ķes dos zooms selecionados. Dentro da pasta de cada zoom haver√£o outras pastas com as coordenadas de do eixo X e dentro destas pastas, estar√£o as respectivas imagens em formato JPG com as coordenadas do eixo Y. O que fazer agora? Vamos adicionar a pasta MapQuest dentro do nosso aplicativo.

Adicionando mapa baixado com MOBAC no aplicativo Ionic

Agora a parte mais esperada do tutorial. Vamos adicionar a pasta MapQuest extraída do arquivo ZIP para dentro de nosso aplicativo na pasta www.

O próximo passo é voltar na diretiva map e alterar o tile layer para não carregar da referência online, e sim da offline. Também vamos alterar a latitude e longitude inicial para carregamento do mapa, fazendo o ajuste para carregar de acordo com o lugar que baixamos o mapa:

link: function ($scope, $element, $attr) {
    function initialize() {
        var mapOptions = {
            center: [-23.568746, -46.647132],
            zoom: 18
        };
        var map = L.map($element[0], mapOptions);
        L.tileLayer('MapQuest/{z}/{x}/{y}.jpg', {
            attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        $scope.onCreate({map: map});
    }

    if (document.readyState === "complete") {
        initialize();
    } else {
        window.addEventListener('load', initialize);
    }
}

Perceba que configuramos uma URL relativa na configuração do tile layer para carregar os mapas. Essa referência relativa aponta para a pasta www. Como a pasta MapQuest já está em www, basta apenas referenciar a pasta e o Leaflet irá carregar as imagens corretamente a partir do endereço local. Note que, como modificamos o tile layer para carregar os mapas localmente, quaisquer outras partes do mapa que tentarmos consumir não poderão ser visualizadas.

Observação importante: o mapa que incluí no aplicativo possui aproximadamente 4 MB. Se você tentar incluir um mapa de uma cidade inteira ou de uma região muito grande, irá perceber que a compilação do aplicativo irá demorar. Isso se deve ao fato das imagens estarem incluídas na pasta www do aplicativo, então, em cada processo de compilação, os mapas incluídos nesta pasta serão processados.

Para evitar que isso aconteça, podemos utilizar de outras técnicas, como por exemplo, baixar o mapa com o aplicativo em execução e chavear a diretiva map para carregar online ou offline, o que acha? Isso poderá ser pauta para um novo artigo.

Por hoje é só. Deixe seu comentário, sendo ele crítica ou sugestão.