Skip to content

Commit 8df9882

Browse files
committed
Add CI linting & tests
1 parent c118c35 commit 8df9882

17 files changed

+256
-139
lines changed

.github/workflows/tests.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main, 0.**]
6+
pull_request:
7+
schedule:
8+
- cron: "0 0 * * *"
9+
10+
concurrency:
11+
group: ${{ github.workflow }}-${{ github.ref }}
12+
cancel-in-progress: true
13+
14+
jobs:
15+
Linting:
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- uses: actions/checkout@v2
20+
- uses: actions/setup-python@v2
21+
- uses: pre-commit/[email protected]
22+
23+
Test:
24+
#needs: Linting
25+
name: ${{ matrix.os }}, ${{ matrix.env }}
26+
runs-on: ${{ matrix.os }}
27+
defaults:
28+
run:
29+
shell: bash -l {0}
30+
strategy:
31+
fail-fast: false
32+
matrix:
33+
os: [ubuntu-latest]
34+
env:
35+
- environment.yml
36+
include:
37+
- env: environment.yml
38+
os: ubuntu-latest
39+
40+
steps:
41+
- uses: actions/checkout@v4
42+
43+
- name: Install Conda environment with Micromamba
44+
uses: mamba-org/setup-micromamba@v2
45+
with:
46+
environment-file: ${{ matrix.env }}
47+
48+
- name: Check and Log Environment
49+
run: |
50+
python -V
51+
micromamba info
52+
micromamba list
53+
54+
- name: Test
55+
run: |
56+
pytest -v -r s --color=yes .
57+
58+
- uses: codecov/codecov-action@v5

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,4 @@ venv.bak/
116116
.vscode/
117117
.vscode/settings.json
118118
tests/sandbox.ipynb
119+
symbology-style.db

