Era da un po’ che non giocavo con le mappe. L’ultimo tentativo risale oramai a qualche anno fa. Questa settimana, complice anche alcuni messaggi diretti su Twitter, ho ripreso in mano il mio vecchio codice. E ho creato due template.

leaflet animation

Il primo usa Leaflet, una libreria abbastanza famosa per inserire mappe in una pagina html. Ma come possiamo integrare Leaflet in Construct 3? In modo tutto sommato semplice.

Per prima cosa ci servono 2 file:

Carichiamo subito questi file:

Contestualmente creiamo anche file CSS per gestire la posizione e lo stile della mappa. Nel mio esempio è semplicemente:

#mapid {
	position: absolute;
	left:304px;
	top:16px;
	right:16px;
	bottom:16px;
	border-style: solid;
	border-color: rgb(91, 31, 31);
	border-radius: 8px;
	opacity: 80%;
}

Dopo di ché possiamo creare un DIV (con ID mapid) dove inserire la nostra mappa:

const myDiv = document.createElement("div");
myDiv.id = 'mapid';
document.body.appendChild(myDiv);
myDiv.style.position = "absolute";

Da qui in avanti è possibile seguire la Quick Start Guide di Leaflet.

Impostiamo la posizione e lo zoom della mappa con:

let mymap = L.map('mapid').setView([51.505, -0.09], 13);

Poi aggiungiamo un livello alla mappa (in questo caso per mostrare OpenStreetMap)

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(mymap);

Infine, se ci serve, disegniamo un cerchio e un triangolo

let circle = L.circle([51.508, -0.11], {
    color: 'red',
    fillColor: '#f03',
    fillOpacity: 0.5,
    radius: 500
}).addTo(mymap);

let polygon = L.polygon([
    [51.509, -0.08],
    [51.503, -0.06],
    [51.51, -0.047]
]).addTo(mymap);

E, visto che ci siamo, aggiungiamo anche un popup:

circle.bindPopup("I am a circle.");
polygon.bindPopup("I am a polygon.");

Tutto molto bello, molto semplice e molto veloce. Però. Però c’è un problema. Non so per quale motivo ma Leaflet non supporta i moduli ES6 di Javascript. Il che vuol dire che siamo costretti a usare come impostazione Scripts type: Classic.

Il che non sarebbe nemmeno un problema adesso. Il problema sorgerà in futuro quando la Classic Mode non verrà più supportata (vedi qui: Upgrading projects from classic scripts to modules)

The old classic mode is deprecated and will soon be removed, so you should update your code soon.

Per fortuna l’Internet è venuto in soccorso. Grazie ad alcuni suggerimenti su Twitter (grazie @laurentgontier e @jmviglino) ho provato OpenLayers:

openlayers animation

Il procedimento per integrare le mappe di OpenLayers in Construct 3 è molto simile a quello con Leaflet. Ci sono però alcune piccole differenze, a partire dai file da importare:

Ovviamente carichiamo subito i file

e creiamo il div per la mappa. Da qui in avanti possiamo seguire la guida OpenLayers - Vector Layer.

Possiamo creare una mappa OSM in questo modo, centrandola su Milano e impostando uno zoom 12.

const mymap = new ol.Map({
	target: 'mapid',
	layers: [
		new ol.layer.Tile({
			source: new ol.source.OSM()
		})
	],
		view: new ol.View({
		center: ol.proj.fromLonLat([9.1892, 45.4641]),
		zoom: 12
	})
});

Ma c’è un altro aspetto interessante, di OpenLayers: la facilità con cui è possibile importare mappe basate su TileJSON e GeoJSON.

Nel primo caso è sufficiente inserire un livello in questo modo:

new ol.layer.Tile({
	source: new ol.source.TileJSON({
		url: 'https://a.tiles.mapbox.com/v3/aj.1x1-degrees.json?secure=1',
		crossOrigin: 'anonymous',
	}),
}),

L’utilizzo di GeoJSON è un attimo più lungo perché occorre stabilire anche lo stile da applicare. Ma prima di tutto dobbiamo collegare il file GeoJSON

const sourceGeoJSON = await runtime.assets.getProjectFileUrl("ds964_nil_wm.geojson");

In questo esempio uso un file importato nel progetto C3 ma volendo possiamo usare anche un file online.

Dopo aver ottenuto i dati geografici possiamo creare un livello nella mappa:

new ol.layer.Vector({
	source: new ol.source.Vector({
		url: sourceGeoJSON,
		format: new ol.format.GeoJSON(),
	}),
	style: function (feature) {
		style.getText().setText(feature.get('NIL'));
		return style;
	},
})

Lo stile viene ricavato dalla funzione:

const style = new ol.style.Style({
  fill: new ol.style.Fill({
    color: 'rgba(255, 255, 255, 0.6)',
  }),
  stroke: new ol.style.Stroke({
    color: '#319FD3',
    width: 1,
  }),
  text: new ol.style.Text({
    font: '10px "SourceCodePro-Regular",Calibri,sans-serif',
    fill: new ol.style.Fill({
      color: '#000',
    }),
    stroke: new ol.style.Stroke({
      color: '#fff',
      width: 1,
    }),
  }),
});

Un’altra cosa interessante è la possibilità di poter gestire eventi come il click del mouse su una particolare zona:


const displayFeatureInfo = function (pixel) {
  const feature = mymap.forEachFeatureAtPixel(pixel, function (feature) {
    return feature;
  });

  const info = runtime.objects.NIL_MILAN.getFirstInstance();
  if (feature) {
    info.text = `${feature.get('ID_NIL')} - ${feature.get('NIL')}`;
  } else {
    info.text = '';
  }

  if (feature !== highlight) {
    if (highlight) {
      featureOverlay.getSource().removeFeature(highlight);
    }
    if (feature) {
      featureOverlay.getSource().addFeature(feature);
    }
    highlight = feature;
  }
};

mymap.on('click', function (evt) {
  displayFeatureInfo(evt.pixel);
});

Penso ci siano le basi per poter sperimentare un pochetto su questo metodo per integrare mappe in Construct 3. Ma per il momento mi fermo qui. Ricordo solamente che, come sempre, il codice di questo progetto è disponibile su GitHub: