Source code for tracklib.algo.Segmentation

"""Class to manage segmentation of GPS tracks"""

import sys
import math
import progressbar
import numpy as np

from tracklib.core.Obs import Obs
from tracklib.core.ObsCoords import ENUCoords
from tracklib.algo.Geometrics import Circle, minCircle

import tracklib.core.Utils as utils
from tracklib.algo.Interpolation import ALGO_LINEAR, MODE_SPATIAL
import tracklib.core.Operator as Operator

# --------------------------------------------------------------------------
# Circular import (not satisfying solution)
# --------------------------------------------------------------------------
from tracklib.core.Track import Track
from tracklib.core.TrackCollection import TrackCollection


MODE_COMPARAISON_AND = 1
MODE_COMPARAISON_OR = 2

MODE_STOPS_LOCAL = 0
MODE_STOPS_GLOBAL = 1
MODE_STOPS_RTK = 2
MODE_STOPS_ACC = 3

MODE_SPLIT_RETURN_EXHAUSTIVE = 0
MODE_SPLIT_RETURN_FAST = 1

MODE_SEGMENTATION_MINIMIZE = 0
MODE_SEGMENTATION_MAXIMIZE = 1

# -------------------------------------------------------------------------
# Segmentation and Split track
# -------------------------------------------------------------------------


