Source code for tracklib.core.TrackCollection

"""This module contain a class to manage the collections of tracks"""

from typing import Literal   
import matplotlib.pyplot as plt

import tracklib.core.Utils as Utils


[docs]class TrackCollection: """TODO"""
[docs] def __init__(self, TRACES=[]): """ TRACES: list of Track """ self.__TRACES = TRACES.copy() self.spatial_index = None
[docs] def addTrack(self, track): """TODO""" self.__TRACES.append(track)
[docs] def size(self): """TODO""" return len(self.__TRACES)
[docs] def length(self): """TODO""" length = 0 for track in self: length += track.length() return length
[docs] def duration(self): """TODO""" duration = 0 for track in self: duration += track.duration() return duration
[docs] def getTracks(self): """TODO""" return self.__TRACES
[docs] def getTrack(self, i): """TODO""" return self.__TRACES[i]
[docs] def copy(self): """TODO""" TRACKS = TrackCollection() for i in range(self.size()): TRACKS.addTrack(self.getTrack(i).copy()) return TRACKS
[docs] def setTimeZone(self, zone): """TODO""" for i in range(len(self)): self[i].setTimeZone(zone)
[docs] def convertToTimeZone(self, zone): """TODO""" for i in range(len(self)): self[i].convertToZone(zone)
# Average frequency of tracks
[docs] def frequency(self, mode="temporal"): """TODO""" m = 0 for track in self: m += track.frequency(mode) return m / self.size()
def __iter__(self): """TODO""" yield from self.__TRACES # ========================================================================= # Spatial index creation, export and import functions # =========================================================================
[docs] def createSpatialIndex(self, resolution=None, verbose=True): """TODO""" from tracklib.core.SpatialIndex import SpatialIndex self.spatial_index = SpatialIndex(self, resolution, verbose)
[docs] def exportSpatialIndex(self, filename): """TODO""" # from tracklib.core.SpatialIndex import SpatialIndex self.spatial_index.save(filename)
[docs] def importSpatialIndex(self, filename): """TODO""" from tracklib.core.SpatialIndex import SpatialIndex self.spatial_index = SpatialIndex.load(filename)
# ========================================================================= # Track collection coordinate transformation # =========================================================================
[docs] def toECEFCoords(self, base=None): """TODO""" if self.__TRACES[0].getSRID() == "Geo": for track in self.__TRACES: track.toECEFCoords() return if self.__TRACES[0].getSRID() == "ENU": if base == None: print( "Error: base coordinates should be specified for conversion ENU -> ECEF" ) exit() for track in self.__TRACES: track.toECEFCoords(base) return
[docs] def toENUCoords(self, base=None): """TODO""" if self.__TRACES[0].getSRID() in ["Geo", "ECEF"]: if base == None: base = self.__TRACES[0].getFirstObs().position message = "Warning: no reference point (base) provided for local projection to ENU coordinates. " message += "Arbitrarily used: " + str(base) print(message) for track in self.__TRACES: track.toENUCoords(base) return if self.__TRACES[0].getSRID() == "ENU": if base == None: print( "Error: new base coordinates should be specified for conversion ENU -> ENU" ) exit() for track in self.__TRACES: if track.base == None: print( "Error: former base coordinates should be specified for conversion ENU -> ENU" ) exit() track.toENUCoords(track.base, base) track.base = base.toGeoCoords() return base
[docs] def toGeoCoords(self, base=None): """TODO""" if self.__TRACES[0].getSRID() == "ECEF": for track in self.__TRACES: track.toGeoCoords() if self.__TRACES[0].getSRID() == "ENU": if base == None: print( "Error: base coordinates should be specified for conversion ENU -> Geo" ) exit() for track in self.__TRACES: track.toGeoCoords(base)
# Function to convert track to ENUCoords if it is in GeoCoords. Returns None # if no transformation operated, and returns used reference point otherwise
[docs] def toENUCoordsIfNeeded(self): """TODO""" base = None if self.getTrack(0).getSRID() in ["GEO", "Geo"]: base = self.getTrack(0).getObs(0).position.copy() self.toENUCoords(base) return base
# ========================================================================= # Thin plates smoothing # =========================================================================
[docs] def smooth(self, constraint=1e3): """TODO""" for track in self: track.smooth(constraint)
[docs] def summary(self): """ Print summary (complete wkt below) """ output = "-------------------------------------\n" output += "Number of GPS track: " + str(len(self.__TRACES)) + "\n" output += "-------------------------------------\n" SIZES = [] for trace in self.__TRACES: SIZES.append(trace.size()) output += " Nb of pt(s): " + str(SIZES) + "\n" # output += " Nb of pt(s): " + str(len(self.__POINTS)) + "\n" print(output)
[docs] def addAnalyticalFeature(self, algorithm, name=None): """TODO""" for trace in self.__TRACES: trace.addAnalyticalFeature(algorithm, name)
[docs] def getAnalyticalFeature(self, af_name, withNan=True): valuesAF = [] for track in self: values = track.getAnalyticalFeature(af_name) if not withNan: values = Utils.removeNan(values) valuesAF = valuesAF + values return valuesAF
[docs] def operate(self, operator, arg1=None, arg2=None, arg3=None): """TODO""" for trace in self.__TRACES: trace.operate(operator, arg1, arg2, arg3)
[docs] def plot(self, symbols=None, markersize=[4], margin=0.05, append=False): """TODO""" if symbols is None: symbols = ["r-", "g-", "b-", "c-", "m-", "y-", "k-"] if len(self) == 0: return symbols = Utils.listify(symbols) markersize = Utils.listify(markersize) if not append: (xmin, xmax, ymin, ymax) = self.bbox().asTuple() dx = margin * (xmax - xmin) dy = margin * (ymax - ymin) plt.xlim([xmin - dx, xmax + dx]) plt.ylim([ymin - dy, ymax + dy]) Ns = len(symbols) Ms = len(markersize) for i in range(len(self.__TRACES)): trace = self.__TRACES[i] X = trace.getX() Y = trace.getY() plt.plot(X, Y, symbols[i % Ns], markersize=markersize[i % Ms])
[docs] def filterOnBBox(self, bbox): """TODO""" xmin, xmax, ymin, ymax = bbox.asTuple() for i in range(len(self) - 1, -1, -1): track = self[i] for j in range(len(track)): inside = True inside = inside & (track[j].position.getX() > xmin) inside = inside & (track[j].position.getY() > ymin) inside = inside & (track[j].position.getX() < xmax) inside = inside & (track[j].position.getY() < ymax) if not inside: self.removeTrack(track) break
[docs] def bbox(self): """TODO""" bbox = self.getTrack(0).bbox() for i in range(1, len(self)): bbox = bbox + self.getTrack(i).bbox() return bbox
[docs] def resample(self, delta, algo: Literal[1,2,3,4]=1, mode:Literal[1,2]=1): """Resampling tracks with linear interpolation :param delta: interpolation interval (time in sec if temporal mode is selected, space in meters if spatial). :param mode: Mode of interpolation. Available modes are: - MODE_SPATIAL (*mode=1*) - MODE_TEMPORAL (*mode=2*) :params algorithm: of interpolation. Available algorithm are : - ALGO_LINEAR (*algo=1*) - ALGO_THIN_SPLINES (*algo=2*) - ALGO_B_SPLINES (*algo=3*) - ALGO_GAUSSIAN_PROCESS (*algo=4*) **NB**: In temporal mode, argument may be: - an integer or float: interval in seconds - a list of timestamps where interpolation should be computed - a reference track """ for track in self: track.resample(delta, algo, mode)
def __collectionnify(tracks): """TODO""" if isinstance(tracks, list): return TrackCollection(tracks) else: return tracks # ========================================================================= # Tracks simplification (returns a new track) # Tolerance is in the unit of track observation coordinates # MODE_SIMPLIFY_DOUGLAS_PEUCKER (1) # MODE_SIMPLIFY_VISVALINGAM (2) # =========================================================================
[docs] def simplify(self, tolerance, mode=1): """TODO""" output = self.copy() for i in range(len(output)): output[i] = output[i].simplify(tolerance, mode)
# ------------------------------------------------------------ # [+] Concatenation of two track collections # ------------------------------------------------------------ def __add__(self, collection): """TODO""" return TrackCollection(self.__TRACES + collection.__TRACES) # ------------------------------------------------------------ # [/] Even split track collection (returns n+1 collections) # ------------------------------------------------------------ def __truediv__(self, number): """TODO""" N = (int)(self.size() / number) # R = self.size()-N*number SPLITS = [] for i in range(number + 1): id_ini = i * N id_fin = min((i + 1) * N, self.size()) + 1 SPLITS.append(TrackCollection(self[id_ini:id_fin].copy())) return SPLITS # ------------------------------------------------------------ # [>] Removes first n points of track # ------------------------------------------------------------ def __gt__(self, nb_points): """TODO""" output = self.copy() for i in range(self.size()): output[i] = output[i] > nb_points return output # ------------------------------------------------------------ # [<] Removes last n points of track # ------------------------------------------------------------ def __lt__(self, nb_points): """TODO""" output = self.copy() for i in range(self.size()): output[i] = output[i] < nb_points return output # ------------------------------------------------------------ # [>=] Available operator # ------------------------------------------------------------ def __ge__(self, arg): """TODO""" return None # ------------------------------------------------------------ # [<=] Available operator # ------------------------------------------------------------ def __le__(self, arg): """TODO""" return None # ------------------------------------------------------------ # [!=] Available operator # ------------------------------------------------------------ def __neq__(self, arg): """TODO""" return None # ------------------------------------------------------------ # [Unary -] Available operator # ------------------------------------------------------------ def __neg__(self, arg): """TODO""" return None # ------------------------------------------------------------ # [**] Resample (spatial) according to a number of points # Linear interpolation and temporal resampling # ------------------------------------------------------------ def __pow__(self, nb_points): """TODO""" output = self.copy() for i in range(self.size()): output[i] **= nb_points return output # ------------------------------------------------------------ # [abs] Available operator # ------------------------------------------------------------ def __abs__(self): """TODO""" return None # ------------------------------------------------------------ # [len] Number of tracks in track collection # ------------------------------------------------------------ def __len__(self): """TODO""" return self.size() # ------------------------------------------------------------ # [-] Computes difference profile of 2 tracks # ------------------------------------------------------------ def __sub__(self, arg): """TODO""" print("Available operator not implemented yet") # ------------------------------------------------------------ # [*] Temporal resampling of tracks # ------------------------------------------------------------ def __mul__(self, number): """TODO""" output = self.copy() for i in range(self.size()): output[i] *= number return output # ------------------------------------------------------------ # [%] Remove one point out of n (or according to list pattern) # ------------------------------------------------------------ def __mod__(self, sample): """TODO""" output = self.copy() for i in range(self.size()): output[i] %= sample return output # ------------------------------------------------------------ # [//] Time resample of a tracks according to another track # ------------------------------------------------------------ def __floordiv__(self, track): """TODO""" output = self.copy() for t in output.__TRACES: t.resample(track) # Mode temporal / linear return output # ------------------------------------------------------------ # [[n]] Get and set track number n # May be tuple with uid, tid # ------------------------------------------------------------ def __getitem__(self, n): """TODO""" if isinstance(n, tuple): tracks = TrackCollection() for track in self: if (Utils.compLike(track.uid, n[0])) and ( Utils.compLike(track.tid, n[1]) ): tracks.addTrack(track) return tracks return TrackCollection.__collectionnify(self.__TRACES[n]) def __setitem__(self, n, track): self.__TRACES[n] = track # =========================================================================
[docs] def removeTrack(self, track): """TODO""" self.__TRACES.remove(track)
[docs] def removeEmptyTrack(self): """ Remove tracks without observation """ for track in self.__TRACES: if track.size() <= 0: self.removeTrack(track)
# ========================================================================= # SEGMENTATION, EXTRACT, REMOVE
[docs] def segmentation(self, afs_input, af_output, thresholds_max, mode_comparaison=1): """TODO""" for t in self.__TRACES: t.segmentation(afs_input, af_output, thresholds_max, mode_comparaison)
[docs] def split_segmentation(self, af_output): """ Découpe les traces suivant la segmentation définie par le paramètre af_output ET Remplace la trace par les traces splittées s'il y a une segmentation. """ NEW_TRACES = [] for track in self.__TRACES: TRACES_SPLIT = track.split_segmentation(af_output) # Si le tableau est nulle pas de segmentation, on ne fait rien # sinon on supprime la trace et on ajoute les traces splittées if len(TRACES_SPLIT) > 0: for split in TRACES_SPLIT: # print (split.size()) NEW_TRACES.append(split) else: NEW_TRACES.append(track) self.__TRACES = NEW_TRACES