Skip to content

Commit 0a36b01

Browse files
committed
Add ClipTrajectoriesByPolygonLayer
1 parent 8b7e233 commit 0a36b01

File tree

6 files changed

+91
-60
lines changed

6 files changed

+91
-60
lines changed

qgis_processing/createTrajectoriesAlgorithm.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
import sys
22

3-
from qgis.PyQt.QtCore import QCoreApplication, QVariant
4-
from qgis.core import (
5-
QgsField,
6-
QgsGeometry,
7-
QgsFeature,
8-
QgsFeatureSink,
9-
QgsProcessingParameterString,
10-
QgsWkbTypes,
11-
)
3+
from qgis.PyQt.QtCore import QCoreApplication
124

135
sys.path.append("..")
146

@@ -17,7 +9,6 @@
179

1810

1911
class CreateTrajectoriesAlgorithm(TrajectoriesAlgorithm):
20-
2112
def __init__(self):
2213
super().__init__()
2314

qgis_processing/overlayAlgorithm.py

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
1-
import os
21
import sys
32

43
import shapely.wkt
54
from shapely.geometry import Polygon
65

7-
from qgis.PyQt.QtCore import QCoreApplication, QVariant
8-
from qgis.PyQt.QtGui import QIcon
6+
from qgis.PyQt.QtCore import QCoreApplication
97
from qgis.core import (
10-
QgsField,
11-
QgsFields,
12-
QgsGeometry,
13-
QgsFeature,
14-
QgsFeatureSink,
15-
QgsFeatureRequest,
16-
QgsProcessing,
17-
QgsProcessingAlgorithm,
18-
QgsProcessingParameterFeatureSource,
19-
QgsProcessingParameterString,
208
QgsProcessingParameterExtent,
9+
QgsProcessingParameterVectorLayer,
2110
)
2211

2312
sys.path.append("..")
@@ -26,7 +15,18 @@
2615
from .trajectoriesAlgorithm import TrajectoriesAlgorithm
2716

2817

29-
class ClipTrajectoriesByExtentAlgorithm(TrajectoriesAlgorithm):
18+
class OverlayTrajectoriesAlgorithm(TrajectoriesAlgorithm):
19+
def __init__(self):
20+
super().__init__()
21+
22+
def group(self):
23+
return self.tr("Trajectory overlay")
24+
25+
def groupId(self):
26+
return "TrajectoryOverlay"
27+
28+
29+
class ClipTrajectoriesByExtentAlgorithm(OverlayTrajectoriesAlgorithm):
3030
EXTENT = "EXTENT"
3131

3232
def __init__(self):
@@ -49,12 +49,6 @@ def tr(self, text):
4949
def displayName(self):
5050
return self.tr("Clip trajectories by extent")
5151

52-
def group(self):
53-
return self.tr("Trajectory overlay")
54-
55-
def groupId(self):
56-
return "TrajectoryOverlay"
57-
5852
def shortHelpString(self):
5953
return self.tr(
6054
"<p>Creates a trajectory point layers with speed and direction information "
@@ -69,15 +63,59 @@ def shortHelpString(self):
6963
def helpUrl(self):
7064
return "https://movingpandas.org/units"
7165

72-
def createInstance(self):
73-
return type(self)()
74-
7566
def processTc(self, tc, parameters, context):
7667
extent = self.parameterAsExtent(parameters, self.EXTENT, context)
7768
extent = shapely.wkt.loads(extent.asWktPolygon())
78-
tc = tc.clip(extent)
79-
tc_to_sink(tc, self.sink_pts, self.fields_pts, self.timestamp_field)
80-
for split in tc:
81-
traj_to_sink(split, self.sink_trajs)
69+
clipped = tc.clip(extent)
70+
tc_to_sink(clipped, self.sink_pts, self.fields_pts, self.timestamp_field)
71+
for traj in clipped:
72+
traj_to_sink(traj, self.sink_trajs)
73+
74+
75+
class ClipTrajectoriesByPolygonLayer(OverlayTrajectoriesAlgorithm):
76+
CLIP_LAYER = "CLIP_LAYER"
77+
78+
def __init__(self):
79+
super().__init__()
80+
81+
def initAlgorithm(self, config=None):
82+
super().initAlgorithm(config)
83+
self.addParameter(
84+
QgsProcessingParameterVectorLayer(
85+
name=self.CLIP_LAYER,
86+
description=self.tr("Overlay layer"),
87+
optional=False,
88+
)
89+
)
90+
91+
def name(self):
92+
return "clip_traj_vector"
93+
94+
def tr(self, text):
95+
return QCoreApplication.translate("clip_traj_vector", text)
96+
97+
def displayName(self):
98+
return self.tr("Clip trajectories by polygon layer")
99+
100+
def shortHelpString(self):
101+
return self.tr(
102+
"<p>Creates a trajectory point layers with speed and direction information "
103+
"as well as a trajectory line layer clipped by the specified vector layer.</p>"
104+
"<p><b>Speed</b> is calculated based on the input layer CRS information and "
105+
"converted to the desired speed units. For more info on the supported units, "
106+
"see https://movingpandas.org/units</p>"
107+
"<p><b>Direction</b> is calculated between consecutive locations. Direction "
108+
"values are in degrees, starting North turning clockwise.</p>"
109+
)
110+
111+
def helpUrl(self):
112+
return "https://movingpandas.org/units"
82113

83-
114+
def processTc(self, tc, parameters, context):
115+
vlayer = self.parameterAsVectorLayer(parameters, self.CLIP_LAYER, context)
116+
for feature in vlayer.getFeatures():
117+
extent = shapely.wkt.loads(feature.geometry().asWkt())
118+
clipped = tc.clip(extent)
119+
tc_to_sink(clipped, self.sink_pts, self.fields_pts, self.timestamp_field)
120+
for traj in clipped:
121+
traj_to_sink(traj, self.sink_trajs)

qgis_processing/qgisUtils.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import sys
22
import pandas as pd
3-
from geopandas import GeoDataFrame
4-
from PyQt5 import QtCore
5-
from shapely.geometry import Point
6-
from datetime import datetime
73
from pyproj import CRS
84

95
sys.path.append("..")
@@ -14,10 +10,7 @@
1410
QgsGeometry,
1511
QgsPointXY,
1612
QgsFeatureSink,
17-
QgsFields,
18-
QgsField,
1913
)
20-
from qgis.PyQt.QtCore import QVariant
2114

