Skip to content

Commit cf1beea

Browse files
committed
Add gtfs alg
1 parent 4100412 commit cf1beea

File tree

4 files changed

+172
-1
lines changed

4 files changed

+172
-1
lines changed

qgis_processing/gtfsAlgorithm.py

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import os
2+
import sys
3+
4+
from qgis.PyQt.QtCore import QCoreApplication, QVariant
5+
from qgis.PyQt.QtGui import QIcon
6+
from qgis.core import (
7+
QgsProcessing,
8+
QgsProcessingAlgorithm,
9+
QgsProcessingParameterFeatureSink,
10+
QgsProcessingParameterFile,
11+
QgsProcessingParameterField,
12+
QgsProcessingUtils,
13+
QgsCoordinateReferenceSystem,
14+
QgsWkbTypes,
15+
QgsProcessingParameterString,
16+
QgsProcessingParameterBoolean,
17+
QgsField,
18+
QgsFields,
19+
QgsFeature,
20+
QgsGeometry,
21+
QgsFeatureSink,
22+
)
23+
24+
try:
25+
from gtfs_functions import Feed
26+
except ImportError as error:
27+
raise ImportError(
28+
"Missing optional dependencies. To use the GTFS algorithms please "
29+
"install gtfs_functions. For details see: "
30+
"https://github.com/Bondify/gtfs_functions."
31+
) from error
32+
33+
sys.path.append("..")
34+
35+
from .qgisUtils import tc_from_pt_layer, feature_from_gdf_row, df_from_pt_layer
36+
37+
pluginPath = os.path.dirname(__file__)
38+
39+
40+
class GtfsAlgorithm(QgsProcessingAlgorithm):
41+
INPUT = "INPUT"
42+
SPEED_OPTION = "SPEED"
43+
OUTPUT = "OUTPUT"
44+
45+
def __init__(self):
46+
super().__init__()
47+
48+
def name(self):
49+
return "gtfs_segments"
50+
51+
def displayName(self):
52+
return self.tr("Extract segments")
53+
54+
def group(self):
55+
return self.tr("GTFS")
56+
57+
def groupId(self):
58+
return "Gtfs"
59+
60+
def tr(self, text):
61+
return QCoreApplication.translate("trajectools", text)
62+
63+
def helpUrl(self):
64+
return "https://github.com/Bondify/gtfs_functions"
65+
66+
def shortHelpString(self):
67+
return self.tr(
68+
"<p>Extracts segments from a GTFS ZIP file using "
69+
"gtfs_functions.Feed.segments</p>"
70+
"<p>Optionally adds scheduled average speeds using"
71+
"gtfs_functions.Feed.avg_speeds</p>"
72+
)
73+
74+
def createInstance(self):
75+
return type(self)()
76+
77+
def initAlgorithm(self, config=None):
78+
self.addParameter(
79+
QgsProcessingParameterFile(
80+
name=self.INPUT,
81+
description=self.tr("Input GTFS file"),
82+
)
83+
)
84+
self.addParameter(
85+
QgsProcessingParameterBoolean(
86+
name=self.SPEED_OPTION,
87+
description=self.tr("Add scheduled speeds"),
88+
)
89+
)
90+
self.addParameter(
91+
QgsProcessingParameterFeatureSink(
92+
name=self.OUTPUT,
93+
description=self.tr("GTFS segments"),
94+
type=QgsProcessing.TypeVectorLine,
95+
)
96+
)
97+
98+
def processAlgorithm(self, parameters, context, feedback):
99+
gtfs_file = self.parameterAsFile(parameters, self.INPUT, context)
100+
add_avg_speed = self.parameterAsBool(parameters, self.SPEED_OPTION, context)
101+
(self.sink_segments, self.dest_segments) = self.parameterAsSink(
102+
parameters,
103+
self.OUTPUT,
104+
context,
105+
self.get_fields(add_avg_speed),
106+
QgsWkbTypes.LineStringM,
107+
QgsCoordinateReferenceSystem("EPSG:4326"),
108+
)
109+
110+
feed = Feed(gtfs_file)
111+
segments = self.get_segments(feed, add_avg_speed)
112+
for _, segment in segments.iterrows():
113+
line = QgsGeometry.fromWkt(segment.geometry.wkt)
114+
f = QgsFeature()
115+
f.setGeometry(line)
116+
attrs = [segment.shape_id, segment.route_id, segment.route_name, segment.direction_id, segment.stop_sequence, segment.segment_name,
117+
segment.start_stop_name, segment.end_stop_name, segment.segment_id, segment.start_stop_id, segment.end_stop_id, segment.distance_m]
118+
if add_avg_speed:
119+
attrs = attrs + [segment.window, segment.speed_kmh, segment.avg_route_speed_kmh, segment.segment_max_speed_kmh, segment.runtime_sec]
120+
f.setAttributes(attrs)
121+
self.sink_segments.addFeature(f, QgsFeatureSink.FastInsert)
122+
123+
return {self.OUTPUT: self.dest_segments}
124+
125+
def get_segments(self, feed, add_avg_speed=False):
126+
if add_avg_speed:
127+
segments = feed.avg_speeds
128+
else:
129+
segments = feed.segments
130+
return segments
131+
132+
def get_fields(self, add_avg_speed):
133+
fields = QgsFields()
134+
fields.append(QgsField("shape_id", QVariant.String))
135+
fields.append(QgsField("route_id", QVariant.String))
136+
fields.append(QgsField("route_name", QVariant.String))
137+
fields.append(QgsField("direction_id", QVariant.String))
138+
fields.append(QgsField("stop_sequence", QVariant.Int))
139+
fields.append(QgsField("segment_name", QVariant.String))
140+
fields.append(QgsField("start_stop_name", QVariant.String))
141+
fields.append(QgsField("end_stop_name", QVariant.String))
142+
fields.append(QgsField("segment_id", QVariant.String))
143+
fields.append(QgsField("start_stop_id", QVariant.String))
144+
fields.append(QgsField("end_stop_id", QVariant.String))
145+
fields.append(QgsField("distance_m", QVariant.Double))
146+
if add_avg_speed:
147+
fields.append(QgsField("window", QVariant.String))
148+
fields.append(QgsField("speed_kmh", QVariant.Double))
149+
fields.append(QgsField("avg_route_speed_kmh", QVariant.Double))
150+
fields.append(QgsField("segment_max_speed_kmh", QVariant.Double))
151+
fields.append(QgsField("runtime_sec", QVariant.Double))
152+
return fields
153+
154+