__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,24 @@
1717
***************************************************************************
1818
"""
1919

20-
__author__ = 'Anita Graser'
21-
__date__ = 'Sep 2025'
22-
__copyright__ = '(c) 2025, Anita Graser'
20+
__author__ = "Anita Graser"
21+
__date__ = "Sep 2025"
22+
__copyright__ = "(c) 2025, Anita Graser"
2323

2424
# This will get replaced with a git SHA1 when you do a git archive
2525

26-
__revision__ = '$Format:%H$'
27-
26+
__revision__ = "$Format:%H$"
2827

2928

3029
from packaging.version import Version
3130
from movingpandas import __version__ as mpd_version
3231

33-
MIN_MPD_VERSION = '0.22.3'
32+
MIN_MPD_VERSION = "0.22.3"
3433
if Version(mpd_version) < Version(MIN_MPD_VERSION):
35-
raise(RuntimeError(f'Please update MovingPandas to >={MIN_MPD_VERSION}'))
34+
raise (RuntimeError(f"Please update MovingPandas to >={MIN_MPD_VERSION}"))
3635

3736
from .qgis_processing.trajectoolsProviderPlugin import TrajectoryProviderPlugin
3837

38+
3939
def classFactory(iface):
4040
return TrajectoryProviderPlugin()

environment.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
name: trajectools
2+
channels:
3+
- conda-forge
4+
dependencies:
5+
- python=3.12
6+
- black
7+
- movingpandas
8+
- pre-commit
9+
- pytest
10+
- qgis

qgis_processing/cleaningAlgorithm.py

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

4-
from movingpandas import (
5-
OutlierCleaner
6-
)
4+
from movingpandas import OutlierCleaner
75

86
from qgis.core import (
97
QgsProcessingParameterString,
@@ -13,7 +11,11 @@
1311

1412
sys.path.append("..")
1513

16-
from .trajectoriesAlgorithm import TrajectoryManipulationAlgorithm, help_str_base, help_str_traj
14+
from .trajectoriesAlgorithm import (
15+
TrajectoryManipulationAlgorithm,
16+
help_str_base,
17+
help_str_traj,
18+
)
1719

1820

1921
class CleaningAlgorithm(TrajectoryManipulationAlgorithm):
@@ -57,12 +59,14 @@ def shortHelpString(self):
5759
"the speed exceeds the provided <b>Speed threshold</b> value </p>"
5860
"<p>For more info see: "
5961
"https://movingpandas.readthedocs.io/en/main/api/trajectorycleaner.html</p>"
60-
""+help_str_base+help_str_traj
62+
"" + help_str_base + help_str_traj
6163
)
6264

6365
def processTc(self, tc, parameters, context):
6466
v_max = self.parameterAsDouble(parameters, self.TOLERANCE, context)
65-
generalized = OutlierCleaner(tc).clean(v_max=v_max, units=tuple(self.speed_units))
67+
generalized = OutlierCleaner(tc).clean(
68+
v_max=v_max, units=tuple(self.speed_units)
69+
)
6670
self.tc_to_sink(generalized)
6771
for traj in generalized:
6872
self.traj_to_sink(traj)

qgis_processing/createTrajectoriesAlgorithm.py

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

33
sys.path.append("..")
44

5-
from .trajectoriesAlgorithm import TrajectoryManipulationAlgorithm, help_str_base, help_str_traj
5+
from .trajectoriesAlgorithm import (
6+
TrajectoryManipulationAlgorithm,
7+
help_str_base,
8+
help_str_traj,
9+
)
610

711

812
class CreateTrajectoriesAlgorithm(TrajectoryManipulationAlgorithm):
@@ -24,7 +28,7 @@ def groupId(self):
2428
def shortHelpString(self):
2529
return self.tr(
2630
"<p>Creates a trajectory point layers with speed and direction information "
27-
"as well as a trajectory line layer.</p>"+help_str_base+help_str_traj
31+
"as well as a trajectory line layer.</p>" + help_str_base + help_str_traj
2832
)
2933

3034
def helpUrl(self):

qgis_processing/extractPtsAlgorithm.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ def displayName(self):
6262
return self.tr("Extract OD points")
6363

6464
def shortHelpString(self):
65-
return self.tr("<p>Extracts start and/or end points of trajectories.</p>"+help_str_base)
65+
return self.tr(
66+
"<p>Extracts start and/or end points of trajectories.</p>" + help_str_base
67+
)
6668

6769
def processAlgorithm(self, parameters, context, feedback):
6870
tc, crs = self.create_tc(parameters, context)
@@ -107,7 +109,7 @@ def processTc(self, tc, parameters, context):
107109
names.append("geometry")
108110
gdf = gdf[names]
109111
# QgsMessageLog.logMessage(str(gdf), "Trajectools", level=Qgis.Info )
110-
112+
111113
for _, row in gdf.iterrows():
112114
f = feature_from_gdf_row(row)
113115
self.sink_orig.addFeature(f, QgsFeatureSink.FastInsert)
@@ -172,7 +174,7 @@ def displayName(self):
172174
return self.tr("Extract stop points")
173175

174176
def shortHelpString(self):
175-
return self.tr("<p>Extracts stop points from trajectories.</p>"+help_str_base)
177+
return self.tr("<p>Extracts stop points from trajectories.</p>" + help_str_base)
176178

177179
def processAlgorithm(self, parameters, context, feedback):
178180
tc, crs = self.create_tc(parameters, context)
@@ -202,13 +204,15 @@ def processTc(self, tc, parameters, context):
202204
min_duration = self.parameterAsString(parameters, self.MIN_DURATION, context)
203205
min_duration = pd.Timedelta(min_duration).to_pytimedelta()
204206

205-
try:
206-
gdf = TrajectoryStopDetector(tc, n_processes=self.cpu_count).get_stop_points(
207-
max_diameter=max_diameter, min_duration=min_duration
208-
)
207+
try:
208+
gdf = TrajectoryStopDetector(
209+
tc, n_processes=self.cpu_count
210+
).get_stop_points(max_diameter=max_diameter, min_duration=min_duration)
209211
except TypeError:
210-
raise TypeError("TypeError: cannot pickle 'QVariant' object. This error is usually caused by None values in input layer fields. Try to remove None values or run without Add movement metrics.")
211-
212+
raise TypeError(
213+
"TypeError: cannot pickle 'QVariant' object. This error is usually caused by None values in input layer fields. Try to remove None values or run without Add movement metrics."
214+
)
215+
212216
gdf = gdf.convert_dtypes()
213217
gdf["stop_id"] = gdf.index.astype(str)
214218

qgis_processing/generalizationAlgorithm.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

1717
sys.path.append("..")
1818

19-
from .trajectoriesAlgorithm import TrajectoryManipulationAlgorithm, help_str_base, help_str_traj
19+
from .trajectoriesAlgorithm import (
20+
TrajectoryManipulationAlgorithm,
21+
help_str_base,
22+
help_str_traj,
23+
)
2024

2125

2226
class GeneralizeTrajectoriesAlgorithm(TrajectoryManipulationAlgorithm):
@@ -59,7 +63,7 @@ def shortHelpString(self):
5963
"(as implemented in shapely/Geos). </p>"
6064
"<p>For more info see: "
6165
"https://movingpandas.readthedocs.io/en/main/api/trajectorygeneralizer.html</p>"
62-
""+help_str_base+help_str_traj
66+
"" + help_str_base + help_str_traj
6367
)
6468

6569
def processTc(self, tc, parameters, context):
@@ -99,7 +103,7 @@ def shortHelpString(self):
99103
"(e.g. EPSG:4326 WGS84) then distance is calculated in metres. </p>"
100104
"<p>For more info see: "
101105
"https://movingpandas.readthedocs.io/en/main/api/trajectorygeneralizer.html</p>"
102-
""+help_str_base+help_str_traj
106+
"" + help_str_base + help_str_traj
103107
)
104108

105109
def processTc(self, tc, parameters, context):
@@ -139,7 +143,7 @@ def shortHelpString(self):
139143
"timedelta apart. </p>"
140144
"<p>For more info see: "
141145
"https://movingpandas.readthedocs.io/en/main/api/trajectorygeneralizer.html</p>"
142-
""+help_str_base+help_str_traj
146+
"" + help_str_base + help_str_traj
143147
)
144148

145149
def processTc(self, tc, parameters, context):
@@ -184,7 +188,7 @@ def shortHelpString(self):
184188
"the segment start and end times and the point time. </p>"
185189
"<p>For more info see: "
186190
"https://movingpandas.readthedocs.io/en/main/api/trajectorygeneralizer.html</p>"
187-
""+help_str_base+help_str_traj
191+
"" + help_str_base + help_str_traj
188192
)
189193

190194
def processTc(self, tc, parameters, context):

qgis_processing/gtfsAlgorithm.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
class GtfsStopsAlgorithm(QgsProcessingAlgorithm):
4141
INPUT = "INPUT"
42-
OUTPUT = "OUTPUT"
42+
OUTPUT = "OUTPUT"
4343

4444
def __init__(self):
4545
super().__init__()
@@ -104,12 +104,12 @@ def processAlgorithm(self, parameters, context, feedback):
104104
f = QgsFeature()
105105
f.setGeometry(pt)
106106
attrs = [stop.stop_id]
107-
if hasattr(stop, 'stop_code'):
107+
if hasattr(stop, "stop_code"):
108108
attrs.append(stop.stop_code)
109109
else:
110-
attrs.append('')
110+
attrs.append("")
111111
attrs.append(stop.stop_name)
112-
112+
113113
f.setAttributes(attrs)
114114
self.sink_stops.addFeature(f, QgsFeatureSink.FastInsert)
115115

@@ -195,9 +195,7 @@ def processAlgorithm(self, parameters, context, feedback):
195195
line = QgsGeometry.fromWkt(shape.geometry.wkt)
196196
f = QgsFeature()
197197
f.setGeometry(line)
198-
attrs = [
199-
shape.shape_id
200-
]
198+
attrs = [shape.shape_id]
201199
f.setAttributes(attrs)
202200
self.sink_shapes.addFeature(f, QgsFeatureSink.FastInsert)
203201

@@ -343,5 +341,7 @@ def get_fields(self, add_avg_speed):
343341

344342
def postProcessAlgorithm(self, context, feedback):
345343
seg_layer = QgsProcessingUtils.mapLayerFromString(self.dest_segments, context)
346-
seg_layer.loadNamedStyle(os.path.join(pluginPath, "styles", "gtfs-segments.qml"))
344+
seg_layer.loadNamedStyle(
345+
os.path.join(pluginPath, "styles", "gtfs-segments.qml")
346+
)
347347
return {self.OUTPUT: self.dest_segments}

0 commit comments

Comments
 (0)