2215

2316
def trajectories_from_qgis_point_layer(
@@ -59,7 +52,10 @@ def feature_from_gdf_row(row):
5952

6053

6154
def tc_to_sink(tc, sink, fields, timestamp_field):
62-
gdf = tc.to_point_gdf()
55+
try:
56+
gdf = tc.to_point_gdf()
57+
except ValueError: # when the tc is empty
58+
return
6359
gdf[timestamp_field] = gdf.index.astype(str)
6460
names = [field.name() for field in fields]
6561
names.append("geometry")

qgis_processing/splitTrajectoriesAlgorithm.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import sys
22
import pandas as pd
33

4-
from datetime import timedelta
54
from movingpandas import TemporalSplitter, ObservationGapSplitter, StopSplitter
65

7-
from qgis.core import QgsProcessingParameterString, QgsProcessingParameterEnum, QgsProcessingParameterNumber
6+
from qgis.core import (
7+
QgsProcessingParameterString,
8+
QgsProcessingParameterEnum,
9+
QgsProcessingParameterNumber,
10+
)
811

912
sys.path.append("..")
1013

@@ -124,7 +127,6 @@ def processTc(self, tc, parameters, context):
124127
traj_to_sink(split, self.sink_trajs)
125128

126129

127-
128130
class StopSplitterAlgorithm(SplitTrajectoriesAlgorithm):
129131
MAX_DIAMETER = "MAX_DIAMETER"
130132
MIN_DURATION = "MIN_DURATION"
@@ -145,7 +147,9 @@ def initAlgorithm(self, config=None):
145147
self.addParameter(
146148
QgsProcessingParameterString(
147149
name=self.MIN_DURATION,
148-
description=self.tr("Min stop duration (timedelta, e.g. 1 hours, 15 minutes)"),
150+
description=self.tr(
151+
"Min stop duration (timedelta, e.g. 1 hours, 15 minutes)"
152+
),
149153
defaultValue="15 minutes",
150154
optional=False,
151155
)
@@ -174,11 +178,9 @@ def processTc(self, tc, parameters, context):
174178
min_duration = self.parameterAsString(parameters, self.MIN_DURATION, context)
175179
min_duration = pd.Timedelta(min_duration).to_pytimedelta()
176180
for traj in tc.trajectories:
177-
splits = StopSplitter(traj).split(max_diameter=max_diameter, min_duration=min_duration)
181+
splits = StopSplitter(traj).split(
182+
max_diameter=max_diameter, min_duration=min_duration
183+
)
178184
tc_to_sink(splits, self.sink_pts, self.fields_pts, self.timestamp_field)
179185
for split in splits:
180186
traj_to_sink(split, self.sink_trajs)
181-
182-
183-
184-

qgis_processing/trajectoolsProvider.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@
1212
from .splitTrajectoriesAlgorithm import (
1313
ObservationGapSplitterAlgorithm,
1414
TemporalSplitterAlgorithm,
15-
StopSplitterAlgorithm
15+
StopSplitterAlgorithm,
16+
)
17+
from .overlayAlgorithm import (
18+
ClipTrajectoriesByExtentAlgorithm,
19+
ClipTrajectoriesByPolygonLayer,
1620
)
17-
from .overlayAlgorithm import ClipTrajectoriesByExtentAlgorithm
1821

1922
pluginPath = os.path.dirname(__file__)
2023

@@ -52,7 +55,8 @@ def getAlgs(self):
5255
ObservationGapSplitterAlgorithm(),
5356
TemporalSplitterAlgorithm(),
5457
StopSplitterAlgorithm(),
55-
ClipTrajectoriesByExtentAlgorithm()
58+
ClipTrajectoriesByExtentAlgorithm(),
59+
ClipTrajectoriesByPolygonLayer(),
5660
]
5761
return algs
5862

qgis_processing/trajectoriesAlgorithm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
QgsWkbTypes,
1414
QgsProcessingParameterString,
1515
QgsField,
16-
QgsFields
16+
QgsFields,
1717
)
1818

1919
sys.path.append("..")
@@ -167,7 +167,7 @@ def get_pt_fields(self, fields_to_add=[]):
167167
for field in self.input_layer.fields():
168168
if field.name() == "fid":
169169
continue
170-
elif (field.name() == self.traj_id_field):
170+
elif field.name() == self.traj_id_field:
171171
# we need to make sure the ID field is String
172172
fields.append(QgsField(self.traj_id_field, QVariant.String))
173173
else:

0 commit comments

Comments
 (0)