Source code for footprint2graph.algo.conflation

# -*- coding: utf-8 -*-

import os
import sys
import time

import tracklib as tkl

from tracklib.core import TrackCollection
from tracklib.algo.interpolation import conflate
from tracklib.algo.comparison import compare, MODE_COMPARISON_POINTWISE

from footprint2graph import log_event


# ---------------------------------------------------------------------------------
# Elastic conflation of segment geometries on a network
# ---------------------------------------------------------------------------------
# Inputs: - geom      : a collection of geometries (TrackCollection)
#         - network   : network containing reference nodes
#         - threshold : distance btw ending points above which conflation aborted
#          - h         : covariance distance of elastic correction
# Output: a collection of geometries (TrackCollection) preserving the shape of the 
# input geometries while enforcing constraints on ending points defined by network
# Each object in geom must have a tid matching with edge ids of the network
# ---------------------------------------------------------------------------------
# Conflation is performed with 'colocation least squares' method and gaussian 
# covariogram of standard deviation h
# ---------------------------------------------------------------------------------
[docs] def conflateOnNetwork(geom, network, threshold=1e300, h=30, RESPATH=None, prefix=None, verbose=True): out = TrackCollection() if (verbose): print("-----------------------------------------------------------------------------------------") print("CORRECTION ELASTIQUE DE LA GEOMETRIE DU RESEAU") print("-----------------------------------------------------------------------------------------") max_total = 0 rmse_total = 0 matched = 0 for segment in geom: if segment.tid == -1: continue edge = network.getEdge(segment.tid) p1 = edge.source.coord p2 = edge.target.coord h11 = p1.distance2DTo(segment[ 0].position); h12 = p2.distance2DTo(segment[-1].position); h1 = (h11**2+h12**2)**0.5/1.414 h21 = p1.distance2DTo(segment[-1].position); h22 = p2.distance2DTo(segment[ 0].position); h2 = (h21**2+h22**2)**0.5/1.141 HMIN = min(h1, h2) if (h2 < h1): ptemp = p1; p1 = p2; p2 = ptemp if (HMIN < threshold): conflated = conflate(segment, [p1, p2], [0, -1], h) MED = compare(segment, conflated, mode=MODE_COMPARISON_POINTWISE, p=1) MSE = compare(segment, conflated, mode=MODE_COMPARISON_POINTWISE, p=2) MAX = compare(segment, conflated, mode=MODE_COMPARISON_POINTWISE, p=float('inf')) if (verbose): print("#{:6s} MED: {:6.3f} m RMSE: {:6.3f} m MAX: {:6.3f} m MATCH: {:6.3f} m".format(segment.tid, MED, MSE, MAX, HMIN)) rmse_total += MSE**2 max_total = max(max_total, MAX) matched += 1 else: conflated = segment.copy() conflated.tid = segment.tid out.addTrack(conflated) if (len(geom) > 2): rmse_total = (rmse_total/len(geom)-1)**0.5 if (verbose): print("-----------------------------------------------------------------------------------------") print("Number of conflated segments : ", matched, " ({:2.2f}%)".format(matched/len(geom)*100)) print("Total distorsion RMSE : {:6.3f} m (MAX: {:6.3f} m)".format(rmse_total, max_total)) if RESPATH is not None: log_event(RESPATH + "conflate" + prefix + ".json", { "Number of conflated segments": matched, "Percent of conflated segments": matched/len(geom)*100, "Total distorsion RMSE (m)": rmse_total, "MAX distorsion RMSE (%)": max_total, "ts": time.time() }) if (verbose): print("-----------------------------------------------------------------------------------------") return out
[docs] def conflateTurnOnTerminalEdge(network, nid, SEARCH, h = 10): ''' conflate point of maximum curvature with the summit of a terminal edge Parameters ---------- network : TYPE DESCRIPTION. nid : TYPE DESCRIPTION. SEARCH : TYPE DESCRIPTION. h : TYPE, optional DESCRIPTION. The default is 10. Returns ------- None. ''' # print (' ', nid) edges = network.getIncidentEdges(nid) #print (' EDGES : ', len(edges), edges) EID0 = edges[0] EID1 = edges[1] EID2 = edges[2] # print (EID0, EID1, EID2) node = network.NODES[nid] l1 = network.EDGES[EID0].geom.length() l2 = network.EDGES[EID1].geom.length() l3 = network.EDGES[EID2].geom.length() idx = -1; idx1 = -1; idx2 = -1; d = sys.float_info.max if l1 < SEARCH: nb = len(network.getIncidentEdges(getAutreNoeud(network, EID0, node)[0].id)) if l1 < d and nb == 1: idx = EID0; idx1 = EID1; idx2 = EID2; d = l1 if l2 < SEARCH: nb = len(network.getIncidentEdges(getAutreNoeud(network, EID1, node)[0].id)) if l2 < d and nb == 1: idx = EID1; idx1 = EID0; idx2 = EID2; d = l2 if l3 < SEARCH: nb = len(network.getIncidentEdges(getAutreNoeud(network, EID2, node)[0].id)) if l3 < d and nb == 1: idx = EID2; idx1 = EID0; idx2 = EID1; d = l3 if idx == -1: print (' Conflation cannot be performed for node ', nid, '; the three incident edges are too long:', round(l1), round(l2), round(l3)) return # print (idx, idx1, idx2, node) # Au moins 1 arc qui est une feuille nb1 = len(network.getIncidentEdges(getAutreNoeud(network, idx, node)[0].id)) nb2 = len(network.getIncidentEdges(getAutreNoeud(network, idx1, node)[0].id)) nb3 = len(network.getIncidentEdges(getAutreNoeud(network, idx2, node)[0].id)) # print (nid, nb1, nb2, nb3) if nb1 > 1 and nb2 > 1 and nb3 > 1: print (' Conflation cannot be performed for node ', nid, '; the three incident edges are not leaves') return # il faut d'abord trouver les trois noeuds (n1, pos1) = getAutreNoeud(network, idx, node) (n2, pos2) = getNoeud(network, idx1, node) (n3, pos3) = getNoeud(network, idx2, node) # on crée un nouvel arc en conflatant pts = [n1.coord] pts_index = [pos2] h2 = h if h2 > l2: h2 = l2*0.9 s2 = conflate(network.EDGES[idx1].geom, pts, pts_index, h=h2) pts_index = [pos3] h3 = h if h3 > l3: h3 = l3*0.9 s3 = conflate(network.EDGES[idx2].geom, pts, pts_index, h=h3) # s3.plot('r-') # s2.plot('r-') (node1, poss1) = getAutreNoeud(network, idx1, node) (node2, poss2) = getAutreNoeud(network, idx2, node) # fusionne if s2.getLastObs().distance2DTo(s3.getFirstObs()) < 0.001: s = s2 + s3 ''' # On force le premier point op = network.EDGES[idx1].geom.getFirstObs() if op.position.distance2DTo(s.getFirstObs().position) > 0.001: s.insertObs(op, 0) # On force le dernier point op = network.EDGES[idx2].geom.getLastObs() if op.position.distance2DTo(s.getLastObs().position) > 0.001: s.addObs(op) ''' elif s2.getLastObs().distance2DTo(s3.getLastObs()) < 0.001: s = s2 + s3.reverse() ''' # On force le premier point op = network.EDGES[idx1].geom.getFirstObs() if op.position.distance2DTo(s.getFirstObs().position) > 0.001: s.insertObs(op, 0) # On force le dernier point op = network.EDGES[idx2].geom.getFirstObs() if op.position.distance2DTo(s.getLastObs().position) > 0.001: s.addObs(op) ''' elif s2.getFirstObs().distance2DTo(s3.getLastObs()) < 0.001: s = s2.reverse() + s3.reverse() ''' # On force le premier point op = network.EDGES[idx1].geom.getLastObs() if op.position.distance2DTo(s.getFirstObs().position) > 0.001: s.insertObs(op, 0) # On force le dernier point op = network.EDGES[idx2].geom.getFirstObs() if op.position.distance2DTo(s.getLastObs().position) > 0.001: s.addObs(op) ''' elif s2.getFirstObs().distance2DTo(s3.getFirstObs()) < 0.001: s = s2.reverse() + s3 ''' # On force le premier point op = network.EDGES[idx1].geom.getLastObs() if op.position.distance2DTo(s.getFirstObs().position) > 0.001: s.insertObs(op, 0) # On force le dernier point op = network.EDGES[idx2].geom.getLastObs() if op.position.distance2DTo(s.getLastObs().position) > 0.001: s.addObs(op) ''' s.setObs(0, tkl.Obs(node1.coord, tkl.ObsTime())) s.setObs(s.size()-1, tkl.Obs(node2.coord, tkl.ObsTime())) # s.plot('r-') edge = tkl.Edge(str(tkl.NetworkReader.counter), s) tkl.NetworkReader.counter += 1 #print (edges[0], edges[1], edges[2]) n = getAutreNoeud(network, idx1, node)[0] noeudIni = tkl.Node(n.id, s.getFirstObs().position.copy()) n = getAutreNoeud(network, idx2, node)[0] noeudFin = tkl.Node(n.id, s.getLastObs().position.copy()) # On supprime les 3 arcs en commencant par les noeuds # (ce n'est plus nécessaire) # network.removeNode(node) #if n1.id in network.getNodesId(): # network.removeNode(n1) #if noeudIni.id in network.getNodesId(): # network.removeNode(noeudIni) #if noeudFin.id in network.getNodesId(): # network.removeNode(noeudFin) network.removeEdge(network.EDGES[EID0]) network.removeEdge(network.EDGES[EID1]) network.removeEdge(network.EDGES[EID2]) # print (' remove edges:', EID0, EID1, EID2) network.addEdge(edge, noeudIni, noeudFin) # On remet à jour la topologie pour les noeuds noeudIni et noeudFin for edge in network: ni = edge.source nf = edge.target if edge.orientation >= 0: if edge.id not in network.NEXT_EDGES[ni.id]: network.NEXT_EDGES[ni.id].append(edge.id) if edge.id not in network.PREV_EDGES[nf.id]: network.PREV_EDGES[nf.id].append(edge.id) if ni.id not in network.NEXT_NODES: network.NEXT_NODES[ni.id] = [] if nf.id not in network.NEXT_NODES[ni.id]: network.NEXT_NODES[ni.id].append(nf.id) if nf.id not in network.PREV_NODES: network.PREV_NODES[nf.id] = [] if ni.id not in network.PREV_NODES[nf.id]: network.PREV_NODES[nf.id].append(ni.id) if edge.orientation <= 0: if edge.id not in network.NEXT_EDGES[nf.id]: network.NEXT_EDGES[nf.id].append(edge.id) if edge.id not in network.PREV_EDGES[ni.id]: network.PREV_EDGES[ni.id].append(edge.id) if nf.id not in network.NEXT_NODES: network.NEXT_NODES[nf.id] = [] if ni.id not in network.NEXT_NODES[nf.id]: network.NEXT_NODES[nf.id].append(ni.id) if ni.id not in network.PREV_NODES: network.PREV_NODES[ni.id] = [] if nf.id not in network.PREV_NODES[ni.id]: network.PREV_NODES[ni.id].append(nf.id) if edge.id not in network.NBGR_EDGES[ni.id]: network.NBGR_EDGES[ni.id].append(edge.id) if ni.id not in network.NBGR_NODES: network.NBGR_NODES[ni.id] = [] if nf.id not in network.NBGR_NODES[ni.id]: network.NBGR_NODES[ni.id].append(nf.id) if edge.id not in network.NBGR_EDGES[nf.id]: network.NBGR_EDGES[nf.id].append(edge.id) if nf.id not in network.NBGR_NODES: network.NBGR_NODES[nf.id] = [] if ni.id not in network.NBGR_NODES[nf.id]: network.NBGR_NODES[nf.id].append(ni.id)
[docs] def selectNodes(network, node, distance): """Selection des autres noeuds dans le cercle dont node.coord est le centre, de rayon distance :param node: le centre du cercle de recherche. :param distance: le rayon du cercle de recherche. :return: tableau de NODES liste des noeuds dans le cercle """ NODES = [] if network.spatial_index is None: for key in network.getIndexNodes(): n = network.NODES[key] if n.coord.distance2DTo(node.coord) <= distance: NODES.append(n) else: print ('INDEX !!!!!!') vicinity_edges = network.spatial_index.neighborhood(node.coord, unit=1) for e in vicinity_edges: source = network.EDGES[network.getEdgeId(e)].source target = network.EDGES[network.getEdgeId(e)].target if source.coord.distance2DTo(node.coord) <= distance: NODES.append(source) if target.coord.distance2DTo(node.coord) <= distance: NODES.append(target) return NODES
[docs] def getNoeud(network, eid, node): edge = network.EDGES[eid] nd = edge.source na = edge.target if node.id == nd.id: return (nd, 0) if node.id == na.id: return (na, edge.geom.size()-1) return None
[docs] def getAutreNoeud(network, eid, node): edge = network.EDGES[eid] nd = edge.source na = edge.target if node.id == nd.id: return (na, edge.geom.size()-1) if node.id == na.id: return (nd, 0) return None