giovedì 23 gennaio 2014

COME CREARE MAPPE NAVIGABILI E VIVERE FELICI (PT. 2)

Riprendiamo da dove ci eravamo lasciati: ora che abbiamo una bella mappa pulita, come piazzare dei segnalini dei luoghi di interesse?
Ancora una volta Leaflet ci viene in aiuto. Aggiungere un segnalino (marker) con relativa nuvoletta di testo a piacere (popup) è facile, basta usare questa sintassi:

L.marker([Lat, Lon]).bindPopup('Testo nel popup').openPopup().addTo(map);
Con questo semplice comando piazzato prima della fine del file html visto nel post precedente, è possibile creare il segnalino desiderato, a patto di sapere dove piazzarlo (già, perché non vi sarà sfuggito che Lat e Lon sono le coordinate... ricordate che nella nostra mappa l'angolo in alto a sinistra ha coordinate Lat 90°, Lon -180°, quello in basso a destra Lat -90°, Lon 180°?)
Ora, trovare le coordinate giuste procedendo per tentativi ogni volta che si deve aggiungere un marker può risultare... fastidioso.
Per questo in fase di costruzione si possono aggiungere le seguenti righe:
map.on('click', function(e) {
     alert(e.latlng);
});
Questi comandi fanno sì che cliccando su un punto qualsiasi della mappa compaia una finestra popup con le coordinate del punto dove sì è cliccato. Basta copiare il set di coordinate e sostituirle alle variabili Lat e Lon nella riga del comando L.marker ed il gioco e fatto. Non dovrete più passare intere giornate cercando di azzeccare i valori giusti di coordinate!
Se avete fatto tutto correttamente dovreste ottenere un risultato così:


Nella versione finale della vostra mappa, quella che consulteranno i vostri PG o che metterete sul web, non è bello che cliccando qua e là compaia continuamente la finestra delle coordinate. Potete inibire l'esecuzione delle tre linee di codice semplicemente aggiungendo all'inizio della riga due barre, così:
// map.on('click', function(e) {
//     alert(e.latlng);
//});
in modo da poter ripristinare la funzione a piacimento quando dovrete aggiungere nuovi segnalini di cui vi servono le coordinate.

Che schifo quei marker blu tutti uguali! Io voglio segnalini diversi per indicare cose diverse!
Niente paura! Tutto è possibile!
In questo caso prima di tutto è bene stabilire il set di icone che si vuole utilizzare. Io ad esempio mi sono creato queste:



Siete liberi di saccheggiarle. L'importante in questa fase è sapere quali sono le dimensioni in pixel delle vostre icone. Ad esempio le mie sono 36x36 pixel.
A questo punto dobbiamo scrivere un po' di codice per dire al nostro script di crearsi delle variabili a cui associare ogni icona.
Il comando è:
 var PlaceIcon = L.Icon.extend({
     options: {
      iconSize:     [36, 36], 
      iconAnchor:   [18, 18],
      popupAnchor:  [0, -21]
     }
 });
dove i numeri [x, y] in rosso dopo iconSize indicano le dimensioni dell'icona (ricordate? Le mie icone sono 36x36 pixel); iconAnchor indica quale punto dell'icona corrisponderà alle coordinate del marker. Nel mio caso l'icona è quadrata per cui mi sta bene che il centro dell'immagine corrisponda alla posizione del marker (se fosse una freccia invece dovrei fare in modo che le coordinate [x, y] della punta corrispondano alla posizione del marker); infine popupAnchor indica la posizione [delta_x, delta_y] relativa rispetto ad iconAnchor dove apparira la nuvoletta di popup con il testo. Nel mio caso il valore [0, -21] significa che la nuvoletta apparirà centrata rispetto all'icona (delta_x = 0), 3 pixel sopra il bordo superiore (y + delta_y = 18 - 21 = -3). Ricordate che le coordinate y cartesiane quando si parla di pixel crescono dall'alto verso il basso dello schermo!

A questo punto assegnamo un nome ad ogni icona con questa sintassi:
var NomeIcona = new PlaceIcon({iconUrl: 'file:///C:/percorso_icona/nome_file_icona.jpg'})
Le parti in rosso sono ovviamente quelle da modificare a seconda di dove avete salvato (in locale o sul web) le icone. Ad esempio questo è un esempio di una mia assegnazione:
var cittaIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/citta.png'});
A questo punto per assegnare una icona specifica ad un marker basta modificare leggermente il comando per piazzare i segnalini (che ovviamente ora dovrà trovarsi dopo la sezione di assegnazione dei nomi alle icone):
L.marker([Lat, Lon], {icon: NomeIcona}).bindPopup('Testo').openPopup().addTo(map);
Bingo! Ecco che anche i segnalini personalizzati sono in mappa. Basta aggiungerne uno nel file script per ogni punto di interesse e il gioco è fatto! Guardate nel mio esempio qua sotto quanti ne ho messi! I più attenti noteranno che alcuni luoghi hanno anche il link alle pagine del diario della nuova campagna di Dungeon World su Obsidian Portal: ovviamente nel testo dei popup potete usare codice html per linkare pagine, o per scrivere colorato, o in grassetto, ecc.
Per maggiore chiarezza riporto qui di seguito il testo completo del html della mappa, che include tutte i comandi visti in questo post e nel precedente. Se non riuscite a vederlo bene perché l'impaginazione taglia le righe, selezionate tutto il testo nel riquadro e fate un copia-incolla su un file di testo (se lo salvate come html e lo lanciate nel vostro browser vedrete inoltre il risultato finale fullscreen!).
<html>
<head>
    <link href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.css" rel="stylesheet"></link>
</head>


<body>
    <div id="map"></div>
    <style>
      body {
      padding: 0;
      margin: 0;
      }
      html, body, #map {
      height: 100%;
      }
    </style>
