VueJS y maps, with Leaflet (part 2)

Posted by in HTML5

In part 1 about VueJs and maps with Leaflet we showed how to make a coordinate change using a urm2lat.js library. In today’s post, we will use what we have learned for coordinate selection and include a map with a marker on that coordinates, also showing a popup with its label, that of course, we can change. Let’s start!

The HTML code

Unlike what you saw in part 1, the code is exactly the same, but we will incorporate two CSS-style properties, which are necessary for the map to display correctly.

The final HTML code looks like this:

<!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>

In addition to access to the libraries of VueJS, and Leaflet, we need Vue-Leaflet and of course, access to our code in app.js.

If you want to complicate the exercise further, you can use some other libraries or front-end framework such as Vuetify or Quasar to give it a new visualization look.

Personally, since I started with VueJS, I have accompanied it with Vuetify, which is a component framework integrated with Material Design , and to which I will dedicate a new entry to this framework, here at manejandodatos.es. On the contrary, I have not tried Quasar beyond a small example. In my opinon, before I learned two bad things, I decided to learn Vuetify thoroughly. but, let’s continue with the task of today’s entry.

App.js

Let’s focus now on the engine of the example, in app.js, where we are going to differentiate 3 parts. On the one hand, the constants necessary for Leaflet to work correctly with VueJS, the second is a simple-map component, and the last, the one that develops the application.

The first part has this assigments:

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: '© <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: © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>, <a href="http://viewfinderpanoramas.org">SRTM</a> | Map style: © <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)',
	}
];

The first one is neede in order to use VueSelect, as you already know in the previous example of vue with HighchartsJS. The rest of the constants are neccesary for creating several components extracted from Vue2Leaflet, and the last one is the image providers, where I put two options: OpenStreetMap y OpenTopoMap. There are more providers, but these are the basic ones.

The mapa-simple component

The second part is the mapa-simple, component, whose code is:

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>
`
})

The component receives as property the mapdata variable, which is prepared in the third part of the Javascript code, in the main application, and which basically contains the coordinates, the label, and other properties required by Leaflet.

The internal data of the component are nothing but default values, which can be determined with the mapdata values, and for this it is necessary to create a series of computed properties.

You can include a popup, or a tooltip associated with the marker, included in the p-map, which is the component where the map is painted. It is very important that this component includes the map2 style, already defined in the HTML to allow it to display correctly.

El VueJS code

The Javascript code of the engine of the example is:

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>
	`
})

In addition to the coordinates already shown, another input has been included to label the point to be displayed.

One of the events that fires the map-simple component is get_coordenadas, what it does is retrieve the coordinates that have been selected on the map and update the data of the point to be located. In this way, we can obtain the coordinates of the point that you search on the map.

vueJS y Leaflet

Conclusion

Perhaps the example is simple and very basic, but I think it has enough value to be able to include map representation in your applications with vueJS, or at least, I have done so just as I have told you. Surely there will be other better ways to do it, but this is the one that I have used, and it works for me!

I am waiting for your comments, also you can write me an email, and if you can, help me to maintain this blog with a donation.