[python] Aide pour mis à jour cadastre

Importer dans OSM, contrôler, suivre et surveiller (osmose, keepright, ...)
sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

[python] Aide pour mis à jour cadastre

Message par sebastien31 » ven. févr. 13, 2015 10:12 pm

Bonjour,

C'est mon tout premier message ici même ça fait un an et demi que je modifie la carte sur openstreetmap. J'apprends petit à petit et il y a un problème auquel j'ai été confronté qui est la mise à jour des bâtiments. Dans la zone où j'ai beaucoup modifié les bâtiments datent de 2010. J'ai donc développé un script python qui permet de détecter de façon assez fiable les bâtiments nouveaux / modifiés / supprimés. Cela permet d'être sélectif et d'aller à l'essentiel.

Voici ce qu'il vous faudra faire pour l'utiliser :
1 : obtenir le bâti actuel tel qu'il est dans osm. Personnellement j'utilise josm. Du coup je télécharge toute une commune et j'isole tous les batiments (avec un filtre building=*) dans un onglet que je sauvegarde dans un fichier bati_as_is.osm.
Une autre façon de faire est de passer par une requête overpass.

Code : Tout sélectionner

{{geocodeArea:Jurançon}}->.searchArea;
// gather results
(
  // query part for: “building=yes”
  node["building"="yes"](area.searchArea);
  way["building"="yes"](area.searchArea);
  relation["building"="yes"](area.searchArea);
);
// print results
out meta;
>;
out meta;
2 : obtenir le bati tel qu'il deviendra en utilisant le site du cadastre (http://cadastre.openstreetmap.fr/). Vous obtenez normalement un fichier NOM-COMMUNE-house.osm que je renomme souvent bati_to_be.osm.

3 : Copier le script ci dessous dans une fichier BatiOsm.py (ou ce que vous voudrez) et appliquer le en tapant la commande :

Code : Tout sélectionner

python BatiOsm.py bati_as_is.osm bati_to_be.osm prefixe
où prefixe est une chaîne de caractère qui débutera chaque nouveau fichier créé.

4 : Si tout s'est bien passé, vous obtenez normalement plusieurs fichiers :
- prefixe_unModified.osm : les bâtiments dont il est raisonnable de penser qu'ils n'ont pas été modifiés. Ils sont communs au deux fichiers en entré.
- prefixe_mod_0_a_xxx.osm : les bâtiments dont il est raisonnable de penser qu'ils ont été modifiés. (xxx est le nombre de bâtiments modifiés).
- prefixe_sup_0_a_yyy.osm : les bâtiments dont il est raisonnable de penser qu'ils ont été supprimés. (yyy est le nombre de bâtiments supprimés).
- prefixe_new_0_a_zzz.osm : les bâtiments dont il est raisonnable de penser qu'ils sont nouveaux. (zzz est le nombre de bâtiments nouveaux).
- prefixe_log.txt : un fichier qui récapitule le classement de chaque bâtiments et la tolérance.

Alors comment ça marche ? Chaque fichier est lu et enregistré. Ils contiennent les latitude / longitude de chaque points de chaque bâtiments et pour chaque bâtiments les numéros des points. On est capable de définir un point moyen par bâtiment en calculant le centre de gravité de chaque bâtiment. Chaque bâtiment des deux fichiers passé en paramètre est résumé à un point. Si on bouge un seul des nœuds d'un bâtiment le point moyen bougera. Ensuite la parti la plus fastidieuse (pour l'ordinateur) consiste à prendre ce point de référence de chaque batiment du fichier bati_as_is et de calculer la distance entre ce point de référence et le point de référence des bâtiments du fichiers bati_to_be. Cela permet de coupler un batiment du fichier bati_as_is et un autre du fichier bati_to_be et d'avoir la distance minimale qui les sépare. On fait la même chose pour les bâtiments du fichier bati_to_be. Ensuite selon la distance mini qu'on obtient pour chaque bâtiment on peut dire :
- si la distance mini est inférieur à BORNE_INF_MODIF : la distance entre le bâtiment X de bati_as_is et le bâtiment Y de bati_to_be est très petite. Donc X et Y sont identiques. Y va dans prefixe_unModified.osm.
- si la distance mini est entre BORNE_INF_MODIF et BORNE_SUP_MODIF : la distance entre les bâtiments X et Y est non négligeable mais pas énorme non plus. X a probablement été modifié et il est devenu Y. Y est retranscrit dans prefixe_mod_0_a_xxx.
- si la distance mini est supérieure à BORNE_SUP_MODIF : la distance entre les bâtiments X et Y est très grande. Lorsque ça concerne le bâtiment X cela veut dire qu'il a été supprimé (donc écrit dans prefixe_sup_0_a_yyy) si cela concerne le bâtiment Y cela veut dire que le bâtiment est nouveau (donc écrit dans prefixe_new_0_a_zzz).

Les bornes sont exprimées en mètres. Les valeurs que j'utilise et qui marche bien sont 1 pour BORNE_INF_MODIF et 10 pour BORNE_SUP_MODIF.

Que dire d'autre ? Ensuite il s'agit de jouer avec les calques dans josm. En générale j'ai un calque de modification en cours de la zone. J'ouvre ensuite chaque fichier et je fais ensuite du copier coller vers le calque officiel. Ca n'est pas parfait et il y a toujours du travail à faire (raccorder un bâtiment à des noeuds existants, problème des faux positifs) mais je pense que ça permet de faire un parcourt d'une commune assez rapidement.

J'ai mis le code sur un dépôt github. Ca permet à ceux qui veulent modifier le code de le faire plus facilement.
https://github.com/sebastien-bugzilla/BatiOsm
Dernière édition par sebastien31 le mar. janv. 12, 2016 7:42 pm, édité 1 fois.

Invité

Re: [python] Aide pour mis à jour cadastre

Message par Invité » ven. févr. 13, 2015 11:45 pm

Salut,

Cool d avoir le courage de mettre les mains dans le camboui et developper quelque chose pour faciliter la vie des mappeurs.
J imagine qu avant de developper cet outil tu as regarde ce qui existait deja pour les comparaisons cadastre/OSM,par exemple batifusion, et que ton outil apporte quelque chose de plus.tu peux nous en dire plus sur ses avantages?

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » sam. févr. 14, 2015 9:11 am

oui j'avais regardé ce qui existe et j'étais tombé sur bati-fusion. Pour moi l'avantage c'est qu'il permet de détecter les nouveaux bâtiments et les bâtiments supprimés. Il y a quelques faux positifs mais ça marche plutôt bien. Et si il y a trop de faux positifs on peut toujours jouer avec les tolérances pour modifier le classement de chaque bâtiment. Quand on veut modifier une zone, la première chose qu'on veut savoir c'est ce qu'il y a de nouveau et ce qu'il y a à supprimer.
A l'inverse, bati-fusion me parait beaucoup plus robuste et plus rapide. Par exemple j'ai préparé les données d'entrée de la commune de Balma (dans la région toulousaine) et avec environ 11000 batiments il faut 5 min 40 à mon script pour obtenir les résultats alors que bati-fusion le fait plus rapidement (2-3 min je dirais).

Avatar de l’utilisateur
cquest
Messages : 1935
Inscription : ven. avr. 16, 2010 12:22 am
Localisation : Val de Marne
Contact :

Re: [python] Aide pour mis à jour cadastre

Message par cquest » sam. févr. 14, 2015 9:25 am

As tu contacté l'auteur de bati-fusion ?

Ca serait vraiment bien de vous rapprocher et de voir si on peut déployer à terme vos outils sur le serveur cadastre.openstreetmap.fr pour offrir à un plus large public ces fichiers prégénérés pour mettre à jour le bâti sur une commune.

