Recette #17
Railways vs Populated Places

Février 2011


Précédent

Table des matières

Suivant


Le problème

ici, on utilisera également la table railways.
Il s'agit par contre d'une recette très épicée: nous passons à la vitesse supérieure.
Comme vous vous en doutez, calculer des distances entre un rail et des Populated Places n'est pas si difficile.
On va donc introduire un peu de complexité au problème.

Imaginons la classification suivante:

Class

Min. distance

Max. distance

A-class

0 Km

1 Km

B-class

1 Km

2.5 Km

C-class

2.5 Km

5 Km

D-class

5 Km

10 Km

E-class

10 Km

20 Km

Le problème que vous devez résoudre est le suivant:

  • identifier chaque Populated Place situé à moins de 20 Km d'un Railway.

  • identifier la classe de distance pour chacune de ces Populated Places.


Railway

PopulatedPlace

A class [< 1Km]

B class [< 2.5Km]

C class [< 5Km]

D class [< 10Km]

E class [< 20Km]

Ferrovia Adriatica

Zapponeta

NULL

NULL

NULL

NULL

1

Ferrovia Adriatica

Villamagna

NULL

NULL

NULL

NULL

1

Ferrovia Adriatica

Villalfonsina

NULL

NULL

NULL

1

0

Ferrovia Adriatica

Vasto

1

0

0

0

0

...

...

...

...

...

...

...



SELECT rw.name AS Railway,
  pp_e.name AS PopulatedPlace,
  (ST_Distance(rw.geometry,
    Transform(pp_a.geometry, 23032)) <= 1000.0)
      AS "A class [< 1Km]",
  (ST_Distance(rw.geometry,
    Transform(pp_b.geometry, 23032)) > 1000.0)
      AS "B class [< 2.5Km]",
  (ST_Distance(rw.geometry,
    Transform(pp_c.geometry, 23032)) > 2500.0)
      AS "C class [< 5Km]",
  (ST_Distance(rw.geometry,
    Transform(pp_d.geometry, 23032)) > 5000.0)
      AS "D class [< 10Km]",
  (ST_Distance(rw.geometry,
    Transform(pp_e.geometry, 23032)) > 10000.0)
      AS "E class [< 20Km]"
FROM railways AS rw
JOIN populated_places AS pp_e ON (
  ST_Distance(rw.geometry,
    Transform(pp_e.geometry, 23032)) <= 20000.0
  AND pp_e.id IN (
    SELECT pkid
    FROM idx_populated_places_geometry
    WHERE pkid MATCH RTreeIntersects(
      MbrMinX(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMinY(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMaxX(
        Transform(
          ST_Envelope(rw.geometry), 4236)),
      MbrMaxY(
        Transform(
          ST_Envelope(rw.geometry), 4326)))))
LEFT JOIN populated_places AS pp_d ON (
  pp_e.id = pp_d.id
  AND ST_Distance(rw.geometry,
    Transform(pp_d.geometry, 23032)) <= 10000.0
  AND pp_d.id IN (
    SELECT pkid
    FROM idx_populated_places_geometry
    WHERE pkid MATCH RTreeIntersects(
      MbrMinX(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMinY(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMaxX(
        Transform(
          ST_Envelope(rw.geometry), 4236)),
      MbrMaxY(
        Transform(
          ST_Envelope(rw.geometry), 4326)))))
LEFT JOIN populated_places AS pp_c ON (
  pp_d.id = pp_c.id
  AND ST_Distance(rw.geometry,
    Transform(pp_c.geometry, 23032)) <= 5000.0
  AND pp_c.id IN (
    SELECT pkid
    FROM idx_populated_places_geometry
    WHERE pkid MATCH RTreeIntersects(
      MbrMinX(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMinY(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMaxX(
        Transform(
          ST_Envelope(rw.geometry), 4236)),
      MbrMaxY(
        Transform(
          ST_Envelope(rw.geometry), 4326)))))
LEFT JOIN populated_places AS pp_b ON (
  pp_c.id = pp_b.id
  AND ST_Distance(rw.geometry,
    Transform(pp_b.geometry, 23032)) <= 2500.0
  AND pp_b.id IN (
    SELECT pkid
    FROM idx_populated_places_geometry
    WHERE pkid MATCH RTreeIntersects(
      MbrMinX(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMinY(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMaxX(
        Transform(
          ST_Envelope(rw.geometry), 4236)),
      MbrMaxY(
        Transform(
          ST_Envelope(rw.geometry), 4326)))))
LEFT JOIN populated_places AS pp_a ON (
  pp_b.id = pp_a.id
  AND ST_Distance(rw.geometry,
    Transform(pp_a.geometry, 23032)) <= 1000.0
  AND pp_a.id IN (
    SELECT pkid
    FROM idx_populated_places_geometry
    WHERE pkid MATCH RTreeIntersects(
      MbrMinX(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMinY(
        Transform(
          ST_Envelope(rw.geometry), 4326)),
      MbrMaxX(
        Transform(
          ST_Envelope(rw.geometry), 4236)),
      MbrMaxY(
        Transform(
          ST_Envelope(rw.geometry), 4326)))));

Oui, cette requête est complexe et intimidante.
Cependant, en y regardant de plus près, elle reste abordable.
Vous connaissez déjà l'astuce: disséquer la requête pièce par pièce.

Examinons le squelette de la requète:

SELECT rw.name AS Railway, ...
FROM railways AS rw
JOIN populated_places AS pp_e ON (...)
LEFT JOIN populated_places AS pp_d ON (...)
LEFT JOIN populated_places AS pp_c ON (...)
LEFT JOIN populated_places AS pp_b ON (...)
LEFT JOIN populated_places AS pp_a ON (...);

...
JOIN populated_places AS pp_e ON (
  ST_Distance(rw.geometry,
    Transform(pp_e.geometry, 23032)) <= 20000.0
...

... AND pp_e.id IN (
  SELECT pkid
  FROM idx_populated_places_geometry
  WHERE pkid MATCH RTreeIntersects(
    MbrMinX(
      Transform(
        ST_Envelope(rw.geometry), 4326)),
    MbrMinY(
      Transform(
        ST_Envelope(rw.geometry), 4326)),
    MbrMaxX(
      Transform(
        ST_Envelope(rw.geometry), 4236)),
    MbrMaxY(
      Transform(
        ST_Envelope(rw.geometry), 4326)))
...

Bien, la structure de la requête est maintenant claire:

  • le premier JOIN inclus dans les résultats les Populated Places situés à moins de 20 Km des lignes de train(railway).

  • les autres LEFT JOIN vont tester les autres classes de distances.

  • et chaque LEFT JOIN vérifie scrupuleusement si l'ID des Populated Places est identique à celui de la classe précédente, comme avec: pp_d.id = pp_c.id

  • chaque fois qu'un LEFT JOIN échouera,la valeur NULL correspondante sera insérée dans les résultats.



SELECT rw.name AS Railway,
  pp_e.name AS PopulatedPlace,
  (ST_Distance(rw.geometry,
    Transform(pp_a.geometry, 23032)) <= 1000.0)
      AS "A class [< 1Km]",
...

Juste un dernier point a expliquer:

A vous de jouer, et de pousser plus loin cette analyse.
i.e vous pouvez rajouter des clauses ORDER BY ou WHERE etc... : c'est facile désormais, n'est-ce pas?


Précédent

Table des matières

Suivant


Author: Alessandro Furieri a.furieri@lqt.it
Traduced From English By RIVIERE Romain

This work is licensed under the Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) license.


Permission is granted to copy, distribute and/or modify this document under the terms of the
GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.