def _topological_process(self): # initialisation des arcs (géométrie calculée, autres attributs à None) init_None = dict.fromkeys(('start_node','end_node','left_face','right_face'),None) if len(self._entries) != 1: merged_union = linemerge(unary_union(self._entries)) else: merged_union = MultiLineString(self._entries) self._edges = [ Edge(tuple(g.coords),**init_None) for g in merged_union.geoms ] del merged_union # liste des géométries Shapely des Edge (arcs du graphe) et index spatial # (facilité d'écriture par la suite + fait une seule fois) edges = map(lambda e: e._geom,self._edges) edge_si = geometry_spatial_index(edges) # construction des noeuds, mise à jour des noeuds départ et fin des arcs self._nodes, already_done = list(), dict() for edge in self._edges: for i,attname in ((0,'_start_node'),(-1,'_end_node')): xy = edge._geom.coords[i] inode = already_done.get(xy,None) if inode is None: already_done[xy] = inode = len(self._nodes) self._nodes.append(Node(xy)) setattr(edge,attname,inode) del already_done # création des faces du graphe planaire self._faces = list() for polygon in polygonize(edges): new_face = Face(polygon.exterior,polygon.interiors,extring=None,intrings=list()) self._faces.append(new_face) # liste des géométries Shapely des Face (faces du graphe et index spatial) # (facilité d'écriture par la suite + fait une seule fois) faces = map(lambda pgf: pgf._geom,self._faces) face_si = geometry_spatial_index(faces) self._process_rings(edges,faces,edge_si,face_si)
def process_sources(self): EPSILON = 1e-9 edges = map(lambda e: e._geom,self._edges) entries = self._entries # ajout, dans les arcs en entrée, des noeuds qui sont apparus points = map(lambda n: n._geom,self._nodes) si = geometry_spatial_index(self._entries) add_points(points,self._entries,EPSILON,si) entry_si = geometry_spatial_index(self._entries) for e,edge in enumerate(edges): candidates = list(entry_si.intersection(edge.bounds)) prepedge = prep(edge) candidates = filter(lambda c: prepedge.intersects(entries[c]),candidates) candidates = filter(lambda c: is_1D_geometry(edge.intersection(entries[c])),candidates) self._edges[e]._sources = map(lambda c: self._idents[c], candidates)
def _process_rings(self,edges=None,faces=None,edge_si=None,face_si=None): self._rings = list() # initialisation des périmètres du graphe # facilité d'écriture pour la suite + travail fait une seule fois if edges is None: edges = map(lambda e: e._geom,self._edges) if faces is None: faces = map(lambda f: f._geom,self._faces) # index spatiaux basés sur les arcs et les faces s'ils n'ont pas été fournis if edge_si is None: edge_si = geometry_spatial_index(edges) if face_si is None: face_si = geometry_spatial_index(faces) # listes des périmètres extérieurs et de leur géométrie préparée # les périmètres n'ont pas leur propre géométrie mais pointent vers les faces rings = map(lambda f: f.exterior,faces) preps = map(lambda r: prep(r), rings) # boucle sur les faces/périmètres extérieurs (ils se correspondent !) for f,(ring,pring) in enumerate(zip(rings,preps)): # indices des arcs qui composent le périmètre courant indexes = list(edge_si.intersection(ring.bounds)) indexes = filter(lambda i: pring.contains(edges[i]),indexes) # séquence d'arcs orientés décrivant le périmètre courant # orientation de l'arc (sens aiguilles d'une montre ou inverse) content = build_ring(ring,map(lambda i: edges[i],indexes),indexes) edge_clockwise = clockwise(ring) # mise à jour des arcs composant le périmètre (_left_face ou _right_face) for noedge, direct in content: right_attrname = '_right_face' if edge_clockwise == direct else '_left_face' setattr(self._edges[noedge],right_attrname,f) # print getattr(self._edges[noedge],right_attrname) # mise à jour de la face correspondante self._faces[f]._extring = len(self._rings) # ajout du périmètre (instance Ring) dans le graphe self._rings.append(Ring(edge_clockwise,content)) for face_container, all_holes in enumerate(holes(faces,face_si)): # quand un trou est bouché par PLUSIEURS faces, le ring n'existe # pas déjà (il n'est pas LE ring extérieur d'UNE FACE). for hole in all_holes: if len(hole) == 1: self._faces[face_container]._intrings.append(self._faces[hole[0]]._extring) continue # il va falloir créer un nouveau ring ... # il faut prendre le périmètre extérieur de l'union des faces du trou # et enlever, dans les arcs constituant les périmètre extérieurs newring = unary_union(map(lambda h: self._faces[h]._geom,hole)).exterior all_edges = set() for h in hole: for noedge, _ in self._rings[self._faces[h]._extring]._edges: all_edges.add(noedge) true_edges = filter(lambda e: newring.contains(self._edges[e]._geom),all_edges) newring = Ring(clockwise(newring),build_ring(newring,map(lambda e: self._edges[e]._geom,true_edges),true_edges)) idring = len(self._rings) self._rings.append(newring) self._faces[face_container]._intrings.append(idring) for included_faces in all_holes: for noface in included_faces: for noedge,_ in self._rings[self._faces[noface]._extring]._edges: if self._edges[noedge]._left_face is None: self._edges[noedge]._left_face = face_container if self._edges[noedge]._right_face is None: self._edges[noedge]._right_face = face_container # il peut rester des arcs "flottants" au beau milieu d'une face for edge in self._edges: if (edge._left_face,edge._right_face) != (None,None): continue candidates = list(face_si.intersection(edge._geom.bounds)) if not candidates: continue candidates = filter(lambda c: self._faces[c]._geom.contains(edge._geom),candidates) assert len(candidates) in (0,1) if len(candidates) == 1: edge._left_face = edge._right_face = candidates[0]