Scriptcaser

Integrando a Biblioteca Leaflet + Open Street Maps ao Scriptcase

Olá pessoal, este é um de uma série de posts sobre mapas, só que dessa vez, ao invés de usar a API Google Maps, iremos utilizar o biblioteca Leaflet que usa o Open Street Maps (OSM) + Mapbox como serviço de mapas.

Sabemos que o Google Maps tem suas vantagens, exemplo são as diretivas places, geocode e routes, como mostramos em post anterior.

No entanto, quando trabalhamos com mapas analíticos, o Letflet + OSM junto com os vários plugins desenvolvidos pela comunidade, obtemos melhores resultados, além de não possuir cotas de impressão em tela como o Google Maps.

Eis que me surgiu a necessidade através de um cliente, fazer um mapa para que ele disponibilizasse para os seus associados onde estão os estabelecimentos conveniados à empresa.

Então! Mão na massa, vou comentar parte por parte do código abaixo.

?>
<html lang="en-US" xmlns="http://www.w3.org/1999/xhtml">
<head>
   <title>Insira aqui o título do seu mapa</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
   <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <!-- Css -->
   <link rel="stylesheet" href="<?php echo sc_url_library("prj", "assets", "vendor/font-awesome/4.7.0/css/font-awesome.min.css"); ?>"/>
   <link rel="stylesheet" href="<?php echo sc_url_library("prj", "assets", "vendor/bootstrap/3.3.7/css/bootstrap.min.css"); ?>"/>
   <link rel="stylesheet" href="<?php echo sc_url_library("prj", "assets", "vendor/leaflet/1.5.1/leaflet.css"); ?>"/>
   <link rel="stylesheet" href="<?php echo sc_url_library("prj", "assets", "css/osm-custom.css"); ?>"/>
</head>
<body> 
    <header class="topo">
        <div class="logo">
	    <h3>
	        <span>Total de registros: <span id="counter"></span></span>
	    </h3>
        </div>
        <div class="options">
	    <h3>
	        <span>Opções Avançadas</span>
	        <a id="settings" href="#"><i class="fa fa-cog"></i></a>
	    </h3>
        </div>
     </header>
     <div id="map" class="container"></div>
<!-- Javascript -->
<script src="<?php echo sc_url_library("prj", "assets", "vendor/jquery/3.4.1/jquery-3.4.1.min.js"); ?>"></script>
<script src="<?php echo sc_url_library("prj", "assets", "vendor/leaflet/1.5.1/leaflet.js"); ?>"></script>
<script src="<?php echo sc_url_library("prj", "assets", "vendor/bootstrap/3.3.7/js/bootstrap.min.js"); ?>"></script> 
<script>
    // Setting global variables
    var map,
        counter = document.querySelector('#counter'),
	lbrace = String.fromCharCode(123),
	rbrace = String.fromCharCode(125);
		  
    // Get dataset
    const url_conv = '<?php echo sc_url_library("prj", "api", "conveniado/all.php"); ?>';
		  
    function initMap() {

    // Set url (scriptcase hack)
    let url_tile = 'https://' + lbrace + 's' + rbrace + '.tile.openstreetmap.org/' + lbrace + 'z' + rbrace + '/' + lbrace + 'x' + rbrace + '/' + lbrace + 'y' + rbrace +'.png?access_token=digite_aqui_o_seu_token_map';
				
    // Instanciating map
    map = new L.Map("map", {
        center: new L.LatLng(-12.973040, -38.502304),
        zoom: 13
    });
				
    // Creating the tilelayer
    let tile_layer = new L.TileLayer(url_tile, {
        attribution: 'Map data © <a 
        href="http://openstreetmap.org">OpenStreetMap</a> contributors',
	maxZoom: 18,
        id: 'mapbox.streets',
	accessToken: 'pk.eyJ1Ijoic2NyaXB0Y2FzZXIiLCJhIjoiY2p3NGEyeDUzMG1tcTQwb3doOTF6YWtiZiJ9.9F5I09pHGPozZZPFO3fW6g'
    });
				
    tile_layer.addTo(map);
				
    // Get markers
    fetch(url_conv)
        .then(res => res.json())
	.then(data => {
	    counter.innerHTML = Object.keys(data).length;
	    data.map(setMarkers);
    })
    .catch(function(error) {
        console.log(error);
    });				
}
		  