vdct
Messages : 225
Inscription : mar. janv. 22, 2013 10:16 pm

Re: [python] Aide pour mis à jour cadastre

Message par vdct » sam. févr. 14, 2015 11:17 am

Bonjour Sebastien,

Merci pour ton script.
J'ai voulu tester mais je bute sur l'erreur suivante :

Code : Tout sélectionner

------------------------------------------------------------------
-                    Lecture des donn├®es                         -
------------------------------------------------------------------
Traceback (most recent call last):
  File "diff.py", line 173, in <module>
    old_nodes.append(Point(champsLigne[1], champsLigne[5], champsLigne[7]))
  File "diff.py", line 18, in __init__
    self.node_lon = float(node_lon)
ValueError: could not convert string to float: vincent_95
Il doit y avoir un micmac quand tu décomposes les lignes 'node', pour récupérer les coordonnées. Vu que tu charges etree en début de script, tu pourrais t'appuyer dessus pour lire les xml, c'est assez pratique.

À suivre,
vincent

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » sam. févr. 14, 2015 11:39 am

Bon je me suis inscrit parce que je ne peux pas modifier le premier message que j'ai posté et que j'aurais sûrement d'autre questions à l'avenir. :D
Le script comportait une erreur pour les batiments supprimés. Ils étaient bien détectés mais ce n'était pas les bons batiments qui étaient transcrit dans le fichier sup.

Pour répondre aux questions :
Vincent :
etree : j'avais commencé à faire une première version avec cet outils. Mais les performances sont beaucoup moins bonnes (en terme de temps). Un exemple : pour une commune avec environ 2000 batiments la version avec etree prenait 214 secondes, la version actuelle en prend 10 secondes. Et c'est bien la partie de lecture des fichiers qui est plus longue dans le premier script. J'ai donc supprimé l'import etree dans cette nouvelle version
Pour l'erreur que tu as c'est ce que je disais : mon script n'est pas robuste. En fait quand le script lit les noeuds, il prend la premier ligne node et repère les colonnes "node id" (colonne 1 par ex), "lat" (colonne 5 par ex) et "lon" (colonne 6 par ex) et il n'y a pas de test ultérieur pour déterminer si ces valeurs sont valables pour toutes les lignes "nodes". J'ai peur qu'on perde en performance. Donc ce qui doit se passer dans ton cas c'est qu'il a déterminé les colonnes en se basant sur le format de la première ligne. Il faut donc que toutes les autres lignes "nodes" aient le même format. Et ça ne doit pas être le cas. Peut être qu'il tombe sur une chaîne de caractère, impossible à transformer en float.

cquest :
Non je ne l'ai pas contacté. Je vais le faire.

Je me sers de ce premier message où je me suis enregistré pour faire vivre l'évolution...

2015-02-20 : Nouvelle version du script avec les améliorations suivantes :
- amélioration de la lecture des fichiers. Je recherche maintenant la longitude et la latitude à chaque ligne "node id" pour éviter de tomber sur le problème qu'a eut Vincent. J'ai remarqué que ça ne change pas trop les performances (le temps de lecture augmente que de 5 %).
- gestion des tag : pour chaque bâtiment identifiés comme identique ou modifié je colle les tag déjà présent dans la version actuelle. Pour les nouveaux il n'y a rien de modifié par rapport à ce qui est fourni dans le cadastre. Ça devrait faciliter les chose lorsque le bâtiment comporte un numéro d'adresse.
- le fichier log a été modifié pour donner la liste de tous les batiments de chaque fichier fourni en entré. Ensuite il liste tous les batiments par catégorie (identiques, modifiés, etc...) et les paires anciens/nouveaux bâtiments. Je trouve que c'est un peu lourd. Je pense que j’allégerai. Pour l'instant ça me sert à vérifier les correspondances entre les bâtiments.
- Je ne gère toujours pas les multi-polygones pour les bâtiments un peu sophistiqué.

2015-02-25 : Nouvelle version du script avec les améliorations suivantes :
- Beaucoup de faux positifs ont été éliminés en modifiant la façon de réduire les bâtiments à un point. Au lieu de prendre la moyenne de la longitude et de la latitude, je calcul le centre de gravité du bâtiment. Du coup, quand il y a une différence de nombre de nœuds entre un ancien bâtiment et un nouveau mais que la forme du bâtiment reste globalement la même, le bâtiment n'est pas forcément détecté comme modifié. Pour donner une idée, pour une commune proche de Toulouse de 5000 bâtiments, la version précédente détectais 141 bâtiments modifiés. La nouvelle en donne 34, le nombre de bâtiments nouveaux reste identique. Il faudrait que je regarde si je loupe pas des modifications.
- J'ai mis quelques commentaires pour expliquer ce que faisaient les méthodes. J'ai essayé de splitter les grandes lignes sur plusieurs pour respecter les recommandations de présentation du code python.
Les petites idées que j'ai derrière la tête en ce moment, c'est :
- réutiliser le fichier log pour recréer un nouveau fichier en fonction de certains critère. Par exemple tous les bâtiments qui ont été détectés comme identique mais avec une valeur proche de la tolérance. Cela permettrait d'éviter de relancer le script juste pour baisser la tolérance de détection.
- réfléchir à un moyen de détecter une éventuelle translation globale de l'ensemble des bâtiments entre l'ancienne et la nouvelle version.
- Toujours la gestion des multipolygones...

2015-03-08 : Nouvelle version du script.
- Modification pour prendre en compte les multipolygones. Finalement ça a été plus compliqué que ce que je pensais. Les polygones intérieurs ne sont plus considérés comme des bâtiments à part entière dans mon script. Je me base uniquement sur le contour extérieur pour dire que le bâtiment est soit nouveau, modifié, supprimé. Donc il risque d'y avoir des problèmes si le contour intérieur change.
- J'ai supprimé mon système de répartition en plusieurs fichiers. Ça me semblait inutile. Et j'ai pensé que ça serait plus utile de développer la possibilité de récréer un fichier osm à partir du fichier log. Donc maintenant on obtient 5 fichiers (le fichier log ainsi qu'un fichier pour chaque type de bâtiments suivant qu'il est identique, nouveau, modifié ou supprimé).

Voici la dernière version du code publiée :

Code : Tout sélectionner

# -*- coding:Utf-8 -*-
#!/usr/bin/env python
import sys
import os
from math import sqrt
import time
from operator import attrgetter

BORNE_INF_MODIF = 1.e-5
BORNE_SUP_MODIF = 1.e-4

class Point:
    """Définition d'un point.
    
    Attributs :
    - identifiant (chaine de caractère) 
    - latitude et longitude (flottant)
    """
    def __init__(self, node_id, node_lat, node_lon):
        self.node_id = node_id
        self.node_lat = float(node_lat)
        self.node_lon = float(node_lon)
        
    def affiche(self):
        print self.node_id, self.node_lat, self.node_lon
        
    def distance(self, other):
        """Calcul de la distance entre deux points"""
        d_lat = self.node_lat - other.node_lat
        d_lon = self.node_lon - other.node_lon
        return sqrt(d_lat**2 + d_lon**2)
        
    def export_node(self):
        """Création du code xml équivalent au point"""
        self.print_node = "  <node id='" + self.node_id + \
            "' action='modify' visible='true' lat='" + \
            str(self.node_lat) + "' lon='" + str(self.node_lon) + "' />"

