[1] | 1 | .. _validity: |
---|
| 2 | |
---|
[29] | 3 | Partie 20 : Validité |
---|
[1] | 4 | ==================== |
---|
| 5 | |
---|
[29] | 6 | Dans 90% des cas la réponse à question "pourquoi mes requêtes me renvoit un message d'erreur du type 'TopologyException' error"" est : "un ou plusieurs des arguments passés sont invalides". Ce qui nous conduit à nous demander : que signifie invalide et pourquoi est-ce important ? |
---|
[1] | 7 | |
---|
[29] | 8 | Qu'est-ce que la validité |
---|
| 9 | ------------------------- |
---|
[1] | 10 | |
---|
[29] | 11 | La validité est surtout importante pour les polygones, qui définissent des aires et requiÚrent une bonne structuration. Les lignes sont vraiment simples et ne peuvent pas être invalides ainsi que les points. |
---|
[1] | 12 | |
---|
[29] | 13 | Certaines des rÚgles de validation des polygones semble évidentes, et d'autre semblent arbitraires (et le sont vraiment). |
---|
[1] | 14 | |
---|
[29] | 15 | * Les contours des Polygon doivent être fermés. |
---|
| 16 | * Les contours qui définissent des trous doivent être inclus dans la zone définit par le coutour extérieur. |
---|
| 17 | * Les contours ne doivent pas s'intersecter (ils ne doivent ni se croiser ni se toucher). |
---|
| 18 | * Les contours ne doivent pas toucher les autres contours, sauf en un point unique. |
---|
[1] | 19 | |
---|
[29] | 20 | Les deux derniÚres rÚgles font partie de la catégorie arbitraires. Il y a d'autre moyen de définir des poygones qui sont consistent mais les rÚgles ci-dessus sont celles utilisées dans le standard :term:`OGC` :term:`SFSQL` que respecte PostGIS. |
---|
[1] | 21 | |
---|
[29] | 22 | La raison pour laquelle ces rÚgles sont importants est que les algorythmes de calcul dépendent de cette structuration consistante des arguments. Il est possible de construire des algorythmes qui n'utilise pas cette structuration, mais ces fonctions tendents à être trÚs lentes, étant donné que la premiÚre étape consistera à "analyser et construire des strcuture à l'intérieur des données". |
---|
[1] | 23 | |
---|
[29] | 24 | Voici un exemple de pourquoi cette structuration est importante. Ce polygon n'est pas valide : |
---|
[1] | 25 | |
---|
| 26 | :: |
---|
| 27 | |
---|
| 28 | POLYGON((0 0, 0 1, 2 1, 2 2, 1 2, 1 0, 0 0)); |
---|
| 29 | |
---|
[29] | 30 | Vous pouvez comprendre ce qui n'est pas valide en regardant cette figure : |
---|
[1] | 31 | |
---|
| 32 | .. image:: ./validity/figure_eight.png |
---|
| 33 | |
---|
[29] | 34 | Le contour externe est exactement en forme en 8 avec une intersection au milieux. Notez que la fonction de rendu graphique est tout de même capable d'en afficher l'intérieur, don visuellement cela ressemble bien à une "aire" : deux unités quarré, donc une aire couplant ces deux unités. |
---|
[1] | 35 | |
---|
[29] | 36 | Essayons maintenant de voir ce que pense la base de données de notre polygone : |
---|
[1] | 37 | |
---|
| 38 | .. code-block:: sql |
---|
| 39 | |
---|
| 40 | SELECT ST_Area(ST_GeometryFromText('POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))')); |
---|
| 41 | |
---|
| 42 | :: |
---|
| 43 | |
---|
| 44 | st_area |
---|
| 45 | --------- |
---|
| 46 | 0 |
---|
| 47 | |
---|
[32] | 48 | Que ce passe-t-il ici ? L'algorythme qui calcule l'aire suppose que les contours ne s'intersectent pas. Un contours normal devra toujours avoir une aire qui est bornée (l'intérieur) dans un sens de la ligne du contour (peu importe quelle sens, juste *un* sens). Néanmoins, dans notre figure en 8, le contour externe est à droite de la ligne pour un lobe et à gauche pour l'autre. Cela entraine que les aire qui sont calculées pour chaque lobe annulent la précédente (l'une vaut 1 et l'autre -1) donc le résultat est une "aire de zéro". |
---|
[1] | 49 | |
---|
| 50 | |
---|
[29] | 51 | Détecté la validité |
---|
| 52 | ------------------- |
---|
[1] | 53 | |
---|
[29] | 54 | Dans l'exemple précédent nous avions un polygone que nous **savions** non-valide. Comment déterminer les géométries non valides dans une tables d'un million d'enregistrements ? Avec la fonction :command:`ST_IsValid(geometry)`. Utilisé avec notre polygone précédent, nous abtenons rapidement la réponse : |
---|
[1] | 55 | |
---|
| 56 | .. code-block:: sql |
---|
| 57 | |
---|
| 58 | SELECT ST_IsValid(ST_GeometryFromText('POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))')); |
---|
| 59 | |
---|
| 60 | :: |
---|
| 61 | |
---|
| 62 | f |
---|
| 63 | |
---|
[29] | 64 | Maintenant nous savons que l'entité est non-valide mais nous ne savons pas pourquoi. Nous pouvons utilier la fonction :command:`ST_IsValidReason(geometry)` pour trtouver la cause de non validité : |
---|
[1] | 65 | |
---|
| 66 | .. code-block:: sql |
---|
| 67 | |
---|
| 68 | SELECT ST_IsValidReason(ST_GeometryFromText('POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))')); |
---|
| 69 | |
---|
| 70 | :: |
---|
| 71 | |
---|
| 72 | Self-intersection[1 1] |
---|
| 73 | |
---|
[29] | 74 | Vous remarquerez qu'en plus de la raison (intersection) la localisation de la non validité (coordonnée (1 1)) est aussi renvoyée. |
---|
[1] | 75 | |
---|
[29] | 76 | Nous pouvons aussi utiiliser la fonction :command:`ST_IsValid(geometry)` pour tester nos tables : |
---|
[1] | 77 | |
---|
| 78 | .. code-block:: sql |
---|
| 79 | |
---|
[29] | 80 | -- Trouver tout les polygones non valides et leur problÚme |
---|
[1] | 81 | SELECT name, boroname, ST_IsValidReason(the_geom) |
---|
| 82 | FROM nyc_neighborhoods |
---|
| 83 | WHERE NOT ST_IsValid(the_geom); |
---|
| 84 | |
---|
| 85 | :: |
---|
| 86 | |
---|
| 87 | name | boroname | st_isvalidreason |
---|
| 88 | -------------------------+---------------+----------------------------------------------------------- |
---|
| 89 | Howard Beach | Queens | Self-intersection[597264.083368305 4499924.54228856] |
---|
| 90 | Corona | Queens | Self-intersection[595483.058764138 4513817.95350787] |
---|
| 91 | Steinway | Queens | Self-intersection[593545.572199759 4514735.20870587] |
---|
| 92 | Red Hook | Brooklyn | Self-intersection[584306.820375986 4502360.51774956] |
---|
| 93 | |
---|
| 94 | |
---|
| 95 | |
---|
[29] | 96 | Réparer les invalides |
---|
| 97 | --------------------- |
---|
[1] | 98 | |
---|
[29] | 99 | Commençons par la mauvaise nouvelle : il n'y a aucune garantie de pouvoir corriger une géométrie non valide. Dans le pire des scénarios, vous pouvez utiliser la fonction :command:`ST_IsValid(geometry)` pour identifier les entités non valides, les déplacer dans une autre table, exporter cette table et les réparer à l'aide d'un outils extérieur. |
---|
[1] | 100 | |
---|
[29] | 101 | Voici un exemple de requête SQL qui déplacent les géométries non valides hors de la table principale dans une table à part pour les exporter vers un programme de réparation. |
---|
[1] | 102 | |
---|
| 103 | .. code-block:: sql |
---|
| 104 | |
---|
[29] | 105 | -- Table à part des géométries non-valide |
---|
[1] | 106 | CREATE TABLE nyc_neighborhoods_invalid AS |
---|
| 107 | SELECT * FROM nyc_neighborhoods |
---|
| 108 | WHERE NOT ST_IsValid(the_geom); |
---|
| 109 | |
---|
[29] | 110 | -- Suppression de la table principale |
---|
[1] | 111 | DELETE FROM nyc_neighborhoods |
---|
| 112 | WHERE NOT ST_IsValid(the_geom); |
---|
| 113 | |
---|
[29] | 114 | Un bon outils pour réparer visuellemen des géométries non valide est OpenJump (http://openjump.org) qui contient un outils de validation depuis le menu **Tools->QA->Validate Selected Layers**. |
---|
[1] | 115 | |
---|
[29] | 116 | Maintenant, la bonne nouvelle : un grand nombre de non-validités **peuvent être résolues dans la base de données** en utilisant la fonction : :command:`ST_Buffer`. |
---|
[1] | 117 | |
---|
[29] | 118 | Le coup du Buffer tire avantafe de la maniÚre dont les buffers sont construit : une géométrie bufferisée est une nouvelle géométrie, construite en déplaçant les lignes de la géométrie d'origine. Si vous déplacez les lignes originales par *rien* (zero) alors la nouvelle géométrie aura une structure identique à l'originale, mais puisqu'elle utilise les rÚgles topologiques de l':term:`OGC, elle sera valide. |
---|
[1] | 119 | |
---|
[29] | 120 | Par exemple, voici un cas classique de non-validité - le "polygone de la banane" - un seul contour que crée une zone mais se touche, laissant un "trou" qui n'en est pas un. |
---|
[1] | 121 | |
---|
| 122 | :: |
---|
| 123 | |
---|
| 124 | POLYGON((0 0, 2 0, 1 1, 2 2, 3 1, 2 0, 4 0, 4 4, 0 4, 0 0)) |
---|
| 125 | |
---|
| 126 | .. image:: ./validity/banana.png |
---|
| 127 | |
---|
[29] | 128 | En créant un buffer de zero sur le polygone retourne un polygone :term:`OGC` valide, le contour externe et un contour interne qui touche l'autre en un seul point. |
---|
[1] | 129 | |
---|
| 130 | .. code-block:: sql |
---|
| 131 | |
---|
| 132 | SELECT ST_AsText( |
---|
| 133 | ST_Buffer( |
---|
| 134 | ST_GeometryFromText('POLYGON((0 0, 2 0, 1 1, 2 2, 3 1, 2 0, 4 0, 4 4, 0 4, 0 0))'), |
---|
| 135 | 0.0 |
---|
| 136 | ) |
---|
| 137 | ); |
---|
| 138 | |
---|
| 139 | :: |
---|
| 140 | |
---|
| 141 | POLYGON((0 0,0 4,4 4,4 0,2 0,0 0),(2 0,3 1,2 2,1 1,2 0)) |
---|
| 142 | |
---|
| 143 | .. note:: |
---|
| 144 | |
---|
[29] | 145 | Le "polygone banane" (ou "coquillage inversé") est un cas où le modÚle topologique de l':term:`OGC` et de ESRI diffÚrent. Le model ESRI considÚre que les contours que se touchent sont non valides et préfÚre la forme de banane pour ce cas de figure. Le modÚle de l'OGC est l'inverse. |
---|
[1] | 146 | |
---|