[docs]def segmentation(track, afs_input, af_output, thresholds_max, mode_comparaison=MODE_COMPARAISON_AND): """ Method to divide a track into multiple according to analytical feaures value. Creates an AF with 0 if change of division, 1 otherwise """ # Gestion cas un seul AF if not isinstance(afs_input, list): afs_input = [afs_input] if not isinstance(thresholds_max, list): thresholds_max = [thresholds_max] track.createAnalyticalFeature(af_output) for i in range(track.size()): # On cumule les comparaisons pour chaque af_input comp = True for index, af_input in enumerate(afs_input): current_value = track.getObsAnalyticalFeature(af_input, i) # on compare uniquement si on peut if not utils.isnan(current_value): seuil_max = sys.float_info.max if thresholds_max != None and len(thresholds_max) >= index: seuil_max = thresholds_max[index] if mode_comparaison == MODE_COMPARAISON_AND: comp = comp and (current_value <= seuil_max) else: comp = comp or (current_value <= seuil_max) # On clot l'intervalle, on le marque a 1 if not comp: track.setObsAnalyticalFeature(af_output, i, 1) else: track.setObsAnalyticalFeature(af_output, i, 0)
[docs]def split(track, source) -> TrackCollection: """Splits track according to : - af name (considered as a marker) if `source` is a string - list of index if `source` is a list :return: No track if no segmentation, otherwise a TrackCollection object """ NEW_TRACES = TrackCollection() # -------------------------------------------- # Split from analytical feature name # -------------------------------------------- if isinstance(source, str): count = 0 # Initialisation du compteur des étapes begin = 0 # indice du premier point de l'étape for i in range(track.size()): if track.getObsAnalyticalFeature(source, i) == 1: # Nouvelle trajectoire # L'identifiant de la trace subdivisée est obtenue par concaténation # de l'identifiant de la trace initiale et du compteur new_id = str(track.uid) + "." + str(count) # La liste de points correspondant à l'intervalle de subdivision est créée new_traj = track.extract(begin, i) new_traj.setUid(new_id) NEW_TRACES.addTrack(new_traj) count += 1 begin = i + 1 # Si tous les points sont dans la même classe, la liste d'étapes reste vide # sinon, on clôt la derniere étape et on l'ajoute à la liste if begin != 0: new_id = str(track.uid) + "." + str(count) new_traj = track.extract(begin, track.size() - 1) new_traj.setUid(new_id) NEW_TRACES.addTrack(new_traj) # -------------------------------------------- # Split from list of indices # -------------------------------------------- if isinstance(source, list): for i in range(len(source) - 1): NEW_TRACES.addTrack(track.extract(source[i], source[i + 1])) return NEW_TRACES
# ------------------------------------------------------------------- # # -------------------------------------------------------------------
[docs]def findStops(track: Track, spatial, temporal, mode, verbose=True) -> Track: ''' Function to find stop positions from a track Parameters ---------- track : Track spatial : float temporal : float mode : MODE_STOPS_LOCAL, MODE_STOPS_GLOBAL, MODE_STOPS_RTK, MODE_STOPS_ACC verbose : bool, optional The default is True. Returns ------- TYPE DESCRIPTION. ''' if mode == MODE_STOPS_LOCAL: return findStopsLocal(track, spatial, temporal) if mode == MODE_STOPS_GLOBAL: return findStopsGlobal(track, spatial, temporal, verbose) if mode == MODE_STOPS_RTK: return findStopsGlobalForRTK(track, spatial, temporal, verbose) if mode == MODE_STOPS_ACC: pass
# ----------------------------------------------------------------------------- # Function to find stop positions from a track # Inputs: # - duration: minimal stop duration (in seconds) # - speed: maximal speed during stop (in ground units / sec) # Output: a track with centroids (and first time of stop sequence) # For classical standard GPS track set 1 m/s for 10 sec) # -----------------------------------------------------------------------------
[docs]def findStopsLocal(track, speed=1, duration=10): track = track.copy() stops = Track() segmentation(track, "speed", "#mark", speed) track.operate(Operator.Operator.DIFFERENTIATOR, "#mark") track.operate(Operator.Operator.RECTIFIER, "#mark") TRACES = split(track, "#mark") TMP_MEAN_X = [] TMP_MEAN_Y = [] TMP_MEAN_Z = [] TMP_STD_X = [] TMP_STD_Y = [] TMP_STD_Z = [] TMP_DURATION = [] TMP_NBPOINTS = [] TMP_SIGMA_X = [] TMP_SIGMA_Y = [] TMP_SIGMA_Z = [] for i in range(0, len(TRACES), 2): if TRACES[i].duration() < duration: continue stops.addObs( Obs( TRACES[i].getCentroid().copy(), TRACES[i].getFirstObs().timestamp.copy() ) ) TMP_SIGMA_X.append(TRACES[i].operate(Operator.Operator.AVERAGER, "x")) TMP_SIGMA_Y.append(TRACES[i].operate(Operator.Operator.AVERAGER, "y")) TMP_SIGMA_Z.append(TRACES[i].operate(Operator.Operator.AVERAGER, "z")) TMP_SIGMA_X.append(TRACES[i].operate(Operator.Operator.STDDEV, "x")) TMP_SIGMA_Y.append(TRACES[i].operate(Operator.Operator.STDDEV, "y")) TMP_SIGMA_Z.append(TRACES[i].operate(Operator.Operator.STDDEV, "z")) TMP_NBPOINTS.append(TRACES[i].size()) TMP_DURATION.append(TRACES[i].duration()) if stops.size() == 0: return stops # stops.createAnalyticalFeature("radius", TMP_RADIUS) stops.createAnalyticalFeature("mean_x", TMP_MEAN_X) stops.createAnalyticalFeature("mean_y", TMP_MEAN_Y) stops.createAnalyticalFeature("mean_z", TMP_MEAN_Z) stops.createAnalyticalFeature("sigma_x", TMP_STD_X) stops.createAnalyticalFeature("sigma_y", TMP_STD_Y) stops.createAnalyticalFeature("sigma_z", TMP_STD_Z) stops.createAnalyticalFeature("duration", TMP_DURATION) stops.createAnalyticalFeature("nb_points", TMP_NBPOINTS) stops.operate(Operator.Operator.QUAD_ADDER, "sigma_x", "sigma_y", "rmse") return stops
# ---------------------------------------------------------------- # Fonctions utilitaires # ----------------------------------------------------------------
[docs]def backtracking(B, i, j): if (B[i, j] < 0) or (abs(i - j) <= 1): return [i] else: id = (int)(B[i, j]) return backtracking(B, i, id) + backtracking(B, id, j)
[docs]def backward(B): n = B.shape[0] return backtracking(B, 0, n - 1) + [n - 1]
[docs]def plotStops(stops, x="x", y="y", r="radius", rf=1, sym="r-"): for i in range(len(stops)): Circle(ENUCoords(stops[x][i], stops[y][i]), rf * stops[r][i]).plot(sym)
''' def removeStops(track, stops=None): if stops is None: stops = extractStopsBis(track) output = track.extract(0, stops["id_ini"][0]) for i in range(len(stops) - 1): output = output + track.extract(stops["id_end"][i], stops["id_ini"][i + 1]) output = output + track.extract(stops["id_end"][-1], track.size() - 1) return output '''
[docs]def findStopsGlobal(track, diameter=20, duration=60, downsampling=1, verbose=True): ''' Find stop points in a track based on two parameters: Maximal size of a stop (as the diameter of enclosing circle, in ground units) and minimal time duration (in seconds). Use downsampling parameter > 1 to speed up the process Parameters ---------- track : Track track to detect stop points diameter : float, optional Maximal size of a stop (as the diameter of enclosing circle, in ground units). The default is 20. duration : float, optional minimal time duration (in seconds). The default is 60. downsampling : float, optional to speed up the process, value need to be > 1. The default is 1. verbose : boolean, optional verbose or not. The default is True. Returns ------- stops : Track stops points constitute a new Track. ''' # If down-sampling is required if downsampling > 1: track = track.copy() track **= track.size() / downsampling # --------------------------------------------------------------------------- # Computes cost matrix as : # Cij = 0 if size of enclosing circle of pi, pi+1, ... pj-1 is > diameter # Cij = 0 if time duration between pi and pj-1 is < duration # Cij = (j-i)**2 = square of the number of points of segment otherwise # --------------------------------------------------------------------------- C = np.zeros((track.size(), track.size())) RANGE = range(track.size() - 2) if verbose: print("Minimal enclosing circles computation:") RANGE = progressbar.progressbar(RANGE) for i in RANGE: for j in range(i + 1, track.size() - 1): if track[i].distance2DTo(track[j - 1]) > diameter: C[i, j] = 0 break if track[j - 1].timestamp - track[i].timestamp <= duration: C[i, j] = 0 continue cercle = minCircle(track.extract(i, j - 1)) if cercle != None: C[i, j] = 2 * cercle.radius C[i, j] = (C[i, j] < diameter) * (j - i) ** 2 else: # TODO : à valider C[i, j] = 0 continue C = C + np.transpose(C) # --------------------------------------------------------------------------- # Computes optimal partition with dynamic programing # --------------------------------------------------------------------------- segmentation = optimalPartition(C, MODE_SEGMENTATION_MAXIMIZE, verbose) stops = Track() TMP_RADIUS = [] TMP_MEAN_X = [] TMP_MEAN_Y = [] TMP_MEAN_Z = [] TMP_IDSTART = [] TMP_IDEND = [] TMP_STD_X = [] TMP_STD_Y = [] TMP_STD_Z = [] TMP_DURATION = [] TMP_NBPOINTS = [] for i in range(len(segmentation) - 1): portion = track.extract(segmentation[i], segmentation[i + 1] - 1) C = minCircle(portion) if C != None: if (C.radius > diameter / 2) or (portion.duration() < duration): continue stops.addObs(Obs(C.center, portion.getFirstObs().timestamp)) TMP_RADIUS.append(C.radius) TMP_MEAN_X.append(portion.operate(Operator.Operator.AVERAGER, "x")) TMP_MEAN_Y.append(portion.operate(Operator.Operator.AVERAGER, "y")) TMP_MEAN_Z.append(portion.operate(Operator.Operator.AVERAGER, "z")) TMP_STD_X.append(portion.operate(Operator.Operator.STDDEV, "x")) TMP_STD_Y.append(portion.operate(Operator.Operator.STDDEV, "y")) TMP_STD_Z.append(portion.operate(Operator.Operator.STDDEV, "z")) TMP_IDSTART.append(segmentation[i] * downsampling) TMP_IDEND.append((segmentation[i + 1] - 1) * downsampling) TMP_NBPOINTS.append(segmentation[i + 1] - segmentation[i]) TMP_DURATION.append(portion.duration()) if stops.size() == 0: return stops stops.createAnalyticalFeature("radius", TMP_RADIUS) stops.createAnalyticalFeature("mean_x", TMP_MEAN_X) stops.createAnalyticalFeature("mean_y", TMP_MEAN_Y) stops.createAnalyticalFeature("mean_z", TMP_MEAN_Z) stops.createAnalyticalFeature("id_ini", TMP_IDSTART) stops.createAnalyticalFeature("id_end", TMP_IDEND) stops.createAnalyticalFeature("sigma_x", TMP_STD_X) stops.createAnalyticalFeature("sigma_y", TMP_STD_Y) stops.createAnalyticalFeature("sigma_z", TMP_STD_Z) stops.createAnalyticalFeature("duration", TMP_DURATION) stops.createAnalyticalFeature("nb_points", TMP_NBPOINTS) stops.operate(Operator.Operator.QUAD_ADDER, "sigma_x", "sigma_y", "rmse") stops.base = track.base return stops
[docs]def findStopsGlobalForRTK( track, std_max=2e-2, duration=5, downsampling=1, verbose=True ): """Find stop points in a track based on maximal size of a stop and minimal time duration Two parameters: - Maximal size of a stop (as the standard deviation per axis, in ground units) - Minimal time duration (in seconds) Use downsampling parameter > 1 to speed up the process. Default is set for precise RTK GNSS survey (2 cm for 5 sec) """ # If down-sampling is required if downsampling > 1: track = track.copy() track **= track.size() / downsampling # --------------------------------------------------------------------------- # Computes cost matrix as : # Cij = 0 if sqrt(0.33*(std_x^2 + std_y^2 + std_Z^2)) > std_max # Cij = 0 if time duration between pi and p-1 is < duration # Cij = (j-i)**2 = square of the number of points of segment otherwise # --------------------------------------------------------------------------- C = np.zeros((track.size(), track.size())) RANGE = range(track.size() - 2) if verbose: print("Minimal enclosing circles computation:") RANGE = progressbar.progressbar(RANGE) for i in RANGE: for j in range(i + 1, track.size() - 1): if track[i].distanceTo(track[j - 1]) > 3 * std_max: C[i, j] = 0 break if track[j - 1].timestamp - track[i].timestamp <= duration: C[i, j] = 0 continue portion = track.extract(i, j - 1) varx = portion.operate(Operator.Operator.VARIANCE, "x") vary = portion.operate(Operator.Operator.VARIANCE, "y") varz = portion.operate(Operator.Operator.VARIANCE, "z") C[i, j] = math.sqrt(varx + vary + varz) C[i, j] = (C[i, j] < std_max) * (j - i) ** 2 C = C + np.transpose(C) # --------------------------------------------------------------------------- # Computes optimal partition with dynamic programing # --------------------------------------------------------------------------- segmentation = optimalPartition(C, MODE_SEGMENTATION_MAXIMIZE, verbose) stops = Track() TMP_RADIUS = [] TMP_MEAN_X = [] TMP_MEAN_Y = [] TMP_MEAN_Z = [] TMP_IDSTART = [] TMP_IDEND = [] TMP_STD_X = [] TMP_STD_Y = [] TMP_STD_Z = [] TMP_DURATION = [] TMP_NBPOINTS = [] for i in range(len(segmentation) - 1): portion = track.extract(segmentation[i], segmentation[i + 1] - 1) radius = C[segmentation[i], segmentation[i + 1]] if radius == 0: continue xm = portion.operate(Operator.Operator.AVERAGER, "x") ym = portion.operate(Operator.Operator.AVERAGER, "y") zm = portion.operate(Operator.Operator.AVERAGER, "z") xv = portion.operate(Operator.Operator.VARIANCE, "x") yv = portion.operate(Operator.Operator.VARIANCE, "y") zv = portion.operate(Operator.Operator.VARIANCE, "z") pt = portion[0].position.copy() pt.setX(xm) pt.setY(ym) pt.setZ(zm) stops.addObs(Obs(pt, portion[0].timestamp)) TMP_RADIUS.append(math.sqrt(xv + yv + zv)) TMP_MEAN_X.append(xm) TMP_MEAN_Y.append(ym) TMP_MEAN_Z.append(zm) TMP_STD_X.append(xv ** 0.5) TMP_STD_Y.append(yv ** 0.5) TMP_STD_Z.append(zv ** 0.5) TMP_IDSTART.append(segmentation[i] * downsampling) TMP_IDEND.append((segmentation[i + 1] - 1) * downsampling) TMP_NBPOINTS.append(segmentation[i + 1] - segmentation[i]) TMP_DURATION.append(portion.duration()) if stops.size() == 0: return stops stops.createAnalyticalFeature("radius", TMP_RADIUS) stops.createAnalyticalFeature("mean_x", TMP_MEAN_X) stops.createAnalyticalFeature("mean_y", TMP_MEAN_Y) stops.createAnalyticalFeature("mean_z", TMP_MEAN_Z) stops.createAnalyticalFeature("id_ini", TMP_IDSTART) stops.createAnalyticalFeature("id_end", TMP_IDEND) stops.createAnalyticalFeature("sigma_x", TMP_STD_X) stops.createAnalyticalFeature("sigma_y", TMP_STD_Y) stops.createAnalyticalFeature("sigma_z", TMP_STD_Z) stops.createAnalyticalFeature("duration", TMP_DURATION) stops.createAnalyticalFeature("nb_points", TMP_NBPOINTS) stops.operate(Operator.Operator.QUAD_ADDER, "sigma_x", "sigma_y", "rmse") stops.base = track.base return stops
[docs]def splitReturnTrip(track, mode): if mode == MODE_SPLIT_RETURN_EXHAUSTIVE: return splitReturnTripExhaustive(track) if mode == MODE_SPLIT_RETURN_FAST: return splitReturnTripFast(track)
[docs]def splitReturnTripExhaustive(track): """Split track when there is a return trip to keep only the first part""" min_val = 1e300 argmin = 0 AVG = Operator.Operator.AVERAGER for return_point in progressbar.progressbar(range(1, track.size() - 1)): T1 = track.extract(0, return_point) T2 = track.extract(return_point, track.size() - 1) avg = (T1 - T2).operate(AVG, "diff") + (T2 - T1).operate(AVG, "diff") if avg < min_val: min_val = avg argmin = return_point first_part = track.extract(0, argmin - 1) second_part = track.extract(argmin, track.size() - 1) TRACKS = TrackCollection() TRACKS.addTrack(first_part) TRACKS.addTrack(second_part) return TRACKS
[docs]def splitReturnTripFast(track, side_effect=0.1, sampling=1): """Split track when there is a return trip to keep only the first part. Second version with Fast Fourier Transform""" track = track.copy() track.toENUCoords(track.getFirstObs().position) track_test = track.copy() track_test.resample( (track_test.length() / track_test.size()) / sampling, ALGO_LINEAR, MODE_SPATIAL ) H = np.fft.fft(track_test.getY()) G = np.fft.fft(track_test.getY()[::-1]) temp = np.flip(np.abs(np.fft.ifft(H * np.conj(G)))) id = np.argmax( temp[int(side_effect * len(temp)) : int((1 - side_effect) * len(temp))] ) pt = track_test[id].position dmin = 1e300 argmin = 0 for i in range(track.size()): d = track[i].position.distance2DTo(pt) if d < dmin: dmin = d argmin = i first_part = track.extract(0, argmin - 1) second_part = track.extract(argmin, track.size() - 1) TRACKS = TrackCollection() TRACKS.addTrack(first_part) TRACKS.addTrack(second_part) return TRACKS
# ------------------------------------------------------------------------- # Generic method to segment a track with dynamic programming # ------------------------------------------------------------------------- # Inputs: # - track: A track to segment (potentially with analytical features) # - cost: a three or four-valued function taking as input a track, two # integers i < j and an optional global parameter, and returning # the cost of a segment from i to j (both included) in track # Note that cost function may as well be considered as a reward # function, simply by setting mode to MODE_SEGMENTATION_MAXIMIZE # - mode: a parameter inidcating wether cost function must be minnimized # (MODE_SEGMENTATION_MINIMIZE) or maximized (MODE_SEGMENTATION_MAXIMIZE) # - verbose: parameter to enable progress bar and console displays # Output: # - A optimal segmentation, represented as a list of indices in track # -------------------------------------------------------------------------
[docs]def optimalPartition(cost_matrix, mode=MODE_SEGMENTATION_MINIMIZE, verbose=True): N = cost_matrix.shape[0] - 1 D = np.zeros((N, N)) M = np.zeros((N, N)) for i in range(N): for j in range(i, N): D[i, j] = cost_matrix[i, j] M[i, j] = -1 # --------------------------------------------------------------------------- # Computes optimal partition with dynamic programing # --------------------------------------------------------------------------- RANGE = range(2, N) if verbose: print("Optimal split search:") RANGE = progressbar.progressbar(RANGE) for diag in RANGE: for i in range(0, N - diag): j = i + diag for k in range(i + 1, j): val = D[i, k] + D[k, j] if val < D[i, j] and MODE_SEGMENTATION_MINIMIZE: D[i, j] = val M[i, j] = k if val > D[i, j] and MODE_SEGMENTATION_MAXIMIZE: D[i, j] = val M[i, j] = k # --------------------------------------------------------------------------- # Backward phase to form optimal split # --------------------------------------------------------------------------- return backward(M)
[docs]def optimalSegmentation( track, cost, glob_param=None, mode=MODE_SEGMENTATION_MINIMIZE, verbose=True ): # --------------------------------------------------------------------------- # Computes cost matrix as : # Cij = width of MBR of point set pi, pi+1, ... pj-1 # --------------------------------------------------------------------------- C = np.zeros((track.size(), track.size())) RANGE = range(track.size() - 2) if verbose: print("Cost matrix computation:") RANGE = progressbar.progressbar(RANGE) for i in RANGE: for j in range(i, track.size() - 1): if glob_param is None: C[i, j] = cost(track, i, j - 1) else: C[i, j] = cost(track, i, j - 1, glob_param) C = C + np.transpose(C) return optimalPartition(C, mode, verbose)
# ------------------------------------------------------------------------- # Method to split a track between two mark points # ------------------------------------------------------------------------- # Inputs: # - track : a track to segment # - pt1 : first point 'departure' # - pt2 : second point 'return' # - radius : threshold distance (in ground units) around pt1 and pt2 # - nb_min_pts : minima number of points to form a track # If pt2 is not provided, it is set automatically equal to pt1 # Output: # - a track collection containing segmented tracks # -------------------------------------------------------------------------
[docs]def splitAR(track, pt1, pt2=None, radius=10, nb_min_pts=10, verbose=True): if pt2 is None: pt2 = pt1 tracks = TrackCollection() subtrack = Track() k = -1 while k < len(track) - 1: k = k + 1 if ( min( track[k].position.distance2DTo(pt1), track[k].position.distance2DTo(pt2) ) < radius ): if len(subtrack) > nb_min_pts: tracks.addTrack(subtrack) if verbose: print( "Add sub-track: ", subtrack[0].timestamp, subtrack[-1].timestamp, "[" + str(len(tracks)) + "]", ) subtrack = Track() subtrack.addObs(track[k].copy()) if len(subtrack) > nb_min_pts: tracks.addTrack(subtrack) if verbose: print( "Add sub-track: ", subtrack[0].timestamp, subtrack[-1].timestamp, "[" + str(len(tracks)) + "]", ) return tracks
[docs]def stdbscan(track, eps1, eps2, minPts, deltaT): ''' Birant, D., & Kut, A. (2007). ST-DBSCAN: An algorithm for clustering spatial–temporal data. Data & Knowledge Engineering, 60(1), 208-221. Parameters ---------- track : TYPE DESCRIPTION. eps1 : TYPE DESCRIPTION. eps2 : TYPE DESCRIPTION. minPts : TYPE DESCRIPTION. deltaT : float threshold value to be included in a cluster. Returns ------- Cluster in AF ''' stack = [] cluster_label = 0 track.createAnalyticalFeature('stdbscan', -1) track.createAnalyticalFeature('noise', -1) for i, obs in enumerate(track): nocluster = track.getObsAnalyticalFeature('stdbscan', i) if nocluster == -1: neighbors_index = retrieveNeighbors(track, i, eps1, eps2) #print (len(neighbors_index)) if len(neighbors_index) < minPts: track.setObsAnalyticalFeature('noise', i, 1) else: cluster_label += 1 for k in neighbors_index: track.setObsAnalyticalFeature('stdbscan', k, cluster_label) for idx in neighbors_index: stack.append(idx) while len(stack) > 0: io = stack[0] stack.remove(io) neighbors_index2 = retrieveNeighbors(track, io, eps1, eps2) if len(neighbors_index2) >= minPts: for k in neighbors_index2: nonoise = track.getObsAnalyticalFeature('noise', k) nocluster = track.getObsAnalyticalFeature('stdbscan', k) if nonoise > -1 or nocluster == -1: track.setObsAnalyticalFeature('stdbscan', k, cluster_label)
[docs]def retrieveNeighbors(track, j, eps1, eps2): neighbors_index = [] for i in range (track.size()): if i == j: continue dd = track.getObs(j).distanceTo(track.getObs(i)) dt = abs(track.getObs(j).timestamp.toAbsTime() - track.getObs(i).timestamp.toAbsTime()) if dd <= eps1 and dt <= eps2: neighbors_index.append(i) return neighbors_index
#def standardizing(neigh_ipm, ipm_cluster): # return (neigh_ipm - ipm_cluster.mean()) / ipm_cluster.std(ddof=0) # ============================================================================= # ============================================================================= from tracklib.algo.Analytics import speed, acceleration
[docs]def stop_point_with_acceleration_criteria(track, i): """ This algorithm detect stop point. A point is a stop when speed is null and acceleration is negative. """ if i == 0: return 0 stop_point = 0 v = speed(track, i) acc = acceleration(track, i) # Si un point d'indice [i] affiche une vitesse nulle suivant une deccelération, # on cherche le prochain point d'accélération if abs(v) < 3 and acc < 0: # Initialisation d'un compteur sur i j = i # Tant qu'aucun des points suivants n'accélère, on ne marque pas le point d'arrêt while j <= track.size() - 2 and acceleration(track, j) <= 0: j += 1 # Si on trouve un point d'accélération, on donne la valeur 1 # au paramètre du point d'indice [i] if acceleration(track, j) > 0: stop_point = 1 return stop_point
VAL_AF_TIME_WINDOW_STOP = 1 VAL_AF_TIME_WINDOW_MOVE = 0 VAL_AF_TIME_WINDOW_NONE = -1
[docs]def stop_point_with_time_window_criteria(trace, i): """This algorithm of stop detection is based on geographical moving distance in time windows. The AF has value: - stop (*1*) - not stop (*0*) - not yet examined (*-1*) """ name_af = "stop_point_with_time_window_criteria" N = trace.size() val = trace.getObsAnalyticalFeature(name_af, i) if val > -1: return val if i == N - 1: return trace.getObsAnalyticalFeature(name_af, N - 2) T = 15 # fenetre de 45s D = 30 # 30 metres S = trace.getAnalyticalFeature("abs_curv") j = i + 1 ispause = False tj = trace.getObs(j).timestamp ti = trace.getObs(i).timestamp # On cherche la fin de la fenetre while (tj - ti) <= T: j = j + 1 if j == N - 1: break tj = trace.getObs(j).timestamp #print(S[j] - S[i]) # On agrandit la fenetre while (tj - ti) >= T and (S[j] - S[i]) <= D: ispause = True # print ('pause ' + str(i) + ',' + str(j)) j = j + 1 if j == N - 1: break tj = trace.getObs(j).timestamp retour = VAL_AF_TIME_WINDOW_MOVE if ispause: # PAUSES.append([i, j-1]) #print ('pause de ' + str(i) + ',' + str(j-1)) retour = VAL_AF_TIME_WINDOW_STOP for k in range(i, j - 1): trace.setObsAnalyticalFeature(name_af, k, VAL_AF_TIME_WINDOW_STOP) else: trace.setObsAnalyticalFeature(name_af, i, VAL_AF_TIME_WINDOW_MOVE) return retour