class Batiment:
    """L'entité Batiment rassemble plusieurs données : 
    
        - bat_id : un identifiant (chaine de caractère)
        - nbre_node : le nombre de points du batiment (nombre entier)
        - node_id : le tableau des Points du batiments
        - pt_moy : le point de référence du batiments (centre de gravité)
        - dist_mini : une valeurs de distance pour détecter la modification du batiment
        - largeur : la largeur du batiment
        - status : le status du batiment (nouveau, identique, modifié, supprimé)
        - nombre_tag : le nombre de tag défini dans le fichier
        - tableau_tag_key : le tableau d'identifiants des tags
        - tableau_tag_value : le tableau des valeurs des tags
        - pbAire : l'information si le batiment a une aire nulle
        - multipolygone : yes si le batiment en est un, no sinon
        - role : le role si le batiment appartient à une relation
        - ind_relation : l'indice de la relation auquel il appartient
    """
    def __init__(self, bat_id, nbre_node, node_id, 
            numTag, tableauTagKey, tableauTagValue, 
            distance=1000, largeur = 0., status = "UNKNOWN", pbAire = "NO",
            multipolygone = "no", role = "outer", ind_relation = 0):
        self.bat_id = bat_id
        self.nbre_node = nbre_node
        self.node_id = node_id
        self.dist_mini = float(distance)
        self.largeur = largeur
        self.status = status
        self.nombre_tag = numTag
        self.tableau_tag_key = tableauTagKey
        self.tableau_tag_value = tableauTagValue
        self.pbAire = "NO"
        self.multipolygone = "no"
        self.role = "outer"
        self.ind_relation = 0
    
    def BatimentToPoint(self):
        """Calcul du centre de gravité du batiment.
        
        les coordonnées sont d'abord exprimés en "pseudo-mètres" en 
        prenant comme origine le premier point du batiment parce qu'il
        n'y a pas suffisamment de différence entre chaque point pour
        faire les calcul (il faudrait augmenter la précision).
        Ensuite le calcul se fait d'après 
            https://fr.wikipedia.org/wiki/Aire_et_centre_de_masse_d%27un_polygone
        Le calcul nécessite de diviser par la surface du batiment. Cela
        pose problème si le batiment a une surface nulle. Les exceptions
        sont traités avec pbAire. Dans ce cas le point de référence de 
        ces batiments est la moyenne des coordonnées de chaque point.
        """
        i_node = 0
        calculLatitude = 0
        calculLongitude = 0
        latMoyenne = 0
        lonMoyenne = 0
        aire = 0
        latLocale = []
        lonLocale = []
        while i_node < self.nbre_node:
            latLocale.append((self.node_id[i_node].node_lat - \
                self.node_id[0].node_lat) * 6500000.)
            lonLocale.append((self.node_id[i_node].node_lon - \
                self.node_id[0].node_lon) * 6500000.)
            i_node = i_node + 1
        i_node = 0
        while i_node < self.nbre_node - 1:
            latMoyenne = latMoyenne + self.node_id[i_node].node_lat
            lonMoyenne = lonMoyenne + self.node_id[i_node].node_lon
            produitEnCroix = (latLocale[i_node] * lonLocale[i_node + 1] - \
                latLocale[i_node + 1] * lonLocale[i_node])
            aire = aire + 0.5 * produitEnCroix
            calculLatitude = calculLatitude + (latLocale[i_node] + \
                latLocale[i_node + 1]) * produitEnCroix
            calculLongitude = calculLongitude + (lonLocale[i_node] + \
                lonLocale[i_node + 1]) * produitEnCroix
            i_node = i_node + 1
        if aire == 0.:
            self.pbAire = "YES"
            latitude = latMoyenne / self.nbre_node
            longitude = lonMoyenne / self.nbre_node
        else:
            latitude = self.node_id[0].node_lat + \
                calculLatitude / (6 * aire * 6500000.)
            longitude = self.node_id[0].node_lon + \
                calculLongitude / (6 * aire * 6500000.)
        self.pt_moy = Point(self.bat_id, latitude, longitude)
        
    def calculLargeur(self):
        """Calcul de la largeur approximative du batiment. 
        
        Cette distance intervient ensuite dans la détermination
        du status du batiment. Si la distance mini est supérieure à cette
        largeur alors cela veut dire que le batiment est nouveau ou 
        supprimé."""
        tableauLatitude = []
        tableauLongitude = []
        for node in range(self.nbre_node):
            tableauLatitude.append(self.node_id[node].node_lat)
            tableauLongitude.append(self.node_id[node].node_lon)
        minLat = min(tableauLatitude)
        maxLat = max(tableauLatitude)
        minLon = min(tableauLongitude)
        maxLon = max(tableauLongitude)
        self.largeur = sqrt((maxLat - minLat)**2 + (maxLon - minLon)**2)
        
    def setDistMini(self, distance):
        """Cette méthode permet de définir la distance mini comme étant celle
            passé en paramètre"""
        self.dist_mini = float(distance)
    
    def setBatProche(self, nomBatProche, indBatProche):
        """Cette méthode permet de définir que le batiment auquel elle est
        appliquée correspond au batiment passé en paramètre"""
        self.id_bat_proche = nomBatProche
        self.ind_bat_proche = indBatProche
        
    def setStatus(self, status):
        """Cette méthode défini le status du batiment."""
        self.status = status
    
    def setRole(self, role):
        """
        Cette méthode défini le role du batiment lorsqu'il appartient à
        une relation. Le role est soit "inner" soit "outer".
        """
        self.role = role
        
    def export_bat(self):
        """Cette méthode défini une version xml du batiment, de ses noeuds
        et de ses éventuels tag dans le but d'être transcrit dans un fichier."""
        export = []
        res_export = ""
        export.append("  <way id='" + self.bat_id + "' visible='true'>")
        i_node = 0
        while i_node < self.nbre_node:
            export.append("    <nd ref='" + self.node_id[i_node].node_id + \
                "' />")
            i_node = i_node + 1
        for i_tag in range(self.nombre_tag):
            export.append("    <tag k='" + self.tableau_tag_key[i_tag] + \
                "' v='" + self.tableau_tag_value[i_tag] + "' />")
        export.append("  </way>")
        i_node = 0
        while i_node < self.nbre_node:
            self.node_id[i_node].export_node()
            export.append(self.node_id[i_node].print_node)
            i_node = i_node + 1
        nb_ligne = len(export)
        i_ligne = 0
        while i_ligne < nb_ligne:
            if i_ligne == nb_ligne - 1:
                res_export = res_export + export[i_ligne]
            else:
                res_export = res_export + export[i_ligne] + "\n"
            i_ligne = i_ligne + 1
        self.print_bat = res_export
        
    def copy_tag(self, other, status):
        """Cette méthode permet de copier les tag d'un batiment passé en 
        paramètre au batiment auquelle elle est appliquée.
        Lorsque le batiment 'self' est détecté comme identique, la source est
        hérité du batiment 'other'. Par contre lorsque le batiment 'self' est
        détecté comme modifié, la source est mis à jour pour prendre la valeur
        du batiment 'other'.
        """
        if status == "IDENTIQUE":
            self.nombre_tag = other.nombre_tag
            self.tableau_tag_key = other.tableau_tag_key
            self.tableau_tag_value = other.tableau_tag_value
        elif status == "MODIFIE":
            rang_tag_source = self.tableau_tag_key.index("source")
            tag_source_save = self.tableau_tag_value[rang_tag_source]
            self.nombre_tag = other.nombre_tag
            self.tableau_tag_key = other.tableau_tag_key
            self.tableau_tag_value = other.tableau_tag_value
            try:
                rang_tag_source = self.tableau_tag_key.index("source")
                self.tableau_tag_value[rang_tag_source] = tag_source_save
            except:
                pass

