Skip to content

Commit b8d5fca

Browse files
committed
Add fields to trajs
1 parent 0d00778 commit b8d5fca

File tree

6 files changed

+60
-42
lines changed

6 files changed

+60
-42
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# QGIS Processing Trajectools
22

3-
The Trajectools plugin adds trajectory tools to the QGIS Processing toolbox.
3+
The Trajectools plugin adds trajectory analysis algorithms to the QGIS Processing toolbox.
44

55
![Trajectools screenshot](screenshots/trajectools.PNG)
66
![Trajectools clipping screenshot](screenshots/trajectools2.PNG)

qgis_processing/createTrajectoriesAlgorithm.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
sys.path.append("..")
66

7-
from .qgisUtils import tc_to_sink, traj_to_sink
87
from .trajectoriesAlgorithm import TrajectoriesAlgorithm
98

109

@@ -45,6 +44,6 @@ def createInstance(self):
4544
return type(self)()
4645

4746
def processTc(self, tc, parameters, context):
48-
tc_to_sink(tc, self.sink_pts, self.fields_pts, self.timestamp_field)
47+
self.tc_to_sink(tc)
4948
for traj in tc.trajectories:
50-
traj_to_sink(traj, self.sink_trajs)
49+
self.traj_to_sink(traj)

qgis_processing/overlayAlgorithm.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
sys.path.append("..")
1313

14-
from .qgisUtils import tc_to_sink, traj_to_sink
1514
from .trajectoriesAlgorithm import TrajectoriesAlgorithm
1615

1716

@@ -67,9 +66,9 @@ def processTc(self, tc, parameters, context):
6766
extent = self.parameterAsExtent(parameters, self.EXTENT, context)
6867
extent = shapely.wkt.loads(extent.asWktPolygon())
6968
clipped = tc.clip(extent)
70-
tc_to_sink(clipped, self.sink_pts, self.fields_pts, self.timestamp_field)
69+
self.tc_to_sink(clipped)
7170
for traj in clipped:
72-
traj_to_sink(traj, self.sink_trajs)
71+
self.traj_to_sink(traj)
7372

7473

7574
class ClipTrajectoriesByPolygonLayer(OverlayTrajectoriesAlgorithm):
@@ -116,6 +115,6 @@ def processTc(self, tc, parameters, context):
116115
for feature in vlayer.getFeatures():
117116
extent = shapely.wkt.loads(feature.geometry().asWkt())
118117
clipped = tc.clip(extent)
119-
tc_to_sink(clipped, self.sink_pts, self.fields_pts, self.timestamp_field)
118+
self.tc_to_sink(clipped)
120119
for traj in clipped:
121-
traj_to_sink(traj, self.sink_trajs)
120+
self.traj_to_sink(traj)

qgis_processing/qgisUtils.py

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,26 +49,3 @@ def feature_from_gdf_row(row):
4949
f.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(row.geometry.x, row.geometry.y)))
5050
f.setAttributes(row.values.tolist()[:-1])
5151
return f
52-
53-
54-
def tc_to_sink(tc, sink, fields, timestamp_field):
55-
try:
56-
gdf = tc.to_point_gdf()
57-
except ValueError: # when the tc is empty
58-
return
59-
gdf[timestamp_field] = gdf.index.astype(str)
60-
names = [field.name() for field in fields]
61-
names.append("geometry")
62-
gdf = gdf[names]
63-
64-
for _, row in gdf.iterrows():
65-
f = feature_from_gdf_row(row)
66-
sink.addFeature(f, QgsFeatureSink.FastInsert)
67-
68-
69-
def traj_to_sink(traj, sink):
70-
line = QgsGeometry.fromWkt(traj.to_linestringm_wkt())
71-
f = QgsFeature()
72-
f.setGeometry(line)
73-
f.setAttributes([traj.id])
74-
sink.addFeature(f, QgsFeatureSink.FastInsert)

qgis_processing/splitTrajectoriesAlgorithm.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
sys.path.append("..")
1313

14-
from .qgisUtils import tc_to_sink, traj_to_sink
1514
from .trajectoriesAlgorithm import TrajectoriesAlgorithm
1615

1716

@@ -69,9 +68,9 @@ def processTc(self, tc, parameters, context):
6968
time_gap = pd.Timedelta(time_gap).to_pytimedelta()
7069
for traj in tc.trajectories:
7170
splits = ObservationGapSplitter(traj).split(gap=time_gap)
72-
tc_to_sink(splits, self.sink_pts, self.fields_pts, self.timestamp_field)
71+
self.tc_to_sink(splits)
7372
for split in splits:
74-
traj_to_sink(split, self.sink_trajs)
73+
self.traj_to_sink(split)
7574

7675

