437 lines
13 KiB
HTML
437 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Mon super projet BD</title>
|
|
|
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
|
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
|
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
|
|
|
<style>
|
|
body, html {
|
|
margin: 0;
|
|
padding: 0;
|
|
width: 100vw;
|
|
height: 100vh;
|
|
overflow: clip;
|
|
}
|
|
|
|
#map {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
}
|
|
|
|
.info {
|
|
padding: 6px 8px;
|
|
font: 14px/16px Arial, Helvetica, sans-serif;
|
|
background: white;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
|
|
border-radius: 5px;
|
|
|
|
max-width: 40ch;
|
|
}
|
|
|
|
.info p {
|
|
margin: 0;
|
|
}
|
|
|
|
.info h4 {
|
|
margin: 0 0 5px;
|
|
color: #777;
|
|
}
|
|
|
|
#hold_on {
|
|
z-index: 9999;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background-color: #222;
|
|
color: white;
|
|
opacity: 0.75;
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-direction: column;
|
|
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
}
|
|
|
|
#hold_on.active {
|
|
visibility: visible;
|
|
pointer-events: all;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div id="hold_on" class="">
|
|
<h1>Veuillez patienter...</h1>
|
|
<p>Chargement des données en cours</p>
|
|
</div>
|
|
<div id="map"></div>
|
|
|
|
|
|
|
|
<script>
|
|
// contains the data from all the .geojsons
|
|
let zones_geojson_data;
|
|
|
|
// the map (lol)
|
|
let map;
|
|
|
|
// infobox in the top right corner
|
|
let info;
|
|
// legend in the bottom right corner
|
|
let legend;
|
|
|
|
let current;
|
|
|
|
|
|
|
|
// https://colorbrewer2.org/#type=sequential&scheme=OrRd&n=9
|
|
const map_colors = [
|
|
"#fff7ec",
|
|
"#fee8c8",
|
|
"#fdd49e",
|
|
"#fdbb84",
|
|
"#fc8d59",
|
|
"#ef6548",
|
|
"#d7301f",
|
|
"#b30000",
|
|
"#7f0000",
|
|
];
|
|
|
|
|
|
function get_color(val) {
|
|
if( val < 0) {
|
|
return '#BBB';
|
|
}
|
|
|
|
// => 0.15, 0.2, etc. the range between each color
|
|
const class_range = 1 / map_colors.length;
|
|
|
|
const color_index = Math.max(0, Math.round(val * map_colors.length) - 1)
|
|
const chosen_color = map_colors[color_index]
|
|
|
|
//console.log(`Picking color for ${val} => ${chosen_color}`)
|
|
|
|
return chosen_color;
|
|
}
|
|
|
|
function style(feature) {
|
|
const ratio = feature.properties.normalized_ratio ?? null;
|
|
|
|
if (ratio && ratio >= 0) {
|
|
return {
|
|
fillColor: get_color(ratio),
|
|
weight: 1,
|
|
opacity: 1,
|
|
color: '#555',
|
|
//color: get_color(ratio), //'white',
|
|
dashArray: '3',
|
|
fillOpacity: 0.8
|
|
};
|
|
}
|
|
else {
|
|
return {
|
|
fillColor: 'grey',
|
|
weight: 1,
|
|
opacity: 1,
|
|
color: 'grey',
|
|
dashArray: '3',
|
|
fillOpacity: 0.7
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
function zoomToFeature(e) {
|
|
map.fitBounds(e.target.getBounds());
|
|
}
|
|
|
|
function highlightFeature(e) {
|
|
var layer = e.target;
|
|
|
|
layer.setStyle({
|
|
weight: 5,
|
|
color: '#666',
|
|
dashArray: '',
|
|
fillOpacity: 0.7
|
|
});
|
|
|
|
layer.bringToFront();
|
|
info.update(layer.feature.properties);
|
|
}
|
|
|
|
function resetHighlight(e) {
|
|
if (current) zones_geojson_data[current].geo.resetStyle(e.target);
|
|
info.update();
|
|
}
|
|
|
|
function onEachFeature(feature, layer) {
|
|
layer.on({
|
|
mouseover: highlightFeature,
|
|
mouseout: resetHighlight,
|
|
click: zoomToFeature
|
|
});
|
|
}
|
|
|
|
function normalize_data(dataset, remove_empty=false) {
|
|
console.log(dataset);
|
|
|
|
let lowest = 10e10;
|
|
let highest = -1
|
|
|
|
dataset.features.forEach(a => {
|
|
const r = a.properties.ratio;
|
|
if (r) {
|
|
if (r < lowest) {
|
|
lowest = r;
|
|
}
|
|
else if (r > highest) {
|
|
highest = r;
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
|
|
dataset.features = dataset.features.map(el => {
|
|
if (el.properties.ratio && el.properties.road_length !== 0) {
|
|
const obj = el;
|
|
obj.properties.normalized_ratio = (obj.properties.ratio - lowest) / (highest - lowest);
|
|
if (obj.properties.normalized_ratio > 1) {
|
|
//console.log(`${obj.properties.normalized_ratio} = (${obj.properties.ratio} - ${lowest}) / ${highest}`)
|
|
}
|
|
return obj;
|
|
}
|
|
else if(remove_empty === false) {
|
|
return el;
|
|
}
|
|
}).filter(el => el !== undefined);
|
|
|
|
dataset.properties = {
|
|
ratio_limits: [lowest, highest],
|
|
};
|
|
|
|
|
|
return dataset
|
|
}
|
|
|
|
function set_current_data(name) {
|
|
console.log('active');
|
|
document.getElementById('hold_on').classList.add('active');
|
|
|
|
if (current) {
|
|
const old_data = zones_geojson_data[current];
|
|
// remove current layer
|
|
old_data.geo.remove();
|
|
}
|
|
|
|
const new_data = zones_geojson_data[name];
|
|
new_data.geo.addTo(map);
|
|
|
|
current = name;
|
|
|
|
generate_map_legend();
|
|
|
|
console.log('INactive');
|
|
document.getElementById('hold_on').classList.remove('active');
|
|
}
|
|
|
|
function generate_map_legend() {
|
|
// remove old legend, if any
|
|
if (legend) legend.remove();
|
|
|
|
legend = L.control({ position: 'bottomright' });
|
|
|
|
legend.onAdd = function (map) {
|
|
const div = L.DomUtil.create('div', 'info legend');
|
|
|
|
const [lowest, highest] = zones_geojson_data[current].data.properties.ratio_limits;
|
|
const step = (highest - lowest) / map_colors.length;
|
|
|
|
div.innerHTML += `
|
|
<p>
|
|
<span style="background: ${get_color(-1)}; width: 1em; height: 1em; display: inline-block"></span>
|
|
<span> Pas de données</span>
|
|
</p>
|
|
`;
|
|
|
|
for (let i = 0; i < map_colors.length; i++) {
|
|
const val = (i * step) / (highest - lowest);
|
|
const text = Math.round(i * step)
|
|
|
|
div.innerHTML += `
|
|
<p>
|
|
<span style="background: ${get_color(val)}; width: 1em; height: 1em; display: inline-block"></span>
|
|
<span> Plus de ${text}km par PI</span>
|
|
</p>
|
|
`;
|
|
}
|
|
|
|
div.innerHTML += `
|
|
<p>PI: Pourcentage Impôt</p>
|
|
`
|
|
|
|
return div;
|
|
};
|
|
|
|
legend.addTo(map);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CREATE THE MAP
|
|
|
|
map = L.map('map').setView([47, 2.7], 6);
|
|
|
|
const tiles = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
}).addTo(map);
|
|
|
|
///////////
|
|
|
|
info = L.control();
|
|
|
|
info.onAdd = function (map) {
|
|
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
|
|
this.update();
|
|
return this._div;
|
|
};
|
|
|
|
// method that we will use to update the control based on feature properties passed
|
|
info.update = function (props) {
|
|
if (props) {
|
|
const road_km = Math.round(props.road_length / 1000);
|
|
const road_per_pi = Math.round((props.road_length / 1000) / props.impots);
|
|
|
|
this._div.innerHTML = `
|
|
<h4>Données pour:</h4>
|
|
<p><b>${props.nom}</b></p>
|
|
<p>Impôts: ${props.impots}% du revenu</p>
|
|
<p>Routes: ${road_km}km</p>
|
|
<hr/>
|
|
<p>Kilomètre de route par pourcentage d'impôt:</p>
|
|
<p>${ road_per_pi }</p>
|
|
`;
|
|
//<p>Ratio route/impôt:<br>${props.normalized_ratio}</p>
|
|
}
|
|
else {
|
|
this._div.innerHTML = `
|
|
<h4>Aucune sélection</h4>
|
|
<p>Survoler une zone pour afficher les données</p>
|
|
`;
|
|
}
|
|
};
|
|
|
|
info.addTo(map);
|
|
|
|
////////////////////
|
|
// add dataset selector
|
|
|
|
const bl_selector = L.control({ position: 'bottomleft' });
|
|
|
|
bl_selector.onAdd = function (map) {
|
|
const div = L.DomUtil.create('div', 'info bl_selector');
|
|
|
|
div.innerHTML += `
|
|
<form>
|
|
<fieldset>
|
|
<legend>Sélectionnez un jeu de données</legend>
|
|
|
|
<div style="display: flex;">
|
|
<input type="radio" id="dataset_dep" name="dataset_choice" checked onclick="set_current_data('deps')"/>
|
|
<label for="dataset_dep">Départements</label>
|
|
</div>
|
|
|
|
<div style="display: flex;">
|
|
<input type="radio" id="dataset_arr" name="dataset_choice" onclick="set_current_data('arrs')"/>
|
|
<label for="dataset_arr">Arrondissements</label>
|
|
</div>
|
|
|
|
<div style="display: flex;">
|
|
<input type="radio" id="dataset_com" name="dataset_choice" onclick="set_current_data('coms_noempty')"/>
|
|
<label for="dataset_com" title="Beaucoup de communes (32k) n'ont pas de données ; cette option n'affiche que celles qui en ont.">Communes (réduites)</label>
|
|
</div>
|
|
|
|
<div style="display: flex;">
|
|
<input type="radio" id="dataset_com" name="dataset_choice" onclick="set_current_data('coms')"/>
|
|
<label for="dataset_com">Communes (toutes, même sans données)</label>
|
|
</div>
|
|
</fieldset>
|
|
</form>
|
|
`
|
|
|
|
return div;
|
|
};
|
|
|
|
bl_selector.addTo(map);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// FETCH DATA
|
|
|
|
document.getElementById('hold_on').classList.add('active');
|
|
|
|
fetch("/data.json").then(res => res.json()).then(data => {
|
|
// load all the data
|
|
const coms = normalize_data(data.coms);
|
|
com_geojson = L.geoJson(coms, {
|
|
style: style,
|
|
onEachFeature: onEachFeature,
|
|
});
|
|
|
|
const arrs = normalize_data(data.arrs);
|
|
arr_geojson = L.geoJson(arrs, {
|
|
style: style,
|
|
onEachFeature: onEachFeature,
|
|
});
|
|
|
|
const deps = normalize_data(data.deps);
|
|
dep_geojson = L.geoJson(deps, {
|
|
style: style,
|
|
onEachFeature: onEachFeature,
|
|
});
|
|
|
|
const coms_noempty = normalize_data(data.coms, true);
|
|
coms_noempty_geojson = L.geoJson(coms_noempty, {
|
|
style: style,
|
|
onEachFeature: onEachFeature,
|
|
});
|
|
|
|
zones_geojson_data = {
|
|
deps: {
|
|
data: deps,
|
|
geo: dep_geojson,
|
|
},
|
|
arrs: {
|
|
data: arrs,
|
|
geo: arr_geojson,
|
|
},
|
|
coms: {
|
|
data: coms,
|
|
geo: com_geojson,
|
|
},
|
|
coms_noempty: {
|
|
data: coms_noempty,
|
|
geo: coms_noempty_geojson,
|
|
},
|
|
};
|
|
|
|
document.getElementById('hold_on').classList.remove('active');
|
|
|
|
set_current_data('deps');
|
|
})
|
|
</script>
|
|
</body>
|
|
|
|
</html> |