function setMarkers(pos) {
    let custom_icon = L.icon({
        iconUrl: 'data:image/png;base64,' + pos.logo,
	iconSize: [75, 75],
	iconAnchor: [0, 75],
	popupAnchor: [38, -75]
    });
				
    L.marker([pos.lat, pos.lng], {icon: custom_icon})
        .addTo(map)
        .bindPopup('<div class="row"><div class="col-xs-12 col-md-4"><img class="img-responsive" src="data:image/png;base64,' + pos.logo + '"></div><div class="col-xs-12 col-md-8"><div class="row"><div class="col-xs-12 col-md-12"><h3>' + pos.nome + '</h3></div><div class="col-xs-12 col-md-12"><strong>Desconto:</strong> ' + pos.desconto + '</div><div class="col-xs-12 col-md-12">' + pos.descricao + '</div><div class="col-xs-12 col-md-12"><strong>Segmento:</strong> ' + pos.segmento + '</div><div class="col-xs-12 col-md-12"><strong>Falar com:</strong> ' + pos.responsavel + '</div><div class="col-xs-12 col-md-12"><strong>Telefone:</strong> ' + pos.tel  + '</div><div class="col-xs-12 col-md-12"><strong>e-mail:</strong> ' + pos.email + '</div><div class="col-xs-12 col-md-12"><strong>Endereço:</strong> ' + pos.logradouro + ', ' + pos.numero + ', ' + pos.bairro + ', ' + pos.cidade + '/' + pos.uf + ', CEP: ' + pos.cep + '</div></div></div><div>', {
            minWidth: 500	
	});
}
		  
$(document).ready(function() {
    initMap();
});
</script>
</body>
</html> 
<?php

A primeira coisa a se fazer é criar uma conta no Mapbox. Para quem não conhece o Mapbox, ele é uma ferramenta de criação de mapas personalizados usando a base cartográfica do Open Street Maps.

Uma vez criada a sua conta vá em tokens e você verá gerado o Default Public Token, que é o token padrão que usa o layout do Open Street Maps sem nenhuma modificação. Caso deseje criar mapas personalizados veja a documentação.

Agora iremos criar o boilerplate do mapa e o seu css. No meu caso optei por utilizar uma barra superior onde tem o contador de registros e um call to action para o filtro personalizado.

<header class="topo">
    <div class="logo">
        <h3>
	    <span>Total de registros: <span id="counter"></span></span>
	</h3>
    </div>
    <div class="options">
        <h3>
	    <span>Opções Avançadas</span>
	    <a id="settings" href="#"><i class="fa fa-cog"></i></a>
	</h3>
    </div>
</header>
<div id="map" class="container"></div>
@import url('https://fonts.googleapis.com/css?family=Open+Sans|Roboto');
body {
	margin: 0 auto;
	padding: 0;
	font-family: 'Open Sans', 'Roboto', sans-serif;
	font-size: 0.75em;
	overflow: hidden;
	color: #000;
}

.topo {
	width: 100%;
	border-bottom: 0.125em solid #000;
	height: 30px;
	background-color: #15426f;
	color: #fff;
}

.topo h3 {
	padding: 8px 10px;
	margin: 0 auto;
	font-size: 1.5em;
	font-weight: bold;
}

.logo {
	float: left;
}

.options {
	float: right;
	color: #fff;
	font-weight: bold;
}

.options img {
	width: 5em;
	padding: 4px 8px;
}

.options i {
	color: #fff;
	text-decoration: none;
}

.options i:hover {
	color: #e9b91c;
}

.container {
	position: absolute;
	width: 100%;
	height: 100%;
}

.leaflet-container {
    font: 14px/0.8em "Open Sans", "Roboto", Helvetica, sans-serif;
}

Por fim, vamos ao script que renderizar todos os pontos no mapa previamente cadastrados no banco de dados. Caso ainda não saiba como integrar o Google Maps em um form do scriptcase, veja o meu post sobre isso.

 // Setting global variables
    var map,
        counter = document.querySelector('#counter'),
	lbrace = String.fromCharCode(123),
	rbrace = String.fromCharCode(125);

Primeiro passo do script é definir as variáveis globais.

Há um problema em usar os brackets {} dentro da scriptcase, pois ele reconhece como se fosse uma variável de sistema. Para contornar essa situação criei duas variáveis globais, lbrace e rbrace, que recebem o valor em ASCII referente aos caracteres { e }, respectivamente.

Armazene a url da sua API REST responsável por fornecer os pontos em uma const. Caso ainda não saiba como desenvolver uma API REST dentro do scriptcase, só clicar aqui.

 // Get dataset
    const url_conv = '<?php echo sc_url_library("prj", "api", "conveniado/all.php"); ?>';

Vamos para a nossa função initMap

//  Set url (scriptcase hack)
    let url_tile = 'https://' + lbrace + 's' + rbrace + '.tile.openstreetmap.org/' + lbrace + 'z' + rbrace + '/' + lbrace + 'x' + rbrace + '/' + lbrace + 'y' + rbrace +'.png?access_token=digite_aqui_o_seu_token_map';
				
    // Instanciating map
    map = new L.Map("map", {
        center: new L.LatLng(-12.973040, -38.502304),
        zoom: 13
    });

