VueJS y mapas, con Leaflet (parte 2)

Posted by in HTML5

En la parte 1 sobre VueJs y mapas con Leaflet vimos cómo realizar un cambio de coordenadas utilizando una libreria urm2lat.js. En la entrada de hoy, utilizaremos lo ya aprendido para la selección de coordenadas e incluiremos el mapa donde se mostrará un marcador con el punto seleccionado, y que podemos ir cambiando. Empezamos!

El código HTML

A diferencia de lo visto en la parte 1, el código es exáctamente el mismo, pero vamos a incorporar dos propiedades de estilo CSS, que son necesarias para que el mapa se muestre correctamente.

El código final queda de la siguiente forma:

<!DOCTYPE html>
<html lang="es">
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
	<title>Manejando Datos - Usando Leaflet</title>
	<link rel="stylesheet" href="https://unpkg.com/vue-select@latest/dist/vue-select.css"  crossorigin="anonymous">
<style>
	.title-row {
	  display: flex;
	  flex-direction: column;
	  align-items: center;
	  background: #eee;
	  padding: 20px;
	}
	.map2 { 
		padding: 10px;
		border: 2px solid #7f7f84;
		width: 100% !important;
		height: 500px !important;
		position:  relative !important;
		margin-top: 10px;
	}
</style>
</head>
<body>

	<div id="app"></div>

<script src="https://unpkg.com/vue@2.6.10/dist/vue.js" crossorigin="anonymous"></script>
<script src="https://unpkg.com/vue-select@latest" crossorigin="anonymous"></script>
<script src="urm2lat.js"></script>

<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
  integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
  crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
  integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
  crossorigin=""></script>
  
 <script src="https://unpkg.com/vue2-leaflet@2.1.1/dist/vue2-leaflet.min.js"></script>
  
<script type="text/javascript" src="app.js"></script>

</body>
</html>

Además del acceso a las librerias de VueJS, y Leaflet, necesitamos Vue-Leaflet y el acceso a nuestro código en app.js.

Si quieres complicar más el ejercicio, puedes utilizar alguna otra librería o framework front-end como Vuetify o Quasar para darle una nueva visualización.

Yo, personalmente, desde que empecé con VueJS, lo he acompañado de Vuetify, que es un framework de componentes integrados con Material Design, y al que dedicaré una nueva entrada a este framework. Por contra, Quasar no lo he probado, más allá de un pequeño ejemplo. Y antes de mal aprender dos cosas, decidí aprender Vuetify a fondo. Seguimos con el cometido de la entrada de hoy.

App.js

Nos centramos ahora en el motor del ejemplo, en app.js, donde vamos a diferenciar 3 partes. Por un lado, las constantes necesarias para que funcione correctamente Leaflet con VueJS, la segunda es un componente mapa-simple, y la última, la que desarrolla la aplicación.

La primera parte tiene preparadas las siguientes constantes:

Vue.component('v-select', VueSelect.VueSelect);

const { LMap, LTileLayer, LMarker, LTooltip, LPopup } = Vue2Leaflet;

const v_map = Vue.component('p-map', LMap); // Vue2Leaflet.Map
const v_tilelayer = Vue.component('p-tilelayer', LTileLayer ); // Vue2Leaflet.TileLayer
const v_marker = Vue.component('p-marker', LMarker); // Vue2Leaflet.Marker
const v_tooltip2 = Vue.component('p-tooltip2', LTooltip); // Vue2Leaflet.LTooltip
const v_popup = Vue.component('p-popup', LPopup); // Vue2Leaflet.LPopup

const BtileProviders = [
	{
		name: 'OpenStreetMap',
		visible: true,
		url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
		attribution: '&copy; <a target="_blank" href="http://osm.org/copyright">OpenStreetMap</a> contributors',
	},
	{
		name: 'OpenTopoMap',
		visible: false,
		url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
		attribution: 'Map data: &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: &copy; <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)',
	}
];

La primera, ya la vimos en el anterior ejemplo con highcharts, para utilizar VueSelect. El resto se corresponde con la creación de distintos componentes extraidos de Vue2Leaflet, y el proveedor de imágenes, donde se han puesto 2 opciones: OpenStreetMap y OpenTopoMap. Hay más, pero estos son los más básicos.

El componente mapa-simple

La segunda parte es el componente mapa-simple, cuyo código es:

