/*
* 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);