<script src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"></script>
        <script>


 var map = L.map('map').setView([0, 0], 2);


 L.tileLayer('http://sussurrodieven.altervista.org/wmap8192/{z}/{x}/{y}.png', {
            minZoom: 0,
            maxZoom: 5,
            attribution: 'Ale',
            tms: true,
            continuousWorld: true
        }).addTo(map);


 var PlaceIcon = L.Icon.extend({
     options: {
      iconSize:     [36, 36], 
      iconAnchor:   [18, 18], 
      popupAnchor:  [0, -21] 
     }
 });
 

 var cavernaIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/caverna.png'}),
         cittaIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/citta.png'}),
     cittadinaIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/cittadina.png'}),
     fortezzaIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/fortezza.png'}),
     minieraIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/miniera.png'}),
     naufragioIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/naufragio.png'}),
     naveIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/nave.png'}),
     tempioIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/tempio.png'}),
     villaggioIcon = new PlaceIcon({iconUrl: 'http://sussurrodieven.altervista.org/markers/villaggio.png'});


 L.marker([74.84493, -10.2832], {icon: cittaIcon}).bindPopup('Auberon').openPopup().addTo(map);
 L.marker([-65.51296, 61.69922], {icon: cittaIcon}).bindPopup('Bakaresh').openPopup().addTo(map);
 L.marker([79.78116, 45.26367], {icon: cittaIcon}).bindPopup('Bled').openPopup().addTo(map);
 L.marker([-76.72022, -59.85352], {icon: cittaIcon}).bindPopup('Boca Chica').openPopup().addTo(map);
 L.marker([-75.36451, -36.5625], {icon: villaggioIcon}).bindPopup('Carvalho').openPopup().addTo(map);
 L.marker([-80.10347, -56.77734], {icon: cittaIcon}).bindPopup('Castellòn de la Plana').openPopup().addTo(map);
 L.marker([69.225, 29.61914], {icon: tempioIcon}).bindPopup('Cedria').openPopup().addTo(map);
 L.marker([84.12047, -75.32227], {icon: villaggioIcon}).bindPopup('<a href="http://dungeon-world-2.obsidianportal.com/wikis/codcliff">Codcliff</a>').openPopup().addTo(map);
 L.marker([78.64732, 4.39453], {icon: tempioIcon}).bindPopup('Il Cuore di Avorio').openPopup().addTo(map);
 L.marker([82.46031, -10.98633], {icon: cittaIcon}).bindPopup('Dagoth Ur').openPopup().addTo(map);
 L.marker([21.69827, -78.22266], {icon: cittaIcon}).bindPopup('Eardale').openPopup().addTo(map);
 L.marker([79.13826, 40.78125], {icon: cittaIcon}).bindPopup('Eboran').openPopup().addTo(map);
 L.marker([78.52557, -12.91992], {icon: cittadinaIcon}).bindPopup('Ebrud').openPopup().addTo(map);
 L.marker([50.51343, -61.17187], {icon: fortezzaIcon}).bindPopup('Forte Wariel').openPopup().addTo(map);
 L.marker([-70.67088, -31.9043], {icon: fortezzaIcon}).bindPopup('Guacanahabibes').openPopup().addTo(map);
 L.marker([-76.49631, -27.42187], {icon: cittaIcon}).bindPopup('Granada').openPopup().addTo(map);
 L.marker([32.24997, -57.48047], {icon: cittaIcon}).bindPopup('Kelvena').openPopup().addTo(map);
 L.marker([-79.78896, -33.35449], {icon: villaggioIcon}).bindPopup('La Mayor').openPopup().addTo(map);
 L.marker([-80.90067, -68.73047], {icon: naveIcon}).bindPopup('La Tortue').openPopup().addTo(map);
 L.marker([-75.36451, -14.67773], {icon: naveIcon}).bindPopup('Madera').openPopup().addTo(map);
 L.marker([-50.1769, 100.01953], {icon: cittaIcon}).bindPopup('Mefesh-Hua').openPopup().addTo(map);
 L.marker([75.9095, 3.07617], {icon: cittaIcon}).bindPopup('Millenia').openPopup().addTo(map);
 L.marker([80.58973, 28.91602], {icon: minieraIcon}).bindPopup('Muros').openPopup().addTo(map);
 L.marker([-58.53959, 73.47656], {icon: cittaIcon}).bindPopup('Naama-Sul').openPopup().addTo(map);
 L.marker([28.61346, -30.32227], {icon: cittaIcon}).bindPopup('Niryastare').openPopup().addTo(map);
 L.marker([-57.98481, 56.60156], {icon: cittaIcon}).bindPopup('Ouarzazade').openPopup().addTo(map);
 L.marker([-79.08846, -7.33887], {icon: villaggioIcon}).bindPopup('Pinar del Rio').openPopup().addTo(map);
 L.marker([-80.20118, -47.24121], {icon: cittadinaIcon}).bindPopup('Puerto del Principe').openPopup().addTo(map);
 L.marker([-69.56523, -53.96484], {icon: villaggioIcon}).bindPopup('Punta del Nazario').openPopup().addTo(map);
 L.marker([70.11048, -18.7207], {icon: tempioIcon}).bindPopup('Renarus').openPopup().addTo(map);
 L.marker([57.98481, -17.92969], {icon: cittaIcon}).bindPopup('Resia').openPopup().addTo(map);
 L.marker([39.77477, -35.41992], {icon: villaggioIcon}).bindPopup('Rochinglanel').openPopup().addTo(map);
 L.marker([62.87519, -55.2832], {icon: cittadinaIcon}).bindPopup('Saffrey').openPopup().addTo(map);
 L.marker([-78.96298, -35.2002], {icon: cittaIcon}).bindPopup('Salamanca').openPopup().addTo(map);
 L.marker([82.92411, -70.26855], {icon: cittadinaIcon}).bindPopup('<a href="http://dungeon-world-2.obsidianportal.com/wikis/stadfloden">Stadfloden</a>').openPopup().addTo(map);
 L.marker([-78.07108, -34.84863], {icon: cittadinaIcon}).bindPopup('Tavistock').openPopup().addTo(map);
 L.marker([63.27318, -15.9082], {icon: cittaIcon}).bindPopup('Tarsibia').openPopup().addTo(map);
 L.marker([-79.3839, -24.52148], {icon: naveIcon}).bindPopup('Valparaiso').openPopup().addTo(map);
 L.marker([80.8449, 49.21875], {icon: fortezzaIcon}).bindPopup('Zergen').openPopup().addTo(map);
 L.marker([-50.40152, 79.80469], {icon: cittaIcon}).bindPopup('Zurrieq').openPopup().addTo(map);


 map.on('click', function(e) {
      alert(e.latlng);
 });
    </script>
    
</body>
</html>

Spero ancora una volta che questo breve tutorial possa essere utile a chiunque voglia cimentarsi, e se c'è qualcosa di poco chiaro i commenti sono a disposizione!