Source code for footprint2graph.algo.segmentation

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

from math import *
import sys
import csv
csv.field_size_limit(sys.maxsize)
import time


import tracklib as tkl
from footprint2graph import log_event



[docs] def candidates_for_aggregate(track_segment, edge, BUFFER): ''' Fonction utilitaire. tn : trace dont on a gardé l'ensemble des points consécutifs recalés sur le troncon edge tn est decoupe si après une première longueur la trace quitte l'arrivée (cas d'un aller-retour sur le troncon) Les pauses au milieu ne sont pas gérées Une trace est gardée pour la fusion si : - son point de départ est "proche" de la source de l'arc. - son point d'arrivée est "proche" de la target de l'arc. - sa géométrie dans le même sens que celui de l'arc. Si trop petite, on ne garde pas Si pas assez de points, on fait un resample de 1m ''' morceaux = tkl.TrackCollection() s = edge.source.coord t = edge.target.coord # Est-ce que le début de la trace est dans une zone de départ ou d'arrivée ? # sinon on coupe le premier morceau BEGIN = 0 END = track_segment.size() - 1 Zone = 'N' p1 = track_segment.getFirstObs().position if p1.distance2DTo(s) < BUFFER: Zone = 'S' if p1.distance2DTo(t) < BUFFER: Zone = 'T' while p1.distance2DTo(s) >= BUFFER and p1.distance2DTo(t) >= BUFFER: BEGIN += 1 if BEGIN > END-1: break p1 = track_segment.getObs(BEGIN).position if p1.distance2DTo(t) < BUFFER: Zone = 'T' if p1.distance2DTo(s) < BUFFER: Zone = 'S' if Zone == 'N': # Aucun des points n'est dans la zone de départ ou d'arrivée #if edge.id == "9476": # print ('---------', edge.id, Zone, BEGIN) # print (track_segment.toWKT()) return morceaux if BEGIN > 0: # Il faut retirer le premier morceau de la trace qui est hors définition # d'une trace candidate track_segment = track_segment.extract(BEGIN, END) if track_segment.size() <= 0: # On ne fait rien, trace à ne pas garder pour la fusion # print ('Pas de candidat pour le segment') return morceaux if Zone == 'T': # On retourne la trace new_track_segment = track_segment.reverse() p1 = new_track_segment.getFirstObs().position if p1.distance2DTo(t) < BUFFER: Zone = 'T' elif p1.distance2DTo(s) < BUFFER: Zone = 'S' else: Zone = 'N' else: new_track_segment = track_segment if Zone == 'N': # Aucun des points n'est dans la zone de départ ou d'arrivée return morceaux p2 = new_track_segment.getLastObs().position #if edge.id == "11338": # print ('---------', edge.id, Zone) # On sait que le début est dans une zone, # on regarde la fin et aussi on s'occupe du sens d'orientation du # premier morceau ''' p2 = track_segment.getLastObs().position if p1.distance2DTo(s) >= BUFFER: if p2.distance2DTo(s) < BUFFER: # sens inverse track_segment = track_segment.reverse() p1 = track_segment.getFirstObs().position p2 = track_segment.getLastObs().position else: print ('---????') # on fait rien, trace à ne pas garder pour la fusion # print ('Pas de candidat pour le segment') return morceaux else: print('!,!,!,') ''' # ------------------------------------------------------------------------- # On découpe si besoin atteint = False dd = 0 start = Zone for idx, o in enumerate(new_track_segment): if start == 'S' and o.position.distance2DTo(t) < BUFFER: atteint = True if start == 'T' and o.position.distance2DTo(s) < BUFFER: atteint = True if atteint and o.position.distance2DTo(t) > BUFFER and start == 'S': # la deuxième partie de la trace est sortie morceau = new_track_segment.extract(dd, idx-1) morceaux.addTrack(morceau) dd = idx atteint = False start = 'T' if atteint and o.position.distance2DTo(s) > BUFFER and start == 'T': # la deuxième partie de la trace est sortie morceau = new_track_segment.extract(dd, idx-1).reverse() morceaux.addTrack(morceau) dd = idx atteint = False start = 'S' if atteint: morceau = new_track_segment.extract(dd, idx) if start == 'T': morceaux.addTrack(morceau.reverse()) else: morceaux.addTrack(morceau) if edge.geom.length() < 2*BUFFER: for morceau in morceaux: morceau.resample(1, mode=tkl.MODE_SPATIAL) return morceaux
[docs] def getcandidates(MM, network, collection, BUFFER=15, RESPATH=None, prefix=None): # ========================================================================= # ========================================================================= # On prépare les traces pour la fusion: # - créer des morceaux # - toutes les traces dans le même sens # On enregistre le MM dans un fichier CSV # EDGE_ID;TRACK_ID;WKT print ('Starting construction of candidate trajectory segments for each topology edge ...') if RESPATH is not None: mmpath = RESPATH + 'mapmatch/resultmm_' + str(prefix) + '.csv' allmmpath = RESPATH + 'mapmatch/resultallmm_' + str(prefix) + '.csv' f1 = open(mmpath,'w') f1.write("EDGE_ID;TRACK_ID;OFNP_ID;WKT\n") f2 = open(allmmpath,'w') f2.write("EDGE_ID;TRACK_ID;WKT\n") TRACES = {} MIN = 100000 MAX = 0 NBC = 0 MOYC = 0 for edgeid, tobstrack in MM.items(): # print (edgeid) #if edgeid != '23': # continue if not edgeid in TRACES: TRACES[edgeid] = [] e = network.EDGES[edgeid] for trackid, tobs in tobstrack.items(): #if trackid != "OV_3901-v1_v1-v1": # continue #print ('------') #if trackid != '121.0-v1': # continue #print (tobs) track = collection.getTrackWithTid(trackid) tid = str(track.getObsAnalyticalFeature('TID', 0)) mid = str(track.getObsAnalyticalFeature('MID', 0)) #print ('------') points_sorted = sorted(tobs, key=lambda x: x[0]) regrouper = tkl.TrackCollection() tn = tkl.Track() v = 1 p1 = None idxp1 = -1 for p in points_sorted: idxp2 = p[0] #print (idxp2) p2 = p[1] if p1 is not None: if idxp1 + 1 < idxp2: #print ('?!?!?!?!?!?!?!?!?!?!', idxp1, idxp2, tn.size()) # On coupe la trace pour créer un nouveau morceau ? # Oui si zone de départ/arrivée cb = candidates_for_aggregate(tn, e, BUFFER) if cb.size() <= 0: regrouper.addTrack(tn) for tb in cb: # if tb.size() < NB_OBS_MIN: # continue # tid = str(trackid) + "-m" + str(v) mid = mid + "-m" + str(v) v += 1 if RESPATH is not None: f1.write(str(edgeid) + ";" + str(tid) + ";" + str(mid) + ";" + tb.toWKT() + "\n") # print ('+++++', tb.size()) trace = tkl.TrackReader.parseWkt(tb.toWKT(), 'ENU') trace.tid = mid trace.uid = tid trace.createAnalyticalFeature('TID', tid) trace.createAnalyticalFeature('MID', mid) TRACES[edgeid].append(trace) if RESPATH is not None: # On garde la trace de la trace sans regarder si c'est un bon candidat f2.write(str(edgeid) + ";" + str(trackid) + ";" + tn.toWKT() + "\n") tn = tkl.Track() p1 = None idxd = 0 tn.addObs(tkl.Obs(p2, tkl.ObsTime())) p1 = p2 idxp1 = idxp2 # dernier morceau de trace #print (tn.size()) cb = candidates_for_aggregate(tn, e, BUFFER) # print (cb.size()) if cb.size() <= 0: regrouper.addTrack(tn) for tb in cb: #if tb.size() < NB_OBS_MIN: # continue # tid = str(trackid) + "-v" + str(v) mid = mid + "-m" + str(v) v += 1 if RESPATH is not None: f1.write(str(edgeid) + ";" + str(tid) + ";" + str(mid) + ";" + tb.toWKT() + "\n") #print ('+++++') trace = tkl.TrackReader.parseWkt(tb.toWKT(), 'ENU') trace.tid = mid trace.uid = tid trace.createAnalyticalFeature('TID', tid) trace.createAnalyticalFeature('MID', mid) TRACES[edgeid].append(trace) if RESPATH is not None: f2.write(str(edgeid) + ";" + str(trackid) + ";" + tn.toWKT() + "\n") n = len(TRACES[edgeid]) print (' ', n, ' candidates for edge', edgeid) NBC += 1 MIN = min([MIN,n]) MAX = max([MAX,n]) MOYC += n if RESPATH is not None: f1.close() f2.close() try: print (" Number of processed edges: ", NBC) print (" Minimum number of candidate tracks per edge: ", MIN) print (" Maximum number of candidate traces per edge: ", MAX) print (" Average number of candidate tracks per edge: ", round(MOYC/NBC)) log_event(RESPATH + "candidate" + str(prefix) + ".json", { "Number of processed edges": NBC, "Minimum number of candidate traces per edge": MIN, "Maximum number of candidate traces per edge": MAX, "Average number of candidate tracks per edge": round(MOYC/NBC), "ts": time.time() }) except Exception as e: print (e) print ('Error while writing candidate information to log.') print (" Segment construction completed.") return TRACES
[docs] def prepareMapMatchResultForCreateCandidates(collection, network, SEARCH, NB_PTS = 5, RESPATH=None, prefix=None): # On construit un dictionnaire qui va contenir l'ensemble des points MM # avec leurs séquences afin de reconstuire des traces candidates # pour la fusion: # la trace doit être proche de la source de l'arc et # proche de la target de l'arc # sans trou (index manquant) # géométrie dans le même que celui de l'arc # MM = {} # [ide][pkid] : liste des observations nbpt = 0 nbmm = 0 nbhp = 0 rmse_total = 0 nbtj = 0 maxd = 0 for i in range(collection.size()): track = collection.getTrack(i) nbtj += 1 MSE = 0 # print (track.getListAnalyticalFeatures()) # print (track.getObsAnalyticalFeature('TID', 0)) track.createAnalyticalFeature('mmtype', 'NOT') track.createAnalyticalFeature('idedge', -1) pkid = track.tid # print (pkid) #if track.size() != 309: # and track.size() != 309: # continue for j in range(track.size()): nbpt += 1 pb = track[j].position ds = float(track["hmm_inference", j][2]) dt = float(track["hmm_inference", j][3]) idxedge = int(track["hmm_inference", j][1]) if idxedge > -1: nbmm += 1 xraw = track[j].position.getX() yraw = track[j].position.getY() xmm = track["hmm_inference", j][0].getX() ymm = track["hmm_inference", j][0].getY() ecartpos = sqrt((xraw-xmm)**2 + (yraw-ymm)**2) MSE += ecartpos**2 if ecartpos > maxd: maxd = ecartpos else: nbhp += 1 # print (idxedge) edgeid = network.getEdgeId(idxedge) #if edgeid != '79': # continue #print (edgeid) #print ('+++++', idxedge, ds, dt) e = network.EDGES[edgeid] if idxedge == -1: track.setObsAnalyticalFeature('mmtype', j, 'NOT') track.setObsAnalyticalFeature('idedge', j, -1) # On s'autorise à combler les petits trous avec des points non appariés # s'ils ont une distance à l'arc petite # Pour connaitre l'arc, il faut passer par les points précédents # et les points suivants # l'arc precedent idxp = -1 for k in range(j-1, max(-1, j-NB_PTS-1), -1): idxp = int(track["hmm_inference", k][1]) if idxp > -1: break # l'arc suivant idxs = -1 for k in range(j+1, min(track.size(), j+NB_PTS+1), 1): idxs = int(track["hmm_inference", k][1]) if idxs > -1: break # print (j, idxp, idxs) # if idxp == idxs or idxp == -1 or idxs == -1: # - Il faut que ce soit le même arc, donc dans ce cas il faut # des points recalés avant et des points recalés après if idxp == idxs and idxp > -1 and idxs > -1: edgeidsupp = network.getEdgeId(idxp) esupp = network.EDGES[edgeidsupp] # on calcule la projection sur l'arc des points avant-apres distmin, xproj, yproj, iproj = tkl.proj_polyligne( esupp.geom.getX(), esupp.geom.getY(), pb.getX(), pb.getY()) # print (distmin, SEARCH) if distmin < SEARCH: if edgeidsupp not in MM: MM[edgeidsupp] = {} if pkid not in MM[edgeidsupp].keys(): MM[edgeidsupp][pkid] = [] # print (j) MM[edgeidsupp][pkid].append((j,pb)) # print ('-----', j, edgeidsupp, pkid) elif ds > 0.01 and dt > 0.01: if edgeid not in MM: MM[edgeid] = {} if pkid not in MM[edgeid].keys(): MM[edgeid][pkid] = [] # print ('-----', j, edgeid, pkid) MM[edgeid][pkid].append((j,pb)) track.setObsAnalyticalFeature('mmtype', j, 'EDGE') track.setObsAnalyticalFeature('idedge', j, edgeid) elif abs(ds) < 0.01: idnode = e.source.id edgesid = network.getIncidentEdges(idnode) for eid in edgesid: if eid not in MM: MM[eid] = {} if pkid not in MM[eid].keys(): MM[eid][pkid] = [] MM[eid][pkid].append((j,pb)) track.setObsAnalyticalFeature('mmtype', j, 'SOURCE') track.setObsAnalyticalFeature('idedge', j, idnode) elif abs(dt) < 0.01: idnode = e.target.id edgesid = network.getIncidentEdges(idnode) for eid in edgesid: if eid not in MM: MM[eid] = {} if pkid not in MM[eid].keys(): MM[eid][pkid] = [] MM[eid][pkid].append((j,pb)) track.setObsAnalyticalFeature('mmtype', j, 'TARGET') track.setObsAnalyticalFeature('idedge', j, idnode) else: print ('????') MSE = sqrt(MSE / track.size()) rmse_total += MSE**2 if nbtj == 0: rmse_total = -1 else: rmse_total = sqrt(rmse_total/nbtj) # print (MM['23']) if RESPATH is not None: if nbpt == 0: percentMM = 0 percentHP = 0 else: percentMM = (nbmm / nbpt * 100) print (' Number of map-matched points = ' + str(nbmm) + ' (' + str(round(percentMM, 2)) + ' %)') percentHP = (nbhp / nbpt * 100) print (' Map-matching results restructuring completed.') log_event(RESPATH + "mapmatch" + prefix + ".json", { "Search radius (m)": SEARCH, "Number of map-matched points": nbmm, "Percentage of map-matched points (%)": str(round(percentMM, 2)), "Number of off-track points": nbhp, "Percentage of off_track points (%)": str(round(percentHP, 2)), "Root Mean Square Error (m)": round(rmse_total), "Maximal displacement (m)": maxd, "ts": time.time() }) return MM