const show_mapas = Vue.component('mapa-simple', {
	props: ['mapdata'],
	data() {
		return {
			// Map Options
			zoom_def: 13,
			markers: [],
			tileLayer: null,
			layers: [],
			selectedTileSet: BtileProviders[0], 
			tileSets: BtileProviders,
			mapOptions_default: { zoomControl: false, attributionControl: false, zoomSnap: true },
			minZoom_def: 1,
			maxZoom_def: 20,
			show_mapsets_default: true,
		}
	},
	computed: {
		mapOptions() { return (this.mapdata.mapOptions) ? this.mapdata.mapOptions : this.mapOptions_default },
		minZoom() { return (this.mapdata.minZoom) ? this.mapdata.minZoom : this.minZoom_def },
		maxZoom() { return (this.mapdata.maxZoom) ? this.mapdata.maxZoom : this.maxZoom_def },
		zoom() { return (this.mapdata.zoom) ? this.mapdata.zoom : this.zoom_def },
		
		coordenadas() {return `${this.mapdata.coordx} - ${this.mapdata.coordy}`},
		// Maps
		coords() {
			return UTMXYToLatLon2 (this.mapdata.coordx, this.mapdata.coordy); // UTMXYToLatLon (x, y, zone, southhemi, latlon);
		},
		center() { return L.latLng(this.coords)},
		label() { return this.mapdata.label },
	},
	methods: {
		get_coordenadas(e) {
			let coord = e.latlng;
			let lat = coord.lat;
			let lng = coord.lng;
			// console.log("You clicked the map at latitude: " + lat + " and longitude: " + lng);
			let xy = LatLonToUTMXY2(lat, lng, 30);
			// console.log("You clicked the map at X: " + xy[0] + " and Y: " + xy[1]);
			this.$emit('get_coordenadas', xy);
		}, 
	},
	template: `
<div>
	<p-map id="map" ref="map" 
		:zoom="zoom" 
		:center="center"
		:min-zoom="minZoom"
		:max-zoom="maxZoom"
		:options="mapOptions"
		@click="get_coordenadas"
		class="map2">
		<p-tilelayer ref="tile" :url="selectedTileSet.url"></p-tilelayer>
		<p-marker :lat-lng="coords">
			<p-popup :content="label" v-if="mapdata.with_popup"></p-popup>
			<p-tooltip2 :content="label" v-if="mapdata.with_tooltip"></p-tooltip2>
		</p-marker>
	</p-map>	
</div>
`
})

El componente recibe como propiedad la variable mapdata, que se prepara en la tercera parte del código Javascript, en la aplicación principal, y que básicamente contiene las coordenadas, la etiqueta, y otras propiedades requeridas por Leaflet.

Los datos internos del componente no son sino valores por defecto, que pueden podificarse con los valores de mapdata, y para ello es necesario crear una serie de propiedades computadas.

Se puede incluir un popup, o un tooltip asociada al marcador, incluido dentro del p-map, que es el componente donde se pinta el mapa. Es importante destacar que este componente incluye el estilo map2, ya definido en el HTML para permitir que se muestre correctamente.

El código VueJS

El código Javascript que es el motor del ejemplo es:

var app = new Vue({
	el: "#app",
	data() {
		return {
			coordx: 125000,
			coordy: 4140000,
			label: 'Punto de ejemplo',
		}
	},
	computed: {
		coordenadas() {return `${this.coordx} - ${this.coordy}`},
		coords() {
			// UTMXYToLatLon (x, y, zone, southhemi, latlon);
			return UTMXYToLatLon2 (this.coordx, this.coordy);
		},
		mapdata() {
			return {
				coordx: this.coordx,
				coordy: this.coordy,
				with_popup: false,
				with_tooltip: true,
				// selectedTileSet: this.selectedTileSet, // Solo sirve si show_mapsets es false
				// minZoom: 1, // opcional
				// maxZoom: 20, // opcional
				// mapOptions: , // opcional
				map_class: 'map2', // opcional
				label: this.label,
			}
		},
	},
	methods: {
		get_coordenadas(info) {
			console.log(info)
			this.coordx = info[0];
			this.coordy = info[1];
		},
	},
	template:
	`
	<div>
	<div class="title-row">
			<span>CoordX:</span><input type="text" v-model="coordx">
			<span>CoordY:</span><input type="text" v-model="coordy">
			<span>Label:</span><input type="text" v-model="label">
			Geograficas: {{ coords }}
		</div>
		<mapa-simple :mapdata="mapdata" @get_coordenadas=get_coordenadas></mapa-simple>
	</div>
	`
})

Además de lo ya mostrado de las coordenadas, se ha incluido otra entrada para poner etiqueta al punto a mostrar.

Uno de los eventos que lanza mapa-simple es get_coordenadas, que lo que hace es recuperar las coordenadas que han sido seleccionadas en el mapa y actualizar los datos del punto a ubicar. De esta forma, podemos obtener las coordenadas del punto buscado.

vueJS y Leaflet

Conclusión

Quizás el ejemplo te resultará sencillo y muy básico, pero creo que tiene suficiente valor cómo para ser capaz de incluri representación de mapas en tus aplicaciones con vueJS, o al menos, yo lo he hecho así tal y como te lo he contado. Seguramente habrá otras formas mejores de hacerlo, pero ésta es la que yo he utilizado, y me funciona!

Espero vuestros comentarios, y si podeis, un poco de ayuda al blog con tu donación.