class Relation:
    """
    Classe qui regroupe les relations pour le traitement des multipolygones.
    """
    
    def __init__(self, id_relation, nb_ways, tab_id_ways, tab_ind_ways, tab_role):
        self.id = id_relation
        self.nb_ways = nb_ways
        self.id_ways = tab_id_ways
        self.ind_ways = tab_ind_ways
        self.role = tab_role
    
    def export_relation(self):
        export = []
        res_export = ""
        export.append("  <relation id='" + self.id + "'>")
        export.append("    <tag k='value' v='multipolygon'/>")
        for ways in range(self.nb_ways):
            export.append("    <member type='way' ref='" + \
                self.id_ways[ways] + "' role='" + \
                self.role[ways] + "'/>")
        export.append("  </relation>")
        nb_ligne = len(export)
        i_ligne = 0
        while i_ligne < nb_ligne:
            if i_ligne == nb_ligne - 1:
                res_export = res_export + export[i_ligne]
            else:
                res_export = res_export + export[i_ligne] + "\n"
            i_ligne = i_ligne + 1
        self.print_relation = res_export

def formatLog(donnees):
    """Cette fonction permet de générer une chaine de caractère formaté et 
    de longueur constante à partir du tableau passé en paramètre"""
    result = ""
    nbData = len(donnees)
    for i_data in range(nbData):
        if i_data < nbData - 1:
            nbCarLimite = 18
        else:
            nbCarLimite = 50
        donnees[i_data] = " " + donnees[i_data]
        nbCar = len(donnees[i_data])
        while nbCar < nbCarLimite:
            donnees[i_data] = donnees[i_data] + " "
            nbCar = len(donnees[i_data])
        result = result + "|" + donnees[i_data]
    result = result + "|"
    return result

#------------------------------------------------------------------------------
#      D E B U T   D U   P R O G R A M M E
#------------------------------------------------------------------------------


adresse = sys.path[0]
fichier_osm_old = sys.argv[1]
fichier_osm_new = sys.argv[2]
prefixe = sys.argv[3]

separation = "--------------------------------------------------------------------------------------------------------------------------------"


tps1 = time.clock()

print "------------------------------------------------------------------"
print "-                    Lecture des données                         -"
print "------------------------------------------------------------------"

#------------------------------------------------------------------------
#lecture des vieux batiments :
#------------------------------------------------------------------------
file_old = open(fichier_osm_old, "r")
print "lecture du fichier " + fichier_osm_old + "..."
#détermination du séparateur du fichier : " ou '
ligne = file_old.readline().rstrip('\n\r')
tabLigne1 = ligne.split("'")
tabLigne2 = ligne.split("\"")
if len(tabLigne1) > len(tabLigne2):
    delim = "'"
else:
    delim = "\""

old_nodes = []
old_id_nodes = []
old_bati = []
old_bati_sorted = []
old_relation = []

old_nbre_nodes = 0
old_nbre_ways = 0
old_nbre_relation = 0
i_way = 0
i_nd_ref = 0

col_id = 0
col_lat = 0
col_lon = 0

for ligne in file_old:
    champsLigne = ligne.rstrip('\n\r').split(delim)
    if champsLigne[0].find("node id") != -1:
        col_id = 1
        col_lat = champsLigne.index(" lat=") + 1
        col_lon = champsLigne.index(" lon=") + 1
        old_nodes.append(Point(champsLigne[col_id], champsLigne[col_lat], \
            champsLigne[col_lon]))
        old_id_nodes.append(champsLigne[col_id])
        old_nbre_nodes = old_nbre_nodes + 1
    elif champsLigne[0].find("way id") != -1: # nouveau batiment : on initialise les données
        way_id = champsLigne[1]
        i_nd_ref = 0
        nodes = []
        tagKey = []
        tagValue = []
        numTag = 0
    elif champsLigne[0].find("nd ref") != -1:
        id_nd_ref = champsLigne[1]
        i_nd_ref = i_nd_ref + 1
        nodes.append(old_nodes[old_id_nodes.index(id_nd_ref)])
    elif champsLigne[0].find("tag") != -1:
        if i_nd_ref != 0:
            tagKey.append(champsLigne[1])
            tagValue.append(champsLigne[3])
            numTag = numTag + 1
    elif champsLigne[0].find("/way") != -1:
        old_bati.append(Batiment(way_id, i_nd_ref, nodes, \
            numTag, tagKey, tagValue, 1000, 0., "UNKNOWN"))
        old_bati[old_nbre_ways].BatimentToPoint()
        if old_bati[old_nbre_ways].pbAire == "YES":
            print "  Warning, surface nulle obtenue pour le batiment :", \
                old_bati[old_nbre_ways].bat_id 
        old_bati[old_nbre_ways].calculLargeur()
        old_nbre_ways = old_nbre_ways + 1
    elif champsLigne[0].find("relation id") !=-1:
        relation_id = champsLigne[1]
        nb_member = 0
        tab_id_member = []
        tab_ind_member = []
        tab_role = []
    elif champsLigne[0].find("member type") != -1:
        col_ref = champsLigne.index(" ref=") + 1
        col_role = champsLigne.index(" role=") + 1
        tab_id_member.append(champsLigne[col_ref])
        tab_role.append(champsLigne[col_role])
        nb_member = nb_member + 1
    elif champsLigne[0].find("/relation") != -1:
        for i_member in range(nb_member):
            for i_bat in range(old_nbre_ways):
                if old_bati[i_bat].bat_id == tab_id_member[i_member]:
                    old_bati[i_bat].multipolygone = "yes"
                    old_bat[i_bat].ind_relation = old_nbre_relation
                    tab_ind_member.append(i_bat)
                    if tab_role[i_member] == "inner":
                        old_bati[i_bat].setRole("inner")
        old_relation.append(Relation(relation_id, nb_member, tab_id_member, \
            tab_ind_member, tab_role))
        old_nbre_relation = old_nbre_relation + 1
        

file_old.close()

print "  " + str(old_nbre_nodes) + " noeuds répertoriés dans le fichier " + \
    fichier_osm_old
print "  " + str(old_nbre_ways) + " batiments répertoriés dans le fichier " + \
    fichier_osm_old


#------------------------------------------------------------------------
#lecture des nouveaux batiments :
#------------------------------------------------------------------------
file_new = open(fichier_osm_new, "r")
print "lecture du fichier " + fichier_osm_new + "..."
ligne = file_new.readline().rstrip('\n\r')
tabLigne1 = ligne.split("'")
tabLigne2 = ligne.split("\"")
if len(tabLigne1) > len(tabLigne2):
    delim = "'"
else:
    delim = "\""

new_nodes = []
new_id_nodes = []
new_bati = []
new_bati_sorted = []
new_relation = []

new_nbre_nodes = 0
new_nbre_ways = 0
new_nbre_relation = 0
i_way = 0
i_nd_ref = 0
col_id = 0
col_lat = 0
col_lon = 0

