Skip to content

Commit 4fa4ee4

Browse files
committed
Merged algorithms
trajectories from point layer + add speed + add heading
1 parent 0ac17fe commit 4fa4ee4

File tree

9 files changed

+248
-350
lines changed

9 files changed

+248
-350
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
The Trajectools plugin adds trajectory tools to the QGIS Processing toolbox.
44

5-
![Trajectools screenshot](https://raw.githubusercontent.com/anitagraser/qgis-processing-trajectory/master/screenshots/trajectools.PNG)
5+
![Trajectools screenshot](screenshots/trajectools.PNG)
66

77

88
**Note: This plugin depends on MovingPandas!** You will need to install MovingPandas in your QGIS Python environment. I recommend installing both QGIS and MovingPandas from conda-forge:
99

1010
```
11-
conda create -n qgis python=3.9
11+
(base) conda create -n qgis python=3.9
1212
(base) PS C:\Users\anita> conda activate qgis
1313
(qgis) PS C:\Users\anita> mamba install -c conda-forge qgis=3.28.2
1414
(qgis) PS C:\Users\anita> mamba install -c conda-forge movingpandas

icons/icon.png

-3.57 KB
Loading

qgis_processing/addHeadingAlgorithm.py

Lines changed: 0 additions & 147 deletions
This file was deleted.
Lines changed: 70 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,4 @@
11
# -*- coding: utf-8 -*-
2-
3-
"""
4-
***************************************************************************
5-
addMetersPerSecAlgorithm.py
6-
---------------------
7-
Date : December 2018
8-
Copyright : (C) 2018 by Anita Graser
9-
Email : anitagraser@gmx.at
10-
***************************************************************************
11-
* *
12-
* This program is free software; you can redistribute it and/or modify *
13-
* it under the terms of the GNU General Public License as published by *
14-
* the Free Software Foundation; either version 2 of the License, or *
15-
* (at your option) any later version. *
16-
* *
17-
***************************************************************************
18-
"""
19-
202
import os
213
import sys
224

@@ -37,7 +19,8 @@
3719
QgsProcessingParameterBoolean,
3820
QgsProcessingParameterFeatureSink,
3921
QgsProcessingParameterEnum,
40-
QgsWkbTypes
22+
QgsWkbTypes,
23+
QgsProcessingUtils
4124
)
4225

4326
sys.path.append("..")
@@ -47,43 +30,49 @@
4730
pluginPath = os.path.dirname(__file__)
4831

4932

50-
class AddMetersPerSecAlgorithm(QgsProcessingAlgorithm):
33+
class CreateTrajectoriesAlgorithm(QgsProcessingAlgorithm):
5134
# script parameters
5235
INPUT = 'INPUT'
5336
TRAJ_ID_FIELD = 'OBJECT_ID_FIELD'
5437
TIMESTAMP_FIELD = 'TIMESTAMP_FIELD'
5538
TIMESTAMP_FORMAT = 'TIMESTAMP_FORMAT'
56-
OUTPUT = 'OUTPUT'
39+
OUTPUT_PTS = 'OUTPUT_PTS'
40+
OUTPUT_SEGS = 'OUTPUT_SEGS'
41+
OUTPUT_TRAJS = 'OUTPUT_TRAJS'
42+
SPEED_UNIT = 'SPEED_UNIT'
5743

5844
def __init__(self):
5945
super().__init__()
6046

6147
def name(self):
62-
return "add_meters_per_sec"
48+
return "create_trajectory"
6349

6450
def icon(self):
6551
return QIcon(os.path.join(pluginPath, "icons", "icon.png"))
6652

6753
def tr(self, text):
68-
return QCoreApplication.translate("add_meters_per_sec", text)
54+
return QCoreApplication.translate("create_trajectory", text)
6955

7056
def displayName(self):
71-
return self.tr("Add speed (m/s) to points")
57+
return self.tr("Create trajectories")
7258

73-
def group(self):
74-
return self.tr("Basic")
59+
#def group(self):
60+
# return self.tr("Basic")
7561

76-
def groupId(self):
77-
return "TrajectoryBasic"
62+
#def groupId(self):
63+
# return "TrajectoryBasic"
7864

7965
def shortHelpString(self):
8066
return self.tr(
81-
"<p>If the input layer CRS is EPSG:4326, distances are computed using spherical " +
82-
"equations. If the layer CRS unit miles, the resulting speed values will be " +
83-
"miles per second but the attribute name is currently hard-coded to meters_per_sec.</p>")
67+
"<p><b>Speed</b> is calculated based on the input layer CRS information and " +
68+
"converted to the desired speed units. For more info on the supported units, " +
69+
"see https://movingpandas.org/units </p>" +
70+
"<p><b>Direction</b> is calculated between consecutive locations. Direction " +
71+
"values are in degrees, starting North turning clockwise.</p>"
72+
)
8473

8574
def helpUrl(self):
86-
return "https://github.com/anitagraser/processing-trajectory"
75+
return "https://movingpandas.org/units"
8776

8877
def createInstance(self):
8978
return type(self)()
@@ -116,37 +105,71 @@ def initAlgorithm(self, config=None):
116105
description=self.tr("Timestamp format"),
117106
defaultValue="%Y-%m-%d %H:%M:%S+00",
118107
optional=False))
108+
self.addParameter(QgsProcessingParameterString(
109+
name=self.SPEED_UNIT,
110+
description=self.tr("Speed units (e.g. km/h, m/s)"),
111+
defaultValue="km/h",
112+
optional=False))
119113
# output layer
120114
self.addParameter(QgsProcessingParameterFeatureSink(
121-
name=self.OUTPUT,
122-
description=self.tr("Trajectories"),
115+
name=self.OUTPUT_PTS,
116+
description=self.tr("Trajectory points"),
123117
type=QgsProcessing.TypeVectorPoint))
124-
125-
118+
self.addParameter(QgsProcessingParameterFeatureSink(
119+
name=self.OUTPUT_TRAJS,
120+
description=self.tr("Trajectories"),
121+
type=QgsProcessing.TypeVectorLine))
126122

127123
def processAlgorithm(self, parameters, context, feedback):
128124
input_layer = self.parameterAsSource(parameters, self.INPUT, context)
129125
traj_id_field = self.parameterAsFields(parameters, self.TRAJ_ID_FIELD, context)[0]
130126
timestamp_field = self.parameterAsFields(parameters, self.TIMESTAMP_FIELD, context)[0]
131127
timestamp_format = self.parameterAsString(parameters, self.TIMESTAMP_FORMAT, context)
128+
speed_units = self.parameterAsString(parameters, self.SPEED_UNIT, context).split("/")
132129

133130
tc = tc_from_pt_layer(input_layer, timestamp_field, traj_id_field, timestamp_format)
134-
tc.add_speed(units=("m","s"))
131+
tc.add_speed(units=tuple(speed_units))
132+
tc.add_direction()
133+
134+
self.dest_pts = self.create_points(parameters, context, input_layer, timestamp_field, tc)
135+
self.dest_trajs = self.create_trajectories(parameters, context, input_layer, traj_id_field, tc)
136+
137+
return {self.OUTPUT_PTS: self.dest_pts, self.OUTPUT_TRAJS: self.dest_trajs}
138+
139+
def postProcessAlgorithm(self, context, feedback):
140+
processed_layer = QgsProcessingUtils.mapLayerFromString(self.dest_pts, context)
141+
processed_layer.loadNamedStyle(os.path.join(pluginPath, "styles", "pts.qml"))
142+
return {self.OUTPUT_PTS: self.dest_pts, self.OUTPUT_TRAJS: self.dest_trajs}
135143

144+
def create_points(self, parameters, context, input_layer, timestamp_field, tc):
136145
output_fields = input_layer.fields()
146+
self.drop_fid_field(output_fields)
137147
output_fields.append(QgsField(tc.get_speed_col(), QVariant.Double))
148+
output_fields.append(QgsField(tc.get_direction_col(), QVariant.Double))
149+
150+
(sink, dest) = self.parameterAsSink(
151+
parameters, self.OUTPUT_PTS, context, output_fields, QgsWkbTypes.Point, input_layer.sourceCrs())
152+
153+
tc_to_sink(tc, sink, output_fields, timestamp_field)
154+
return dest
155+
156+
def drop_fid_field(self, output_fields):
138157
i = output_fields.indexFromName("fid")
139158
if i >= 0:
140159
output_fields.remove(i)
141-
142-
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
143-
output_fields,
144-
QgsWkbTypes.Point,
145-
input_layer.sourceCrs())
146160

147-
tc_to_sink(tc, sink, output_fields, timestamp_field)
148-
149-
150-
# default return type for function
151-
return {self.OUTPUT: dest_id}
161+
def create_trajectories(self, parameters, context, input_layer, traj_id_field, tc):
162+
output_fields_lines = QgsFields()
163+
output_fields_lines.append(QgsField(traj_id_field, QVariant.String))
164+
165+
(sink, dest) = self.parameterAsSink(
166+
parameters, self.OUTPUT_TRAJS, context, output_fields_lines, QgsWkbTypes.LineStringM, input_layer.sourceCrs())
167+
168+
for traj in tc.trajectories:
169+
line = QgsGeometry.fromWkt(traj.to_linestringm_wkt())
170+
f = QgsFeature()
171+
f.setGeometry(line)
172+
f.setAttributes([traj.id])
173+
sink.addFeature(f, QgsFeatureSink.FastInsert)
174+
return dest
152175

qgis_processing/icons/icon.png

-3.57 KB
Loading

0 commit comments

Comments
 (0)