Skip to content

Commit 648ce7a

Browse files
authored
Merge pull request #1106 from MTES-MCT/feature/page-friches
Base page friches urbaines
2 parents ed35ca3 + a433923 commit 648ce7a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2323
-272
lines changed

airflow/dags/update_app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ def copy_public_data_land(**context):
427427
to_table="public.public_data_land",
428428
environment=context["params"]["environment"],
429429
custom_columns_type={
430+
"friche_status_details": "jsonb",
430431
"millesimes": "jsonb[]",
431432
"millesimes_by_index": "jsonb[]",
432433
},
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
2+
{{
3+
config(
4+
materialized='table',
5+
indexes=[
6+
{'columns': ['land_id'], 'type': 'btree'},
7+
{'columns': ['land_type'], 'type': 'btree'},
8+
{'columns': ['status'], 'type': 'btree'},
9+
]
10+
)
11+
}}
12+
13+
14+
with statut_counts as (
15+
SELECT
16+
land_id,
17+
land_type,
18+
count(*) as friche_count,
19+
count(*) filter(where friche_statut = 'friche sans projet') as friche_sans_projet_count,
20+
count(*) filter(where friche_statut = 'friche avec projet') as friche_avec_projet_count,
21+
count(*) filter(where friche_statut = 'friche reconvertie') as friche_reconvertie_count,
22+
COALESCE(sum(surface), 0) as friche_surface,
23+
COALESCE(sum(surface) filter(where friche_statut = 'friche reconvertie'), 0) as friche_reconvertie_surface,
24+
COALESCE(sum(surface) filter(where friche_statut = 'friche avec projet'), 0) as friche_avec_projet_surface,
25+
COALESCE(sum(surface) filter(where friche_statut = 'friche sans projet'), 0) as friche_sans_projet_surface
26+
FROM {{ ref('friche_land') }}
27+
GROUP BY
28+
land_id,
29+
land_type
30+
), status as (
31+
SELECT
32+
land_id,
33+
land_type,
34+
CASE
35+
WHEN
36+
friche_sans_projet_surface = 0 AND
37+
friche_avec_projet_surface = 0 AND
38+
friche_reconvertie_surface = 0 THEN 'gisement nul et sans potentiel'
39+
WHEN
40+
/* pas de sans projet, mais avec projet et reconvertie != 0 */
41+
(
42+
friche_sans_projet_surface = 0 AND
43+
friche_avec_projet_surface > 0 AND
44+
friche_reconvertie_surface > 0
45+
) OR (
46+
friche_sans_projet_surface = 0 AND
47+
friche_avec_projet_surface > 0 AND
48+
friche_reconvertie_surface = 0
49+
) OR (
50+
friche_sans_projet_surface = 0 AND
51+
friche_avec_projet_surface = 0 AND
52+
friche_reconvertie_surface > 0
53+
) THEN 'gisement nul car potentiel déjà exploité'
54+
WHEN
55+
friche_sans_projet_surface > 0 AND
56+
friche_avec_projet_surface = 0 AND
57+
friche_reconvertie_surface = 0 THEN 'gisement potentiel et non exploité'
58+
59+
WHEN
60+
/* avec des sans projet, et un autre type */
61+
(
62+
friche_sans_projet_surface > 0 AND
63+
friche_avec_projet_surface > 0 AND
64+
friche_reconvertie_surface > 0
65+
) OR (
66+
friche_sans_projet_surface > 0 AND
67+
friche_avec_projet_surface > 0 AND
68+
friche_reconvertie_surface = 0
69+
) OR (
70+
friche_sans_projet_surface > 0 AND
71+
friche_avec_projet_surface > 0 AND
72+
friche_reconvertie_surface > 0
73+
) OR (
74+
friche_sans_projet_surface > 0 AND
75+
friche_avec_projet_surface = 0 AND
76+
friche_reconvertie_surface > 0
77+
) THEN 'gisement potentiel et en cours d’exploitation'
78+
ELSE 'erreur'
79+
END as status,
80+
friche_surface,
81+
friche_sans_projet_surface,
82+
friche_avec_projet_surface,
83+
friche_reconvertie_surface,
84+
friche_count,
85+
friche_avec_projet_count,
86+
friche_sans_projet_count,
87+
friche_reconvertie_count
88+
89+
90+
FROM statut_counts
91+
)
92+
SELECT
93+
land_id,
94+
land_type,
95+
status,
96+
friche_surface,
97+
friche_reconvertie_surface,
98+
friche_avec_projet_surface,
99+
friche_sans_projet_surface,
100+
friche_count,
101+
friche_reconvertie_count,
102+
friche_avec_projet_count,
103+
friche_sans_projet_count
104+
FROM
105+
status
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
version: 2
3+
4+
models:
5+
- name: land_friche_status
6+
columns:
7+
- name: status
8+
data_tests:
9+
- accepted_values:
10+
values: [
11+
'gisement nul et sans potentiel',
12+
'gisement nul car potentiel déjà exploité',
13+
'gisement potentiel et non exploité',
14+
'gisement potentiel et en cours d’exploitation'
15+
]