7776
class TemporalSplitterAlgorithm(SplitTrajectoriesAlgorithm):
@@ -122,9 +121,9 @@ def processTc(self, tc, parameters, context):
122121
split_mode = self.SPLIT_MODE_OPTIONS[split_mode]
123122
for traj in tc.trajectories:
124123
splits = TemporalSplitter(traj).split(mode=split_mode)
125-
tc_to_sink(splits, self.sink_pts, self.fields_pts, self.timestamp_field)
124+
self.tc_to_sink(splits)
126125
for split in splits:
127-
traj_to_sink(split, self.sink_trajs)
126+
self.traj_to_sink(split)
128127

129128

130129
class StopSplitterAlgorithm(SplitTrajectoriesAlgorithm):
@@ -181,6 +180,6 @@ def processTc(self, tc, parameters, context):
181180
splits = StopSplitter(traj).split(
182181
max_diameter=max_diameter, min_duration=min_duration
183182
)
184-
tc_to_sink(splits, self.sink_pts, self.fields_pts, self.timestamp_field)
183+
self.tc_to_sink(splits)
185184
for split in splits:
186-
traj_to_sink(split, self.sink_trajs)
185+
self.traj_to_sink(split)

qgis_processing/trajectoriesAlgorithm.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,25 @@
1414
QgsProcessingParameterString,
1515
QgsField,
1616
QgsFields,
17+
QgsFeature,
18+
QgsGeometry,
19+
QgsFeatureSink,
1720
)
1821

1922
sys.path.append("..")
2023

21-
from .qgisUtils import tc_from_pt_layer
24+
from .qgisUtils import tc_from_pt_layer, feature_from_gdf_row
2225

2326
pluginPath = os.path.dirname(__file__)
2427

28+
TIME_FACTOR = {
29+
"s": 1,
30+
"min": 60,
31+
"h": 3600,
32+
"d": 3600 * 24,
33+
"a": 3600 * 24 * 365,
34+
}
35+
2536

2637
class TrajectoriesAlgorithm(QgsProcessingAlgorithm):
2738
# script parameters
@@ -112,15 +123,15 @@ def processAlgorithm(self, parameters, context, feedback):
112123
self.timestamp_field = self.parameterAsFields(
113124
parameters, self.TIMESTAMP_FIELD, context
114125
)[0]
115-
speed_units = self.parameterAsString(
126+
self.speed_units = self.parameterAsString(
116127
parameters, self.SPEED_UNIT, context
117128
).split("/")
118129
crs = self.input_layer.sourceCrs()
119130

120131
tc = tc_from_pt_layer(
121132
self.input_layer, self.timestamp_field, self.traj_id_field
122133
)
123-
tc.add_speed(units=tuple(speed_units), overwrite=True)
134+
tc.add_speed(units=tuple(self.speed_units), overwrite=True)
124135
tc.add_direction(overwrite=True)
125136

126137
self.fields_pts = self.get_pt_fields(
@@ -179,6 +190,39 @@ def get_pt_fields(self, fields_to_add=[]):
179190
return fields
180191

181192
def get_traj_fields(self):
193+
length_units = f"{self.speed_units[0]}"
194+
speed_units = f"{self.speed_units[0]}{self.speed_units[1]}"
182195
fields = QgsFields()
183196
fields.append(QgsField(self.traj_id_field, QVariant.String))
197+
fields.append(QgsField("start_time", QVariant.String))
198+
fields.append(QgsField("end_time", QVariant.String))
199+
fields.append(QgsField("duration_seconds", QVariant.Double))
200+
fields.append(QgsField(f"length_{length_units}", QVariant.Double))
201+
fields.append(QgsField(f"speed_{speed_units}", QVariant.Double))
184202
return fields
203+
204+
def traj_to_sink(self, traj):
205+
line = QgsGeometry.fromWkt(traj.to_linestringm_wkt())
206+
f = QgsFeature()
207+
f.setGeometry(line)
208+
start_time = traj.get_start_time().isoformat()
209+
end_time = traj.get_end_time().isoformat()
210+
duration = float(traj.get_duration().total_seconds())
211+
length = traj.get_length(units=self.speed_units[0])
212+
speed = length / (duration / TIME_FACTOR[self.speed_units[1]])
213+
f.setAttributes([traj.id, start_time, end_time, duration, length, speed])
214+
self.sink_trajs.addFeature(f, QgsFeatureSink.FastInsert)
215+
216+
def tc_to_sink(self, tc):
217+
try:
218+
gdf = tc.to_point_gdf()
219+
except ValueError: # when the tc is empty
220+
return
221+
gdf[self.timestamp_field] = gdf.index.astype(str)
222+
names = [field.name() for field in self.fields_pts]
223+
names.append("geometry")
224+
gdf = gdf[names]
225+
226+
for _, row in gdf.iterrows():
227+
f = feature_from_gdf_row(row)
228+
self.sink_pts.addFeature(f, QgsFeatureSink.FastInsert)

0 commit comments

Comments
 (0)