for ligne in file_new:
    champsLigne = ligne.rstrip('\n\r').split(delim)
    if champsLigne[0].find("node id") != -1:
        col_id = 1
        col_lat = champsLigne.index(" lat=") + 1
        col_lon = champsLigne.index(" lon=") + 1
        new_nodes.append(Point(champsLigne[col_id], champsLigne[col_lat], \
            champsLigne[col_lon]))
        new_id_nodes.append(champsLigne[col_id])
        new_nbre_nodes = new_nbre_nodes + 1
    elif champsLigne[0].find("way id") != -1:
        way_id = champsLigne[1]
        i_nd_ref = 0
        nodes = []
        tagKey = []
        tagValue = []
        numTag = 0
    elif champsLigne[0].find("nd ref") != -1:
        id_nd_ref = champsLigne[1]
        i_nd_ref = i_nd_ref + 1
        nodes.append(new_nodes[new_id_nodes.index(id_nd_ref)])
    elif champsLigne[0].find("tag") != -1:
        if i_nd_ref != 0:
            tagKey.append(champsLigne[1])
            tagValue.append(champsLigne[3])
            numTag = numTag + 1
    elif champsLigne[0].find("/way") != -1:
        new_bati.append(Batiment(way_id, i_nd_ref, nodes, \
            numTag, tagKey, tagValue, 1000, 0., "UNKNOWN"))
        new_bati[new_nbre_ways].BatimentToPoint()
        if new_bati[new_nbre_ways].pbAire == "YES":
            print "  Attention, surface nulle obtenue pour le batiment :", \
                new_bati[new_nbre_ways].bat_id 
        new_bati[new_nbre_ways].calculLargeur()
        new_nbre_ways = new_nbre_ways + 1
    elif champsLigne[0].find("relation id") !=-1:
        relation_id = champsLigne[1]
        nb_member = 0
        tab_id_member = []
        tab_ind_member = []
        tab_role = []
    elif champsLigne[0].find("member type") != -1:
        col_ref = champsLigne.index(" ref=") + 1
        col_role = champsLigne.index(" role=") + 1
        tab_id_member.append(champsLigne[col_ref])
        tab_role.append(champsLigne[col_role])
        nb_member = nb_member + 1
    elif champsLigne[0].find("/relation") != -1:
        for i_member in range(nb_member):
            for i_bat in range(new_nbre_ways):
                if new_bati[i_bat].bat_id == tab_id_member[i_member]:
                    new_bati[i_bat].multipolygone = "yes"
                    new_bati[i_bat].ind_relation = new_nbre_relation
                    tab_ind_member.append(i_bat)
                    if tab_role[i_member] == "inner":
                        new_bati[i_bat].setRole("inner")
        new_relation.append(Relation(relation_id, nb_member, tab_id_member, \
            tab_ind_member, tab_role))
        new_nbre_relation = new_nbre_relation + 1

file_new.close()

print "  " + str(new_nbre_nodes) + " noeuds répertoriés dans le fichier " + \
    fichier_osm_new
print "  " + str(new_nbre_ways) + " batiments répertoriés dans le fichier " + \
    fichier_osm_new
print "------------------------------------------------------------------"
print "-  Recherche des similitudes et des différences entre batiments  -"
print "------------------------------------------------------------------"
#------------------------------------------------------------------------------
#calcul des distances mini entre chaque anciens batiments
# pour chaque batiment anciens (resp. nouveau) on détermine la distance 
# la plus petite avec tous les nouveaux batiments (resp. anciens)
#------------------------------------------------------------------------------
nbre_comparaison = 2 * old_nbre_ways * new_nbre_ways
avancement = 0
i_old = 0
while i_old < old_nbre_ways:
    i_new = 0
    if old_bati[i_old].role == "outer":
        while i_new < new_nbre_ways:
            if new_bati[i_new].role == "outer":
                distance = old_bati[i_old].pt_moy.distance(new_bati[i_new].pt_moy)
                if old_bati[i_old].dist_mini > distance:
                    old_bati[i_old].setDistMini(distance)
                    old_bati[i_old].setBatProche(new_bati[i_new].bat_id, i_new)
            i_new = i_new + 1
    else:
        old_bati[i_old].setDistMini(9999.)
        old_bati[i_old].setBatProche("-9999", 9999)
    avancement = int(float(i_old) / (old_nbre_ways + new_nbre_ways) * 100.)
    sys.stdout.write("Calcul en cours : " + str(avancement) + " %" + chr(13))
    i_old = i_old + 1

i_new = 0
while i_new < new_nbre_ways:
    i_old = 0
    if new_bati[i_new].role == "outer":
        while i_old < old_nbre_ways:
            if old_bati[i_old].role == "outer":
                distance = new_bati[i_new].pt_moy.distance(old_bati[i_old].pt_moy)
                if new_bati[i_new].dist_mini > distance:
                    new_bati[i_new].setDistMini(distance)
                    new_bati[i_new].setBatProche(old_bati[i_old].bat_id, i_old)
            i_old = i_old + 1
    else:
        new_bati[i_new].setDistMini(9999.)
        new_bati[i_new].setBatProche("-9999", 9999)
    avancement = int(float(old_nbre_ways + i_new) / \
        (old_nbre_ways + new_nbre_ways) * 100.)
    sys.stdout.write("Calcul en cours : " + str(avancement) + " %" + chr(13))
    i_new = i_new + 1

#------------------------------------------------------------------------
#Classement des batiments :
#  - dist_mini < BORNE_INF_MODIF : identique
#  - BORNE_INF_MODIF < dist_mini < BORNE_SUP_MODIF : modifié
#  - dist_mini > BORNE_SUP_MODIF : nouveau ou supprimé
#  - dist_mini > largeur : nouveau ou supprimé
#------------------------------------------------------------------------

nb_bat_new = 0
nb_bat_mod = 0
nb_bat_del = 0
nb_bat_noMod = 0
nb_bat_inner_new = 0
nb_bat_inner_old = 0

for batiments in range(new_nbre_ways):
    if new_bati[batiments].role == "outer":
        if new_bati[batiments].dist_mini < BORNE_INF_MODIF:
            new_bati[batiments].setStatus("IDENTIQUE")
            new_bati[batiments].copy_tag(old_bati[new_bati[batiments].ind_bat_proche], \
                "IDENTIQUE")
        elif new_bati[batiments].dist_mini > BORNE_INF_MODIF and \
                new_bati[batiments].dist_mini < BORNE_SUP_MODIF:
            new_bati[batiments].setStatus("MODIFIE")
            new_bati[batiments].copy_tag(old_bati[new_bati[batiments].ind_bat_proche], \
                "MODIFIE")
        elif new_bati[batiments].dist_mini > BORNE_SUP_MODIF:
            new_bati[batiments].setStatus("NOUVEAU")
        if new_bati[batiments].dist_mini > new_bati[batiments].largeur:
            new_bati[batiments].setStatus("NOUVEAU")
    else:
        new_bati[batiments].setStatus("INNER")

for batiments in range(old_nbre_ways):
    if old_bati[batiments].role == "outer":
        if old_bati[batiments].dist_mini > BORNE_SUP_MODIF:
            old_bati[batiments].setStatus("SUPPRIME")
        if old_bati[batiments].dist_mini > old_bati[batiments].largeur:
            old_bati[batiments].setStatus("SUPPRIME")
    else:
        old_bati[batiments].setStatus("INNER")

# Classement des batiments en fonction du status et de la distance mini.
new_bati_sorted = sorted(new_bati, key = attrgetter("status", \
    "dist_mini"))
old_bati_sorted = sorted(old_bati, key = attrgetter("status", \
    "dist_mini"))

dernier_id_inner_new = 0
i_new = 0
while i_new < new_nbre_ways:
    if new_bati_sorted[i_new].status == "IDENTIQUE":
        nb_bat_noMod = nb_bat_noMod + 1
        dernier_id_identique = i_new
    elif new_bati_sorted[i_new].status == "INNER":
        nb_bat_inner_new = nb_bat_inner_new + 1
        dernier_id_inner_new = i_new
    elif new_bati_sorted[i_new].status == "MODIFIE":
        nb_bat_mod = nb_bat_mod + 1
        dernier_id_modifie = i_new
    elif new_bati_sorted[i_new].status == "NOUVEAU":
        nb_bat_new = nb_bat_new + 1
    i_new = i_new + 1

dernier_id_inner_old = 0
i_old = 0
while i_old < old_nbre_ways:
    if old_bati_sorted[i_old].status == "INNER":
        nb_bat_inner_old = nb_bat_inner_old + 1
        dernier_id_inner_old = i_old
    elif old_bati_sorted[i_old].status == "SUPPRIME":
        nb_bat_del = nb_bat_del + 1
    i_old = i_old + 1