Como expliquei anteriormente, as variáveis lbrace e rbrace fazem o papel do brackets {}. Se não houvesse esse problema no scriptcase a url seria passada normalmente e ficaria assim https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?access_token=digite_aqui_o_seu_token_mapbox

Crie um objeto do tipo do L.map e armazene na variável global map (Para saber mais consulte a documentação do leaflet.

Adicione o layout do mapa desejado ao objeto map.

// Creating the tilelayer
    let tile_layer = new L.TileLayer(url_tile, {
        attribution: 'Map data © <a 
        href="http://openstreetmap.org">OpenStreetMap</a> contributors',
	maxZoom: 18,
        id: 'mapbox.streets',
	accessToken: digite_aqui_o_seu_token_mapbox
    });
				
    tile_layer.addTo(map);

Execute o fetch, armazene os pontos e atualize o contador.

// Get markers
    fetch(url_conv)
        .then(res => res.json())
	.then(data => {
	    counter.innerHTML = Object.keys(data).length;
	    data.map(setMarkers);
    })
    .catch(function(error) {
        console.log(error);
    });

Perceba que existe a função setMarkers. Ela é reponsável para renderizar os marcadores no mapa.

function setMarkers(pos) {
    let custom_icon = L.icon({
        iconUrl: 'data:image/png;base64,' + pos.logo,
	iconSize: [75, 75],
	iconAnchor: [0, 75],
	popupAnchor: [38, -75]
    });
				
    L.marker([pos.lat, pos.lng], {icon: custom_icon})
        .addTo(map)
        .bindPopup('<div class="row"><div class="col-xs-12 col-md-4"><img class="img-responsive" src="data:image/png;base64,' + pos.logo + '"></div><div class="col-xs-12 col-md-8"><div class="row"><div class="col-xs-12 col-md-12"><h3>' + pos.nome + '</h3></div><div class="col-xs-12 col-md-12"><strong>Desconto:</strong> ' + pos.desconto + '</div><div class="col-xs-12 col-md-12">' + pos.descricao + '</div><div class="col-xs-12 col-md-12"><strong>Segmento:</strong> ' + pos.segmento + '</div><div class="col-xs-12 col-md-12"><strong>Falar com:</strong> ' + pos.responsavel + '</div><div class="col-xs-12 col-md-12"><strong>Telefone:</strong> ' + pos.tel  + '</div><div class="col-xs-12 col-md-12"><strong>e-mail:</strong> ' + pos.email + '</div><div class="col-xs-12 col-md-12"><strong>Endereço:</strong> ' + pos.logradouro + ', ' + pos.numero + ', ' + pos.bairro + ', ' + pos.cidade + '/' + pos.uf + ', CEP: ' + pos.cep + '</div></div></div><div>', {
            minWidth: 500	
	});
}

No caso do meu mapa, eu utilizei ícones personalizados armazenados no meu banco de dados, um para cada ponto, a logo das empresas, criando eles usando o objeto tipo L.icon.

E para inserir o pontos no mapa é utilizado o objeto do tipo L.maker, onde é passado as coordenadas, o ícone personalizado (opcional) e anexado um popup para cada marcador com as informações disponibilidades na API.

Se você fez tudo direito, o sua aplicação de mapa ficará parecida com a minha, conforme GIF animado abaixo.

Gostou? Então comente, se inscreva na minha newsletter e saiba assim que novos posts saírem do forno.

scriptcaser

8 comments

    • Isso! O Google quando você chega a um limite a coisa é paga e não é barato. É uma forma de contornar, só usa o Google Maps quando não tiver jeito.

  • Ví código referente a API, facilmente encontrado na web, mas não vi referência alguma de utilização no scriptcase na prática.
    Vai sugerir usar em aplicações do tipo blank?

    • Sim, aplicações tipo Blank, mas também poder integrada em um form ou control, veja meu post sobre integrar o Google Maps a um form para ter uma visão clara.

      O diferencial é o pulo do gato, quebrei cabeça com as brackets {} que o scriptcase reconhece como variável de sistema mesmo entre aspas simples, que tem na url do Open Street Maps.

      Meu papel é encurtar o seu caminho, fazer com que você troque horas de dor de cabeça por alguns minutos de leitura. E para fazer fetch dos dados, veja o post da construção da API nas bibliotecas externas do scriptcase.

Newsletter

Se inscreva na nossa newsletter
Se increva hoje na nossa lista de e-mail para receber atualizações, tutoriais e ofertas especiais!

Respeitarei sua privacidade. Seu e-mail nunca será compartilhado.