NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name AWAG4MAPS // @namespace http://tampermonkey.net/ // @version 4.0 // @description Introduces a map with bird sightings recorded in the Mar Menor. It also shows open data on the state of the lagoon water. // @author Paula Gonzalez Martinez // @match https://es.wikipedia.org/* // @icon https://www.google.com/s2/favicons?sz=64&domain=wikipedia.org // @license GPL-3.0-or-later // @resource leaflet_css https://unpkg.com/leaflet@1.7.1/dist/leaflet.css // @resource legend_css https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.Legend-master/src/leaflet.legend.css // @resource own_css https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/styles.css // @resource markercluster_css https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.markercluster-1.4.1/dist/MarkerCluster.css // @resource markerclusterdefault_css https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.markercluster-1.4.1/dist/MarkerCluster.Defaults.css // @require https://unpkg.com/leaflet@1.7.1/dist/leaflet.js // @require https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/echarts.min.js // @require https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.Legend-master/src/leaflet.legend.js // @require https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/heatmap.js-2.0.5/build/heatmap.js // @require https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/heatmap.js-2.0.5/plugins/leaflet-heatmap/leaflet-heatmap.js // @require https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.markercluster-1.4.1/dist/leaflet.markercluster.js // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_addElement // ==/UserScript== // CSS loadCSS("own_css"); loadCSS("leaflet_css"); loadCSS("legend_css"); loadCSS("markerclusterdefault_css"); loadCSS("markercluster_css"); // ---------------------------------------------------------------------------------------------------------------------------- // JAVASCRIPT loadScript("https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"); loadScript("https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/heatmap.js-2.0.5/build/heatmap.js"); loadScript("https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/heatmap.js-2.0.5/plugins/leaflet-heatmap/leaflet-heatmap.js"); loadScript("https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.markercluster-1.4.1/dist/leaflet.markercluster.js"); loadScript("https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/echarts.min.js"); loadScript("https://raw.githubusercontent.com/lucyleia28/AWAG4Maps/main/plugin/Leaflet.Legend-master/src/leaflet.legend.js"); // ---------------------------------------------------------------------------------------------------------------------------- // General values var pathname = window.location.pathname; const list_pathname = await getLists("pathName"); const specific_pathname = "/wiki/Mar_Menor"; if (list_pathname.includes(pathname)) { let mapContainer = createLateralMapContainer(); createMap("specific", mapContainer); } else if(pathname.includes(specific_pathname)){ let mapContainer = createGeneralMapContainer(); createMap("general", mapContainer); } // ---------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------FUNTIONS------------------------------------------------------------- // ---------------------------------------------------------------------------------------------------------------------------- // Load files function loadCSS(string){ const css = GM_getResourceText(string); GM_addStyle(css); } function loadScript(scriptURL) { 'use strict'; function httpGetAsync(theUrl, callback) { var xmlHttp = new XMLHttpRequest(); xmlHttp.onreadystatechange = function() { if (xmlHttp.readyState == 4 && xmlHttp.status == 200) callback(xmlHttp.responseText); } xmlHttp.open("GET", theUrl, true); // true for asynchronous xmlHttp.send(null); } return new Promise(function(resolve){ httpGetAsync(scriptURL, function(response){ var s = document.createElement("script"); s.text = response; document.getElementsByTagName("head")[0].appendChild(s); resolve(); }); }); } // ---------------------------------------------------------------------------------------------------------------------------- // General and Specific maps // Main function async function createMap(type, mapContainer){ let zoom = 11; if (type == "general") { zoom = 11.5; } let map = L.map('map').setView([37.7325, -0.7790], zoom); let base = addLayerBase(map); let area = addLayerArea(map); let scale = addLayerScale(map); var markers = L.markerClusterGroup({ iconCreateFunction: function(cluster) { var childCount = cluster.getChildCount(); var c = 'marker-cluster marker-cluster-'; if (childCount > 0 && childCount <= 2) { c += 'smallest'; } else if (childCount > 2 && childCount <= 5) { c += 'smaller'; } else if (childCount > 5 && childCount <= 10) { c += 'normal'; } else if (childCount > 10 && childCount <= 15) { c += 'big'; } else if (childCount > 15) { c += 'max'; } return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: c, style: 'border: solid 2px rgba(0, 0, 0, 0.4y);', iconSize: new L.Point(40, 40) }); } }); var markersSecondaryLayer = L.markerClusterGroup({ iconCreateFunction: function(cluster) { var childCount = cluster.getChildCount(); var containerStyle = 'width: 10px; height: 10px;'; var contentStyle = 'display: flex; align-items: center; justify-content: center; height: 100%; width: 100%; border-radius: 50%; background-color: rgba(0, 0, 0, 0.8); border: solid white;'; return new L.DivIcon({ html: '<div style="' + containerStyle + '"><div style="' + contentStyle + '"><span style="margin-top: 50px;">Sensors</span></div></div>', className: "secondaryLayer", iconSize: new L.Point(10, 10) }); } }); addVisualGuide(); const regionCode = await getLists("regionCode"); const pointNamesList = document.getElementById("pointNamesList"); let observationPeriod = document.querySelector('input[name="period"]:checked').value; let period = document.getElementsByName("period"); let pointLocationsList = ""; let heatMap = document.getElementById("heatMap"); let layer = ""; switch (type) { case "specific": var pointNames = getPointNames(); getMainLayer(map, markers, regionCode, observationPeriod, pointNames); setPointLocationsList(map, markers, regionCode, observationPeriod, pointNames); for (var i = 0; i < period.length; i++) { period[i].addEventListener("change", function() { var selectedValue = document.querySelector('input[name="period"]:checked').value; updateDate(map, markers, markersSecondaryLayer, regionCode, selectedValue, "specific"); deselectHeatMap(map); }); } pointLocationsList = document.getElementById("pointLocationsListLateral"); pointLocationsList.addEventListener("change", function(event) { if (event.target.classList.contains("check_pointLocations")) { uptadeMarkers(map, markers, regionCode, document.querySelector('input[name="period"]:checked').value, "specific-zones"); } }); getLayerInfo(map, markersSecondaryLayer, observationPeriod, layer); heatMap.addEventListener("change", function() { if (this.checked) { addHeatMap(map, markers, regionCode, document.querySelector('input[name="period"]:checked').value, "specific"); } else { deselectHeatMap(map); } }); break; case "general": getAllMainLayers(map, markers, regionCode, observationPeriod); setLists(map, markers, regionCode, observationPeriod, "pointNames-pointLocations"); pointNamesList.addEventListener("change", function(event) { if (event.target.classList.contains("check_pointNames")) { uptadeMarkers(map, markers, regionCode, document.querySelector('input[name="period"]:checked').value, "pointNames"); } }); pointLocationsList = document.getElementById("pointLocationsList"); pointLocationsList.addEventListener("change", function(event) { if (event.target.classList.contains("check_pointLocations")) { uptadeMarkers(map, markers, regionCode, document.querySelector('input[name="period"]:checked').value, "pointLocations"); } }); for (var j = 0; j < period.length; j++) { period[j].addEventListener("change", function() { var selectedValue = document.querySelector('input[name="period"]:checked').value; updateDate(map, markers, markersSecondaryLayer, regionCode, selectedValue, "general"); deselectHeatMap(map); }); } getLayerInfo(map, markersSecondaryLayer, observationPeriod, layer); heatMap.addEventListener("change", function() { if (this.checked) { addHeatMap(map, markers, regionCode, document.querySelector('input[name="period"]:checked').value, "general"); } else { deselectHeatMap(map); } }); break; default: console.log("ERROR"); } mapContainer.insertAdjacentHTML("afterbegin", map); } function createLateralMapContainer(){ const infobox = document.getElementsByClassName("infobox")[0]; let body = infobox.firstChild; let row = body.childNodes; let last = row[row.length-1]; var row_title = document.createElement("tr"); row_title.innerHTML = '<th colspan="3" style="text-align:center;background-color: #FF9800;"><a href="https://es.wikipedia.org/wiki/Mar_Menor" style="color: #2C2C2C; text-decoration: none;" title="Mar Menor Bird wathching">Bird watching in the Mar Menor</a></th>'; body.insertBefore(row_title, last); var row_link = document.createElement("tr"); row_link.innerHTML = '<th colspan="3"><a href="https://es.wikipedia.org/wiki/Mar_Menor" title="Mar Menor Link">See Mar Menor</a></th>'; body.insertBefore(row_link, last); let mapContainer = document.createElement("div"); mapContainer.id = "map"; mapContainer.style = "height: 35em; width: 25em"; row_link.firstChild.appendChild(mapContainer); let infoContainer = document.createElement("div"); infoContainer.id = "infoLateral"; infoContainer.innerHTML = `<h3>Sighting areas</h3> <div id="pointLocationsListLateral"></div> <h3>Date of sightings</h3> <div id="buttonsContentLateral"> <input type="radio" id="date2years" class="date" name="period" value="2years"/> <label for="date2years" class="boton">Last 2 years</label> <input type="radio" id="date1year" class="date" name="period" value="1year"/> <label for="date1year" class="boton">Last year</label> <input type="radio" id="date6months" class="date" name="period" value="6months" checked/> <label for="date6months" class="boton">Last 6 months</label> </div> <h3>Number of sightings</h3> <div id="mainLayerList"></div> <input id="heatMap" type="checkbox" name="calor" style="margin-bottom: 20px;"/> Show heat map`; let modal = document.createElement("div"); modal.id = "graph"; row_link.firstChild.appendChild(modal); row_link.firstChild.appendChild(infoContainer); return mapContainer; } function createGeneralMapContainer(){ /*---------- WIKIPEDIA UPDATE 01/2024 ----------*/ const pageContainer = document.querySelector(".mw-page-container"); pageContainer.style.margin = "0 auto"; pageContainer.style.display = "flex"; pageContainer.style.width = "100%"; pageContainer.style.maxWidth= "100%"; var element = document.querySelector('.mw-body'); if (element) { if (window.matchMedia("(min-width: 1000px)").matches) { element.style.display = 'grid'; element.style.gridTemplate = 'min-content min-content min-content 1fr / minmax(0,129.25rem) min-content'; element.style.gridTemplateAreas = "'titlebar-cx .' 'titlebar columnEnd' 'toolbar columnEnd' 'content columnEnd'"; } else { element.style.display = 'grid'; element.style.gridTemplate = 'min-content min-content min-content 1fr / minmax(0,59.25rem) min-content'; element.style.gridTemplateAreas = "'titlebar-cx .' 'titlebar columnEnd' 'toolbar columnEnd' 'content columnEnd'"; } } const header = document.querySelector(".vector-header"); header.style.maxWidth= "100%"; const h2Elements = document.getElementsByTagName('h2'); const firstElementH2 = h2Elements[1]; const mainLayerSection = document.createElement("div"); mainLayerSection.id = "mainLayer_section"; firstElementH2.parentNode.insertBefore(mainLayerSection, firstElementH2); /*--------------------------------------------------------------------------*/ let title = document.createElement("h2"); title.innerHTML = '<span class="mw-headline" id="mainLayer">Bird watching in the Mar Menor</span><span class="mw-editsection"></span>'; let mapContainer = document.createElement("div"); mapContainer.id = "map"; let infoContainer = document.createElement("div"); infoContainer.id = "info"; let infoElements = document.createElement("div"); infoElements.id = "infoElements"; infoElements.innerHTML = `<h3>Sighting areas</h3> <div id="pointLocationsList"></div> <h3>Species sighted</h3> <div id="pointNamesList"></div> <h3>Date of sightings</h3> <div id="buttonsContent"> <input type="radio" id="date2years" class="date" name="period" value="2years"/> <label for="date2years" class="boton">Last 2 years</label> <input type="radio" id="date1year" class="date" name="period" value="1year"/> <label for="date1year" class="boton">Last year</label> <input type="radio" id="date6months" class="date" name="period" value="6months" checked/> <label for="date6months" class="boton">Last 6 months</label> </div> <h3>Number of sightings</h3> <div id="mainLayerList"></div> <input id="heatMap" type="checkbox" name="calor" style="margin-bottom: 20px;"/> Show heat map`; infoContainer.appendChild(infoElements); let generalMap = document.createElement("div"); generalMap.id = "generalMap"; generalMap.appendChild(mapContainer); generalMap.appendChild(infoContainer); let info = document.createElement("div"); info.id = "infoUbicacion"; let modal = document.createElement("div"); modal.id = "graph"; let seccion = document.getElementById("mainLayer_section"); seccion.appendChild(title); seccion.appendChild(generalMap); seccion.appendChild(info); seccion.appendChild(modal); return mapContainer } // ---------------------------------------------------------------------------------------------------------------------------- // Lists async function getLists(type) { const apiUrl = 'http://localhost:8080/ebird'; var list = []; var content = ""; try { const response = await fetch(apiUrl); if (!response.ok) { throw new Error('The request was not successful.'); } const data = await response.json(); if (data && Array.isArray(data.results)) { var validObservations = data.results.filter(observation => { return observation.value !== undefined && observation.value > 0; }); validObservations.forEach(observation => { var count = 0; switch(type){ case "pathName": if(observation.sciname.includes(" ")){ content = "/wiki/" + observation.sciname.replace(" ", "_"); } else { content = "/wiki/" + observation.sciname; } break; case "regionCode": content = observation.idubication; break; } if(list.length > 0){ list.forEach(element => { if(element == content){ count ++; } }); if (count == 0){ list.push(content); } } else { list.push(content); } }); return list; } else { console.warn('The data received from the API does not have the expected structure.'); } } catch (error) { console.error('Request error:', error); throw error; } } async function setLists(map, markers, regionCode, date, type) { var data = await getAllMainLayersInfo(map, markers, regionCode, date); var species = []; var regions = []; switch (type) { case "pointNames-pointLocations": species = []; regions = []; data.forEach(observation => { var idSpecies = observation.sciname.replace(" ", "").toLowerCase(); if(!species.includes(idSpecies) && observation.value > 0){ species.push(idSpecies); document.getElementById("pointNamesList").innerHTML += `<label> <input type="checkbox" id="${idSpecies}" name="pointNames" class="check_pointNames" \> ${observation.sciname} <span class="checkmark"></span> </label>`; } var idRegion = observation.idubication.toLowerCase(); if(!regions.includes(idRegion) && observation.value > 0){ regions.push(idRegion); document.getElementById("pointLocationsList").innerHTML += `<label> <input type="checkbox" id="${idRegion}" name="pointLocations" class="check_pointLocations" \> ${observation.ubication} <span class="checkmark"></span> </label>`; } }); if (regions.length < 1 && species.length < 1) { document.getElementById("pointLocationsList").innerHTML = `<label>No observations have been recorded on this date</label>`; document.getElementById("pointNamesList").innerHTML = `<label>No observations have been recorded on this date</label>`; } break; case "pointNames": species = []; data.forEach(observation => { var idSpecies = observation.sciname.replace(" ", "").toLowerCase(); if(!species.includes(idSpecies) && observation.value > 0){ species.push(idSpecies); document.getElementById("pointNamesList").innerHTML += `<label> <input type="checkbox" id="${idSpecies}" name="pointNames" class="check_pointNames" \> ${observation.sciname} <span class="checkmark"></span> </label>`; } }); if (species.length < 1) { document.getElementById("pointNamesList").innerHTML = `<label>No observations have been recorded on this date</label>`; } break; case "pointLocations": regions = []; data.forEach(observation => { var idRegion = observation.idubication.toLowerCase(); if(!regions.includes(idRegion) && observation.value > 0){ regions.push(idRegion); document.getElementById("pointLocationsList").innerHTML += `<label> <input type="checkbox" id="${idRegion}" name="pointLocations" class="check_pointLocations" \> ${observation.ubication} <span class="checkmark"></span> </label>`; } }); if (regions.length < 1) { document.getElementById("pointLocationsList").innerHTML = `<label>No observations have been recorded on this date</label>`; } break; } } async function setPointLocationsList(map, markers, regionCode, date, pointNames) { var regions = []; var data = await getAllMainLayersInfo(map, markers, regionCode, date); data.forEach(observation => { if(observation.sciname.toLowerCase() === pointNames.toLowerCase() && observation.value > 0){ var idRegion = observation.idubication.toLowerCase(); if(!regions.includes(idRegion)){ regions.push(idRegion); document.getElementById("pointLocationsListLateral").innerHTML += `<label> <input type="checkbox" id="${idRegion}" name="pointLocations" class="check_pointLocations" \> ${observation.ubication} <span class="checkmark"></span> </label>`; } } }); if (regions.length < 1) { document.getElementById("pointLocationsListLateral").innerHTML = `<label>No observations have been recorded on this date</label>`; } } // ---------------------------------------------------------------------------------------------------------------------------- // Names and Date function getPointNames(){ const value = window.location.pathname.split("/")[2].split("_"); const pointNames = value[0] + " " + value[1]; return pointNames; } function getStartDate(months) { let currentDate = new Date(); currentDate.setMonth(currentDate.getMonth() - months); return currentDate; } function getDate(date) { let startDate; switch (date) { case "2years": startDate = getStartDate(24); break; case "1year": startDate = getStartDate(12); break; case "6months": startDate = getStartDate(6); break; default: startDate = new Date(); break; } return startDate; } async function updateDate(map, markers, markersSecondaryLayer, regionCode, date, type) { markers.clearLayers(); markersSecondaryLayer.clearLayers(); let layer = ""; getLayerInfo(map, markersSecondaryLayer, date, layer); switch (type) { case "general": document.getElementById("pointNamesList").innerHTML = ""; document.getElementById("pointLocationsList").innerHTML = ""; await getAllMainLayers(map, markers, regionCode, date); setLists(map, markers, regionCode, date, "pointNames-pointLocations"); break; case "specific": var pointNames = getPointNames(); document.getElementById("pointLocationsListLateral").innerHTML = ""; getMainLayer(map, markers, regionCode, date, pointNames); setPointLocationsList(map, markers, regionCode, date, pointNames); break; } } // ---------------------------------------------------------------------------------------------------------------------------- // Draw Map async function uptadeMarkers(map, markers, regionCode, date, type) { var selectedSpecies = Array.from(document.querySelectorAll('input.check_pointNames:checked')) .map((checkbox) => checkbox.id); var selectedZones = Array.from(document.querySelectorAll('input.check_pointLocations:checked')) .map((checkbox) => checkbox.id); if (selectedSpecies.length > 0 && selectedZones.length > 0) { type = "pointLocations-pointNames"; } else if (selectedSpecies.length < 1 && selectedZones.length < 1 && type != "specific-zones") { type = "clean"; } switch (type) { case "pointNames": var dataPoints = await getAllMainLayersInfo(map, markers, regionCode, date); var regionCodes = []; var speciesIds = []; if (selectedSpecies.length > 0) { markers.clearLayers(); dataPoints.forEach(observation => { for (const selectedSpecie of selectedSpecies) { if (observation.sciname.toLowerCase().replace(" ", "") === selectedSpecie && observation.value > 0) { if (!speciesIds.includes(observation.sciname)) { speciesIds.push(observation.sciname); } if (!regionCodes.includes(observation.idubication) && speciesIds.includes(observation.sciname)) { regionCodes.push(observation.idubication); } } } }); document.getElementById("pointLocationsList").innerHTML = ""; for (var i=0; i<speciesIds.length; i++) { getMainLayer(map, markers, regionCodes, date, speciesIds[i]); } setLists(map, markers, regionCodes, date, "pointLocations"); } else { markers.clearLayers(); uptadeMarkers(map, markers, regionCode, date, "pointLocations"); } break; case "pointLocations": var dataZones = await getAllMainLayersInfo(map, markers, regionCode, date); var regionCodeIds = []; if (selectedZones.length > 0) { markers.clearLayers(); dataZones.forEach(observation => { for (const selectedZone of selectedZones) { if (observation.idubication.toLowerCase() === selectedZone && observation.value > 0) { if (!regionCodeIds.includes(observation.idubication)) { regionCodeIds.push(observation.idubication); } } } }); document.getElementById("pointNamesList").innerHTML = ""; await getAllMainLayers(map, markers, regionCodeIds, date); setLists(map, markers, regionCodeIds, date, "pointNames"); } else { markers.clearLayers(); uptadeMarkers(map, markers, regionCode, date, "pointNames"); } break; case "specific-zones": var selectedZonesEspe = Array.from(document.querySelectorAll("input.check_pointLocations:checked")) .map((checkbox) => checkbox.id); var dataZonesEspe = await getAllMainLayersInfo(map, markers, regionCode, date); var regionCodeIdsEspe = []; var pointNames = getPointNames(); if (selectedZonesEspe.length > 0) { markers.clearLayers(); dataZonesEspe.forEach(observation => { for (const selectedZoneEspe of selectedZonesEspe) { if (observation.idubication.toLowerCase() === selectedZoneEspe && observation.value > 0) { if (!regionCodeIdsEspe.includes(observation.idubication)) { regionCodeIdsEspe.push(observation.idubication); } } } }); getMainLayer(map, markers, regionCodeIdsEspe, date, pointNames); } else { markers.clearLayers(); getMainLayer(map, markers, regionCode, date, pointNames); } break; case "pointLocations-pointNames": var data = await getAllMainLayersInfo(map, markers, regionCode, date); var regions = []; markers.clearLayers(); data.forEach(observation => { for (const selectedZone of selectedZones) { for (const selectedSpecie of selectedSpecies) { if (observation.idubication.toLowerCase() === selectedZone && observation.sciname.toLowerCase().replace(" ", "") === selectedSpecie && observation.value > 0) { addLayerPoint(map, markers, observation); } } } }); break; case "clean": markers.clearLayers(); document.getElementById("pointNamesList").innerHTML = ""; document.getElementById("pointLocationsList").innerHTML = ""; await getAllMainLayers(map, markers, regionCode, date); setLists(map, markers, regionCode, date, "pointNames"); setLists(map, markers, regionCode, date, "pointNames-pointLocations"); break; } } function addLayerBase(map){ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 13, attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); return map; } function addLayerArea(map){ fetch("https://raw.githubusercontent.com/lucyleia28/marmenor.github.io/main/areaMarMenor.json") .then(res => res.json()) .then(response => { var marMenor = response; var marMenorJS = L.geoJson(marMenor).addTo(map); let legend = addLayerLegend(map, marMenorJS, marMenor); }); } function addLayerPoint(map, markers, observation){ let color = ""; let url = ""; if(observation.value <= 2){ color = "#8F9CA0"; url = ""; } else if(observation.value > 2 && observation.value <= 5){ color = "#C7E466"; url = ""; } else if(observation.value > 5 && observation.value <= 10){ color = "#FAC500"; url = ""; } else if(observation.value > 10 && observation.value <= 15){ color = "#E57701"; url = ""; } else if(observation.value > 15){ color = "#E33B15"; url = ""; } else{ color = "black"; url = ""; } var observationPoint = L.circleMarker(L.latLng(observation.lat, observation.long), { radius: 8, fillColor: color, color: "black", weight: 2, opacity: 1, fillOpacity: 1, }); var popupContent = ""; if (pathname.includes(specific_pathname)) { // General popupContent += "<strong>Species:</strong> " + observation.sciname + "<br>" + "<strong>Last sighting date:</strong> " + observation.date + "<br>" + "<strong>Number of birds:</strong> " + observation.value + "<br>" + "<strong>Location:</strong> " + observation.ubication + "<br>" + "<strong>Coordinates:</strong> <br> Latitude: " + observation.lat + ", Longitude: " + observation.long + "<br>" + "<strong>Link:</strong> <a href='https://es.wikipedia.org/wiki/" + observation.sciname.split(" ")[0]+"_"+observation.sciname.split(" ")[1] + "' target='_blank'>See in Wikipedia</a>"; } else { // Especifico popupContent += "<strong>Last sighting date:</strong> " + observation.date + "<br>" + "<strong>Number of birds:</strong> " + observation.value + "<br>" + "<strong>Location:</strong> " + observation.ubication + "<br>" + "<strong>Coordinates:</strong> <br> Latitude: " + observation.lat + ", Longitude: " + observation.long; } observationPoint.bindPopup(popupContent); observationPoint.on("click", function() { observationPoint.openPopup(); }); markers.addLayer(observationPoint); map.addLayer(markers); } function addSecondaryLayer(map, markers, observation){ let color = ""; let url = ""; color = "black"; var observationPoint = L.circleMarker(L.latLng(observation.lat, observation.long), { radius: 8, fillColor: color, color: "red", weight: 2, opacity: 1, fillOpacity: 1, }); var popupContent = ""; popupContent += "<strong>Sensor:</strong> " + observation.name + "<br>" + "<strong>Last register date:</strong> " + observation.date + "<br>" + "<strong>Number of registers:</strong> " + observation.total + "<br>" + "<strong>Average value:</strong> " + observation.media + " " + observation.units + "<br>" + "<strong>Coordinates:</strong> <br> Latitude: " + observation.lat + ", Longitude: " + observation.long + "<br>" + "<a href='#' id='showRegisters'>See more</a>"; observationPoint.bindPopup(popupContent); observationPoint.on("click", function() { observationPoint.openPopup(); var showRegisters = document.getElementById("showRegisters"); showRegisters.addEventListener("click", function() { showModalMessage(observation); }); }); markers.addLayer(observationPoint); map.addLayer(markers); } function addLayerScale(map){ var scale = new L.control.scale({ imperial: false, position: "bottomright" }).addTo(map); return scale; } function addLayerLegend(map, marMenorJS, marMenor){ var legend = new L.control.Legend({ id: "legend", title: "Legend", position: "topright", collapsed: false, symbolWidth: 24, opacity: 1, column: 1, legends: [{ label: "Mar Menor", type: "rectangle", color: "#0074f0", fillColor: "#009ff0", weight: 2, layers: marMenorJS, marMenor }] }).addTo(map); legend._container.setAttribute("id", "legend"); return legend; } function addVisualGuide() { document.getElementById("mainLayerList").innerHTML += `<div class="mainLayer"> <div class="mainLayerItem red"></div> <div class="mainLayerLabel"> <div class="mainLayerTitle">15+</div> </div> </div> <div class="mainLayer"> <div class="mainLayerItem orange"></div> <div class="mainLayerLabel"> <div class="mainLayerTitle">10 - 15</div> </div> </div> <div class="mainLayer"> <div class="mainLayerItem yellow"></div> <div class="mainLayerLabel"> <div class="mainLayerTitle">5 - 10</div> </div> </div> <div class="mainLayer"> <div class="mainLayerItem green"></div> <div class="mainLayerLabel"> <div class="mainLayerTitle">2 - 5</div> </div> </div> <div class="mainLayer"> <div class="mainLayerItem gray"></div> <div class="mainLayerLabel"> <div class="mainLayerTitle">1 - 2</div> </div> </div>`; } // ---------------------------------------------------------------------------------------------------------------------------- // HeatMap function deselectHeatMap(map) { document.getElementById("heatMap").checked = false; map.eachLayer(function(layer) { if (layer instanceof HeatmapOverlay) { map.removeLayer(layer); } }); } async function getHeatMapCoordinates(map, markers, regionCode, date, type) { var data = await getAllMainLayersInfo(map, markers, regionCode, date); const total = {}; switch (type) { case "specific": var pointNames = getPointNames(); data.forEach(observation => { for (const region of regionCode) { if (observation.sciname === pointNames && observation.idubication === region && observation.value > 0) { if (!total[region]) { total[region] = { total: 0, coordenadas: { lat: observation.lat, lng: observation.long } }; } else { total[region].coordenadas.lat = observation.lat; total[region].coordenadas.lng = observation.long; } total[region].total += parseInt(observation.value); } } }); break; case "general": data.forEach(observation => { for (const region of regionCode) { if (observation.idubication === region && observation.value > 0) { if (!total[region]) { total[region] = { total: 0, coordenadas: { lat: observation.lat, lng: observation.long } }; } else { total[region].coordenadas.lat = observation.lat; total[region].coordenadas.lng = observation.long; } total[region].total += parseInt(observation.value); } } }); break; } return total; } async function addHeatMap(map, markers, regionCode, date, type) { const totalCoordinates = await getHeatMapCoordinates(map, markers, regionCode, date, type); let data = []; for (const coordinate in totalCoordinates) { data.push({lat: totalCoordinates[coordinate].coordenadas.lat, lng: totalCoordinates[coordinate].coordenadas.lng, count: totalCoordinates[coordinate].total}); } var coordinates = { max: 200, min: 0, data: data, length: 11 }; var config = { "radius": 0.03, "maxOpacity": .8, "scaleRadius": true, "useLocalExtrema": true, latField: 'lat', lngField: 'lng', valueField: 'count' }; var heatmapLayer = new HeatmapOverlay(config); heatmapLayer.setData(coordinates); heatmapLayer.addTo(map); } // ---------------------------------------------------------------------------------------------------------------------------- // Main Layer function getMainLayer(map, markers, regionCode, date, pointNames) { const apiUrl = 'http://localhost:8080/ebird'; let startDate = getDate(date); fetch(apiUrl) .then(response => { if (!response.ok) { throw new Error('The request was not successful.'); } return response.json(); }) .then(data => { if (data && Array.isArray(data.results)) { var filteredResults = data.results.filter(result => { var resultDate = new Date(result.date); return resultDate >= startDate; }); var validObservations = filteredResults.filter(observation => { return observation.value !== undefined && observation.value > 0; }); regionCode.forEach(region => { validObservations.forEach(observation => { if (region == observation.idubication) { if(observation.sciname.toLowerCase() === pointNames.toLowerCase() && observation.value > 0){ addLayerPoint(map, markers, observation); } } }); }); } else { console.warn('The data received from the API does not have the expected structure.'); } }) .catch(error => { console.error('Request Error:', error); }); } function getAllMainLayers(map, markers, regionCode, date) { const apiUrl = 'http://localhost:8080/ebird'; let startDate = getDate(date); fetch(apiUrl) .then(response => { if (!response.ok) { throw new Error('The request was not successful.'); } return response.json(); }) .then(data => { if (data && Array.isArray(data.results)) { var filteredResults = data.results.filter(result => { var resultDate = new Date(result.date); return resultDate >= startDate; }); var validObservations = filteredResults.filter(observation => { return observation.value !== undefined && observation.value > 0; }); regionCode.forEach(region => { validObservations.forEach(observation => { if (region == observation.idubication) { addLayerPoint(map, markers, observation); } }); }); } else { console.warn('The data received from the API does not have the expected structure.'); } }) .catch(error => { console.error('Request Error:', error); }); } async function getAllMainLayersInfo(map, markers, regionCode, date) { const apiUrl = 'http://localhost:8080/ebird'; let startDate = getDate(date); let allObservations = []; try { const response = await fetch(apiUrl); if (!response.ok) { throw new Error('The request was not successful.'); } const data = await response.json(); if (data && Array.isArray(data.results)) { var filteredResults = data.results.filter(result => { var resultDate = new Date(result.date); return resultDate >= startDate; }); var validObservations = filteredResults.filter(observation => { return observation.value !== undefined && observation.value > 0; }); regionCode.forEach(region => { validObservations.forEach(observation => { if (region == observation.idubication) { allObservations.push(observation); } }); }); return allObservations; } else { console.warn('The data received from the API does not have the expected structure.'); } } catch (error) { console.error('Request Error:', error); } } // ---------------------------------------------------------------------------------------------------------------------------- // Secondary Layer async function getData(apiUrl, startDate) { try { const response = await fetch(apiUrl); if (!response.ok) { throw new Error('The request was not successful.'); } const data = await response.json(); if (data && Array.isArray(data.results)) { var filteredResults = data.results.filter(result => { var resultDate = new Date(result.date); return resultDate >= startDate; }); var validObservations = filteredResults.filter(observation => { return observation.value !== undefined && observation.value > 0; }); return validObservations; } else { console.warn('The data received from the API does not have the expected structure.'); } } catch (error) { console.error('Request Error:', error); } } async function getLayerInfo(map, markers, date, layer) { const apiUrl = "http://localhost:8080/" + layer; var startDate = getDate(date); var data = await getData(apiUrl, startDate); getSecondaryLayer(map, markers, data); } function getSecondaryLayer(map, markers, data) { let total = 0, media = 0; let name = "", lastDate = "", lat = "", long = "", units = "", others = ""; let allRegisters = []; console.log(data); data.forEach(register => { if (register) { total++; media += parseInt(register.value); if(lastDate == "" || lastDate <= register.date){ lastDate = register.date; } name = register.sensor; lat = register.lat; long = register.long; units = register.units; others = register.others; allRegisters.push({ name: name, id: register.locationId, lat: lat, long: long, value: register.value, units: units, others: register.others, date: register.date }); } }); let observation = { name: name, lat: lat, long: long, total: total, media: (media/total).toFixed(2), units: units, date: lastDate, all: allRegisters }; addSecondaryLayer(map, markers, observation); } // ---------------------------------------------------------------------------------------------------------------------------- function showModalMessage(values) { var name = values.name.charAt(0).toUpperCase() + values.name.slice(1); document.getElementById("graph").innerHTML = `<div id="modalWindow" class="modal"> <div class="modalContent"> <span class="modalClose">×</span> <h2>${name} sensor logs</h2> <div id="columnRegisters"></div> <div id="registers"></div> </div> </div>`; var modal = document.getElementById("modalWindow"); modal.style.display = "block"; var span = document.getElementsByClassName("modalClose")[0]; span.addEventListener("click", function() { modal.style.display = "none"; }); window.addEventListener("click", function(event) { if (event.target == modal) { modal.style.display = "none"; } }); addRegisters(values); } function addRegisters(values) { var registers = ""; var classname = "darkBackground"; for (let i in values.all) { if(i == 0){ document.getElementById("columnRegisters").innerHTML = "<div class='rows main'><div>Name</div><div>Id</div><div>Latitude</div><div>Longitude</div><div>Value</div><div>Units</div><div>Date</div><div>Others</div></div>"; } if(i%2 == 0){ classname = "clearBackground"; } else { classname = "darkBackground"; } let value = values.all[i]; registers += "<div class='rows " + classname + "'><div>" + value.name + "</div><div>" + value.id + "</div><div>" + value.lat + "</div><div>" + value.long + "</div><div>" + value.value + "</div><div>" + value.units + "</div><div>" + value.date + "</div><div>" + value.others + "</div></div>"; } document.getElementById("registers").innerHTML = registers; } // ----------------------------------------------------------------------------------------------------------------------------