const version = "2022.10.27.1";
const mapStore = localforage.createInstance({
name: "maps",
storeName: "saved_maps"
});
const featureStore = localforage.createInstance({
name: "maps",
storeName: "saved_features"
});
const map = L.map("map", {
zoomSnap: 0,
// tap: (L.Browser.safari && !L.Browser.mobile) ? false : true,
maxZoom: 17, minZoom: 15,
zoomControl: true,
renderer: L.canvas({
padding: 0.5,
tolerance: 10
})
}).fitWorld();
map.attributionControl.setPrefix(`◉ `);
map.once("locationfound", (e) => {
hideLoader();
if (Object.values(layers.overlays)[0] instanceof L.TileLayer.MBTiles) {
setTimeout(() =>{
map.fitBounds(Object.values(layers.overlays)[0].options.bounds);
controls.locateCtrl.stopFollowing();
}, 1);
} else {
map.fitBounds(e.bounds, {maxZoom: 18});
}
});
map.on("click", (e) => {
layers.select.clearLayers();
});
map.on("baselayerchange", (e) => {
localStorage.setItem("basemap", e.name);
});
const layers = {
basemaps: {
"Streets": L.tileLayer("https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}.@2xpng", {
maxNativeZoom: 18,
maxZoom: map.getMaxZoom(),
attribution: '© OpenStreetMap contributors, © CARTO',
}).addTo(map),
"Aerial": L.tileLayer("https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/{y}/{x}", {
maxNativeZoom: 16,
maxZoom: map.getMaxZoom(),
attribution: "USGS",
}),
"Topo": L.tileLayer("https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}", {
maxNativeZoom: 16,
maxZoom: map.getMaxZoom(),
attribution: "USGS",
}),
"None": L.tileLayer("", {
maxZoom: map.getMaxZoom()
})
},
select: L.featureGroup(null).addTo(map),
overlays: {},
groups: {}
};
var starok= L.icon({ iconUrl: '../images/starok.png', iconAnchor: [16, 37], });
var marker = [];
marker[1] = L.marker([39.79668394774688, 2.6944001956176677], {icon: starok}).addTo(map);
marker[1].on("click", function() { parent.datosPunto(1,0); });
marker[37] = L.marker([39.79766860952766, 2.698834077149357], {icon: starok}).addTo(map);
marker[37].on("click", function() { parent.datosPunto(37,0); });
marker[7] = L.marker([39.796707374063864, 2.68921602124788], {icon: starok}).addTo(map);
marker[7].on("click", function() { parent.datosPunto(7,0); });
marker[8] = L.marker([39.806466609768655, 2.6985715662918253], {icon: starok}).addTo(map);
marker[8].on("click", function() { parent.datosPunto(8,0); });
marker[9] = L.marker([39.810832204856375, 2.7002194166048676], {icon: starok}).addTo(map);
marker[9].on("click", function() { parent.datosPunto(9,0); });
marker[11] = L.marker([39.80848412608556, 2.7033965747429267], {icon: starok}).addTo(map);
marker[11].on("click", function() { parent.datosPunto(11,0); });
marker[19] = L.marker([39.8173709211968, 2.709937223455845], {icon: starok}).addTo(map);
marker[19].on("click", function() { parent.datosPunto(19,0); });
marker[20] = L.marker([39.81723906978286, 2.713885435125767], {icon: starok}).addTo(map);
marker[20].on("click", function() { parent.datosPunto(20,0); });
marker[21] = L.marker([39.8232710130262, 2.7283049907898294], {icon: starok}).addTo(map);
marker[21].on("click", function() { parent.datosPunto(21,0); });
marker[22] = L.marker([39.82768750998747, 2.7339483912492923], {icon: starok}).addTo(map);
marker[22].on("click", function() { parent.datosPunto(22,0); });
marker[23] = L.marker([39.82926946968831, 2.7368666346574955], {icon: starok}).addTo(map);
marker[23].on("click", function() { parent.datosPunto(23,0); });
marker[25] = L.marker([39.85517876881287, 2.778120594918665], {icon: starok}).addTo(map);
marker[25].on("click", function() { parent.datosPunto(25,0); });
marker[26] = L.marker([39.85517876881287, 2.787905293404993], {icon: starok}).addTo(map);
marker[26].on("click", function() { parent.datosPunto(26,0); });
marker[27] = L.marker([39.83072946247496, 2.7602248963713016], {icon: starok}).addTo(map);
marker[27].on("click", function() { parent.datosPunto(27,0); });
marker[28] = L.marker([39.84654671962563, 2.776575642526087], {icon: starok}).addTo(map);
marker[28].on("click", function() { parent.datosPunto(28,0); });
marker[29] = L.marker([39.839758926344565, 2.7760606583952274], {icon: starok}).addTo(map);
marker[29].on("click", function() { parent.datosPunto(29,0); });
marker[31] = L.marker([39.80824951131639, 2.7927118119596805], {icon: starok}).addTo(map);
marker[31].on("click", function() { parent.datosPunto(31,0); });
marker[32] = L.marker([39.850244783822575, 2.7993902898716128], {icon: starok}).addTo(map);
marker[32].on("click", function() { parent.datosPunto(32,0); });
marker[34] = L.marker([39.85169443868433, 2.8057846761631167], {icon: starok}).addTo(map);
marker[34].on("click", function() { parent.datosPunto(34,0); });
marker[35] = L.marker([39.85864576725907, 2.8024372793125307], {icon: starok}).addTo(map);
marker[35].on("click", function() { parent.datosPunto(35,0); });
marker[15] = L.marker([39.776601285573626, 2.7087780448903898], {icon: starok}).addTo(map);
marker[15].on("click", function() { parent.datosPunto(15,0); });
marker[16] = L.marker([39.76611933154883, 2.715440195484744], {icon: starok}).addTo(map);
marker[16].on("click", function() { parent.datosPunto(16,0); });
marker[17] = L.marker([39.76632744661204, 2.715303388632617], {icon: starok}).addTo(map);
marker[17].on("click", function() { parent.datosPunto(17,0); });
marker[18] = L.marker([39.766223330397544, 2.7157486353290894], {icon: starok}).addTo(map);
marker[18].on("click", function() { parent.datosPunto(18,0); });
const controls = {
locateCtrl: L.control.locate({
icon: "icon-gps_fixed",
iconLoading: "spinner icon-gps_fixed",
setView: "untilPan",
cacheLocation: true,
position: "bottomright",
flyTo: false,
keepCurrentZoomLevel: true,
circleStyle: {
interactive: false
},
markerStyle: {
interactive: true
},
metric: false,
strings: {
title: "My location",
popup: (options) => {
const loc = controls.locateCtrl._marker.getLatLng();
return `
You are within ${Number(options.distance).toLocaleString()} ${options.unit}
from ${loc.lat.toFixed(6)}, ${loc.lng.toFixed(6)}
`;
}
},
locateOptions: {
enableHighAccuracy: true,
maxZoom: 18
},
onLocationError: (e) => {
hideLoader();
document.querySelector(".leaflet-control-locate").getElementsByTagName("span")[0].className = "icon-gps_off";
// alert(e.message);
}
}).addTo(map),
scaleCtrl: L.control.scale({
position: "bottomleft"
}).addTo(map)
};
function handleFile(file) {
showLoader();
const name = file.name.split(".").slice(0, -1).join(".");
if (file.name.endsWith(".mbtiles")) {
loadRaster(file, name);
} else if (file.name.endsWith(".geojson") || file.name.endsWith(".kml") || file.name.endsWith(".gpx") || file.name.endsWith(".csv")) {
const format = file.name.split(".").pop();
loadVector(file, name, format);
} else {
// alert("MBTiles, GeoJSON, KML, GPX, and CSV files supported.");
hideLoader();
}
}
function loadVector(file, name, format) {
const reader = new FileReader();
let geojson = null;
reader.onload = function(e) {
if (format == "geojson") {
geojson = JSON.parse(reader.result);
name = geojson.name ? geojson.name : name;
} else if (format == "kml") {
const kml = (new DOMParser()).parseFromString(reader.result, "text/xml");
geojson = toGeoJSON.kml(kml, {styles: true});
} else if (format == "gpx") {
const gpx = (new DOMParser()).parseFromString(reader.result, "text/xml");
geojson = toGeoJSON.gpx(gpx);
} else if (format == "csv") {
const columns = reader.result.split(/n/).filter(Boolean)[0].split(",");
const options = {};
if (columns.includes("Y") && columns.includes("X")) {
options.latfield = "Y",
options.lonfield = "X"
}
csv2geojson.csv2geojson(reader.result, options, function(err, data) {
if (data) {
geojson = data;
}
});
}
createVectorLayer(name, geojson, null, true);
}
reader.readAsText(file);
}
function createVectorLayer(name, data, key, save) {
let radius = 4;
var key = key ? key : Date.now().toString();
const layer = L.geoJSON(data, {
key: key,
bubblingMouseEvents: false,
style: (feature) => {
return {
color: feature.properties.hasOwnProperty("stroke") ? feature.properties["stroke"] : feature.properties["marker-color"] ? feature.properties["marker-color"] : feature.geometry.type == "Point" ? "#ffffff" : "#ff0000",
opacity: feature.properties.hasOwnProperty("stroke-opacity") ? feature.properties["stroke-opacity"] : 1.0,
weight: feature.properties.hasOwnProperty("stroke-width") ? feature.properties["stroke-width"] : feature.geometry.type == "Point" ? 1.5 : 3,
fillColor: feature.properties.hasOwnProperty("fill") ? feature.properties["fill"] : feature.properties["marker-color"] ? feature.properties["marker-color"] : "#ff0000",
fillOpacity: feature.properties.hasOwnProperty("fill-opacity") ? feature.properties["fill-opacity"] : feature.geometry.type != "Point" ? 0.2 : feature.geometry.type == "Point" ? 1 : "",
};
},
pointToLayer: (feature, latlng) => {
const size = feature.properties.hasOwnProperty("marker-size") ? feature.properties["marker-size"] : "medium";
const sizes = {
small: 4,
medium: 6,
large: 8
};
radius = sizes[size];
return L.circleMarker(latlng, {
radius: radius
});
},
onEachFeature: (feature, layer) => {
let table = "";
const hiddenProps = ["styleUrl", "styleHash", "styleMapHash", "stroke", "stroke-opacity", "stroke-width", "opacity", "fill", "fill-opacity", "icon", "scale", "coordTimes", "marker-size", "marker-color", "marker-symbol"];
for (const key in feature.properties) {
if (feature.properties.hasOwnProperty(key) && hiddenProps.indexOf(key) == -1) {
table += "" + key.toUpperCase() + " | " + formatProperty(feature.properties[key]) + " |
";
}
}
table += "
";
layer.bindPopup(table, {
// closeButton: false,
autoPanPadding: [15, 15],
maxHeight: 300,
maxWidth: 250
});
layer.on({
popupclose: (e) => {
layers.select.clearLayers();
},
click: (e) => {
layers.select.clearLayers();
layers.select.addLayer(L.geoJSON(layer.toGeoJSON(), {
style: {
color: "#00FFFF",
weight: 5
},
pointToLayer: (feature, latlng) => {
return L.circleMarker(latlng, {
radius: radius,
color: "#00FFFF",
fillColor: "#00FFFF",
fillOpacity: 1
});
}
}))
}
});
}
});
if (save) {
const value = {
"name": name,
"features": data
};
featureStore.setItem(key, value).then((value) => {
addOverlayLayer(layer, name, null, true);
layers.overlays[L.Util.stamp(layer)] = layer;
layer.addTo(map);
zoomToLayer(L.Util.stamp(layer));
}).catch((err) => {
alert("Error saving data!");
});
} else {
addOverlayLayer(layer, name, null, true);
layers.overlays[L.Util.stamp(layer)] = layer;
}
}
function loadRaster(file, name) {
const reader = new FileReader();
reader.onload = (e) => {
createRasterLayer(name, reader.result);
}
reader.readAsArrayBuffer(file);
}
function createRasterLayer(name, data) {
const key = Date.now().toString();
const layer = L.tileLayer.mbTiles(data, {
autoScale: true,
fitBounds: true,
updateWhenIdle: false,
key: key
}).on("databaseloaded", (e) => {
name = (layer.options.name ? layer.options.name : name);
// addOverlayLayer(layer, name);
const value = {
"name": name,
"mbtiles": data
};
mapStore.setItem(key, value).then((value) => {
addOverlayLayer(layer, name, null, false);
}).catch((err) => {
alert("Error saving data!");
});
}).addTo(map);
layers.overlays[L.Util.stamp(layer)] = layer;
}
function addOverlayLayer(layer, name, group, saved) {
hideLoader();
const layerState = getLayerState();
setTimeout(() => {
updateLayerState(layerState);
}, 100);
layer.on("add", () => {
// document.querySelector(`[data-layer='${L.Util.stamp(layer)}']`).disabled = false;
});
layer.on("remove", () => {
// document.querySelector(`[data-layer='${L.Util.stamp(layer)}']`).disabled = true;
});
}
function setAnimation(ok,id){
if(ok){
L.DomUtil.addClass(marker[id]._icon, "blinking");
}else{
L.DomUtil.removeClass(marker[id]._icon, "blinking");
}
}
function getLayerState() {
const layers = {};
document.querySelectorAll(".layer-buttons input").forEach(element => {
// const id = element.getAttribute("data-layer");
const layer = map._layers[id];
layers[id] = {
disabled: (layer && map.hasLayer(layer)) ? false : true,
opacity: element.value
}
});
return layers;
}
function updateLayerState(layers) {
document.querySelectorAll(".layer-buttons input").forEach(element => {
// const id = element.getAttribute("data-layer");
if (layers[id]) {
element.disabled = layers[id].disabled;
element.value = layers[id].opacity;
}
});
}
function zoomToLayer(id) {
const layer = layers.overlays[id];
if (!map.hasLayer(layer)) {
// map.addLayer(layers.overlays[id]);
alert("Layer must be active first!");
}
else if (layer.options.bounds) {
map.fitBounds(layer.options.bounds);
controls.locateCtrl.stopFollowing();
}
else {
map.fitBounds(layer.getBounds(), {padding: [20, 20]});
controls.locateCtrl.stopFollowing();
}
}
function removeLayer(id, name, group) {
if (confirm(`Remove ${name.replace(/_/g, " ")}?`)) {
const layerState = getLayerState();
const layer = layers.overlays[id];
if (map.hasLayer(layer)) {
map.removeLayer(layer);
}
if (layer instanceof L.TileLayer.MBTiles) {
layer._db.close();
}
if (layer && layer.options && layer.options.key) {
if (layer instanceof L.TileLayer.MBTiles) {
mapStore.removeItem(layer.options.key).then(function () {
// controls.layerCtrl.removeLayer(layer);
updateLayerState(layerState);
});
} else if (layer instanceof L.GeoJSON) {
featureStore.removeItem(layer.options.key).then(function () {
// controls.layerCtrl.removeLayer(layer);
updateLayerState(layerState);
});
}
}
if (group) {
const groupLayer = layers.groups[group];
const key = groupLayer.options.key;
mapStore.removeItem(key).then(() => {
// controls.layerCtrl.removeLayer(groupLayer);
updateLayerState(layerState);
});
}/* else {
// controls.layerCtrl.removeLayer(layer);
updateLayerState(layerState);
}*/
}
}
function changeOpacity(id) {
// const value = document.querySelector(`[data-layer='${id}']`).value;
const layer = layers.overlays[id];
if (!map.hasLayer(layer)) {
// map.addLayer(layers.overlays[id]);
alert("Layer must be active first!");
}
else if (layer instanceof L.TileLayer.MBTiles) {
layer.setOpacity(value);
} else if (layer instanceof L.GeoJSON) {
layer.eachLayer((layer) => {
if (layer.feature.properties["fill-opacity"] != 0) {
layer.setStyle({
fillOpacity: value
});
}
if (layer.feature.properties["opacity"] != 0) {
layer.setStyle({
opacity: value
});
}
});
}
}
function formatProperty(value) {
if (typeof value == "string" && value.startsWith("http")) {
return `${value}`;
} else {
return value;
}
}
function showLoader() {
document.getElementById("progress-bar").style.display = "block";
}
function hideLoader() {
document.getElementById("progress-bar").style.display = "none";
}
function switchBaseLayer(name) {
const basemaps = Object.keys(layers.basemaps);
for (const layer of basemaps) {
if (layer == name) {
map.addLayer(layers.basemaps[layer]);
} else {
map.removeLayer(layers.basemaps[layer]);
}
}
}
function loadBasemapConfig(file) {
const reader = new FileReader();
reader.onload = (e) => {
const config = JSON.parse(reader.result);
if (confirm("Are you sure you want to overwrite the default basemaps?")) {
loadCustomBasemaps(config);
localStorage.setItem("basemapConfig", JSON.stringify(config));
}
}
reader.readAsText(file);
}
function loadCustomBasemaps(config) {
const basemaps = Object.keys(layers.basemaps);
for (const layer of basemaps) {
map.removeLayer(layers.basemaps[layer]);
// controls.layerCtrl.removeLayer(layers.basemaps[layer]);
}
layers.basemaps = {};
config.forEach((element, index) => {
let layer = null;
if (element.type == "wms") {
layer = L.tileLayer.wms(element.url, {
maxNativeZoom: element.maxZoom ? element.maxZoom : 18,
maxZoom: map.getMaxZoom(),
layers: element.layers,
format: element.format ? element.format : "image/png",
attribution: element.attribution ? element.attribution : ""
});
} else if (element.type == "xyz") {
layer = L.tileLayer(element.url, {
maxNativeZoom: element.maxZoom ? element.maxZoom : 18,
maxZoom: map.getMaxZoom(),
attribution: element.attribution ? element.attribution : ""
});
}
if (index == 0) {
layer.addTo(map);
}
layers.basemaps[element.name] = layer;
// controls.layerCtrl.addBaseLayer(layer, element.name);
});
layers.basemaps["None"] = L.tileLayer("", {maxZoom: map.getMaxZoom()});
// controls.layerCtrl.addBaseLayer(layers.basemaps["None"], "None");
}
function showInfo() {
alert("Welcome to GPSMap.app, an offline capable map viewer with GPS integration!nn- Tap the + button to load a raster MBTiles, GeoJSON, KML, GPX, or CSV file directly from your device or cloud storage.n- Tap the layers button to view online basemaps and manage offline layers.nnDeveloped by Bryan McBride - mcbride.bryan@gmail.comn" + version);
}
function loadSavedFeatures() {
featureStore.length().then((numberOfKeys) => {
if (numberOfKeys > 0) {
featureStore.iterate((value, key, iterationNumber) => {
createVectorLayer(value.name, value.features, key, false);
}).then(() => {
// console.log("saved features loaded!");
}).catch((err) => {
console.log(err);
});
}
}).catch((err) => {
console.log(err);
});
}
function loadSavedMaps() {
const urlParams = new URLSearchParams(window.location.search);
mapStore.length().then((numberOfKeys) => {
/*if (!urlParams.has("map") && numberOfKeys != 1) {
controls.locateCtrl.start();
}*/
if (numberOfKeys > 0) {
mapStore.iterate((value, key, iterationNumber) => {
const group = L.layerGroup(null, {key: key});
const groupID = L.Util.stamp(group);
layers.groups[groupID] = group;
addOverlayLayer(group, value.name, groupID, true);
group.once("add", (e) => {
const layer = L.tileLayer.mbTiles(value.mbtiles, {
autoScale: true,
fitBounds: (urlParams.has("map") && urlParams.get("map") == key) ? true : (numberOfKeys == 1) ? true : false,
updateWhenIdle: false,
zIndex: 10
});
group.addLayer(layer);
layers.overlays[groupID] = layer;
});
if ((numberOfKeys == 1) || (urlParams.has("map") && urlParams.get("map") == key)) {
map.addLayer(group);
switchBaseLayer("None");
// document.querySelector(`[data-layer='${groupID}']`).disabled = false;
}
}).then(() => {
// console.log("saved maps loaded!");
loadSavedFeatures();
}).catch((err) => {
console.log(err);
});
} else {
loadSavedFeatures();
}
}).catch((err) => {
console.log(err);
});
}
function loadURLparams() {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("map")) {
const url = urlParams.get("map");
mapStore.keys().then((keys) => {
if (!keys.includes(url)) {
fetchFile(url);
}
}).catch((err) => {
console.log(err);
});
// window.history.replaceState(null, null, window.location.pathname);
}
}
function fetchFile(url) {
if (navigator.onLine) {
showLoader();
fetch(url)
.then(response => response.arrayBuffer())
.then(data => {
hideLoader();
const layer = L.tileLayer.mbTiles(data, {
autoScale: true,
fitBounds: true,
updateWhenIdle: false
}).on("databaseloaded", (e) => {
const name = layer.options.name ? layer.options.name : url.split("/").pop().split(".").slice(0, -1).join(".");
const value = {
"name": name,
"mbtiles": data
};
mapStore.setItem(url, value).then((value) => {
addOverlayLayer(layer, name);
}).catch((err) => {
alert("Error saving data!");
});
}).addTo(map);
layers.overlays[L.Util.stamp(layer)] = layer;
})
.catch((error) => {
hideLoader();
console.error("Error:", error);
alert("Error fetching remote file...");
});
} else {
alert("Must be online to fetch data!");
hideLoader();
}
}
function updateNetworkStatus() {
if (navigator.onLine) {
document.getElementById("status").style.color = "green";
} else {
switchBaseLayer("None");
document.getElementById("status").style.color = "red";
}
}
// Drag and drop files
const dropArea = document.getElementById("map");
["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
dropArea.addEventListener(eventName, (e) => {
e.preventDefault();
e.stopPropagation();
}, false);
});
["dragenter", "dragover"].forEach((eventName) => {
dropArea.addEventListener(eventName, showLoader, false);
});
["dragleave", "drop"].forEach(eventName => {
dropArea.addEventListener(eventName, hideLoader, false);
});
dropArea.addEventListener("drop", (e) => {
const file = e.dataTransfer.files[0];
handleFile(file);
}, false);
window.addEventListener("offline", (e) => {
updateNetworkStatus();
});
window.addEventListener("online", (e) => {
updateNetworkStatus();
});
document.addEventListener("DOMContentLoaded", () => {
showLoader();
controls.locateCtrl.start();
});
initSqlJs({
locateFile: function() {
return "assets/vendor/sqljs-1.8.0/sql-wasm.wasm";
}
}).then(function(SQL){
loadURLparams();
loadSavedMaps();
if (localStorage.getItem("basemapConfig")) {
loadCustomBasemaps(JSON.parse(localStorage.getItem("basemapConfig")));
}
if (!navigator.onLine) {
switchBaseLayer("None");
} else if (localStorage.getItem("basemap")) {
switchBaseLayer(localStorage.getItem("basemap"));
}
setTimeout(() => { switchBaseLayer("Streets"); }, 1000);
});