<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Par-ci par-là - géotraitements</title><link href="https://par-ci.fr/par-l%C3%A0/" rel="alternate"/><link href="https://par-ci.fr/par-l%C3%A0/cat%C3%A9gories/g%C3%A9otraitements/feeds/atom/" rel="self"/><id>https://par-ci.fr/par-là/</id><updated>2024-02-04T20:11:00+01:00</updated><entry><title>Données géographiques : avant de manipuler les géométries</title><link href="https://par-ci.fr/par-l%C3%A0/billets/donn%C3%A9es-g%C3%A9ographiques-avant-de-manipuler-les-g%C3%A9om%C3%A9tries/" rel="alternate"/><published>2024-02-04T20:11:00+01:00</published><updated>2024-02-04T20:11:00+01:00</updated><author><name>namori</name></author><id>tag:par-ci.fr,2024-02-04:/par-là/billets/données-géographiques-avant-de-manipuler-les-géométries/</id><summary type="html">&lt;p class="first last"&gt;Salut ! Je pensais vous montrer dès aujourd’hui quelques petites
manipulations sur des données géographiques. En cherchant un exemple,
j’ai repensé à un projet que j’avais commencé il y a quelques années,
et ça m’a donné une autre idée. En plus, même pas besoin de s’y
connaître en&amp;nbsp;code…&lt;/p&gt;
</summary><content type="html">&lt;div class="contents local topic" id="sommaire"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="#top"&gt;Sommaire&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="#introduction" id="toc-entry-1"&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#le-probleme" id="toc-entry-2"&gt;Le&amp;nbsp;problème&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#la-solution" id="toc-entry-3"&gt;La&amp;nbsp;solution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#premiere-amelioration-le-fenetrage" id="toc-entry-4"&gt;Première amélioration&amp;nbsp;: le&amp;nbsp;fenêtrage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#deuxieme-amelioration-la-rotation-du-referentiel" id="toc-entry-5"&gt;Deuxième amélioration&amp;nbsp;: la rotation du&amp;nbsp;référentiel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#en-conclusion" id="toc-entry-6"&gt;En&amp;nbsp;conclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="#notes" id="toc-entry-7"&gt;Notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="introduction"&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Salut ! Je pensais vous montrer dès aujourd’hui quelques petites manipulations sur des données géographiques. En cherchant un exemple, j’ai repensé à un projet que j’avais commencé il y a quelques années, et ça m’a donné une autre idée. On ne va pas faire de code tout de suite, on va d’abord s’échauffer les&amp;nbsp;neurones.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Remarque&lt;/em&gt;&amp;nbsp;: si vous êtes spécialiste &lt;span class="caps"&gt;SIG&lt;/span&gt;, ou si vous touchez un peu aux données géométriques (par exemple pour du rendu 3D), vous n’apprendrez pas grand-chose (voire rien du tout)&amp;nbsp;! Je vous promets de faire quelque chose pour vous un de ces jours. À bientôt&amp;nbsp;😉️&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="le-probleme"&gt;
&lt;h2&gt;Le&amp;nbsp;problème&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Il faut savoir qu’on n’a pas toujours envie de réinventer le fil à couper le poil dans la main, et qu’on utilise donc beaucoup d’outils clé sur étagère qui font généralement à peu près ce qu’on veut (s’ils fonctionnent&amp;nbsp;bien).&lt;/p&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;Par&amp;nbsp;exemple&amp;nbsp;:&lt;/dt&gt;
&lt;dd&gt;&lt;div class="first last line-block"&gt;
&lt;div class="line"&gt;On a l’emprise du département dans un fichier et des limites de canton dans un autre&amp;nbsp;; un merveilleux outil va alors découper le territoire et nous fournir les cantons, non plus en limites linéaires, mais en zones surfaciques, ce qui permettra de les utiliser dans des requêtes spatiales ou pour de la représentation cartographique. Honnêtement, pas trop besoin de s’y connaître pour l’opération, même si trouver le bon outil prend plus de temps quand on n’a pas l’habitude.&lt;/div&gt;
&lt;/div&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Notez les nuances dans le paragraphe précédent&amp;nbsp;: parfois, on préfère créer nous-même notre outil, ou bien on doit le faire car il n’existe pas (du moins, pas à notre connaissance). On a encore potentiellement le&amp;nbsp;choix&amp;nbsp;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;on peut préparer les données avec des outils génériques, faire une opération minime «&amp;nbsp;à la main&amp;nbsp;», puis revenir à des outils génériques pour finir les traitements, enregistrer, exporter,&amp;nbsp;etc.&lt;/li&gt;
&lt;li&gt;ou bien on peut tout faire à la main&amp;nbsp;: on lit les données, on en génère des données formatées selon l’outil de notre langage, on fait tout, et on exporte (ou on met à jour les quelques éléments modifiés) à la&amp;nbsp;fin.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Dans le premier cas, l’opération est généralement peu gourmande, et on peut se passer d’optimisation (à moins de la faire tourner en temps réel ou même en production à la demande sur le serveur). Dans le deuxième cas, ou si on tombe dans les cas particuliers de la parenthèse, on doit toujours optimiser un minimum. Si on ne le fait pas, notre nouvel outil va bien marcher sur le fichier test avec 10 éléments, puis une fois lancé sur la table de données réelles, sur tout le département ou tout le pays, vous êtes bon pour attendre un loooong moment. C’est le problème avec les données géographiques&amp;nbsp;: la géométrie, c’est facilement coûteux en temps de calcul et, souvent, les données sont&amp;nbsp;lourdes.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="la-solution"&gt;
&lt;h2&gt;La&amp;nbsp;solution&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Donc que fait-on&amp;nbsp;? Le plus simple, c’est d’&lt;strong&gt;éviter au maximum de calculer pour rien&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Dans mon exemple (que je ne détaillerai pas ici), je dois évaluer la proximité deux-à-deux entre des éléments linéaires de deux sources distinctes avant d’appliquer un traitement aux couples ainsi formés. J’ai fait ce qui est fait dans toutes les fonctions des &lt;span class="caps"&gt;SIG&lt;/span&gt; (systèmes d’information géographique, comme &lt;span class="caps"&gt;QGIS&lt;/span&gt;) travaillant sur les éléments selon leur proximité, j’ai commencé par &lt;strong&gt;évaluer si je peux ne pas faire le calcul&lt;/strong&gt; pour chaque&amp;nbsp;élément.&lt;/p&gt;
&lt;p&gt;Pour ça, calculons les «&amp;nbsp;&lt;em&gt;bounding boxes&lt;/em&gt;&amp;nbsp;», ou boîtes englobantes, de chaque élément&amp;nbsp;: nous retenons leurs coordonnées minimale et maximale sur chaque axe. Si je souhaite exclure de tout calcul les éléments distants de plus de 50 m entre eux, j’ajoute une marge de 50 m autour des boîtes de l’une des sources (ou 25 m de toutes les boîtes). &lt;strong&gt;Si les boîtes englobantes de deux éléments ne s’intersectent pas, on s’arrête là&lt;/strong&gt;. Évaluer cette condition revient à évaluer quelques inégalités, et &lt;strong&gt;c’est très rapide&lt;/strong&gt; par rapport au calcul de distance complet&amp;nbsp;: sur tout un jeu de données, on s’épargne un temps fou. Bien sûr, on n’a pas calculé une distance précise, donc quand il y a intersection des boîtes, il faut vérifier un peu la disposition des éléments entre&amp;nbsp;eux.&lt;/p&gt;
&lt;p&gt;J’ai fait ça, et c’était toujours (un peu) trop lent. Avant même d’optimiser les calculs suivants, j’ai amélioré encore ce processus de sélection, parce que, contrairement aux outils génériques, je peux tenir compte de mes connaissances sur les données qui seront utilisées par l’outil. En l’occurrence, ce sont des tronçons, découpés à chaque intersection, dans un réseau relativement dense donc avec des longueurs toujours petites par rapport au dimension du&amp;nbsp;territoire.&lt;/p&gt;
&lt;p&gt;J’ai eu plusieurs &lt;strong&gt;idées d’amélioration&lt;/strong&gt; sur&amp;nbsp;mesure.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="premiere-amelioration-le-fenetrage"&gt;
&lt;h2&gt;Première amélioration&amp;nbsp;: le&amp;nbsp;fenêtrage&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Compte tenu de ma connaissance des données, et je travaille alors par fenêtrage : &lt;strong&gt;on ne comparera des éléments que s’ils sont tous deux dans la même fenêtre&lt;/strong&gt;. Je calcule la longueur maximale des tronçons de chacun des jeux de données, et ma fenêtre sera dimensionnée en fonction du plus petit des maxima des longueurs des deux jeux de données. Après, je traite, position de la fenêtre après position : du premier jeu de données, je retire les données déjà sélectionnées et traitées&amp;nbsp;; dans le deuxième jeu de données, je les remets à chaque fois en jeu, mais je calcule une seule fois leur position par rapport à la fenêtre après chaque déplacement de&amp;nbsp;celle-ci.&lt;/p&gt;
&lt;p&gt;Au niveau algorithmique, en considérant que l’on a respectivement m éléments dans le premier jeu de données et n dans le deuxième, l’idée est ici de se rapprocher d’un calcul de complexité linéaire en O(m+n) plutôt que quadratique en&amp;nbsp;O(m×n).&lt;/p&gt;
&lt;p&gt;Bon, en y regardant un peu plus les temps de calcul de mon premier essai, la vraie raison de la lenteur du programme, ce n’est pas les comparaisons des boîtes. En fait, cette étape est tellement rapide que je n’y gagnerais quasiment rien à l’optimiser. J’ai alors laissé tombé de côté cette idée car j’en avais une autre en tête, potentiellement bien plus efficace. En plus, le fenêtre, ça pourrait améliorer un peu le temps de calcul avec un réseau dense, donc pour un territoire urbain&amp;nbsp;; mais pour un territoire rural, le gain serait encore plus réduit (et tant qu’à faire, si ce n’est pas un gros effort, autant permettre d’utiliser l’outil pour tout type de&amp;nbsp;territoire).&lt;/p&gt;
&lt;p&gt;On la met de côté dans notre cas actuel, mais gardez l’idée du fenêtrage dans un coin de la tête, elle peut parfois être très&amp;nbsp;efficace&amp;nbsp;!&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="deuxieme-amelioration-la-rotation-du-referentiel"&gt;
&lt;h2&gt;Deuxième amélioration&amp;nbsp;: la rotation du&amp;nbsp;référentiel&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mon vrai problème, c’est que les boîtes ne sont pas adaptées. Reprenons quelques paragraphes plus haut: j’ai simplement utilisé les coordonnées dans le système de référence des jeux de données. Du coup, si une ligne est dirigée selon un axe nord-ouest sud-est, je fais un carré, traversé en diagonale par la ligne, et dont 2 des coins sont situés à une distance de la moitié de la longueur de la ligne. C’est beaucoup trop englobant&amp;nbsp;! Et c’est très fréquent, d’autant plus que mes tronçons ne sont pas franchement sinueux et ne remplissent pas beaucoup les&amp;nbsp;boîtes.&lt;/p&gt;
&lt;p&gt;La solution que j’ai appliquée, c’est de calculer des boîtes dans d’autres référentiels, avec une rotation depuis le référentiel initial &lt;a class="footnote-reference" href="#footnote-1" id="footnote-reference-1"&gt;[1]&lt;/a&gt; (à 30° et 60°, par exemple). On augmente un peu le temps de calcul pour les évaluations d’intersection (on prend un peu de temps de préparation initiale puis on peut faire plusieurs évaluations pour un seul couple de lignes), mais pour un réel gain de temps&amp;nbsp;ensuite.&lt;/p&gt;
&lt;p&gt;Dans la suite du programme, je passais directement à des calculs plus compliqués et longs, qui servaient à la fois à valider le couple et dont les résultats étaient utiles ensuite pour d’autres calculs, le cas échéant&amp;nbsp;: distance de Hausdorff et comparaison des orientations locales (sur leur partie commune après projection de l’une sur l’autre). Pour éviter encore quelques uns de ces calculs, j’ai ajouté une étape intermédiaire&amp;nbsp;: calcul de la distance du centre de la première ligne à la seconde. Avec la technique des boîtes dans 3 référentiels différents, c’était un peu du bonus&amp;nbsp;; j’y ai quand même gagné entre 10 et 15&amp;nbsp;% de cas où j’évite le calcul de la distance de Hausdorff. Là, la pertinence doit être évaluée en fonction des données&amp;nbsp;: avec des lignes simples, la distance est calculée très vite, et cet ajout fait perdre du temps, mais avec des lignes complexes (contenant beaucoup de sommets), on peut être gagnant. J’ai donc laissé cette étape en option dans&amp;nbsp;l’outil.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="en-conclusion"&gt;
&lt;h2&gt;En&amp;nbsp;conclusion&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bien connaître ses données&amp;nbsp;: c’est&amp;nbsp;important.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;J’ai parlé d’opérations préalables aux calculs, au sein de l’outil de traitement, mais il y a autre chose à faire avant même de créer l’outil (ou de le lancer)&amp;nbsp;: il faut préparer ses données. Si on n’a pas besoin d’une précision décimétrique ou plus précise, on peut certainement simplifier les géométries (ce qui fera moins de sommets pour les lignes et les contours des polygones) et on y gagnera sur tous les calculs à venir, ainsi que sur le stockage et sur la réactivité de l’affichage dans une carte dynamique. J’ai pu travailler sur des algorithmes de simplification au travail, et on en parlera peut-être un&amp;nbsp;jour.&lt;/p&gt;
&lt;p&gt;À noter que j’ai commencé mes études par une licence ès mathématiques, et ça m’aide. Si vous n’avez pas compris ce qu’était la «&amp;nbsp;distance de Hausdorff&amp;nbsp;», ce n’est pas grave (mais je vous invite à chercher). On peut se passer de ce genre de subtilités et arriver à pas mal de choses quand même. (D’ailleurs, j’utilise plus souvent la semi-distance de Hausdorff, qui n’est pas une distance, parce que les relations sont rarement symétriques et que j’ai rarement besoin d’une vraie&amp;nbsp;distance.)&lt;/p&gt;
&lt;p&gt;J’ai surtout fait &lt;strong&gt;de la trigonométrie au collège&lt;/strong&gt; et c’est &lt;strong&gt;très souvent utile&lt;/strong&gt; dans un travail où on fait de la géométrie. Remercions nos enseignants qui ont supporté nos tempéraments de l’époque et qui nous ont tant appris&amp;nbsp;!&lt;/p&gt;
&lt;p&gt;A&amp;nbsp;bientôt&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="notes"&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference internal" href="#sommaire"&gt;retourner au&amp;nbsp;sommaire&lt;/a&gt;&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="footnote-1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Pour l’anecdote, depuis le début, sans le dire, je travaillais dans un référentiel local (par translation) pour éviter de tripatouiller des gros nombres pour rien.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="géotraitements"/><category term="données"/><category term="système d’information géographique"/><category term="SIG"/><category term="traitements"/><category term="prétraitements"/></entry></feed>