print "------------------------------------------------------------------"
print "-                    Création des fichiers                       -"
print "------------------------------------------------------------------"
print nb_bat_noMod, " batiments identiques"
print nb_bat_mod, " batiments modifiés"
print nb_bat_new, " batiments nouveaux"
print nb_bat_del, " batiments supprimés"

tps2 = time.clock()


file_log = open(adresse + "/" + prefixe + "_log.txt", "w")
file_log.write("Rappel des input : \n")
file_log.write("    BORNE_INF_MODIF : " + str(BORNE_INF_MODIF) + "\n")
file_log.write("    BORNE_SUP_MODIF : " + str(BORNE_SUP_MODIF) + "\n")
file_log.write("Le fichier " + fichier_osm_old + " contient :" + "\n")
file_log.write("    - " + str(old_nbre_nodes) + " noeuds" + "\n")
file_log.write("    - " + str(old_nbre_ways) + " batiments" + "\n")
file_log.write("Le fichier " + fichier_osm_new + " contient :" + "\n")
file_log.write("    - " + str(new_nbre_nodes) + " noeuds" + "\n")
file_log.write("    - " + str(new_nbre_ways) + " batiments" + "\n")
file_log.write("Résultat de la comparaison :" + "\n")
file_log.write("    Nombre de batiments identiques trouvés : " + \
    str(nb_bat_noMod) + "\n")
file_log.write("    Nombre de batiments modifiés trouvés : " + \
    str(nb_bat_mod) + "\n")
file_log.write("    Nombre de batiments nouveaux trouvés : " + \
    str(nb_bat_new) + "\n")
file_log.write("    Nombre de batiments supprimés trouvés : " + \
    str(nb_bat_del) + "\n")
file_log.write("Temps de calcul : " + str(tps2 - tps1) + " secondes." + "\n")
file_log.write(separation + "\n")
file_log.write("Récapitulatif des nouveaux batiments" + "\n")
file_log.write(separation + "\n")

i_new = 0
while i_new < new_nbre_ways:
    Resultat = [new_bati_sorted[i_new].bat_id, new_bati_sorted[i_new].status, \
        str(round(new_bati_sorted[i_new].dist_mini, 9)), \
        str(new_bati_sorted[i_new].pt_moy.node_lat), \
        str(new_bati_sorted[i_new].pt_moy.node_lon)]
    file_log.write(formatLog(Resultat) + "\n")
    i_new = i_new + 1
file_log.write(separation + "\n")
file_log.write("Récapitulatif des Anciens batiments" + "\n")
file_log.write(separation + "\n")
i_old = 0
while i_old < old_nbre_ways:
    Resultat = [old_bati_sorted[i_old].bat_id, old_bati_sorted[i_old].status, \
        str(round(old_bati_sorted[i_old].dist_mini, 9)), \
        str(old_bati_sorted[i_old].pt_moy.node_lat), \
        str(old_bati_sorted[i_old].pt_moy.node_lon)]
    file_log.write(formatLog(Resultat) + "\n")
    i_old = i_old + 1

file_log.write(separation + "\n")
file_log.write(str(nb_bat_noMod) + " batiments classés comme identiques" + "\n")
file_log.write(separation + "\n")

enTete = ["STATUS", "ANCIEN BATIMENT", "TOLERANCE", "NOUVEAU BATIMENT", "fichier"]
file_log.write(formatLog(enTete) +"\n")

# écriture des batiments identiques dans un même fichier

noMod_building = prefixe + "_unModified.osm"
file_noMod_building = open(adresse + "/" + noMod_building, "w")
file_noMod_building.write("<?xml version='1.0' encoding='UTF-8'?>" + "\n")
file_noMod_building.write("<osm version='0.6' upload='true' generator='JOSM'>" + "\n")
for i_bat in range(dernier_id_identique):
    new_bati_sorted[i_bat].export_bat()
    file_noMod_building.write(new_bati_sorted[i_bat].print_bat + "\n")
    if new_bati_sorted[i_bat].multipolygone == "yes":
        relation = new_bati_sorted[i_bat].ind_relation
        for members in range(new_relation[relation].nb_ways):
            if new_relation[relation].role[members] == "inner":
                indic_bati_membre = new_relation[relation].ind_ways[members]
                new_bati[indic_bati_membre].export_bat()
                file_noMod_building.write(new_bati[indic_bati_membre].print_bat + "\n")
        new_relation[relation].export_relation()
        file_noMod_building.write(new_relation[relation].print_relation + "\n")
    Ligne = ["IDENTIQUE", new_bati_sorted[i_bat].id_bat_proche , \
        str(round(new_bati_sorted[i_bat].dist_mini, 9)), \
        new_bati_sorted[i_bat].bat_id  , noMod_building ]
    file_log.write(formatLog(Ligne) + "\n")

file_noMod_building.write("</osm>")
file_noMod_building.close()

enTete = ["STATUS", "ANCIEN BATIMENT", "TOLERANCE", "NOUVEAU BATIMENT", "fichier"]
# écriture des batiments modifiés dans plusieurs fichier
file_log.write(separation + "\n")
file_log.write(str(nb_bat_mod) + " batiments classés comme modifiés" + "\n")
file_log.write(separation + "\n")
file_log.write(formatLog(enTete) +"\n")
nom_file = prefixe + "_mod_1_a_" + str(nb_bat_mod) + ".osm"
file_mod_building = open(adresse + "/" + nom_file, "w")
file_mod_building.write("<?xml version='1.0' encoding='UTF-8'?>" + "\n")
file_mod_building.write("<osm version='0.6' upload='true' generator='JOSM'>" + "\n")
for i_bat in range(nb_bat_mod):
    indice = dernier_id_inner_new + i_bat + 1
    new_bati_sorted[indice].export_bat()
    file_mod_building.write(new_bati_sorted[indice].print_bat + "\n")
    if new_bati_sorted[indice].multipolygone == "yes":
        relation = new_bati_sorted[indice].ind_relation
        for members in range(new_relation[relation].nb_ways):
            if new_relation[relation].role[members] == "inner":
                indic_bati_membre = new_relation[relation].ind_ways[members]
                new_bati[indic_bati_membre].export_bat()
                file_mod_building.write(new_bati[indic_bati_membre].print_bat + "\n")
        new_relation[relation].export_relation()
        file_mod_building.write(new_relation[relation].print_relation + "\n")
    Ligne = ["MODIFIE", new_bati_sorted[indice].id_bat_proche, \
        str(round(new_bati_sorted[indice].dist_mini, 9)), \
        new_bati_sorted[indice].bat_id, nom_file]
    file_log.write(formatLog(Ligne) + "\n")
file_mod_building.write("</osm>")
file_mod_building.close()


enTete = ["STATUS", "ANCIEN BATIMENT", "TOLERANCE", "NOUVEAU BATIMENT", "fichier"]
# écriture des batiments nouveaux dans plusieurs fichier
file_log.write(separation + "\n")
file_log.write(str(nb_bat_new) + " batiments classés comme nouveaux" + "\n")
file_log.write(separation + "\n")
file_log.write(formatLog(enTete) +"\n")
nom_file = prefixe + "_new_1_a_" + str(nb_bat_new) + ".osm"
file_new_building = open(adresse + "/" + nom_file , "w")
file_new_building.write("<?xml version='1.0' encoding='UTF-8'?>" + "\n")
file_new_building.write("<osm version='0.6' upload='true' generator='JOSM'>" + "\n")
for i_bat in range(nb_bat_new):
    indice = dernier_id_modifie + i_bat + 1
    new_bati_sorted[indice].export_bat()
    file_new_building.write(new_bati_sorted[indice].print_bat + "\n")
    if new_bati_sorted[indice].multipolygone == "yes":
        relation = new_bati_sorted[indice].ind_relation
        for members in range(new_relation[relation].nb_ways):
            if new_relation[relation].role[members] == "inner":
                indic_bati_membre = new_relation[relation].ind_ways[members]
                new_bati[indic_bati_membre].export_bat()
                file_new_building.write(new_bati[indic_bati_membre].print_bat + "\n")
        new_relation[relation].export_relation()
        file_new_building.write(new_relation[relation].print_relation + "\n")
    Ligne = ["NOUVEAU", new_bati_sorted[indice].id_bat_proche, \
        str(round(new_bati_sorted[indice].dist_mini, 9)), \
        new_bati_sorted[indice].bat_id, nom_file]
    file_log.write(formatLog(Ligne) + "\n")