qgis_processing/qgisUtils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def tc_from_df(df, time_field_name, trajectory_id_field, crs):
5555
y="geom_y",
5656
t=time_field_name,
5757
crs=crs,
58+
min_length=min_length
5859
)
5960
return tc
6061

qgis_processing/trajectoolsProvider.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
)
2121
from .extractPtsAlgorithm import ExtractODPtsAlgorithm, ExtractStopsAlgorithm
2222
from .privacyAttackAlgorithm import HomeWorkAttack
23+
from .gtfsAlgorithm import GtfsAlgorithm
2324

2425
pluginPath = os.path.dirname(__file__)
2526

@@ -62,6 +63,7 @@ def getAlgs(self):
6263
ExtractODPtsAlgorithm(),
6364
ExtractStopsAlgorithm(),
6465
HomeWorkAttack(),
66+
GtfsAlgorithm(),
6567
]
6668
return algs
6769

qgis_processing/trajectoriesAlgorithm.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
QgsProcessingUtils,
1313
QgsWkbTypes,
1414
QgsProcessingParameterString,
15+
QgsProcessingParameterNumber,
1516
QgsField,
1617
QgsFields,
1718
QgsFeature,
@@ -39,6 +40,7 @@ class TrajectoriesAlgorithm(QgsProcessingAlgorithm):
3940
TRAJ_ID_FIELD = "TRAJ_ID_FIELD"
4041
TIMESTAMP_FIELD = "TIME_FIELD"
4142
SPEED_UNIT = "SPEED_UNIT"
43+
MIN_LENGTH = "MIN_LENGTH"
4244

4345
def __init__(self):
4446
super().__init__()
@@ -93,6 +95,15 @@ def initAlgorithm(self, config=None):
9395
optional=False,
9496
)
9597
)
98+
self.addParameter(
99+
QgsProcessingParameterNumber(
100+
name=self.MIN_LENGTH,
101+
description=self.tr("Minimum trajectory length"),
102+
defaultValue=0,
103+
#optional=True,
104+
minValue=0
105+
)
106+
)
96107

97108
def create_df(self, parameters, context):
98109
self.prepare_parameters(parameters, context)
@@ -114,13 +125,16 @@ def prepare_parameters(self, parameters, context):
114125
self.speed_units = self.parameterAsString(
115126
parameters, self.SPEED_UNIT, context
116127
).split("/")
128+
self.min_length = self.parameterAsDouble(
129+
parameters, self.MIN_LENGTH, context
130+
)
117131

118132
def create_tc(self, parameters, context):
119133
self.prepare_parameters(parameters, context)
120134
crs = self.input_layer.sourceCrs()
121135

122136
tc = tc_from_pt_layer(
123-
self.input_layer, self.timestamp_field, self.traj_id_field
137+
self.input_layer, self.timestamp_field, self.traj_id_field, self.min_length
124138
)
125139

126140
if len(tc.trajectories) < 1:

0 commit comments

Comments
 (0)