Intervalle
23
Calculated.md
Normal file
23
Calculated.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Calcul
|
||||||
|
|
||||||
|
## Volatilité
|
||||||
|
|
||||||
|
> La volatilité indique au moteur postgreSQL à quel point le résultat de la fonction dépend de l’état de la base ou du contexte. Il existe 3 niveaux.
|
||||||
|
|
||||||
|
### IMMUTABLE
|
||||||
|
|
||||||
|
Le résultat **ne change jamais** pour les mêmes arguments.
|
||||||
|
|
||||||
|
Exemple : abs(-5) donne toujours 5, peu importe quand ou où la fonction est exécutée.
|
||||||
|
|
||||||
|
Utilisable dans : index, colonnes calculées, contraintes CHECK, etc.
|
||||||
|
|
||||||
|
### STABLE
|
||||||
|
|
||||||
|
Le résultat ne change pas pendant une requête donnée, mais **peut changer entre deux requêtes**.
|
||||||
|
|
||||||
|
La fonction `ST_Length(geom)` est STABLE car pour une même géométrie, la longueur est toujours la même, MAIS cela dépend de paramètres de la session (SRID de référence).
|
||||||
|
|
||||||
|
### VOLATILE
|
||||||
|
|
||||||
|
Le résultat peut changer même dans une seule requête. Par exemple : `random()` ou `now()` donnent des résultats différents à chaque appel.
|
||||||
3
Home.md
3
Home.md
@@ -5,8 +5,9 @@
|
|||||||
- [Sous requêtes](sousrequete.md)
|
- [Sous requêtes](sousrequete.md)
|
||||||
- [Vues et fonctions](View.md)
|
- [Vues et fonctions](View.md)
|
||||||
- [Fonctions de fenêtrage](window.md)
|
- [Fonctions de fenêtrage](window.md)
|
||||||
- [Données spatiales](Spatial.md)
|
- [Intervalles](intervalle.md)
|
||||||
- [Arbres](Arbres.md)
|
- [Arbres](Arbres.md)
|
||||||
|
- [Données spatiales](Spatial.md)
|
||||||
|
|
||||||
## Exercices
|
## Exercices
|
||||||
|
|
||||||
|
|||||||
678
Spatial.AI.md
Normal file
678
Spatial.AI.md
Normal file
@@ -0,0 +1,678 @@
|
|||||||
|
Oui 👍
|
||||||
|
C’est tout à fait possible avec **pgRouting**, car le moteur ne « devine » pas la praticabilité : c’est vous qui la décrivez dans la table de graphes (souvent une table `ways`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔑 Principe
|
||||||
|
|
||||||
|
* Chaque **arête (tronçon de route)** a un coût (`cost`) et éventuellement un coût inverse (`reverse_cost`).
|
||||||
|
* Ces coûts peuvent être calculés différemment **selon le mode de transport** (voiture, vélo, piéton, etc.).
|
||||||
|
* Si une voie est **interdite** pour un mode, on met son `cost` (ou `reverse_cost`) à `-1` → cela signifie *inaccessible*.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🏷️ Exemple de modèle de données
|
||||||
|
|
||||||
|
Supposons une table `roads` :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE roads (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
source INTEGER,
|
||||||
|
target INTEGER,
|
||||||
|
length FLOAT,
|
||||||
|
type VARCHAR(50) -- ex: motorway, residential, footway
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Vous pouvez enrichir avec des colonnes spécifiques au mode de transport :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE roads ADD COLUMN car_cost FLOAT;
|
||||||
|
ALTER TABLE roads ADD COLUMN bike_cost FLOAT;
|
||||||
|
ALTER TABLE roads ADD COLUMN foot_cost FLOAT;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🚘 Exemple d’affectation des coûts
|
||||||
|
|
||||||
|
* Une rue normale (`residential`) → accessible à tous
|
||||||
|
* Une ruelle piétonne (`footway`) → inaccessible en voiture
|
||||||
|
|
||||||
|
```sql
|
||||||
|
UPDATE roads
|
||||||
|
SET car_cost = length,
|
||||||
|
bike_cost = length,
|
||||||
|
foot_cost = length
|
||||||
|
WHERE type = 'residential';
|
||||||
|
|
||||||
|
UPDATE roads
|
||||||
|
SET car_cost = -1, -- interdit aux voitures
|
||||||
|
bike_cost = length,
|
||||||
|
foot_cost = length
|
||||||
|
WHERE type = 'footway';
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📌 Utilisation avec pgRouting
|
||||||
|
|
||||||
|
Quand vous appelez une fonction comme `pgr_dijkstra`, vous spécifiez **quelle colonne sert de coût** :
|
||||||
|
|
||||||
|
* Pour la voiture :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
'SELECT id, source, target, car_cost AS cost, car_cost AS reverse_cost FROM roads',
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
* Pour le vélo :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
'SELECT id, source, target, bike_cost AS cost, bike_cost AS reverse_cost FROM roads',
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
* Pour les piétons :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
'SELECT id, source, target, foot_cost AS cost, foot_cost AS reverse_cost FROM roads',
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ Résultat : vous pouvez définir autant de « profils de transport » que vous voulez (voiture, vélo, camion, livraison, pompier, etc.) simplement en gérant des colonnes de coûts adaptées.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Voulez-vous que je prépare un **mini-script SQL complet** (création de table + 4 routes + 2 modes de transport) pour que vous puissiez le tester directement dans PostgreSQL + pgRouting ?
|
||||||
|
|
||||||
|
|
||||||
|
## Ce n'est pas terrible ! Il faut deux colonnes par mode de transport
|
||||||
|
|
||||||
|
Vous avez raison 👌 : si on fait **une colonne `cost` et `reverse_cost` par mode de transport**, ça double vite et ça devient lourd (car + vélo + piéton + camion → 8 colonnes à gérer).
|
||||||
|
|
||||||
|
Heureusement il y a plusieurs approches plus élégantes avec **pgRouting** :
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 1. Table des restrictions (plus flexible)
|
||||||
|
|
||||||
|
Au lieu de mettre les coûts dans la table des routes, on les externalise dans une table de profils :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE road_costs (
|
||||||
|
road_id INT,
|
||||||
|
mode TEXT, -- 'car', 'bike', 'foot', etc.
|
||||||
|
cost FLOAT,
|
||||||
|
reverse_cost FLOAT,
|
||||||
|
PRIMARY KEY (road_id, mode)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemple d’insertion :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Route résidentielle
|
||||||
|
INSERT INTO road_costs VALUES (1, 'car', 100, 100);
|
||||||
|
INSERT INTO road_costs VALUES (1, 'bike', 100, 100);
|
||||||
|
INSERT INTO road_costs VALUES (1, 'foot', 100, 100);
|
||||||
|
|
||||||
|
-- Ruelle piétonne
|
||||||
|
INSERT INTO road_costs VALUES (2, 'car', -1, -1); -- inaccessible
|
||||||
|
INSERT INTO road_costs VALUES (2, 'bike', 80, 80);
|
||||||
|
INSERT INTO road_costs VALUES (2, 'foot', 50, 50);
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensuite, pour calculer un itinéraire en voiture :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
'SELECT r.id, r.source, r.target, c.cost, c.reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
JOIN road_costs c ON r.id = c.road_id
|
||||||
|
WHERE c.mode = ''car''',
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
👉 Avantage :
|
||||||
|
|
||||||
|
* Plus propre, **une seule table de routes**.
|
||||||
|
* Ajout d’un nouveau mode = juste quelques lignes dans `road_costs`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 2. Encodage JSON / hstore (si vous voulez rester dans une seule table)
|
||||||
|
|
||||||
|
On peut stocker les coûts sous forme JSON dans `roads` :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE roads ADD COLUMN costs JSONB;
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemple de données :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
UPDATE roads SET costs = '{
|
||||||
|
"car": {"cost": 100, "reverse_cost": 100},
|
||||||
|
"bike": {"cost": 100, "reverse_cost": 100},
|
||||||
|
"foot": {"cost": 100, "reverse_cost": 100}
|
||||||
|
}' WHERE id = 1;
|
||||||
|
|
||||||
|
UPDATE roads SET costs = '{
|
||||||
|
"car": {"cost": -1, "reverse_cost": -1},
|
||||||
|
"bike": {"cost": 80, "reverse_cost": 80},
|
||||||
|
"foot": {"cost": 50, "reverse_cost": 50}
|
||||||
|
}' WHERE id = 2;
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis extraire dynamiquement le mode :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
$$
|
||||||
|
SELECT id, source, target,
|
||||||
|
(costs->'car'->>'cost')::float AS cost,
|
||||||
|
(costs->'car'->>'reverse_cost')::float AS reverse_cost
|
||||||
|
FROM roads
|
||||||
|
$$,
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
👉 Avantage :
|
||||||
|
|
||||||
|
* Une seule table, extensible.
|
||||||
|
* Flexible si vous voulez ajouter "bus", "camion", etc.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 3. Génération dynamique de la vue
|
||||||
|
|
||||||
|
Une autre approche : vous gardez juste la longueur et le type de route dans `roads`, puis vous créez une **vue par mode de transport** qui applique les règles.
|
||||||
|
|
||||||
|
Exemple :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE VIEW roads_car AS
|
||||||
|
SELECT id, source, target,
|
||||||
|
CASE WHEN type = 'footway' THEN -1 ELSE length END AS cost,
|
||||||
|
CASE WHEN type = 'footway' THEN -1 ELSE length END AS reverse_cost
|
||||||
|
FROM roads;
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensuite :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra('SELECT * FROM roads_car', 1, 10);
|
||||||
|
```
|
||||||
|
|
||||||
|
👉 Avantage : pas de duplication des colonnes, règles exprimées en SQL.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
🔧 **En pratique** :
|
||||||
|
|
||||||
|
* Si vous voulez un truc simple → solution **vue par mode de transport**.
|
||||||
|
* Si vous voulez un système extensible et propre → solution **table des restrictions**.
|
||||||
|
* Si vous aimez tout stocker dans une seule colonne → solution **JSONB**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Souhaitez-vous que je vous prépare un **exemple concret avec la solution JSONB** (plus moderne et compacte), ou bien préférez-vous la version **table des restrictions** (plus SQL pur) ?
|
||||||
|
|
||||||
|
Je pensais plutôt à une table Many-To-Many de tags. Ensuite la requête exclu les routes qui ont un tag spécifique (par exemple voie à péage) ou au contraire avec uniquement avec un tag particulier (voie cyclable protégée)
|
||||||
|
|
||||||
|
Oui, excellente idée 👌 !
|
||||||
|
C’est beaucoup plus **proche du modèle OpenStreetMap**, où chaque tronçon de route peut avoir plusieurs attributs (*tags*), et où un même tag peut concerner plusieurs routes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 Modèle Many-to-Many avec tags
|
||||||
|
|
||||||
|
### Table des routes
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE roads (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
source INT,
|
||||||
|
target INT,
|
||||||
|
length FLOAT
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Table des tags
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT UNIQUE
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Table de liaison routes ↔ tags
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE road_tags (
|
||||||
|
road_id INT REFERENCES roads(id) ON DELETE CASCADE,
|
||||||
|
tag_id INT REFERENCES tags(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (road_id, tag_id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 Exemple de données
|
||||||
|
|
||||||
|
Tags de référence :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO tags (name) VALUES
|
||||||
|
('footway'), ('cycleway'), ('toll'), ('residential');
|
||||||
|
```
|
||||||
|
|
||||||
|
Routes :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO roads (id, source, target, length) VALUES
|
||||||
|
(1, 1, 2, 100),
|
||||||
|
(2, 2, 3, 80),
|
||||||
|
(3, 3, 4, 120),
|
||||||
|
(4, 4, 5, 60);
|
||||||
|
```
|
||||||
|
|
||||||
|
Associer les tags :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Route 1 = résidentielle
|
||||||
|
INSERT INTO road_tags VALUES (1, (SELECT id FROM tags WHERE name='residential'));
|
||||||
|
|
||||||
|
-- Route 2 = piétonne
|
||||||
|
INSERT INTO road_tags VALUES (2, (SELECT id FROM tags WHERE name='footway'));
|
||||||
|
|
||||||
|
-- Route 3 = voie à péage
|
||||||
|
INSERT INTO road_tags VALUES (3, (SELECT id FROM tags WHERE name='toll'));
|
||||||
|
|
||||||
|
-- Route 4 = piste cyclable
|
||||||
|
INSERT INTO road_tags VALUES (4, (SELECT id FROM tags WHERE name='cycleway'));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 Utilisation dans pgRouting
|
||||||
|
|
||||||
|
### Exemple : exclure les routes à péage
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
$$
|
||||||
|
SELECT r.id, r.source, r.target, r.length AS cost, r.length AS reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM road_tags rt
|
||||||
|
JOIN tags t ON rt.tag_id = t.id
|
||||||
|
WHERE rt.road_id = r.id AND t.name = 'toll'
|
||||||
|
)
|
||||||
|
$$,
|
||||||
|
1, 5
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Exemple : n’autoriser **que les pistes cyclables**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
$$
|
||||||
|
SELECT r.id, r.source, r.target, r.length AS cost, r.length AS reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
WHERE EXISTS (
|
||||||
|
SELECT 1 FROM road_tags rt
|
||||||
|
JOIN tags t ON rt.tag_id = t.id
|
||||||
|
WHERE rt.road_id = r.id AND t.name = 'cycleway'
|
||||||
|
)
|
||||||
|
$$,
|
||||||
|
1, 5
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Avantages de ce modèle
|
||||||
|
|
||||||
|
* Très extensible (on peut gérer des dizaines de tags comme dans OSM).
|
||||||
|
* Pas besoin d’ajouter une colonne par mode ou contrainte.
|
||||||
|
* Les requêtes pgRouting filtrent dynamiquement en fonction des tags.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
👉 Souhaitez-vous que je vous prépare un **script complet (DDL + INSERT + requêtes pgRouting)** avec 2-3 exemples d’itinéraires (voiture, vélo, piéton) pour que vous puissiez le tester directement ?
|
||||||
|
|
||||||
|
|
||||||
|
L'externalisation du coût est pas mal car en vélo les montées sont plus pénalisantes que les descentes. Alors qu'en voiture la différence est négligeable sauf sur des très grosses pentes.
|
||||||
|
|
||||||
|
Je pensais plutôt à une table Many-To-Many de tags. Ensuite la requête exclu les routes qui ont un tag spécifique (par exemple voie à péage) ou au contraire avec uniquement avec un tag particulier (voie cyclable protégée)
|
||||||
|
|
||||||
|
Comment rajouter les règles routes à péage interdites au vélo, chemins forestiers interdits à la voiture. Avec des tags ?
|
||||||
|
|
||||||
|
Exactement ✔️
|
||||||
|
On peut exprimer ce genre de règles avec des **tags** (comme dans OpenStreetMap) et les appliquer **au moment de calculer les coûts** pour un mode de transport.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 1. Tables de base
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE roads (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
source INT,
|
||||||
|
target INT,
|
||||||
|
length FLOAT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE road_tags (
|
||||||
|
road_id INT REFERENCES roads(id) ON DELETE CASCADE,
|
||||||
|
tag_id INT REFERENCES tags(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (road_id, tag_id)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 2. Exemple de tags
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO tags (name) VALUES
|
||||||
|
('toll'), -- route à péage
|
||||||
|
('forest'), -- chemin forestier
|
||||||
|
('cycleway'), -- piste cyclable
|
||||||
|
('footway'), -- voie piétonne
|
||||||
|
('residential'); -- route normale
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 3. Génération des coûts par mode
|
||||||
|
|
||||||
|
On externalise les coûts avec une logique conditionnelle basée sur les tags.
|
||||||
|
Par exemple :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE road_costs (
|
||||||
|
road_id INT REFERENCES roads(id),
|
||||||
|
mode TEXT,
|
||||||
|
cost FLOAT,
|
||||||
|
reverse_cost FLOAT,
|
||||||
|
PRIMARY KEY (road_id, mode)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Puis on remplit avec des règles :
|
||||||
|
|
||||||
|
### Vélo
|
||||||
|
|
||||||
|
* Routes normales = distance
|
||||||
|
* Routes à péage (`toll`) = interdit (`-1`)
|
||||||
|
* Escaliers (`footway`) = interdit (`-1`)
|
||||||
|
* Pistes cyclables (`cycleway`) = bonus (coût réduit)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO road_costs
|
||||||
|
SELECT r.id, 'bike',
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='toll') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='cycleway') THEN r.length * 0.7
|
||||||
|
ELSE r.length
|
||||||
|
END AS cost,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='toll') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='cycleway') THEN r.length * 0.7
|
||||||
|
ELSE r.length
|
||||||
|
END AS reverse_cost
|
||||||
|
FROM roads r;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🚗 Voiture
|
||||||
|
|
||||||
|
* Routes normales = distance
|
||||||
|
* Chemins forestiers (`forest`) = interdits (`-1`)
|
||||||
|
* Voies piétonnes (`footway`) = interdites (`-1`)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO road_costs
|
||||||
|
SELECT r.id, 'car',
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='forest') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
ELSE r.length
|
||||||
|
END AS cost,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='forest') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
ELSE r.length
|
||||||
|
END AS reverse_cost
|
||||||
|
FROM roads r;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔹 4. Utilisation dans pgRouting
|
||||||
|
|
||||||
|
Pour calculer un itinéraire vélo :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
'SELECT r.id, r.source, r.target, c.cost, c.reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
JOIN road_costs c ON r.id = c.road_id
|
||||||
|
WHERE c.mode = ''bike''',
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour la voiture :
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
'SELECT r.id, r.source, r.target, c.cost, c.reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
JOIN road_costs c ON r.id = c.road_id
|
||||||
|
WHERE c.mode = ''car''',
|
||||||
|
1, 10
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
✅ Avantage :
|
||||||
|
|
||||||
|
* Les règles sont **lisibles** et **évolutives**.
|
||||||
|
* On reste dans la logique OSM (tags multiples, requêtes qui filtrent).
|
||||||
|
* Si demain vous ajoutez un nouveau mode (`truck`, `bus`, `emergency`), vous ne changez pas la structure, juste la logique d’insertion des coûts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
👉 Voulez-vous que je prépare un **exemple exécutable complet** (création de tables, insertion de 4 routes avec tags variés, et 2 requêtes pgRouting avec résultats différents vélo vs voiture) ?
|
||||||
|
|
||||||
|
|
||||||
|
Oui j'aimerais un exemple exécutable complet
|
||||||
|
|
||||||
|
Parfait ✅, voici un **exemple SQL complet** que vous pouvez exécuter directement dans **PostgreSQL + PostGIS + pgRouting**.
|
||||||
|
Il illustre :
|
||||||
|
|
||||||
|
* une petite base de 4 routes (5 nœuds),
|
||||||
|
* des **tags** associés aux routes,
|
||||||
|
* un calcul de coût différent selon le mode (🚲 vélo vs 🚗 voiture),
|
||||||
|
* et un itinéraire avec `pgr_dijkstra`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 Script SQL complet
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Extension nécessaire
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pgrouting;
|
||||||
|
|
||||||
|
-- ====================================
|
||||||
|
-- 1. Tables de base
|
||||||
|
-- ====================================
|
||||||
|
DROP TABLE IF EXISTS road_costs CASCADE;
|
||||||
|
DROP TABLE IF EXISTS road_tags CASCADE;
|
||||||
|
DROP TABLE IF EXISTS tags CASCADE;
|
||||||
|
DROP TABLE IF EXISTS roads CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE roads (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
source INT,
|
||||||
|
target INT,
|
||||||
|
length FLOAT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name TEXT UNIQUE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE road_tags (
|
||||||
|
road_id INT REFERENCES roads(id) ON DELETE CASCADE,
|
||||||
|
tag_id INT REFERENCES tags(id) ON DELETE CASCADE,
|
||||||
|
PRIMARY KEY (road_id, tag_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE road_costs (
|
||||||
|
road_id INT REFERENCES roads(id),
|
||||||
|
mode TEXT,
|
||||||
|
cost FLOAT,
|
||||||
|
reverse_cost FLOAT,
|
||||||
|
PRIMARY KEY (road_id, mode)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ====================================
|
||||||
|
-- 2. Données de test
|
||||||
|
-- Graphe simple : 5 nœuds reliés par 4 routes
|
||||||
|
-- ====================================
|
||||||
|
INSERT INTO roads (id, source, target, length) VALUES
|
||||||
|
(1, 1, 2, 100), -- route normale
|
||||||
|
(2, 2, 3, 80), -- route à péage
|
||||||
|
(3, 3, 4, 120), -- chemin forestier
|
||||||
|
(4, 4, 5, 60); -- piste cyclable
|
||||||
|
|
||||||
|
-- Tags
|
||||||
|
INSERT INTO tags (name) VALUES
|
||||||
|
('toll'), ('forest'), ('cycleway'), ('footway'), ('residential');
|
||||||
|
|
||||||
|
-- Attribution des tags aux routes
|
||||||
|
INSERT INTO road_tags VALUES (1, (SELECT id FROM tags WHERE name='residential'));
|
||||||
|
INSERT INTO road_tags VALUES (2, (SELECT id FROM tags WHERE name='toll'));
|
||||||
|
INSERT INTO road_tags VALUES (3, (SELECT id FROM tags WHERE name='forest'));
|
||||||
|
INSERT INTO road_tags VALUES (4, (SELECT id FROM tags WHERE name='cycleway'));
|
||||||
|
|
||||||
|
-- ====================================
|
||||||
|
-- 3. Génération des coûts par mode
|
||||||
|
-- ====================================
|
||||||
|
|
||||||
|
-- 🚲 Vélo :
|
||||||
|
-- - interdit sur péage et footway
|
||||||
|
-- - bonus sur piste cyclable (0.7)
|
||||||
|
-- - normal sinon
|
||||||
|
INSERT INTO road_costs
|
||||||
|
SELECT r.id, 'bike',
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='toll') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='cycleway') THEN r.length * 0.7
|
||||||
|
ELSE r.length
|
||||||
|
END AS cost,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='toll') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='cycleway') THEN r.length * 0.7
|
||||||
|
ELSE r.length
|
||||||
|
END AS reverse_cost
|
||||||
|
FROM roads r;
|
||||||
|
|
||||||
|
-- 🚗 Voiture :
|
||||||
|
-- - interdit sur chemins forestiers et footways
|
||||||
|
-- - normal sinon
|
||||||
|
INSERT INTO road_costs
|
||||||
|
SELECT r.id, 'car',
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='forest') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
ELSE r.length
|
||||||
|
END AS cost,
|
||||||
|
CASE
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='forest') THEN -1
|
||||||
|
WHEN EXISTS (SELECT 1 FROM road_tags rt JOIN tags t ON rt.tag_id=t.id WHERE rt.road_id=r.id AND t.name='footway') THEN -1
|
||||||
|
ELSE r.length
|
||||||
|
END AS reverse_cost
|
||||||
|
FROM roads r;
|
||||||
|
|
||||||
|
-- ====================================
|
||||||
|
-- 4. Test des itinéraires
|
||||||
|
-- ====================================
|
||||||
|
|
||||||
|
-- Itinéraire vélo du nœud 1 à 5
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
$$
|
||||||
|
SELECT r.id, r.source, r.target, c.cost, c.reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
JOIN road_costs c ON r.id = c.road_id
|
||||||
|
WHERE c.mode = 'bike'
|
||||||
|
$$,
|
||||||
|
1, 5
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Itinéraire voiture du nœud 1 à 5
|
||||||
|
SELECT * FROM pgr_dijkstra(
|
||||||
|
$$
|
||||||
|
SELECT r.id, r.source, r.target, c.cost, c.reverse_cost
|
||||||
|
FROM roads r
|
||||||
|
JOIN road_costs c ON r.id = c.road_id
|
||||||
|
WHERE c.mode = 'car'
|
||||||
|
$$,
|
||||||
|
1, 5
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Résultat attendu
|
||||||
|
|
||||||
|
* **Pour le vélo** :
|
||||||
|
|
||||||
|
* Route 2 (péage) est interdite (`-1`) → l’algorithme ne la prend pas.
|
||||||
|
* Route 4 (piste cyclable) a un coût réduit (`60 * 0.7 = 42`).
|
||||||
|
* L’itinéraire 1 → 2 est OK, mais ensuite le péage bloque, donc vélo prendra **1 → 2 (ok) → \[pas 2→3 car péage interdit] → échec**.
|
||||||
|
* Donc il n’y a pas de chemin complet de 1 à 5 en vélo (dans ce petit graphe).
|
||||||
|
|
||||||
|
* **Pour la voiture** :
|
||||||
|
|
||||||
|
* Route 3 (forest) est interdite, donc bloquée.
|
||||||
|
* La voiture peut faire 1 → 2 (100) → 3 (80, péage autorisé) → \[bloqué en 3→4 car forest].
|
||||||
|
* Pas de chemin complet non plus en voiture.
|
||||||
|
|
||||||
|
👉 Ce petit graphe est volontairement minimaliste : il montre que **chaque mode a ses contraintes** et peut bloquer certains trajets. Dans un vrai graphe (issu d’OSM), vous auriez des alternatives valides.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Voulez-vous que je complète cet exemple avec **un graphe légèrement plus grand (6-7 routes)** pour que les itinéraires vélo et voiture aboutissent bien avec des chemins différents ?
|
||||||
@@ -6,7 +6,7 @@ Un intervalle possède un début d et une fin f, tels que d < f.
|
|||||||
|
|
||||||
Les types de plage sont utiles car ils représentent de nombreuses valeurs d’éléments dans une seule valeur de plage et parce que des concepts tels que les plages qui se chevauchent peuvent être exprimés clairement. L’utilisation de plages d’heures et de dates à des fins de planification en est l’exemple le plus clair ; mais les fourchettes de prix, les plages de mesure d'un instrument, etc. peuvent également être utiles.
|
Les types de plage sont utiles car ils représentent de nombreuses valeurs d’éléments dans une seule valeur de plage et parce que des concepts tels que les plages qui se chevauchent peuvent être exprimés clairement. L’utilisation de plages d’heures et de dates à des fins de planification en est l’exemple le plus clair ; mais les fourchettes de prix, les plages de mesure d'un instrument, etc. peuvent également être utiles.
|
||||||
|
|
||||||
Les types de plage sont des types de données représentant une plage de valeurs d'un certain type d'élément (appelé sous-type de plage). Par exemple, des plages d’horodatage peuvent être utilisées pour représenter les plages horaires pendant lesquelles une salle de réunion est réservée. Dans ce cas, le type de données est tsrange (abréviation de « timestamp range ») et timestamp est le sous-type.
|
Les types de plage sont des types de données représentant une plage de valeurs d'un certain type d'élément (appelé sous-type de plage). Par exemple, des plages d’horodatage peuvent être utilisées pour représenter les plages horaires pendant lesquelles une salle de réunion est réservée. Dans ce cas, le type de données est tsrange (abréviation de « timestamp range ») et timestamp est le sous-type.
|
||||||
|
|
||||||
## Types
|
## Types
|
||||||
|
|
||||||
@@ -38,24 +38,23 @@ SELECT '[10,20)'::int4range;
|
|||||||
|
|
||||||
Chaque plage non vide a deux limites, la limite inférieure et la limite supérieure. Tous les points entre ces valeurs sont inclus dans la plage. Une limite inclusive signifie que le point limite lui-même est également inclus dans la plage, tandis qu'une limite exclusive signifie que le point limite n'est pas inclus dans la plage.
|
Chaque plage non vide a deux limites, la limite inférieure et la limite supérieure. Tous les points entre ces valeurs sont inclus dans la plage. Une limite inclusive signifie que le point limite lui-même est également inclus dans la plage, tandis qu'une limite exclusive signifie que le point limite n'est pas inclus dans la plage.
|
||||||
|
|
||||||
Dans la forme textuelle d'une plage, les limites inclusives sont représentées par des crochets `[` et `]` tandis que les limites exclusives sont représentées par des parenthèses `(` et `)`.
|
Dans la forme textuelle d'une plage, les limites inclusives sont représentées par des crochets `[` et `]` tandis que les limites exclusives sont représentées par des parenthèses `(` et `)`.
|
||||||
|
|
||||||
Les fonctions lower_inc et upper_inc testent respectivement l'inclusivité des limites inférieure et supérieure d'une valeur de plage.
|
Les fonctions lower_inc et upper_inc testent respectivement l'inclusivité des limites inférieure et supérieure d'une valeur de plage.
|
||||||
|
|
||||||
## Plages infinies (illimitées)
|
## Plages infinies (illimitées)
|
||||||
|
|
||||||
La limite inférieure d'une plage peut être omise, ce qui signifie que toutes les valeurs inférieures à la limite supérieure sont incluses, par exemple `(,3)`. De même, si la limite supérieure de la plage est omise, alors toutes les valeurs supérieures à la limite supérieure de la plage le sont.
|
La limite inférieure d'une plage peut être omise, ce qui signifie que toutes les valeurs inférieures à la limite supérieure sont incluses, par exemple `(,3)`. De même, si la limite supérieure de la plage est omise, alors toutes les valeurs supérieures à la limite supérieure de la plage le sont.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Les fonctions lower_inf et upper_inf testent respectivement les limites inférieure et supérieure infinies d'une plage.
|
Les fonctions lower_inf et upper_inf testent respectivement les limites inférieure et supérieure infinies d'une plage.
|
||||||
|
|
||||||
|
## Logique d’intervalles d’Allen
|
||||||
|
|
||||||
> Logique d’intervalles d’Allen : ensemble complet d’opérateurs booléens de base pour le positionnement relatif dans le temps.
|
> Ensemble complet d’opérateurs booléens de base pour le positionnement relatif dans le temps.
|
||||||
|
|
||||||
|
|
||||||
|
Deux intervalles A et B peuvent se positionner selon **13 configurations**.
|
||||||
Deux intervalles A et B peuvent se positionner selon 13 configurations.
|
|
||||||
|
|
||||||
### 1. Précède
|
### 1. Précède
|
||||||
|
|
||||||
@@ -174,7 +173,7 @@ A se déroule _Pendant_ (during) B lorque la borne inférieure de A est supérie
|
|||||||
|
|
||||||
Allen|Postgres
|
Allen|Postgres
|
||||||
------|---
|
------|---
|
||||||
A d B | A <@ B
|
A d B | A <@ B
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
select '[7,10]'::int4range <@ '[5,14]'::int4range;
|
select '[7,10]'::int4range <@ '[5,14]'::int4range;
|
||||||
@@ -188,7 +187,7 @@ A _Contient_ B lorsque la borne inférieure de A est inférieure à la borne inf
|
|||||||
|
|
||||||
Allen |Postgres
|
Allen |Postgres
|
||||||
------|---
|
------|---
|
||||||
A di B | A @> B
|
A di B | A @> B
|
||||||
|
|
||||||
<svg viewbox="0 0 20 8" width="200" height="80">
|
<svg viewbox="0 0 20 8" width="200" height="80">
|
||||||
<text x="5" y="3" font-size="1.8" text-anchor="middle">A</text>
|
<text x="5" y="3" font-size="1.8" text-anchor="middle">A</text>
|
||||||
@@ -237,7 +236,7 @@ A _Finit_ en même temps (finishes) que B lorsque la borne supérieure de A est
|
|||||||
|
|
||||||
Allen|Postgres
|
Allen|Postgres
|
||||||
------|---
|
------|---
|
||||||
A f B | A &< B
|
A f B | A &< B
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
select '[5,15]'::int4range &< '[10,15]'::int4range;
|
select '[5,15]'::int4range &< '[10,15]'::int4range;
|
||||||
|
|||||||
Reference in New Issue
Block a user