file_new_building.write("</osm>")
file_new_building.close()


enTete = ["STATUS", "ANCIEN BATIMENT", "TOLERANCE", "fichier"]
# écriture des batiments supprimés dans plusieurs fichier
file_log.write(separation + "\n")
file_log.write(str(nb_bat_del) + " batiments classés comme supprimés" + "\n")
file_log.write(separation + "\n")
file_log.write(formatLog(enTete) +"\n")
nom_file = prefixe + "_sup_1_a_" + str(nb_bat_del) + ".osm"
file_del_building = open(adresse + "/" + nom_file , "w")
file_del_building.write("<?xml version='1.0' encoding='UTF-8'?>" + "\n")
file_del_building.write("<osm version='0.6' upload='true' generator='JOSM'>" + "\n")
for i_bat in range(nb_bat_del):
    indice = dernier_id_inner_old + i_bat
    old_bati_sorted[indice].export_bat()
    file_del_building.write(old_bati_sorted[indice].print_bat + "\n")
    if old_bati_sorted[indice].multipolygone == "yes":
        relation = old_bati_sorted[indice].ind_relation
        for members in range(old_relation[relation].nb_ways):
            if old_relation[relation].role[members] == "inner":
                indic_bati_membre = old_relation[relation].ind_ways[members]
                old_bati[indic_bati_membre].export_bat()
                file_del_building.write(old_bati[indic_bati_membre].print_bat + "\n")
        old_relation[relation].export_relation()
        file_del_building.write(old_relation[relation].print_relation + "\n")
    Ligne = ["SUPPRIME", old_bati_sorted[indice].bat_id, \
        str(round(old_bati_sorted[indice].dist_mini, 9)), nom_file]
    file_log.write(formatLog(Ligne) + "\n")
file_del_building.write("</osm>")
file_del_building.close()

file_log.close()

print "Durée du calcul : ", tps2 - tps1
print "------------------------------------------------------------------"
print "-                       FIN DU PROCESS                           -"
print "------------------------------------------------------------------"

Dernière édition par sebastien31 le dim. mars 08, 2015 11:28 pm, édité 3 fois.

vdct
Messages : 225
Inscription : mar. janv. 22, 2013 10:16 pm

Re: [python] Aide pour mis à jour cadastre

Message par vdct » sam. févr. 14, 2015 1:00 pm

En regardant le code, en fait tu laissais en dur des indices (1,5,7) sans tirer parti des variables juste au dessus. J'ai donc changé les lignes 173,174 :

Code : Tout sélectionner

      -- old_nodes.append(Point(champsLigne[1], champsLigne[5], champsLigne[7]))
      -- old_id_nodes.append(champsLigne[1])
      ++ old_nodes.append(Point(champsLigne[col_id], champsLigne[col_lat], champsLigne[col_lon]))
      ++ old_id_nodes.append(champsLigne[col_id])
J'ai aussi changé la ligne 314 en l'adaptant à mon arborescence, et ensuite le script fonctionne. Je vais regarder ce que donnent les fichiers, j'ai le cas d'une commune où j'ai tracé les bâtiments à la main il y a longtemps, et qui récemment a basculé en cadastre vectoriel.

vincent

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » dim. mars 08, 2015 11:33 pm

Je ne sais pas s'il y a des personnes qui ont utilisé mon script mais leur retour serait très apprécié. :) De même que les idées d'améliorations. ^^
En tout cas, faites attention la dernière version du script que j'ai faite se trouve dans le 7ème message du post.

vdct
Messages : 225
Inscription : mar. janv. 22, 2013 10:16 pm

Re: [python] Aide pour mis à jour cadastre

Message par vdct » lun. mars 09, 2015 10:15 am

Bonjour Sebastien,
De mon côté j'ai du mal à dégager du temps en ce moment. Si pas trop de réactions ici, n'hésite pas à diffuser ton message (en postant ton code par ailleurs) sur la liste francophone https://lists.openstreetmap.org/listinfo/talk-fr qui a une plus grande audience que le forum.

vincent

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » mer. avr. 08, 2015 9:08 pm

Bonjour

J'ai ouvert un github pour que les modifications / participations soient plus facile à gérer.

https://github.com/sebastien-bugzilla/BatiOsm

Sébastien

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » mer. août 12, 2015 4:02 pm

Bonjour,

Un message juste pour dire que j'ai apporté des améliorations au script (correction de bug suggéré par un utilisateur et amélioration du code). J'ai trouvé un moyen pour réduire considérablement le temps de calcul du script. Par exemple pour la commune de Colomier (21000 bâtiments) la plus grande que j'ai testé me prenait avant 23 min. Maintenant il suffit d'un peu moins de 5 min. Pour une petite commune de 2000 bâtiments c'est seulement quelques secondes. En fait la majeur parti du temps viens de la lecture des fichiers. La comparaisons se fait rarement en plus d'une ou deux secondes.

J'envisage de faciliter l'exploitation des résultats parce que c'est encore un peu rébarbatif de faire des copier-coller...

BrunoC
Messages : 374
Inscription : sam. juin 23, 2012 11:07 am
Localisation : Nantes
Contact :

Re: [python] Aide pour mis à jour cadastre

Message par BrunoC » mar. sept. 22, 2015 12:07 pm

Hello

Super outil, que je vais m'empresser de tester...

Axes d'amélioration :
  • Parser/Ecrire un fichier osm : il y a plein de bouts de code python fiables pour parser et écrire des fichiers .osm. De plus un fichier .osm est un fichier XML: tu pourrais au moins utiliser le parser XML intégré à Python ( Python est fournit "piles incluses" ;) ).
    Regarde cet exemple, en 3 lignes c'est fait.
  • Connais-tu Shapely et RTree ? Pour gérer la topologie en Python c'est le bonheur. L'utilisation d'un index RTree va accélérer ton traitement par 100 !
A+
Bruno

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » mer. oct. 21, 2015 11:22 pm

:oops: Un mois après ton message je te réponds... Bref... Merci pour les compliments !
Oui etree était effectivement mon idée initiale. Je voulais surtout pas parser du fichier xml alors que d'autres beaucoup plus expérimentés l'ont déjà fait... Mais soit etree est pas terrible soit je m'y suis très mal pris, j'ai trouvé que etree était vraiment très lent. J'en ai parlé dans mon message du 14/02. Pour une petite ville de 2000 bâtiments j'arrivais à 214 secondes avec etree et la version du 14/02 prenait 10 secondes. Maintenant avec les améliorations que j'ai fait cet été ça ne dure plus que 3.3 secondes ! 8-) Du coup j'ai fait test en parsant à la mano les fichiers osm et j'ai pas chercher à changer. J'ai un peu amélioré la robustesse pour que ça marche le plus souvent possible mais je ne suis pas à l'abri d'un fichier exotique qui fasse tout planter.

Non je ne connais pas shapely et RTree. J'ai l'impression quand même que RTree correspond à ce que j'ai changé cet été pour diminuer le nombre de comparaison faite pour déterminer le bâtiments les plus proches.

