Data Engineering
Evaluation de sentier de rendonnée
Cette requête PostgreSQL analyse des sentiers de randonnée en évaluant leurs caractéristiques géographiques (élévation, points de vue, météo). Elle découpe les sentiers en segments d'un kilomètre et calcule un score scénique pour chaque sentier.
WITH trail_segments AS (
-- Break trails into 1km segments for detailed analysis
SELECT
t.trail_id,
t.trail_name,
ST_LineSubstring(
t.geometry,
generate_series(0, ceil(ST_Length(t.geometry::geography)/1000)::integer)::float/ceil(ST_Length(t.geometry::geography)/1000),
LEAST(((generate_series(0, ceil(ST_Length(t.geometry::geography)/1000)::integer) + 1)::float/ceil(ST_Length(t.geometry::geography)/1000)), 1)
) AS segment_geom
FROM trails t
),
elevation_stats AS (
-- Calculate elevation statistics for each segment
SELECT
ts.trail_id,
ts.segment_geom,
ST_Summarystats(ST_Clip(dem.rast, ts.segment_geom)) AS elev_stats
FROM trail_segments ts
CROSS JOIN LATERAL (
SELECT rast FROM elevation_dem
WHERE ST_Intersects(rast, ts.segment_geom)
) dem
),
scenic_points AS (
-- Find scenic viewpoints along trails
SELECT
ts.trail_id,
ts.trail_name,
COUNT(v.viewpoint_id) AS viewpoint_count,
array_agg(DISTINCT v.viewpoint_type) AS viewpoint_types,
ST_CollectionExtract(
ST_Collect(ST_Buffer(v.geom, 100)::geometry),
3
) AS viewshed_area
FROM trail_segments ts
JOIN viewpoints v ON ST_DWithin(ts.segment_geom::geography, v.geom::geography, 200)
GROUP BY ts.trail_id, ts.trail_name
),
weather_exposure AS (
-- Analyze weather exposure risk using terrain and historical data
SELECT
ts.trail_id,
sum(CASE
WHEN ST_Value(w.wind_rast, ST_Centroid(ts.segment_geom)) > 30
AND e.elev_stats->>'max' > 2000
THEN 1 ELSE 0
END) AS high_wind_exposure_segments,
avg(ST_Value(w.precipitation_rast, ST_Centroid(ts.segment_geom))) AS avg_annual_precipitation
FROM trail_segments ts
JOIN elevation_stats e ON ts.trail_id = e.trail_id
CROSS JOIN LATERAL (
SELECT rast AS wind_rast, rast AS precipitation_rast
FROM weather_data
WHERE ST_Intersects(rast, ts.segment_geom)
) w
GROUP BY ts.trail_id
)
-- Final query combining all analyses
SELECT
t.trail_name,
t.difficulty_level,
ST_Length(t.geometry::geography)/1000 AS length_km,
sp.viewpoint_count,
sp.viewpoint_types,
ST_Area(sp.viewshed_area::geography)/1000000 AS viewshed_area_km2,
we.high_wind_exposure_segments,
we.avg_annual_precipitation,
CASE
WHEN we.high_wind_exposure_segments > 3 THEN 'High wind risk'
WHEN we.avg_annual_precipitation > 1500 THEN 'High precipitation risk'
ELSE 'Normal conditions'
END AS weather_risk_category,
-- Calculate a "scenic rating" based on various factors
(
(sp.viewpoint_count * 2) +
(ST_Area(sp.viewshed_area::geography)/1000000) +
(CASE WHEN e.elev_stats->>'max' > 2500 THEN 5 ELSE 0 END)
)::numeric(10,2) AS scenic_rating
FROM trails t
JOIN scenic_points sp ON t.trail_id = sp.trail_id
JOIN weather_exposure we ON t.trail_id = we.trail_id
JOIN elevation_stats e ON t.trail_id = e.trail_id
WHERE
-- Filter for trails that meet certain criteria
ST_Length(t.geometry::geography) > 5000 -- Longer than 5km
AND EXISTS (
SELECT 1
FROM parking_areas p
WHERE ST_DWithin(
ST_StartPoint(t.geometry)::geography,
p.geom::geography,
1000
)
)
ORDER BY scenic_rating DESC, length_km;
Matching et comparaison d'entitées graphique
Le script compare les zones géographiques de deux ensembles de polygones en calculant la différence entre la somme des surfaces de ces polygones, en fonction de types spécifiques, et les insère dans une table de comparaison.
WITH route_sections AS (
-- Break urban routes into 500m sections
SELECT
r.route_id,
r.route_name,
ST_LineSubstring(
r.geom,
generate_series(0, ceil(ST_Length(r.geom::geography)/500)::integer)::float/ceil(ST_Length(r.geom::geography)/500),
LEAST(((generate_series(0, ceil(ST_Length(r.geom::geography)/500)::integer) + 1)::float/ceil(ST_Length(r.geom::geography)/500)), 1)
) AS section_geom
FROM urban_routes r
),
traffic_density AS (
-- Calculate traffic patterns for each section
SELECT
rs.route_id,
rs.section_geom,
ST_Summarystats(ST_Clip(traffic.rast, rs.section_geom)) AS traffic_stats
FROM route_sections rs
CROSS JOIN LATERAL (
SELECT rast FROM traffic_density_map
WHERE ST_Intersects(rast, rs.section_geom)
) traffic
),
poi_clusters AS (
-- Find points of interest along routes
SELECT
rs.route_id,
rs.route_name,
COUNT(p.poi_id) AS poi_count,
array_agg(DISTINCT p.poi_category) AS poi_types,
ST_CollectionExtract(
ST_Collect(ST_Buffer(p.geom, 50)::geometry),
3
) AS service_area
FROM route_sections rs
JOIN points_of_interest p ON ST_DWithin(rs.section_geom::geography, p.geom::geography, 100)
GROUP BY rs.route_id, rs.route_name
),
population_density AS (
-- Analyze population density along routes
SELECT
rs.route_id,
sum(CASE
WHEN ST_Value(pd.density_rast, ST_Centroid(rs.section_geom)) > 5000
AND t.traffic_stats->>'max' > 1000
THEN 1 ELSE 0
END) AS high_density_sections,
avg(ST_Value(pd.density_rast, ST_Centroid(rs.section_geom))) AS avg_population_density
FROM route_sections rs
JOIN traffic_density t ON rs.route_id = t.route_id
CROSS JOIN LATERAL (
SELECT rast AS density_rast
FROM population_data
WHERE ST_Intersects(rast, rs.section_geom)
) pd
GROUP BY rs.route_id
)
SELECT
r.route_name,
r.transport_type,
ST_Length(r.geom::geography)/1000 AS length_km,
pc.poi_count,
pc.poi_types,
ST_Area(pc.service_area::geography)/1000000 AS service_area_km2,
pd.high_density_sections,
pd.avg_population_density,
CASE
WHEN pd.high_density_sections > 5 THEN 'High congestion risk'
WHEN pd.avg_population_density > 8000 THEN 'High usage area'
ELSE 'Normal capacity'
END AS density_category,
(
(pc.poi_count * 1.5) +
(ST_Area(pc.service_area::geography)/1000000) +
(CASE WHEN t.traffic_stats->>'max' > 1500 THEN 3 ELSE 0 END)
)::numeric(10,2) AS route_importance_score
FROM urban_routes r
JOIN poi_clusters pc ON r.route_id = pc.route_id
JOIN population_density pd ON r.route_id = pd.route_id
JOIN traffic_density t ON r.route_id = t.route_id
WHERE
ST_Length(r.geom::geography) > 2000 -- Longer than 2km
AND EXISTS (
SELECT 1
FROM transport_hubs h
WHERE ST_DWithin(
ST_StartPoint(r.geom)::geography,
h.geom::geography,
500
)
)
ORDER BY route_importance_score DESC, length_km;