Skip to content

Commit 8effb12

Browse files
committed
Start Split Trajectories group
1 parent 4d34f1f commit 8effb12

File tree

4 files changed

+93
-74
lines changed

4 files changed

+93
-74
lines changed

qgis_processing/createTrajectoriesAlgorithm.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ def initAlgorithm(self, config=None):
102102
type=QgsProcessingParameterField.Any,
103103
allowMultiple=False,
104104
optional=False))
105-
self.addParameter(QgsProcessingParameterString(
106-
name=self.TIMESTAMP_FORMAT,
107-
description=self.tr("Timestamp format"),
108-
defaultValue="%Y-%m-%d %H:%M:%S+00",
109-
optional=False))
105+
# self.addParameter(QgsProcessingParameterString(
106+
# name=self.TIMESTAMP_FORMAT,
107+
# description=self.tr("Timestamp format (e.g. %Y-%m-%d %H:%M:%S)"),
108+
# #defaultValue="%Y-%m-%d %H:%M:%S+00",
109+
# optional=True))
110110
self.addParameter(QgsProcessingParameterString(
111111
name=self.SPEED_UNIT,
112112
description=self.tr("Speed units (e.g. km/h, m/s)"),
@@ -126,10 +126,10 @@ def processAlgorithm(self, parameters, context, feedback):
126126
self.input_layer = self.parameterAsSource(parameters, self.INPUT, context)
127127
self.traj_id_field = self.parameterAsFields(parameters, self.TRAJ_ID_FIELD, context)[0]
128128
self.timestamp_field = self.parameterAsFields(parameters, self.TIMESTAMP_FIELD, context)[0]
129-
timestamp_format = self.parameterAsString(parameters, self.TIMESTAMP_FORMAT, context)
129+
#timestamp_format = self.parameterAsString(parameters, self.TIMESTAMP_FORMAT, context)
130130
speed_units = self.parameterAsString(parameters, self.SPEED_UNIT, context).split("/")
131131

132-
tc = tc_from_pt_layer(self.input_layer, self.timestamp_field, self.traj_id_field, timestamp_format)
132+
tc = tc_from_pt_layer(self.input_layer, self.timestamp_field, self.traj_id_field)# , timestamp_format)
133133
tc.add_speed(units=tuple(speed_units), overwrite=True)
134134
tc.add_direction(overwrite=True)
135135

qgis_processing/qgisUtils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def trajectories_from_qgis_point_layer(layer, time_field_name, trajectory_id_fie
3737
return tc_from_pt_layer(layer, time_field_name, trajectory_id_field, time_format)
3838

3939

40-
def tc_from_pt_layer(layer, time_field_name, trajectory_id_field, time_format):
40+
def tc_from_pt_layer(layer, time_field_name, trajectory_id_field):
4141
names = [field.name() for field in layer.fields()]
4242
data = []
4343
for feature in layer.getFeatures():

qgis_processing/splitTrajectoriesAlgorithm.py

Lines changed: 82 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from qgis.PyQt.QtCore import QCoreApplication, QVariant
99
from qgis.PyQt.QtGui import QIcon
10-
from qgis.core import (QgsField,QgsFields,
10+
from qgis.core import (QgsField, QgsFields,
1111
QgsGeometry,
1212
QgsFeature,
1313
QgsFeatureSink,
@@ -37,48 +37,24 @@ class SplitTrajectoriesAlgorithm(QgsProcessingAlgorithm):
3737
INPUT = 'INPUT'
3838
TRAJ_ID_FIELD = 'OBJECT_ID_FIELD'
3939
TIMESTAMP_FIELD = 'TIMESTAMP_FIELD'
40-
TIMESTAMP_FORMAT = 'TIMESTAMP_FORMAT'
4140
OUTPUT_PTS = 'OUTPUT_PTS'
4241
OUTPUT_SEGS = 'OUTPUT_SEGS'
4342
OUTPUT_TRAJS = 'OUTPUT_TRAJS'
44-
SPLIT_MODE = 'SPLIT_MODE'
45-
SPLIT_MODE_OPTIONS = ["observation gap", "year", "month", "day", "hour", ]
46-
TIME_GAP = 'TIME_GAP'
4743

4844
def __init__(self):
4945
super().__init__()
5046

51-
def name(self):
52-
return "split"
53-
5447
def icon(self):
5548
return QIcon(os.path.join(pluginPath, "icons", "icon.png"))
56-
49+
5750
def tr(self, text):
58-
return QCoreApplication.translate("split", text)
59-
60-
def displayName(self):
51+
return QCoreApplication.translate("split_traj", text)
52+
53+
def group(self):
6154
return self.tr("Split trajectories")
6255

63-
#def group(self):
64-
# return self.tr("Basic")
65-
66-
#def groupId(self):
67-
# return "TrajectoryBasic"
68-
69-
def shortHelpString(self):
70-
return self.tr(
71-
"<p>Splits trajectories into subtrajectories using one of the "
72-
"following supported modes: </p>"
73-
"<p><b>Observation gap: </b>"
74-
"splits whenever there is a gap in the observations "
75-
"(for supported time gap formats see: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_timedelta.html)</p>"
76-
"<p><b>Temporal (year, month, day, hour): </b>"
77-
"splits using regular time intervals "
78-
"(time gap parameter will be ignored)</p>"
79-
"<p>For more information on trajectory splitters see: "
80-
"https://movingpandas.readthedocs.io/en/main/trajectorysplitter.html</p>"
81-
)
56+
def groupId(self):
57+
return "split_trajectories"
8258

8359
def helpUrl(self):
8460
return "https://github.com/movingpandas/processing-trajectory"
@@ -108,23 +84,7 @@ def initAlgorithm(self, config=None):
10884
parentLayerParameterName=self.INPUT,
10985
type=QgsProcessingParameterField.Any,
11086
allowMultiple=False,
111-
optional=False))
112-
self.addParameter(QgsProcessingParameterString(
113-
name=self.TIMESTAMP_FORMAT,
114-
description=self.tr("Timestamp format"),
115-
defaultValue="%Y-%m-%d %H:%M:%S+00",
116-
optional=False))
117-
self.addParameter(QgsProcessingParameterEnum(
118-
name=self.SPLIT_MODE,
119-
description=self.tr("Splitting mode"),
120-
defaultValue="day",
121-
options=self.SPLIT_MODE_OPTIONS,
122-
optional=False))
123-
self.addParameter(QgsProcessingParameterString(
124-
name=self.TIME_GAP,
125-
description=self.tr("Time gap (timedelta, e.g. 1 hours, 15 minutes)"),
126-
defaultValue="1 hours",
127-
optional=True))
87+
optional=False))
12888
# output layer
12989
self.addParameter(QgsProcessingParameterFeatureSink(
13090
name=self.OUTPUT_PTS,
@@ -140,11 +100,6 @@ def processAlgorithm(self, parameters, context, feedback):
140100
self.traj_id_field = self.parameterAsFields(parameters, self.TRAJ_ID_FIELD, context)[0]
141101
self.timestamp_field = self.parameterAsFields(parameters, self.TIMESTAMP_FIELD, context)[0]
142102

143-
timestamp_format = self.parameterAsString(parameters, self.TIMESTAMP_FORMAT, context)
144-
split_mode = self.parameterAsInt(parameters, self.SPLIT_MODE, context)
145-
split_mode = self.SPLIT_MODE_OPTIONS[split_mode]
146-
time_gap = self.parameterAsString(parameters, self.TIME_GAP, context)
147-
148103
crs = self.input_layer.sourceCrs()
149104

150105
self.fields_pts = get_pt_fields(self.input_layer, self.traj_id_field)
@@ -155,34 +110,97 @@ def processAlgorithm(self, parameters, context, feedback):
155110
(self.sink_trajs, self.dest_trajs) = self.parameterAsSink(
156111
parameters, self.OUTPUT_TRAJS, context, self.fields_trajs, QgsWkbTypes.LineStringM, crs)
157112

158-
tc = tc_from_pt_layer(self.input_layer, self.timestamp_field, self.traj_id_field, timestamp_format)
113+
tc = tc_from_pt_layer(self.input_layer, self.timestamp_field, self.traj_id_field)#, timestamp_format)
159114

160-
if split_mode == "observation gap":
161-
self.split_on_gaps(time_gap, tc)
162-
else:
163-
self.split_temporally(split_mode, tc)
115+
self.split(tc, parameters, context)
164116

165117
return {self.OUTPUT_PTS: self.dest_pts, self.OUTPUT_TRAJS: self.dest_trajs}
118+
119+
def split(self, tc, parameters, context):
120+
pass # needs to be implemented by each splitter
166121

167122
def postProcessAlgorithm(self, context, feedback):
168123
processed_layer = QgsProcessingUtils.mapLayerFromString(self.dest_pts, context)
169124
processed_layer.loadNamedStyle(os.path.join(pluginPath, "styles", "pts.qml"))
170125
return {self.OUTPUT_PTS: self.dest_pts, self.OUTPUT_TRAJS: self.dest_trajs}
171126

172-
def split_on_gaps(self, time_gap, tc):
173-
time_gap = pd.Timedelta(time_gap).to_pytimedelta()
127+
128+
class SplitObservationGapAlgorithm(SplitTrajectoriesAlgorithm):
129+
TIME_GAP = 'TIME_GAP'
130+
131+
def __init__(self):
132+
super().__init__()
133+
134+
def initAlgorithm(self, config=None):
135+
super().initAlgorithm(config)
136+
self.addParameter(QgsProcessingParameterString(
137+
name=self.TIME_GAP,
138+
description=self.tr("Time gap (timedelta, e.g. 1 hours, 15 minutes)"),
139+
defaultValue="1 hours",
140+
optional=True))
141+
142+
def name(self):
143+
return "split_gap"
144+
145+
def displayName(self):
146+
return self.tr("Split trajectories at observation gaps")
147+
148+
def shortHelpString(self):
149+
return self.tr(
150+
"<p>Splits trajectories into subtrajectories "
151+
"whenever there is a gap in the observations "
152+
"(for supported time gap formats see: "
153+
"https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.to_timedelta.html)</p>"
154+
"<p>For more information on trajectory splitters see: "
155+
"https://movingpandas.readthedocs.io/en/main/trajectorysplitter.html</p>"
156+
)
157+
158+
def split(self, tc, parameters, context):
159+
time_gap = self.parameterAsString(parameters, self.TIME_GAP, context)
174160
for traj in tc.trajectories:
175161
splits = ObservationGapSplitter(traj).split(gap=time_gap)
176162
tc_to_sink(splits, self.sink_pts, self.fields_pts, self.timestamp_field)
177163
for split in splits:
178164
traj_to_sink(split, self.sink_trajs)
179165

180-
def split_temporally(self, split_mode, tc):
166+
167+
class SplitTemporallyAlgorithm(SplitTrajectoriesAlgorithm):
168+
SPLIT_MODE = 'SPLIT_MODE'
169+
SPLIT_MODE_OPTIONS = ["year", "month", "day", "hour", ]
170+
171+
def __init__(self):
172+
super().__init__()
173+
174+
def initAlgorithm(self, config=None):
175+
super().initAlgorithm(config)
176+
self.addParameter(QgsProcessingParameterEnum(
177+
name=self.SPLIT_MODE,
178+
description=self.tr("Splitting mode"),
179+
defaultValue="day",
180+
options=self.SPLIT_MODE_OPTIONS,
181+
optional=False))
182+
183+
def name(self):
184+
return "split_temporally"
185+
186+
def displayName(self):
187+
return self.tr("Split trajectories at time intervals")
188+
189+
def shortHelpString(self):
190+
return self.tr(
191+
"<p>Splits trajectories into subtrajectories "
192+
"using regular time intervals (year, month, day, hour): </p>"
193+
"<p>For more information on trajectory splitters see: "
194+
"https://movingpandas.readthedocs.io/en/main/trajectorysplitter.html</p>"
195+
)
196+
197+
def split(self, tc, parameters, context):
198+
split_mode = self.parameterAsInt(parameters, self.SPLIT_MODE, context)
199+
split_mode = self.SPLIT_MODE_OPTIONS[split_mode]
181200
for traj in tc.trajectories:
182201
splits = TemporalSplitter(traj).split(mode=split_mode)
183202
tc_to_sink(splits, self.sink_pts, self.fields_pts, self.timestamp_field)
184203
for split in splits:
185-
traj_to_sink(split, self.sink_trajs)
186-
187-
204+
traj_to_sink(split, self.sink_trajs)
205+
188206

qgis_processing/trajectoryProvider.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
sys.path.append("..")
2929

3030
from .createTrajectoriesAlgorithm import CreateTrajectoriesAlgorithm
31-
from .splitTrajectoriesAlgorithm import SplitTrajectoriesAlgorithm
31+
from .splitTrajectoriesAlgorithm import SplitObservationGapAlgorithm, SplitTemporallyAlgorithm
3232

3333
pluginPath = os.path.dirname(__file__)
3434

@@ -63,7 +63,8 @@ def setActive(self, active):
6363

6464
def getAlgs(self):
6565
algs = [CreateTrajectoriesAlgorithm(),
66-
SplitTrajectoriesAlgorithm()
66+
SplitObservationGapAlgorithm(),
67+
SplitTemporallyAlgorithm()
6768
]
6869
return algs
6970

0 commit comments

Comments
 (0)