Personnellement je n'ai pas vraiment le temps en ce moment de m'y replonger. D'autant que le site du cadastre est en panne... Mais bon je garde ça sous le coude.
La dernière version est disponible sur github (cf. mon message en bas de première page).

A dans un mois... :mrgreen: :oops:

Ab_fab

Re: [python] Aide pour mis à jour cadastre

Message par Ab_fab » jeu. nov. 12, 2015 11:29 am

Bonjour,

Je commence à m'intéresser à ce script. Je ne l'ai pas encore essayé en pratique, mais je voulais voir comment récupérer proprement le bâti "as is" comme défini dans le premier message, en respectant les limites de la commune.
J'ai retrouvé dans les archives de la liste talk-fr cette méthode proposée par Marc Sibert, que je trouve plutôt chouette :
https://lists.openstreetmap.org/piperma ... 74528.html

"J'adore l'overpass-turbo et son wizard. On peut lui demander :
building in "Sainte-Geneviève-des-Bois", puis dans exporter, proposer JOSM (oui, réparer la requête)
"et voilà" !"

La requête ne se fait pas sur l'API OSM et les données de bâti sont filtrées à la source.
Personnellement, j'adhère également :)

Bonne journée

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » jeu. nov. 12, 2015 11:30 pm

Oui effectivement overpass-turbo est de loin la meilleure pour obtenir directement le fichier qui contient le bâti actuellement affiché sur la carte. Même si pour les grandes villes / communes ça risque de poser problème face au volume requis.
D'ailleurs une amélioration du script pourrait être d'abandonner ce travail par commune. J'ai vu qu'on pouvait extraire une zone du cadastre et que les limites de la boite téléchargée est marqué dans le fichier obtenu. Une idée pourrait être de récupérer ces limites, générer une requête overpass-turbo et de l’exécuter pour récupérer directement le fichier. Mais j'ai vu que ça entraînait des erreurs sur les bords.

Ab_fab

Re: [python] Aide pour mis à jour cadastre

Message par Ab_fab » ven. nov. 13, 2015 9:37 am

Bonjour,
J'ai pu essayer le script hier soir, sur de petites communes.
Côté exécution, aucun souci, bravo ^^

Concernant le tri entre les nouveaux bâtiments et les modifiés, je pense qu'une catégorie complémentaire serait super utile en pratique :
celle des fusions probables dans la base osm de deux bouts de bâtiment coupés par l'outil d'extraction du cadastre au niveau d'une limite de parcelle.
L'analyse du fichier "to-be" donne le plus petit morceau en nouveau bâtiment, et le plus gros des deux en bâtiment modifié.

Cela pourrait épargner un bout de travail manuel. Mais sinon en jouant avec les calques de JOSM on doit pouvoir les identifier rapidement.

Avatar de l’utilisateur
Lenny
Messages : 60
Inscription : jeu. janv. 17, 2013 7:37 pm

Re: [python] Aide pour mis à jour cadastre

Message par Lenny » mer. déc. 02, 2015 10:50 am

Bonjour.
J'ai voulu tester ton script ; je suis sous windows 7.

J'ai utilisé la dernière version du script. Il se lance, fait les comparaisons, crée les fichiers et s'arrête avec l'erreur :
Traceback (most recent call last):
File "BatiOsm.py", line 839, in <module>
file_noMod.write((new_bati[i_lat][i_lon][i_bat].print_bat + "\n").encode('utf-8'))
TypeError: must be str, not bytes
Les fichiers ont été créés, mais ils ne contiennent que les deux lignes :
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" upload="true" generator="JOSM">
cordialement
Lenny

jcr83
Messages : 106
Inscription : ven. nov. 25, 2011 5:22 pm

Re: [python] Aide pour mis à jour cadastre

Message par jcr83 » mer. déc. 02, 2015 2:20 pm

Bonjour,
Quelle version de Python utilises-tu ?

Avatar de l’utilisateur
Lenny
Messages : 60
Inscription : jeu. janv. 17, 2013 7:37 pm

Re: [python] Aide pour mis à jour cadastre

Message par Lenny » mer. déc. 02, 2015 7:15 pm

Bonsoir !

Python 3.2.5 et lxml 3.5.0 (récupérée sur le site https://pypi.python.org/pypi/lxml/3.5.0#downloads)

jcr83
Messages : 106
Inscription : ven. nov. 25, 2011 5:22 pm

Re: [python] Aide pour mis à jour cadastre

Message par jcr83 » mer. déc. 02, 2015 10:29 pm

BatiOsm a été écrit pour Python 2. Sébastien devait le mettre à jour pour le rendre compatible avec Python 3, mais je ne sais pas s'il a terminé.

Avatar de l’utilisateur
Lenny
Messages : 60
Inscription : jeu. janv. 17, 2013 7:37 pm

Re: [python] Aide pour mis à jour cadastre

Message par Lenny » jeu. déc. 03, 2015 6:39 pm

Bonsoir.
Merci jcr83, c'était bien cela.
Après installation de Python 2.7 et de lxml correspondant, Cela fonctionne.

Les fichiers sont bien remplis, mais celui contenant les suppressions ne peut pas être ouvert avec josm : "Attribut 'version' manquant pour l'objet OSM avec l'identifiant 64169016. (à la ligne 3, colonne 37). 133 octets ont été lus"

JOSM (version 9060) semble avoir besoin de la version du way, quand celui-ci existe en base. L'objet recherché (le premier du fichier -ligne 3) n'a qu'une version.

cordialement
Lenny

jcr83
Messages : 106
Inscription : ven. nov. 25, 2011 5:22 pm

Re: [python] Aide pour mis à jour cadastre

Message par jcr83 » ven. déc. 04, 2015 9:38 am

Je me demande si tu n'utilises pas une version obsolète de BatiOsm. Tu as bien téléchargé la dernière version sur github ?

Avatar de l’utilisateur
Lenny
Messages : 60
Inscription : jeu. janv. 17, 2013 7:37 pm

Re: [python] Aide pour mis à jour cadastre

Message par Lenny » ven. déc. 04, 2015 10:30 am

C'est bien la dernière, je l'ai téléchargée le 30/11/2015, c'est celle qui utilise etree ...

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » dim. déc. 06, 2015 11:30 am

Bonjour tout le monde,

Effectivement j'ai modifié récemment pour utiliser etree. Mais je ne pense pas que ça soit le problème. Je teste aussi avec python3 à chaque fois que je modifie le code.
Est-ce que tu pourrais me fournir les fichiers que tu utilises et me dire comment tu les obtenus (surtout pour la version actuelle) ?

Je viens de tester avec les fichiers que j'ai mis en exemple (sur la commune de Buzy). J'arrive à reproduire le bug.

Je regarde ça et je me note pour la prochaine modif de faire des test un peu plus poussés... :roll:

sebastien31
Messages : 17
Inscription : sam. févr. 14, 2015 11:07 am

Re: [python] Aide pour mis à jour cadastre

Message par sebastien31 » dim. déc. 06, 2015 12:19 pm

Voilà j'ai publié une nouvelle version qui règle le problème normalement. J'avais oublié de gérer l'historique des anciens batiments. Effectivement lorsque le numéros d'un batiment est positif, cela veut dire qu'il existe déjà dans osm donc il a tout un historique (la version, l'utilisateur, la date d'enregistrement). En passant à etree j'ai adopté le même code pour la lecture des batiments anciens et des nouveaux mais je ne me souvenais pas que le code était différent pour les deux fichiers.
Bon ben du coup ce n'est plus la peine de me fournir les fichiers. Enfin sauf si tu trouves un autre bug bien sûr ! :mrgreen:

Répondre

Qui est en ligne ?

Utilisateurs parcourant ce forum : Aucun utilisateur inscrit et 7 invités