Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,49 @@ html`<div class="explorer">
</div>`
```

## Ne garder que les polygones utiles

Pas besoin de télécharger toute une région pour n'en garder que quelques communes : l'argument `where`, une condition SQL, filtre les polygones [selon leurs attributs]{.souligne} (population, intercommunalité…) ou [selon leur géométrie]{.souligne} (emprise, distance à un point), dès le téléchargement.

[Voir un exemple de la vraie vie <i class="fa-solid fa-arrow-right"></i>](use-case/usecase3.qmd){.exemple target="_blank" rel="noopener"}

```{ojs}
//| output: true
html`<div class="explorer">
<div class="explorer-controls">
${viewof affichage_where}
${viewof filtre_where}
${filtre_where == "attribut" ? viewof seuil_where : ""}
${viewof annee_where}
<p class="viz-meta">${description_where}</p>
${telechargements(requete_where)}
</div>
<div class="explorer-viz">${visualisation(donnees_where, affichage_where)}</div>
<div class="explorer-code">${panneauCode(programmes(requete_where, filtre_where == "attribut" ? "grandes_communes" : "camargue"), notesV4)}</div>
</div>`
```

## Les IRIS, à l'intérieur des communes

Pour descendre [sous la commune]{.souligne}, `cartiflette` fournit les [contours IRIS]{.souligne} de l'IGN, à partir de 2025 : des quartiers d'environ 2 000 habitants, le niveau auquel l'Insee diffuse revenus, logement ou emploi. Avec `where`, on ne garde que ceux d'une intercommunalité ou d'un rayon autour d'un point.

[Voir un exemple de la vraie vie <i class="fa-solid fa-arrow-right"></i>](use-case/usecase4.qmd){.exemple target="_blank" rel="noopener"}

```{ojs}
//| output: true
html`<div class="explorer">
<div class="explorer-controls">
${viewof affichage_iris}
${viewof espace_iris}
${viewof annee_iris}
<p class="viz-meta">${espace_iris.description}</p>
${telechargements(requete_iris)}
</div>
<div class="explorer-viz">${visualisation(donnees_iris, affichage_iris)}</div>
<div class="explorer-code">${panneauCode(programmes(requete_iris, "iris"), notesV4)}</div>
</div>`
```

:::

<!-- Réglages : France entière -->
Expand Down Expand Up @@ -132,3 +175,81 @@ donnees_departements = requete_departements.values.length == 0
? {type: "FeatureCollection", features: []}
: d3.json(apiUrl(requete_departements))
```

<!-- Réglages : where (exemples du tutoriel use-case/usecase3.qmd) -->

```{ojs}
viewof affichage_where = Inputs.radio(["Carte", "Métadonnées"], {value: "Carte"})
viewof filtre_where = Inputs.radio(
new Map([["Selon un attribut", "attribut"], ["Selon la géométrie", "geometrie"]]),
{label: "Filtre", value: "attribut"}
)
viewof seuil_where = Inputs.select(
new Map([["2 000 habitants", 2000], ["10 000 habitants", 10000], ["50 000 habitants", 50000]]),
{label: "Population minimale"}
)
viewof annee_where = Inputs.select(annees.filter(a => Number(a) >= PARQUET_FIRST_YEAR), {label: "Millésime"})
```

```{ojs}
// Attribut : les communes d'Occitanie au-dessus d'un seuil de population.
// Géométrie : les communes qui touchent un rectangle autour de la Camargue, à
// cheval sur l'Occitanie (76) et Provence-Alpes-Côte d'Azur (93)
requete_where = filtre_where == "attribut"
? ({
borders: "COMMUNE",
filter_by: "REGION",
values: ["76"],
year: annee_where,
simplification: 80,
where: `POPULATION > ${seuil_where}`
})
: ({
borders: "COMMUNE",
filter_by: "REGION",
values: ["76", "93"],
year: annee_where,
simplification: 80,
where: "ST_Intersects(geometry, ST_MakeEnvelope(4.1, 43.3, 4.9, 43.75))"
})
description_where = filtre_where == "attribut"
? `Les communes d'Occitanie de plus de ${seuil_where.toLocaleString("fr-FR")} habitants.`
: "Les communes d'Occitanie et de Provence-Alpes-Côte d'Azur qui touchent un rectangle autour de la Camargue : longitude de 4,1 à 4,9, latitude de 43,3 à 43,75."
donnees_where = d3.json(apiUrl(requete_where))
```

<!-- Réglages : IRIS (exemples du tutoriel use-case/usecase4.qmd) -->

```{ojs}
viewof affichage_iris = Inputs.radio(["Carte", "Métadonnées"], {value: "Carte"})
espaces_iris = [
{
label: "Toute la Seine-Saint-Denis",
description: "Tous les IRIS de la Seine-Saint-Denis."
},
{
label: "Selon un attribut",
where: "EPT = '200057867'",
description: "Les IRIS de Plaine Commune (Saint-Denis, Aubervilliers, La Courneuve…), d'après le code de leur établissement public territorial."
},
{
label: "Selon la géométrie",
where: "ST_Distance_Sphere(ST_Centroid(geometry), ST_Point(2.3599, 48.9356)) < 3000",
description: "Les IRIS dont le centre est à moins de 3 km de la basilique de Saint-Denis."
}
]
viewof espace_iris = Inputs.radio(espaces_iris, {label: "Espace", format: d => d.label, value: espaces_iris[1]})
viewof annee_iris = Inputs.select(["2026", "2025"], {label: "Millésime"})
```

```{ojs}
requete_iris = ({
borders: "IRIS",
filter_by: "DEPARTEMENT",
values: ["93"],
year: annee_iris,
simplification: 80,
where: espace_iris.where
})
donnees_iris = d3.json(apiUrl(requete_iris))
```
10 changes: 8 additions & 2 deletions src/_globals.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ niveaux = new Map([
```

