Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/detector/detector.py
169674 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2007-2025 German Aerospace Center (DLR) and others.
4
# This program and the accompanying materials are made available under the
5
# terms of the Eclipse Public License 2.0 which is available at
6
# https://www.eclipse.org/legal/epl-2.0/
7
# This Source Code may also be made available under the following Secondary
8
# Licenses when the conditions for such availability set forth in the Eclipse
9
# Public License 2.0 are satisfied: GNU General Public License, version 2
10
# or later which is available at
11
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
14
# @file detector.py
15
# @author Daniel Krajzewicz
16
# @author Michael Behrisch
17
# @date 2007-06-28
18
19
from __future__ import absolute_import
20
from __future__ import print_function
21
import os
22
import sys
23
from collections import defaultdict
24
from xml.sax import make_parser, handler
25
26
MAX_POS_DEVIATION = 10
27
28
29
class LaneMap:
30
def get(self, key, default):
31
return key[0:-2]
32
33
34
def relError(actual, expected):
35
if expected == 0:
36
if actual == 0:
37
return 0
38
else:
39
return 1
40
else:
41
return (actual - expected) / expected
42
43
44
def parseFlowFile(flowFile, detCol="Detector", timeCol="Time", flowCol="qPKW", speedCol="vPKW", begin=None, end=None):
45
detIdx = -1
46
flowIdx = -1
47
speedIdx = -1
48
timeIdx = -1
49
with open(flowFile) as f:
50
for fl in f:
51
if ';' not in fl:
52
continue
53
flowDef = [e.strip() for e in fl.split(';')]
54
if detIdx == -1 and detCol in flowDef:
55
# init columns
56
detIdx = flowDef.index(detCol)
57
if flowCol in flowDef:
58
flowIdx = flowDef.index(flowCol)
59
if speedCol in flowDef:
60
speedIdx = flowDef.index(speedCol)
61
if timeCol in flowDef:
62
timeIdx = flowDef.index(timeCol)
63
elif flowIdx != -1:
64
# columns are initialized
65
if timeIdx == -1 or begin is None:
66
curTime = None
67
timeIsValid = True
68
else:
69
curTime = float(flowDef[timeIdx])
70
timeIsValid = (end is None and curTime == begin) or (
71
curTime >= begin and curTime < end)
72
if timeIsValid:
73
speed = float(flowDef[speedIdx]) if speedIdx != -1 else None
74
yield (flowDef[detIdx], curTime, float(flowDef[flowIdx]), speed)
75
76
77
class DetectorGroupData:
78
79
def __init__(self, pos, isValid, id=None, detType=None):
80
self.ids = []
81
self.pos = pos
82
self.isValid = isValid
83
self.totalFlow = 0
84
self.avgSpeed = 0
85
self.entryCount = 0
86
self.type = detType
87
if id is not None:
88
self.ids.append(id)
89
self.begin = 0
90
self.lastTime = None
91
self.interval = None
92
self.timeline = []
93
94
def addDetFlow(self, flow, speed):
95
oldFlow = self.totalFlow
96
self.totalFlow += flow
97
if flow > 0 and speed is not None:
98
self.avgSpeed = (
99
self.avgSpeed * oldFlow + speed * flow) / self.totalFlow
100
self.entryCount += 1
101
102
def addDetFlowTime(self, time, flow, speed):
103
if self.interval is None:
104
raise RuntimeError("DetectorGroupData interval not initialized")
105
time -= self.begin
106
index = int(time / self.interval)
107
if index > len(self.timeline):
108
if len(self.timeline) == 0:
109
# init
110
self.timeline = [[None, None] for i in range(index)]
111
self.timeline.append([0, 0])
112
else:
113
sys.stderr.write(("Gap in data for group=%s. Or data interval is higher than aggregation interval " +
114
"(i=%s, time=%s, begin=%s, lastTime=%s)\n") % (
115
self.ids, self.interval, time, self.begin, len(self.timeline) * self.interval))
116
while len(self.timeline) < index:
117
self.timeline.append([None, None])
118
self.timeline.append([0, 0])
119
if index == len(self.timeline):
120
# new entry
121
if time % self.interval != 0 and time > self.interval:
122
sys.stderr.write(("Aggregation interval is not a multiple of data interval for group=%s (i=%s " +
123
"time=%s begin=%s)\n") % (
124
self.ids, self.interval, time, self.begin))
125
self.timeline.append([0, 0])
126
oldFlow, oldSpeed = self.timeline[index]
127
newFlow = oldFlow + flow
128
if flow > 0 and speed is not None:
129
newSpeed = (oldSpeed * oldFlow + speed * flow) / newFlow
130
else:
131
newSpeed = oldSpeed
132
self.timeline[index] = [newFlow, newSpeed]
133
self.entryCount += 1
134
135
def clearFlow(self, begin, interval):
136
self.totalFlow = 0
137
self.avgSpeed = 0
138
self.entryCount = 0
139
self.begin = begin
140
self.lastTime = None
141
self.interval = interval
142
self.timeline = []
143
144
def getName(self, longName, firstName, sep='|'):
145
if firstName:
146
return self.ids[0]
147
name = os.path.commonprefix(self.ids)
148
if name == "" or longName:
149
name = sep.join(sorted(self.ids))
150
return name
151
152
153
class DetectorReader(handler.ContentHandler):
154
155
def __init__(self, detFile=None, laneMap=None):
156
self._edge2DetData = defaultdict(list)
157
self._det2edge = {}
158
self._currentGroup = None
159
self._currentEdge = None
160
self._laneMap = {} if laneMap is None else laneMap
161
if detFile:
162
parser = make_parser()
163
parser.setContentHandler(self)
164
parser.parse(detFile)
165
166
def addDetector(self, id, pos, edge, detType):
167
if id in self._det2edge:
168
print("Warning! Detector %s already known." % id, file=sys.stderr)
169
return
170
if edge is None:
171
raise RuntimeError("Detector '%s' has no edge" % id)
172
if self._currentGroup:
173
self._currentGroup.ids.append(id)
174
else:
175
haveGroup = False
176
for data in self._edge2DetData[edge]:
177
if abs(data.pos - pos) <= MAX_POS_DEVIATION:
178
data.ids.append(id)
179
haveGroup = True
180
break
181
if not haveGroup:
182
self._edge2DetData[edge].append(
183
DetectorGroupData(pos, True, id, detType))
184
self._det2edge[id] = edge
185
186
def getEdgeDetGroups(self, edge):
187
return self._edge2DetData[edge]
188
189
def startElement(self, name, attrs):
190
if name == 'detectorDefinition' or name == 'e1Detector' or name == 'inductionLoop':
191
detType = attrs['type'] if 'type' in attrs else None
192
self.addDetector(attrs['id'], float(attrs['pos']),
193
self._laneMap.get(attrs['lane'], self._currentEdge), detType)
194
elif name == 'group':
195
self._currentGroup = DetectorGroupData(float(attrs['pos']),
196
attrs.get('valid', "1") == "1")
197
edge = attrs['orig_edge']
198
self._currentEdge = edge
199
self._edge2DetData[edge].append(self._currentGroup)
200
201
def endElement(self, name):
202
if name == 'group':
203
self._currentGroup = None
204
205
def getGroup(self, det):
206
if det in self._det2edge:
207
edge = self._det2edge[det]
208
for group in self._edge2DetData[edge]:
209
if det in group.ids:
210
return group
211
return None
212
213
def addFlow(self, det, flow, speed=0.0):
214
group = self.getGroup(det)
215
if group is not None:
216
group.addDetFlow(flow, speed)
217
218
def clearFlows(self, begin=0, interval=None):
219
for groupList in self._edge2DetData.values():
220
for group in groupList:
221
group.clearFlow(begin, interval)
222
223
def readFlows(self, flowFile, det="Detector", flow="qPKW",
224
speed=None, time=None, timeVal=None, timeMax=None, addDetectors=False):
225
values = parseFlowFile(
226
flowFile,
227
detCol=det, timeCol=time, flowCol=flow,
228
speedCol=speed, begin=timeVal, end=timeMax)
229
hadFlow = False
230
for det, time, flow, speed in values:
231
if addDetectors and det not in self._det2edge:
232
self.addDetector(det, 0., det, None)
233
hadFlow = True
234
self.addFlow(det, flow, speed)
235
return hadFlow
236
237
def readFlowsTimeline(self, flowFile, interval, **args):
238
values = parseFlowFile(flowFile, **args)
239
hadFlow = False
240
for det, time, flow, speed in values:
241
hadFlow = True
242
group = self.getGroup(det)
243
if group is not None:
244
group.addDetFlowTime(time, flow, speed)
245
return hadFlow
246
247
def findTimes(self, flowFile, tMin, tMax, det="Detector", time="Time"):
248
timeIdx = 1
249
with open(flowFile) as f:
250
for fl in f:
251
if ';' not in fl:
252
continue
253
flowDef = [e.strip() for e in fl.split(';')]
254
if det in flowDef:
255
if time in flowDef:
256
timeIdx = flowDef.index(time)
257
elif len(flowDef) > timeIdx:
258
curTime = float(flowDef[timeIdx])
259
if tMin is None or tMin > curTime:
260
tMin = curTime
261
if tMax is None or tMax < curTime:
262
tMax = curTime
263
return tMin, tMax
264
265
def getGroups(self):
266
for edge, detData in self._edge2DetData.items():
267
for group in detData:
268
if group.isValid:
269
yield edge, group
270
271