/* * eccc_map - Tool to show a map of ECCC stations and generate a list based on * a polygon drawn by user. * Copyright (C) 2020 Pierre Choffet * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ // Download station inventory as distributed with eccc_map since we cannot // get the upstream one that is on a ftp server. // Original URL: ftp://client_climate@ftp.tor.ec.gc.ca/Pub/Get_More_Data_Plus_de_donnees/Station Inventory EN.csv const csv_url = 'Station Inventory EN.csv'; const map = L.map('map').setView([70.44,-88.07], 4); const draws = new L.FeatureGroup(); const cluster = L.markerClusterGroup(); const icon = L.icon({iconUrl: 'leaflet/marker-icon.png', shadowUrl: 'leaflet/marker-shadow.png'}); var markers_selected = []; L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { id: 'osm', attribution: 'OpenStreetMap contributors', maxZoom: 18, tileSize: 256 } ).addTo(map); // Activate draw map.addLayer(draws); const draw_control = new L.Control.Draw({ edit: { featureGroup: draws }, draw: { polygon: { showArea: true }, polyline: false, rectangle: false, circle: false, marker: false, circlemarker: false }, edit: false }); map.on(L.Draw.Event.DRAWSTART, function (event) { draws.clearLayers(); }); map.on(L.Draw.Event.CREATED, function (event) { const layer = event.layer; const polygon_latlngs = layer.getLatLngs(); draws.addLayer(layer); markers_selected = []; cluster.eachLayer(function(e) { if (e instanceof L.Marker) { let inside = false; const marker_lat = e.getLatLng().lat; const marker_lon = e.getLatLng().lng; for (let latlngs_browse = 0; latlngs_browse < polygon_latlngs.length; ++latlngs_browse){ let points = polygon_latlngs[latlngs_browse]; for (let i = 0, j = points.length - 1; i < points.length; j = i++) { let xi = points[i].lat; let yi = points[i].lng; let xj = points[j].lat; let yj = points[j].lng; let intersect = ((yi > marker_lon) != (yj > marker_lon)) && (marker_lat < (xj - xi) * (marker_lon - yi) / (yj - yi) + xi); if (intersect) inside = !inside; } } if (inside) { markers_selected.push(e); } updateOutput(); } }); }); map.addControl(draw_control); // Add custom draw button click event let button = document.getElementById('draw-polygon'); button.addEventListener('click', function() {draw_control._toolbars.draw._modes.polygon.handler.enable();}); // Fetch csv file, read it line by line async function readStationsEntries(callback) { let text = ''; fetch(csv_url).then( function(response) { return response.body.getReader(); } ).then( function (reader) { reader.read().then( function readChunk({done, value}) { if (done) return; const decoder = new TextDecoder(); text += decoder.decode(value); return reader.read().then(readChunk); } ).then( function () { const text_split = text.split("\n"); const lines_count = text_split.length; let lines_browse = 0; // Consume csv header for (; lines_browse < lines_count; ++lines_browse) { if (text_split[lines_browse].startsWith('"Name",')) break; } ++lines_browse; // Read stations rows for (; lines_browse < lines_count - 1; ++lines_browse) { // To keep code simple, we expect CSV entries to be surrounded by // two '"' characters and containing no additional one in their values. // We just count occurences of '"' per line to ensure sanity. if (text_split[lines_browse].split('"').length != 39) { console.error('Illegal station entry: line '+(lines_browse+1)); continue; } const entry = text_split[lines_browse].split(','); const name = entry[0].replaceAll('"', ''); const cid = entry[2].replaceAll('"', ''); const lat = entry[6].replaceAll('"', ''); const lon = entry[7].replaceAll('"', ''); const marker = L.marker([lat, lon], {icon: icon, cid: cid}); marker.bindPopup('

'+name+'

'+cid+'

'); marker.addTo(cluster); } map.addLayer(cluster); } ) } ) } function updateOutput() { let output = ''; for (let markers_browse = 0; markers_browse < markers_selected.length; ++markers_browse) { if (markers_browse != 0) output += document.getElementById('separator').value; output += document.getElementById('pattern').value.replace('{cid}', markers_selected[markers_browse].options.cid); } document.getElementById('output').value = output; } readStationsEntries(); // Add events in settings fields document.getElementById('pattern').addEventListener('blur', updateOutput); document.getElementById('separator').addEventListener('blur', updateOutput);