```{ojs}
// Requête cartiflette : { borders, filter_by, values, year, simplification }.
// route : "geojson" ou "geoparquet" ; extra : paramètres en plus (download)
// Requête cartiflette : { borders, filter_by, values, year, simplification,
// where (facultatif) }. route : "geojson" ou "geoparquet" ; extra : paramètres
// en plus (download)
function apiUrl(q, route = "geojson", extra = {}) {
const params = new URLSearchParams({
year: q.year,
Expand All @@ -44,6 +45,7 @@ function apiUrl(q, route = "geojson", extra = {}) {
...extra
})
for (const v of q.values) params.append("values", v)
if (q.where) params.append("where", q.where)
return `${API}/v1/${route}?${params}`
}
```
Expand All @@ -61,6 +63,10 @@ COLONNES = ({
STATUT: ["ign", "Statut de la commune"],
POPULATION: ["ign", "Population municipale (somme pour les niveaux agrégés)"],
INSEE_COM: ["ign", "Code commune"],
CODE_IRIS: ["ign", "Code IRIS"],
NOM_IRIS: ["ign", "Nom de l'IRIS"],
TYP_IRIS: ["ign", "Type d'IRIS : habitat (H), activité (A), divers (D), commune non découpée (Z)"],
NOM_COM: ["ign", "Nom de la commune"],
INSEE_COG: ["cartiflette", "Code commune, ou d'arrondissement municipal à Paris, Lyon et Marseille"],
AREA: ["cartiflette", "Territoire : métropole ou DROM"],
PAYS: ["cartiflette", "Pays"],
Expand Down
23 changes: 22 additions & 1 deletion src/_programs.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ function pyValues(values) {
: `[${values.map(v => `"${v}"`).join(", ")}]`
}

// Condition where en Python : sur une ligne si elle est courte, sinon en
// chaînes juxtaposées, coupées avant les fonctions ST_, pour tenir dans le panneau
function pyWhere(where, largeur = 45) {
if (where.length <= largeur) return `"${where}"`
const morceaux = [""]
for (const mot of where.split(/(?<=, )(?=ST_)/)) {
const i = morceaux.length - 1
if (morceaux[i] && (morceaux[i] + mot).length > largeur) morceaux.push(mot)
else morceaux[i] += mot
}
return `(\n${morceaux.map(m => ` "${m}"`).join("\n")}\n )`
}

// Découpe une URL d'API en morceaux lisibles (un paramètre par ligne)
function urlParts(url) {
const [base, query] = url.split("?")
Expand All @@ -34,7 +47,7 @@ ${variable} = carti_download(
borders="${q.borders}",
filter_by="${q.filter_by}",
year=${q.year},
simplification=${q.simplification},
simplification=${q.simplification},${q.where ? `\n where=${pyWhere(q.where)},` : ""}
)`,
R: `library(sf)

Expand Down Expand Up @@ -78,6 +91,14 @@ notes = ({
JavaScript: html`${avertissement("JavaScript")}L'API renvoie du GeoJSON (EPSG:4326), utilisable avec d3, Observable Plot, Leaflet ou MapLibre.`
})

// Note Python des explorateurs qui utilisent where ou les IRIS : client 0.4.0
notesV4 = ({
...notes,
Python: html`L'argument <code>where</code> et le niveau IRIS arrivent avec la
version 0.4.0 du client, pas encore publiée sur PyPI. En attendant :
<code>pip install "git+https://github.com/InseeFrLab/cartiflette#subdirectory=python-package/cartiflette"</code>.`
})

// Les packages R et JavaScript ne suivent pas encore les évolutions du client
// Python (GeoParquet, millésimes depuis 2023) : on passe par l'API en attendant
function avertissement(langage) {
Expand Down
29 changes: 29 additions & 0 deletions styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,32 @@ body.quarto-dark a.exemple {
color: #2B1D14;
}
}

// Observable Plot (version de Quarto) impose un fond blanc à ses graphiques et
// légendes : fond transparent, info-bulles de la couleur de la page
svg[class^="plot-"] {
background: transparent !important;
--plot-background: var(--bs-body-bg);

// fill="white" écrit en attribut, que le CSS remplace
g[aria-label="tip"] {
fill: var(--bs-body-bg);
}
}

// Couleurs des cartes qui suivent le thème : polygones sans donnée, accent
// (contours surlignés)
:root {
--carte-vide: #e0e0e0;
--carte-accent: #1f3b8a;
}

body.quarto-dark {
--carte-vide: #4a4a4a;
--carte-accent: #8ab4f8;

// Code dans les titres et les encadrés : même fond que dans le texte
:is(h1, h2, h3, h4, h5, h6, .callout-title-container) code {
background-color: rgba(67, 67, 67, 0.65);
}
}
5 changes: 5 additions & 0 deletions use-case/index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ couches publiées par l'IGN.
: Les communes de plus de 2 000 habitants d'Occitanie, filtrées dès le
téléchargement avec l'argument `where`, qui accepte aussi des filtres
géographiques (emprise, distance).

[Revenus par IRIS et quartiers prioritaires de la petite couronne](usecase4.qmd)
: Les IRIS d'une intercommunalité ou d'un quartier prioritaire, récupérés avec
`where`, puis le revenu médian de l'Insee (Filosofi) cartographié avec les
quartiers prioritaires de la politique de la ville.
Loading
Loading