airflow/include/sql/sparte/models/for_app/administration/for_app_land.sql

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ SELECT
1111
name,
1212
{{ m2_to_ha('land.surface') }} as surface,
1313
'ha' as surface_unit,
14+
ARRAY[
15+
ST_XMin(ST_Transform(geom, 4326)),
16+
ST_YMin(ST_Transform(geom, 4326)),
17+
ST_XMax(ST_Transform(geom, 4326)),
18+
ST_YMax(ST_Transform(geom, 4326))
19+
] as bounds,
20+
ARRAY[
21+
ST_XMin(ST_Transform(ST_buffer(geom, 0.2), 4326)),
22+
ST_YMin(ST_Transform(ST_buffer(geom, 0.2), 4326)),
23+
ST_XMax(ST_Transform(ST_buffer(geom, 0.2), 4326)),
24+
ST_YMax(ST_Transform(ST_buffer(geom, 0.2), 4326))
25+
] as max_bounds,
1426
ST_Transform(geom, 4326) as geom,
1527
ST_Transform(simple_geom, 4326) as simple_geom,
1628
{{ m2_to_ha('artif.surface') }} as surface_artif,
@@ -19,9 +31,9 @@ SELECT
1931
land_ocsge_status.status as ocsge_status,
2032
land_ocsge_status.has_ocsge as has_ocsge,
2133
land_zonages.zonage_count > 0 as has_zonage,
22-
land_friche.friche_count > 0 as has_friche,
23-
land_friche.friche_source_producteurs as friche_source_producteurs,
24-
land_friche.friche_natures as friche_natures,
34+
friche_status.friche_count > 0 as has_friche,
35+
friche_status.status as friche_status,
36+
friche_status.status_details as friche_status_details,
2537
land_millesimes.millesimes as millesimes,
2638
land_millesimes_by_index.millesimes_by_index as millesimes_by_index,
2739
land.child_land_types,
@@ -91,12 +103,21 @@ LEFT JOIN LATERAL (
91103
) land_ocsge_status ON true
92104
LEFT JOIN LATERAL (
93105
SELECT
94-
count(*) as friche_count,
95-
array_agg(distinct source_producteur) as friche_source_producteurs,
96-
array_agg(distinct nature) as friche_natures
97-
FROM
98-
{{ ref('friche_land') }}
106+
status,
107+
friche_count,
108+
jsonb_build_object(
109+
'friche_surface', friche_surface / 10000,
110+
'friche_sans_projet_surface', friche_sans_projet_surface / 10000,
111+
'friche_avec_projet_surface', friche_avec_projet_surface / 10000,
112+
'friche_reconvertie_surface', friche_reconvertie_surface / 10000,
113+
'friche_count', friche_count,
114+
'friche_sans_projet_count', friche_sans_projet_count,
115+
'friche_avec_projet_count', friche_avec_projet_count,
116+
'friche_reconvertie_count', friche_reconvertie_count
117+
) as status_details
118+
FROM {{ ref('land_friche_status') }}
99119
WHERE
100-
friche_land.land_id = land.land_id AND
101-
friche_land.land_type = land.land_type
102-
) land_friche ON true
120+
land_friche_status.land_id = land.land_id AND
121+
land_friche_status.land_type = land.land_type
122+
123+
) friche_status ON true

assets/scripts/components/charts/ChartDataSource.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const SOURCES_DETAILS: Record<string, { label: string; html: string }> = {
5353
label: 'SITADEL',
5454
html: `Base des permis de construire et autres autorisations d'urbanisme (SITADEL) produite par le Ministère de la Transition écologique.`
5555
},
56+
cartofriches: {
57+
label: 'Cartofriches',
58+
html: `Cartofriches utilise des données nationales (BASIAS et BASOL1, appels à projets...) pour assurer une pré-identification des friches sur tout le territoire. Il a vocation à consolider ce recensement avec la participation des acteurs locaux au plus près du terrain, en intégrant les données des observatoires locaux et des études de recensement portées par des acteurs de l’aménagement. <a href='https://cartofriches.cerema.fr/cartofriches/' target='_blank' rel='noopener'>En savoir plus</a>`
59+
}
5660
};
5761

