map_multiple_departement = topohelper.fromGeojson(
data_multiple_departement_combined,
{deep: true}
)
.project({ proj: l93 })
.view({
tooltip: true,
zoom: true,
size: [
availableWidth > smallScreen ? availableWidth*0.4 : availableWidth*0.95, availableHeight * 0.6
]})
attributes_map_france = topojson
.feature(data_france, data_france.objects.France)
.features
.map(d=>d.properties)
button_france = html`
${download_button(
create_url_france(selectedlevel, format, year, drom_rapproches, simplification_percent)
)}
`
button_departements = html`
${download_button(
create_url_departement(selected_departements, selectedlevel, format, year, simplification_percent)
)}
`
departement_shape = get_departement(
departement,
arrondissement ? "COMMUNE_ARRONDISSEMENT" : "COMMUNE",
"topojson",
year
)
data_multiple_departement_combined = {
if (selected_departements.length == 1){
return data_multiple_departement
}
return mergeGeoJSONs(data_multiple_departement)
}
data_multiple_departement = {
let topos ;
if (selected_departements.length == 1){
topos = get_departement(
selected_departements,
arrondissement ? "COMMUNE_ARRONDISSEMENT" : "COMMUNE",
"geojson",
year,
simplification_percent
)
return topos
}
topos = get_multiple_departement(
selected_departements,
arrondissement ? "COMMUNE_ARRONDISSEMENT" : "COMMUNE",
"geojson",
year,
simplification_percent
)
return topos
}
annotationsSources = await db.query(
"SELECT * FROM read_csv_auto('https://minio.lab.sspcloud.fr/projet-cartiflette/documentation-website/sources_annotations.csv', header=true) "
)
// Function to merge multiple GeoJSON FeatureCollections
function mergeGeoJSONs(geojsonList) {
// Initialize an empty FeatureCollection
const mergedGeoJSON = {
type: "FeatureCollection",
features: []
};
// Loop through all GeoJSON objects in the list
geojsonList.forEach(geojson => {
if (geojson.type === "Feature") {
// If it's a Feature, add directly to the features array
mergedGeoJSON.features.push(geojson);
} else if (geojson.type === "FeatureCollection") {
// If it's a FeatureCollection, add all its features to the features array
mergedGeoJSON.features = mergedGeoJSON.features.concat(geojson.features);
}
});
return mergedGeoJSON;
}
availableHeight = window.innerHeight
screenHeight = 0.9*(availableHeight - document.getElementById("quarto-header").offsetHeight)
availableWidth = window.innerWidth
mapWidth = (width > 400) ? 0.4*width : 0.95*width
mapHeight = (width > 400) ? 0.85*screenHeight : 0.5*screenHeight
smallScreen = 500
// Projection Lambert 93 (EPSG: 2154)
l93 = "+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs"
import {
button, url_file_available,
get_france, get_departement, liste_departements,
get_multiple_departement, create_url_departement, create_url_france,
to_single_geojson} from "@linogaliana/cartiflette-js"
function create_projection2154(
geodata
){
let proj_epsg2154 = proj4d3(2154)
let proj_e4326_to_map_e2154 = proj_epsg2154.fitExtent(map_extent, geodata)
return proj_e4326_to_map_e2154
}
function _proj_epsg2154(proj4d3){
return(proj4d3(2154))
}
function map_multiple_2154(data_multiple_departement){
Promise.all(data_multiple_departement).then(departments => {
// Use .map() to extract the features from each department
const departmentFeatures = departments.map(department => topojson.feature(department, "data").features);
// Use .concat() to combine all the features into a single array
const allFeatures = [].concat(...departmentFeatures);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
const g = svg.append("g");
const path = d3.geoPath().projection(
proj_epsg2154.fitExtent(map_extent, {type: "FeatureCollection", features: allFeatures})
);
let paths = svg.selectAll('path')
.data(allFeatures)
.join('path')
.attr('d', path)
.attr("fill", 'white')
.attr("stroke", '#CCC')
return svg.node()
});
}
function map_multiple_4326(data_multiple_departement){
if (data_multiple_departement.length === 0){
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
svg.append("text")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("text-anchor", "middle")
.style("font-size", "20px")
.text("Select a region to get a map");
return svg.node();
}
let p = Promise.all(data_multiple_departement).then(departments => {
// Use .map() to extract the features from each department
const departmentFeatures = departments.map(department => topojson.feature(department, "data").features);
// Use .concat() to combine all the features into a single array
const allFeatures = [].concat(...departmentFeatures);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height)
const g = svg.append("g");
const path = d3.geoPath().projection(
d3.geoMercator().fitExtent(map_extent, {type: "FeatureCollection", features: allFeatures})
);
let paths = svg.selectAll('path')
.data(allFeatures)
.join('path')
.attr('d', path)
.attr("fill", 'white')
.attr("stroke", '#CCC')
return svg.node()
});
return p
}
function make_projection(geodata){
let statemesh = topojson.mesh(geodata, geodata.objects.data, (a, b) => a == b)
let projection = d3.geoMercator().fitExtent([[0, 0], [width, height]], statemesh)
return projection
}
function make_map(geodata, print_text = "yes", annotation = false) {
let projection_map = make_projection(geodata)
let temp = topojson.feature(geodata, geodata.objects.data)
let p =
Plot.plot({
width: width,
height: height,
projection: projection_map,
marks: [
Plot.geo(temp, {strokeOpacity: 0.4}),
print_text == "yes" ?
Plot.text(
temp.features.map((f) => ({centroid: d3.geoCentroid(f), name: f.properties.NOM})),
{
x: (d) => d.centroid[0], // longitude
y: (d) => d.centroid[1], // latitude
//text: "name",
//textAnchor: "middle",
stroke: "white",
fill: "black"
}
) :
Plot.text(
temp.features.map((f) => ({centroid: d3.geoCentroid(f), name: f.properties.NOM})),
{
x: (d) => d.centroid[0], // longitude
y: (d) => d.centroid[1], // latitude
stroke: "white",
fill: "black",
title: "name"
}
)
]
})
if (!annotation) {
return p
}
let p2 = addTooltips(
p,
{fill: "grey"}
) ;
return p2;
}
function download_button(url) {
return `
<form method="get" action="${url}" target="_blank" rel="noopener noreferrer">
<button class="btn btn-download" type="submit">
<i class="fa-solid fa-download"></i> Download !
</button>
</form>
`;
}
viewof choice_print_map_france = Inputs.bind(
Inputs.radio(['Carte', 'Métadonnées associées'], {value: "Carte"}),
viewof choice_print_map_departement
)
viewof selectedlevel = Inputs.select(['DEPARTEMENT', 'REGION', 'BASSIN_VIE', 'AIRE_ATTRACTION_VILLES'], {label: "Zonage désiré:"})
viewof simplification_percent = Inputs.range([0, 50], {step: 50, value: 50, label: "Degré de simplification (%)"})
viewof arrondissement = Inputs.toggle({label: "Arrondissement dans les grandes villes ?", value: true})
level_arrondissement = arrondissement ? "COMMUNE_ARRONDISSEMENT" : "COMMUNE"
viewof simplification_percent_departement = Inputs.bind(
Inputs.range([0, 50], {step: 50, value: 50, label: "Degré de simplification (%)"}),
viewof simplification_percent
)
viewof langage_requete = Inputs.radio(
["Python", "Javascript" ,"R"], {label: "Langage de programmation pour les exemples de code"},
{value: null, format: x => x ?? "Python"}
)
viewof langage_requete_departements = Inputs.radio(
["Python", "Javascript" ,"R"], {label: "Langage de programmation pour les exemples de code"},
{value: null, format: x => x ?? "Python"}
)
viewof selected_departements = multiAutoSelect({
options: liste_departements,
placeholder: "Liste des départements",
value: ["75", "92", "93", "94"]
})
language_emprise = Inputs.bind(
Inputs.radio(
["Python", "Javascript" ,"R"], {label: "Langage de programmation pour les exemples de code"},
{value: null, format: x => x ?? "Python"}
),
viewof langage_requete
)
language_emprise_departements = Inputs.bind(
Inputs.radio(
["Python", "Javascript" ,"R"], {label: "Langage de programmation pour les exemples de code"},
{value: null, format: x => x ?? "Python"}
),
viewof langage_requete_departements
)
year_emprise = Inputs.bind(
Inputs.select(
["2022",], {label: "Année de la géographie de référence"}
),
viewof year
)
format_emprise = Inputs.bind(
Inputs.select(
["topojson", "geojson"],{label: "Format désiré:"}
),
viewof format
)
simplification_percent_emprise = Inputs.bind(
Inputs.range(
[0, 50], {step: 50, value: 50, label: "Degré de simplification (%)"}
),
viewof simplification_percent
)
langage_departements = langage_requete_departements == null ? "Python" : langage_requete_departements
// Function to display the code bloc to use Cartiflette for whole France
function print_program_france(langage, selectedlevel, format, year, rapproche_drom, simplification) {
const filter_by = rapproche_drom ? "FRANCE_ENTIERE_DROM_RAPPROCHES" : "FRANCE_ENTIERE";
switch (langage) {
case "Python":
return md`
from cartiflette import carti_download
shp_communes = carti_download(
values = ["France"],
crs = 4326,
borders = "${selectedlevel}",
vectorfile_format="${format}",
simplification=${simplification},
filter_by="${filter_by}",
source="EXPRESS-COG-CARTO-TERRITOIRE",
year=${year})
`;
case "Javascript":
return md`
import {carti_download} from "@linogaliana/cartiflette-js"
carti_download({
value: "France",
crs: 4326,
borders: "${selectedlevel}",
vectorfile_format: "${format}",
simplification: ${simplification},
filter_by: "${filter_by}",
source: "EXPRESS-COG-CARTO-TERRITOIRE",
year: ${year}
});
`;
case "R":
return md`
library(cartiflette)
shp_communes <- carti_download(
values = c("France"),
crs = 4326,
borders = "${selectedlevel}",
vectorfile_format = "${format}",
simplification = ${simplification},
filter_by = "${filter_by}",
source = "EXPRESS-COG-CARTO-TERRITOIRE",
year = ${year}
)
`;
default:
throw new Error("Unsupported language specified");
}
}
function print_program_departement_single(langage, value, selectedlevel, format, year) {
switch (langage) {
case "Python":
return md`
from cartiflette import carti_download
shp_communes = carti_download(
crs = 4326,
values = "${value}",
borders="${selectedlevel}",
vectorfile_format="${format}",
filter_by="DEPARTEMENT",
source="EXPRESS-COG-CARTO-TERRITOIRE",
year=${year})
`;
case "Javascript":
return md`
import {carti_download} from "@linogaliana/cartiflette-js"
carti_download({
value: "${value}",
borders: "${selectedlevel}",
vectorfileFormat: "${format}",
year: ${year},
crs: 4326,
filter_by: "DEPARTEMENT",
source: "EXPRESS-COG-CARTO-TERRITOIRE"
});
`;
case "R":
return md`
library(cartiflette)
shp_communes <- carti_download(
crs = 4326,
values = c("${value}"),
borders = "${selectedlevel}",
vectorfile_format = "${format}",
filter_by = "DEPARTEMENT",
source = "EXPRESS-COG-CARTO-TERRITOIRE",
year = ${year}
)
`;
default:
throw new Error("Unsupported language specified");
}
}
function annotateFields(x, annotationsSources) {
// Extract the keys from the input object
const fields = Object.keys(x[0]);
// Create a new object with annotated values for each field
const annotatedObject = {};
fields.forEach(field => {
annotatedObject[field] = annotateColumn(field, annotationsSources);
});
// Return an array where the first element is the annotated object and the second is the original object
return [annotatedObject, ...x];
}
function annotateColumn(columnName, annotationsSources) {
const sourceData = annotationsSources.filter(d => d.variable == columnName)[0];
const sourceSelected = sourceData['source'];
const url = sourceData['url'];
const annotation = sourceData['note'];
// Generate the HTML with a colored footnote, and add an anchor tag to link to the source documentation
return html`
<span style="font-weight: bold;"><span style="background-color: #ffcc00; color: black; padding: 2px 5px; border-radius: 3px; cursor: pointer;">
<a href="${url}" target="_blank" style="color: black; text-decoration: none;" title="${sourceSelected} - ${annotation}">
${annotation}
</a>
</span></span>
`;
}
Récupérer un fond de carte France entière
Récupérer un fond de carte sur une emprise limitée
// spanner pour les exemples de code
// là si on a une carte,
// pas là si on affiche les métadonnées (pour laisser plus de places)
right_spanner_map_france = (choice_print_map_france == "Carte") ? spanner_code_france : ""
spanner_code_france = html`
<div name="code-bloc" class="code-bloc">
<div name="choice-code" style="grid-area: choice-code; position: relative;">
${viewof langage_requete}
</div>
<div name="code" style="grid-area: code; position: relative;" class="code">
<span class="code-bloc-title">
Comment faire en <code>${langage}</code> ${logo[langage.toLowerCase()]}
</span>
<span class="code-content">
${print_program_france(langage, selectedlevel, format, year, drom_rapproches, simplification_percent)}
</span>
</div>
</div>
`
grid_structure = (choice_print_map_france == "Carte") ?
`'selectors map choice-code'
'. map code'
'. map .'
'. map .'
'download map .'`
:
`'selectors map info'
'. map info'
'. map info'
'download map info'
'code code info'`
grid_repartition = (choice_print_map_france == "Carte") ?
'23% 45% 32%' :
'23% 65% 12%'
// Source: https://observablehq.com/@mbostock/dashboard
grid = {
let grid ;
if (availableWidth>smallScreen){
grid = html`
<div class="cartiflette-example" style="
margin: 0;
border: none;
display: grid;
width: ${availableWidth};
grid-template-areas: ${grid_structure};
grid-template-columns: ${grid_repartition};
">
<div name="selectors" style="grid-area: selectors; position: relative;">
${viewof choice_print_map_france}
${viewof year}
${viewof selectedlevel}
${viewof format}
${viewof simplification_percent}
${viewof drom_rapproches}
</div>
<div name="map" style="grid-area: map; position: relative;">
${object_print_map_france}
</div>
${right_spanner_map_france}
<div name="download-button" style="grid-area: download; position: relative;">
${button_france}
</div>
</div>
`
return grid
}
grid = html`
<div class="cartiflette-example" style="
margin: 0;
border: none;
display: grid;
width: ${availableWidth};
grid-template-areas:
'selectors'
'map'
'download'
'choice-code'
'code';
grid-template-rows: auto auto auto auto auto;
grid-gap: 10px;
">
<div name="selectors" style="grid-area: selectors; position: relative;">
${viewof choice_print_map_france}
${viewof year}
${viewof selectedlevel}
${viewof format}
${viewof simplification_percent}
${viewof drom_rapproches}
</div>
<div name="map" style="grid-area: map; position: relative;">
${object_print_map_france}
</div>
<div name="download-button" style="grid-area: download; position: relative;">
${button_france}
</div>
<div name="code-bloc" class="code-bloc">
<div name="choice-code" style="grid-area: choice-code; position: relative;">
${viewof langage_requete}
</div>
<div name="code" style="grid-area: code; position: relative;" class="code">
<span class="code-bloc-title">
Comment faire en <code>${langage}</code> ${logo[langage.toLowerCase()]}
</span>
<span class="code-content">
${print_program_france(langage, selectedlevel, format, year, drom_rapproches, simplification_percent)}
</span>
</div>
</div>
</div>
`
return grid
}
grid_departements = {
let grid ;
if (availableWidth>smallScreen){
grid = html`
<div style="
margin: 0;
border: none;
display: grid;
width: ${availableWidth};
grid-template-areas:
'selectors_departements map_departements choice-code_departements'
'. map_departements code_departements'
'. map_departements .'
'. map_departements .'
'download_departements map_departements .';
grid-template-columns: 23% 45% 32%;
">
<div name="selectors_departements" style="grid-area: selectors_departements; position: relative;">
${viewof choice_print_map_departement}
${viewof selected_departements}
${year_emprise}
${format_emprise}
${simplification_percent_emprise}
${viewof arrondissement}
</div>
<div name="map_departements" style="grid-area: map_departements; position: relative;">
${object_print_map_departement}
</div>
<div name="code-bloc-departement" class="code-bloc">
<div name="choice-code_departements" style="grid-area: choice-code_departements; position: relative;">
${language_emprise_departements}
</div>
<div name="code_departements" style="grid-area: code_departements; position: relative;" class="code">
<span class="code-bloc-title">
Comment faire en <code>${langage_departements}</code> ${logo[langage_departements.toLowerCase()]}
</span>
<span class="code-content">
${print_program_departement_single(langage_departements, selected_departements, selectedlevel, format, year)}
</span>
</div>
</div>
<div name="download-button_departements" style="grid-area: download_departements; position: relative;">
${button_departements}
</div>
</div>
`
return grid
}
grid = html`
<div style="
margin: 0;
border: none;
display: grid;
width: ${availableWidth};
grid-template-areas:
'selectors_departements'
'map_departements'
'download_departements'
'choice-code_departements'
'code_departements';
grid-template-rows: auto auto auto auto auto;
grid-gap: 10px;
">
<div name="selectors_departements" style="grid-area: selectors_departements; position: relative;">
${viewof choice_print_map_departement}
${viewof selected_departements}
${year_emprise}
${format_emprise}
${simplification_percent_emprise}
${viewof arrondissement}
</div>
<div name="map_departements" style="grid-area: map_departements; position: relative;">
${object_print_map_departement}
</div>
<div name="download-button_departements" style="grid-area: download_departements; position: relative;">
${button_departements}
</div>
<div name="code-bloc-departement" class="code-bloc">
<div name="choice-code_departements" style="grid-area: choice-code_departements; position: relative;">
${language_emprise_departements}
</div>
<div name="code_departements" style="grid-area: code_departements; position: relative;" class="code">
<span class="code-bloc-title">
Comment faire en <code>${langage_departements}</code> ${logo[langage_departements.toLowerCase()]}
</span>
<span class="code-content">
${print_program_departement_single(langage_departements, selected_departements, selectedlevel, format, year)}
</span>
</div>
</div>
</div>
`
return grid
}
map_france = topohelper
.from(
await data_france
)
.project({ proj: l93 })
.view({
tooltip: true,
zoom: true,
size: [
availableWidth > smallScreen ? availableWidth*0.4 : availableWidth*0.95, availableHeight * 0.6
]})
// On affiche la carte ou les métadonnées selon la valeur du bouton radio
object_print_map_france = (choice_print_map_france == "Carte") ?
map_france :
viewof table_attributes_map_france
// Injecter un formattage html pour le tableau de métadonnées
mapping = Object.keys(attributes_map_france[0]).reduce((acc, key) => {
acc[key] = (d) => html`<div>${d}</div>`;
return acc;
}, {})
viewof table_attributes_map_france = Inputs.table(
annotateFields(attributes_map_france, annotationsSources),
{
format: mapping
}
)