114 lines
3.6 KiB
TypeScript
114 lines
3.6 KiB
TypeScript
import * as L from 'leaflet';
|
|
import { Feature, FeatureCollection, GeoJSON } from 'geojson';
|
|
|
|
interface PlaceModel {
|
|
name: string;
|
|
address: string;
|
|
open_hours: string;
|
|
icon: string;
|
|
description: string;
|
|
latitude: number;
|
|
longitude: number;
|
|
|
|
}
|
|
|
|
async function loadPlaces(): Promise<Array<PlaceModel>> {
|
|
return await fetch('places/').then(response => response.json());
|
|
}
|
|
|
|
function toLeafletPlaces(backendPlaces: Array<PlaceModel>): GeoJSON {
|
|
let result: FeatureCollection = {
|
|
type: "FeatureCollection",
|
|
features: new Array<Feature>(),
|
|
}
|
|
for (const place of backendPlaces) {
|
|
result.features.push({
|
|
"type": "Feature",
|
|
"properties": {
|
|
"name": place.name,
|
|
"address": place.address,
|
|
"open_hours": place.open_hours,
|
|
"icon": place.icon,
|
|
"description": place.description
|
|
},
|
|
"geometry": {
|
|
"type": "Point",
|
|
"coordinates": [place.latitude, place.longitude]
|
|
}
|
|
});
|
|
}
|
|
return result;
|
|
}
|
|
|
|
async function setupMap(): Promise<void> {
|
|
const backendPlaces = await loadPlaces();
|
|
const leafletPlaces = toLeafletPlaces(backendPlaces);
|
|
|
|
/* Set up the map*/
|
|
const map = new L.Map('map');
|
|
|
|
/* Create the tile layer with correct attribution*/
|
|
const osmUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
|
|
const osmAttrib = 'Mapa © <a href="https://openstreetmap.org">OpenStreetMap</a>';
|
|
const osm = new L.TileLayer(osmUrl, { minZoom: 4, maxZoom: 20, attribution: osmAttrib });
|
|
|
|
/* Start the map in Santiago */
|
|
map.setView(new L.LatLng(-33.45, -70.666667), 13);
|
|
/* Try to get user position, if not, put the map in Santiago again */
|
|
map.locate({ setView: true, maxZoom: 16 })
|
|
.on('locationerror', function(_event: L.LocationEvent) {
|
|
map.setView(new L.LatLng(-33.45, -70.666667), 13);
|
|
});
|
|
map.addLayer(osm);
|
|
|
|
function onEachFeature(feature: Feature, layer: L.Layer) {
|
|
if (feature.properties) {
|
|
let popupStr = "<h3>" + feature.properties.name + "</h3>";
|
|
popupStr += "<ul>"
|
|
if (feature.properties.address)
|
|
popupStr += "<li><b>Dirección:</b> " + feature.properties.address + "</li>";
|
|
if (feature.properties.open_hours)
|
|
popupStr += "<li><b>Horario:</b> " + feature.properties.open_hours + "</li>";
|
|
if (feature.properties.description)
|
|
popupStr += "<li>" + feature.properties.description + "</li>";
|
|
popupStr += "</ul>";
|
|
|
|
layer.bindPopup(popupStr);
|
|
}
|
|
}
|
|
|
|
/* Icons */
|
|
const icons = new Map<string, L.Icon>();
|
|
icons.set('bar', new L.Icon({ iconUrl: 'icons/bar.svg' }));
|
|
icons.set('coffee', new L.Icon({ iconUrl: 'icons/coffee.svg' }));
|
|
icons.set('cinema', new L.Icon({ iconUrl: 'icons/film.svg' }));
|
|
icons.set('dining', new L.Icon({ iconUrl: 'icons/dining.svg' }));
|
|
icons.set('food', new L.Icon({ iconUrl: 'icons/food.svg' }));
|
|
icons.set('jazz', new L.Icon({ iconUrl: 'icons/saxophone.svg' }));
|
|
icons.set('library', new L.Icon({ iconUrl: 'icons/book.svg' }));
|
|
icons.set('marker', new L.Icon({ iconUrl: 'icons/marker.svg' }));
|
|
icons.set('mask', new L.Icon({ iconUrl: 'icons/mask.svg' }));
|
|
icons.set('museum', new L.Icon({ iconUrl: 'icons/museum.svg' }));
|
|
icons.set('shop', new L.Icon({ iconUrl: 'icons/store.svg' }));
|
|
|
|
for (let [_name, icon] of icons) {
|
|
icon.options.iconSize = [36, 36];
|
|
icon.options.popupAnchor = [0, -18];
|
|
}
|
|
|
|
function pointToLayer(feature: Feature, latlng: L.LatLng) {
|
|
let markerIcon = null;
|
|
if (feature.properties && feature.properties.icon) {
|
|
markerIcon = icons.get(feature.properties.icon);
|
|
}
|
|
if (markerIcon !== null && markerIcon !== undefined)
|
|
return L.marker(latlng, { icon: markerIcon });
|
|
else
|
|
return L.marker(latlng);
|
|
}
|
|
|
|
map.addLayer(L.geoJSON(leafletPlaces, {
|
|
onEachFeature: onEachFeature,
|
|
pointToLayer: pointToLayer
|
|
}));
|
|
}
|