5862
interface ChartDataSourceProps {

assets/scripts/components/charts/ChartDataTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const ChartDataTable: React.FC<ChartDataTableProps> = ({ data, title }) => {
1818

1919
const { headers, rows } = data;
2020
return (
21-
<div className="fr-table--sm fr-table fr-table--bordered fr-mb-0 fr-mt-0">
21+
<div className="fr-table--sm fr-table fr-table--bordered fr-table--no-caption fr-mb-0 fr-mt-0">
2222
<div className="fr-table__wrapper">
2323
<div className="fr-table__container">
2424
<div className="fr-table__content">

assets/scripts/components/charts/GenericChart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type GenericChartProps = {
2828
isLoading?: boolean;
2929
error?: any;
3030
showToolbar?: boolean;
31-
sources?: string[]; // ['insee', 'majic', 'gpu', 'lovac', 'ocsge', 'rpls', 'sitadel']
31+
sources?: string[]; // ['insee', 'majic', 'gpu', 'lovac', 'ocsge', 'rpls', 'sitadel', 'cartofriches']
3232
children?: React.ReactNode;
3333
showDataTable?: boolean;
3434
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
3+
import { useGetChartConfigQuery } from '@services/api';
4+
import GenericChart from '../GenericChart';
5+
6+
type FrichesChartProps = {
7+
id: string;
8+
land_id: string;
9+
land_type: string;
10+
isMap?: boolean;
11+
params?: object;
12+
containerProps?: object;
13+
sources?: string[];
14+
children?: React.ReactNode;
15+
showDataTable?: boolean;
16+
};
17+
18+
export const FrichesChart = ({
19+
id,
20+
land_id,
21+
land_type,
22+
isMap = false,
23+
params,
24+
containerProps,
25+
sources = [],
26+
children,
27+
showDataTable = false
28+
} : FrichesChartProps) => {
29+
const { data, isLoading, error } = useGetChartConfigQuery({ id, land_id, land_type, ...params })
30+
31+
return (
32+
<GenericChart
33+
isMap={isMap}
34+
chartOptions={data}
35+
containerProps={containerProps}
36+
isLoading={isLoading}
37+
error={error}
38+
sources={sources}
39+
showDataTable={showDataTable}
40+
>
41+
{children}
42+
</GenericChart>
43+
)
44+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import styled from 'styled-components';
3+
4+
const NoticeBody = styled.div`
5+
flex-direction: column;
6+
display: flex;
7+
gap: 0.5rem;
8+
`;
9+
10+
11+
const FricheStatus: React.FC = () => {
12+
return (
13+
<div className="fr-notice fr-notice--info fr-my-3w">
14+
<div className="fr-container--fluid fr-p-3w">
15+
<NoticeBody className="fr-notice__body flex-column">
16+
<p className="fr-notice__title">Données de friches non disponibles.</p>
17+
<p className="fr-notice__desc fr-text--sm">Les données de friches issues des données Cartofriches ne sont pas disponibles sur ce territoire.</p>
18+
</NoticeBody>
19+
</div>
20+
</div>
21+
);
22+
};
23+
24+
export default FricheStatus;

assets/scripts/components/layout/Dashboard.tsx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ import RapportLocal from '@components/pages/RapportLocal';
2020
import { Artificialisation } from '@components/pages/Artificialisation';
2121
import Update from '@components/pages/Update';
2222
import Downloads from '@components/pages/Downloads';
23+
import { Friches } from '@components/pages/Friches';
2324
import RouteWrapper from '@components/ui/RouteWrapper';
2425
import ConsoCorrectionStatus, { ConsoCorrectionStatusEnum } from '@components/features/status/ConsoCorrectionStatus';
2526
import OcsgeStatus, { OcsgeStatusEnum } from '@components/features/status/OcsgeStatus';
2627
import LogementVacantStatus from '@components/features/status/LogementVacantStatus';
28+
import FricheStatus from '@components/features/status/FricheStatus';
2729

2830
interface DashboardProps {
2931
projectId: string;
@@ -59,7 +61,8 @@ const Dashboard: React.FC<DashboardProps> = ({ projectId }) => {
5961
}
6062
);
6163

62-
const { ocsge_status, has_ocsge } = landData || {};
64+
const { ocsge_status, has_ocsge, has_friche } = landData || {};
65+
6366
const { urls, consommation_correction_status, logements_vacants_available } = projectData || {};
6467

6568
const isOpen = useSelector((state: RootState) => selectIsNavbarOpen(state));
@@ -172,6 +175,24 @@ const Dashboard: React.FC<DashboardProps> = ({ projectId }) => {
172175
</RouteWrapper>
173176
}
174177
/>
178+
<Route
179+
path={urls.friches}
180+
element={
181+
<RouteWrapper
182+
title="Friches"
183+
showPage={has_friche}
184+
showStatus={!has_friche}
185+
status={
186+
<FricheStatus />
187+
}
188+
>
189+
<Friches
190+
projectData={projectData}
191+
landData={landData}
192+
/>
193+
</RouteWrapper>
194+
}
195+
/>
175196
<Route
176197
path={urls.rapportLocal}
177198
element={

0 commit comments

Comments
 (0)