sumolib.net.edge
1# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo 2# Copyright (C) 2011-2026 German Aerospace Center (DLR) and others. 3# This program and the accompanying materials are made available under the 4# terms of the Eclipse Public License 2.0 which is available at 5# https://www.eclipse.org/legal/epl-2.0/ 6# This Source Code may also be made available under the following Secondary 7# Licenses when the conditions for such availability set forth in the Eclipse 8# Public License 2.0 are satisfied: GNU General Public License, version 2 9# or later which is available at 10# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html 11# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later 12 13# @file edge.py 14# @author Daniel Krajzewicz 15# @author Laura Bieker 16# @author Karol Stosiek 17# @author Michael Behrisch 18# @author Jakob Erdmann 19# @date 2011-11-28 20 21import sumolib.geomhelper 22from .connection import Connection 23from .lane import addJunctionPos 24 25 26class Edge: 27 28 """ Edges from a sumo network """ 29 30 def __init__(self, id, fromN, toN, prio, function, name, edgeType='', routingType=''): 31 self._id = id 32 self._from = fromN 33 self._to = toN 34 self._priority = prio 35 if fromN: 36 fromN.addOutgoing(self) 37 if toN: 38 toN.addIncoming(self) 39 self._lanes = [] 40 self._speed = None 41 self._length = None 42 self._incoming = {} 43 self._outgoing = {} 44 self._crossingEdges = [] 45 self._shape = None 46 self._shapeWithJunctions = None 47 self._shape3D = None 48 self._shapeWithJunctions3D = None 49 self._rawShape = None 50 self._rawShape3D = None 51 self._function = function 52 self._tls = None 53 self._name = name 54 self._type = edgeType 55 self._routingType = routingType 56 self._params = {} 57 self._bidi = None 58 self._selected = False 59 self._lengthGeometryFactor = 1 60 61 def __lt__(self, other): 62 return self.getID() < other.getID() 63 64 def getName(self): 65 return self._name 66 67 def isSpecial(self): 68 """ Check if the edge has a special function. 69 70 Returns False if edge's function is 'normal', else False, e.g. for 71 internal edges or connector edges """ 72 73 return self._function != "" 74 75 def getFunction(self): 76 return self._function 77 78 def getPriority(self): 79 return self._priority 80 81 def getType(self): 82 return self._type 83 84 def getRoutingType(self): 85 """ Return the effective routingType that would be used by duarouter or sumo""" 86 return self._routingType if self._routingType != "" else self._type 87 88 def getTLS(self): 89 return self._tls 90 91 def getCrossingEdges(self): 92 return self._crossingEdges 93 94 def addLane(self, lane): 95 self._lanes.append(lane) 96 self._speed = lane.getSpeed() 97 self._length = lane.getLength() 98 99 def addOutgoing(self, conn): 100 if conn._to not in self._outgoing: 101 self._outgoing[conn._to] = [] 102 self._outgoing[conn._to].append(conn) 103 104 def _addIncoming(self, conn): 105 if conn._from not in self._incoming: 106 self._incoming[conn._from] = [] 107 self._incoming[conn._from].append(conn) 108 109 def _addCrossingEdge(self, edge): 110 if edge not in self._crossingEdges: 111 self._crossingEdges.append(edge) 112 113 def setRawShape(self, shape): 114 self._rawShape3D = shape 115 116 def getID(self): 117 return self._id 118 119 def getIncoming(self): 120 return self._incoming 121 122 def getOutgoing(self): 123 return self._outgoing 124 125 def getAllowedOutgoing(self, vClass): 126 if vClass is None or vClass == "ignoring": 127 return self._outgoing 128 else: 129 result = {} 130 for e, conns in self._outgoing.items(): 131 allowedConns = [c for c in conns if 132 c.getFromLane().allows(vClass) and 133 c.getToLane().allows(vClass) and 134 c.allows(vClass)] 135 if allowedConns: 136 result[e] = allowedConns 137 return result 138 139 def getConnections(self, toEdge): 140 """Returns all connections to the given target edge""" 141 return self._outgoing.get(toEdge, []) 142 143 def getRawShape(self): 144 """Return the shape that was used in netconvert for building this edge (2D).""" 145 if self._shape is None: 146 self.rebuildShape() 147 return self._rawShape 148 149 def getRawShape3D(self): 150 """Return the shape that was used in netconvert for building this edge (3D).""" 151 if self._shape is None: 152 self.rebuildShape() 153 return self._rawShape3D 154 155 def getShape(self, includeJunctions=False): 156 """Return the 2D shape that is the average of all lane shapes (segment-wise)""" 157 if self._shape is None: 158 self.rebuildShape() 159 if includeJunctions: 160 return self._shapeWithJunctions 161 return self._shape 162 163 def getShape3D(self, includeJunctions=False): 164 if self._shape is None: 165 self.rebuildShape() 166 if includeJunctions: 167 return self._shapeWithJunctions3D 168 return self._shape3D 169 170 def getBoundingBox(self, includeJunctions=True): 171 xmin, ymin, xmax, ymax = sumolib.geomhelper.addToBoundingBox(self.getShape(includeJunctions)) 172 assert xmin != xmax or ymin != ymax or self._function == "internal" 173 return (xmin, ymin, xmax, ymax) 174 175 def getClosestLanePosDist(self, point, perpendicular=False): 176 minDist = 1e400 177 minIdx = None 178 minPos = None 179 for i, l in enumerate(self._lanes): 180 pos, dist = l.getClosestLanePosAndDist(point, perpendicular) 181 if dist < minDist: 182 minDist = dist 183 minIdx = i 184 minPos = pos 185 return minIdx, minPos, minDist 186 187 def getSpeed(self): 188 return self._speed 189 190 def getLaneNumber(self): 191 return len(self._lanes) 192 193 def getLane(self, idx): 194 return self._lanes[idx] 195 196 def getLanes(self): 197 return self._lanes 198 199 def select(self, value=True): 200 self._selected = value 201 202 def isSelected(self): 203 return self._selected 204 205 def rebuildShape(self): 206 numLanes = len(self._lanes) 207 if numLanes % 2 == 1: 208 self._shape3D = self._lanes[int(numLanes / 2)].getShape3D() 209 else: 210 self._shape3D = [] 211 minLen = -1 212 for _lane in self._lanes: 213 if minLen == -1 or minLen > len(_lane.getShape()): 214 minLen = len(_lane.getShape()) 215 for i in range(minLen): 216 x = 0. 217 y = 0. 218 z = 0. 219 for _lane in self._lanes: 220 x += _lane.getShape3D()[i][0] 221 y += _lane.getShape3D()[i][1] 222 z += _lane.getShape3D()[i][2] 223 self._shape3D.append((x / float(numLanes), y / float(numLanes), z / float(numLanes))) 224 225 if self._function in ["crossing", "walkingarea"]: 226 self._shapeWithJunctions3D = self._shape3D 227 self._rawShape3D = self._shape3D 228 else: 229 self._shapeWithJunctions3D = addJunctionPos(self._shape3D, 230 self._from.getCoord3D(), self._to.getCoord3D()) 231 if self._rawShape3D == []: 232 self._rawShape3D = [self._from.getCoord3D(), self._to.getCoord3D()] 233 234 # 2d - versions 235 self._shape = [(x, y) for x, y, z in self._shape3D] # noqa 236 self._shapeWithJunctions = [(x, y) for x, y, z in self._shapeWithJunctions3D] # noqa 237 self._rawShape = [(x, y) for x, y, z in self._rawShape3D] # noqa 238 shapeLength = sumolib.geomhelper.polyLength(self.getShape()) 239 if shapeLength > 0: 240 self._lengthGeometryFactor = self.getLength() / shapeLength 241 242 def getLength(self): 243 return self._lanes[0].getLength() 244 245 def getLengthGeometryFactor(self): 246 return self._lengthGeometryFactor 247 248 def setTLS(self, tls): 249 self._tls = tls 250 251 def getFromNode(self): 252 return self._from 253 254 def getToNode(self): 255 return self._to 256 257 def getBidi(self): 258 return self._bidi 259 260 def is_fringe(self, connections=None, checkJunctions=False): 261 """true if this edge has no incoming or no outgoing connections (except turnarounds) 262 If connections is given, only those connections are considered""" 263 if connections is None: 264 return (self.is_fringe(self._incoming, checkJunctions) or 265 self.is_fringe(self._outgoing, checkJunctions)) 266 else: 267 if checkJunctions: 268 assert connections is not None 269 if connections == self._incoming: 270 return self.getFromNode().getFringe() is not None 271 elif connections == self._outgoing: 272 return self.getToNode().getFringe() is not None 273 cons = sum([c for c in connections.values()], []) 274 return len([c for c in cons if c._direction not in ( 275 Connection.LINKDIR_TURN, Connection.LINKDIR_TURN_LEFTHAND)]) == 0 276 277 def getPermissions(self): 278 """return the allowed vehicle classes for all lanes""" 279 allowed = set() 280 for lane in self._lanes: 281 allowed.update(lane.getPermissions()) 282 return list(allowed) 283 284 def allows(self, vClass): 285 """true if this edge has a lane which allows the given vehicle class""" 286 for lane in self._lanes: 287 if lane.allows(vClass): 288 return True 289 return False 290 291 def setParam(self, key, value): 292 self._params[key] = value 293 294 def getParam(self, key, default=None): 295 return self._params.get(key, default) 296 297 def getParams(self): 298 return self._params 299 300 def __repr__(self): 301 if self.getFunction() == '': 302 return '<edge id="%s" from="%s" to="%s"/>' % (self._id, self._from.getID(), self._to.getID()) 303 else: 304 return '<edge id="%s" function="%s"/>' % (self._id, self.getFunction())
27class Edge: 28 29 """ Edges from a sumo network """ 30 31 def __init__(self, id, fromN, toN, prio, function, name, edgeType='', routingType=''): 32 self._id = id 33 self._from = fromN 34 self._to = toN 35 self._priority = prio 36 if fromN: 37 fromN.addOutgoing(self) 38 if toN: 39 toN.addIncoming(self) 40 self._lanes = [] 41 self._speed = None 42 self._length = None 43 self._incoming = {} 44 self._outgoing = {} 45 self._crossingEdges = [] 46 self._shape = None 47 self._shapeWithJunctions = None 48 self._shape3D = None 49 self._shapeWithJunctions3D = None 50 self._rawShape = None 51 self._rawShape3D = None 52 self._function = function 53 self._tls = None 54 self._name = name 55 self._type = edgeType 56 self._routingType = routingType 57 self._params = {} 58 self._bidi = None 59 self._selected = False 60 self._lengthGeometryFactor = 1 61 62 def __lt__(self, other): 63 return self.getID() < other.getID() 64 65 def getName(self): 66 return self._name 67 68 def isSpecial(self): 69 """ Check if the edge has a special function. 70 71 Returns False if edge's function is 'normal', else False, e.g. for 72 internal edges or connector edges """ 73 74 return self._function != "" 75 76 def getFunction(self): 77 return self._function 78 79 def getPriority(self): 80 return self._priority 81 82 def getType(self): 83 return self._type 84 85 def getRoutingType(self): 86 """ Return the effective routingType that would be used by duarouter or sumo""" 87 return self._routingType if self._routingType != "" else self._type 88 89 def getTLS(self): 90 return self._tls 91 92 def getCrossingEdges(self): 93 return self._crossingEdges 94 95 def addLane(self, lane): 96 self._lanes.append(lane) 97 self._speed = lane.getSpeed() 98 self._length = lane.getLength() 99 100 def addOutgoing(self, conn): 101 if conn._to not in self._outgoing: 102 self._outgoing[conn._to] = [] 103 self._outgoing[conn._to].append(conn) 104 105 def _addIncoming(self, conn): 106 if conn._from not in self._incoming: 107 self._incoming[conn._from] = [] 108 self._incoming[conn._from].append(conn) 109 110 def _addCrossingEdge(self, edge): 111 if edge not in self._crossingEdges: 112 self._crossingEdges.append(edge) 113 114 def setRawShape(self, shape): 115 self._rawShape3D = shape 116 117 def getID(self): 118 return self._id 119 120 def getIncoming(self): 121 return self._incoming 122 123 def getOutgoing(self): 124 return self._outgoing 125 126 def getAllowedOutgoing(self, vClass): 127 if vClass is None or vClass == "ignoring": 128 return self._outgoing 129 else: 130 result = {} 131 for e, conns in self._outgoing.items(): 132 allowedConns = [c for c in conns if 133 c.getFromLane().allows(vClass) and 134 c.getToLane().allows(vClass) and 135 c.allows(vClass)] 136 if allowedConns: 137 result[e] = allowedConns 138 return result 139 140 def getConnections(self, toEdge): 141 """Returns all connections to the given target edge""" 142 return self._outgoing.get(toEdge, []) 143 144 def getRawShape(self): 145 """Return the shape that was used in netconvert for building this edge (2D).""" 146 if self._shape is None: 147 self.rebuildShape() 148 return self._rawShape 149 150 def getRawShape3D(self): 151 """Return the shape that was used in netconvert for building this edge (3D).""" 152 if self._shape is None: 153 self.rebuildShape() 154 return self._rawShape3D 155 156 def getShape(self, includeJunctions=False): 157 """Return the 2D shape that is the average of all lane shapes (segment-wise)""" 158 if self._shape is None: 159 self.rebuildShape() 160 if includeJunctions: 161 return self._shapeWithJunctions 162 return self._shape 163 164 def getShape3D(self, includeJunctions=False): 165 if self._shape is None: 166 self.rebuildShape() 167 if includeJunctions: 168 return self._shapeWithJunctions3D 169 return self._shape3D 170 171 def getBoundingBox(self, includeJunctions=True): 172 xmin, ymin, xmax, ymax = sumolib.geomhelper.addToBoundingBox(self.getShape(includeJunctions)) 173 assert xmin != xmax or ymin != ymax or self._function == "internal" 174 return (xmin, ymin, xmax, ymax) 175 176 def getClosestLanePosDist(self, point, perpendicular=False): 177 minDist = 1e400 178 minIdx = None 179 minPos = None 180 for i, l in enumerate(self._lanes): 181 pos, dist = l.getClosestLanePosAndDist(point, perpendicular) 182 if dist < minDist: 183 minDist = dist 184 minIdx = i 185 minPos = pos 186 return minIdx, minPos, minDist 187 188 def getSpeed(self): 189 return self._speed 190 191 def getLaneNumber(self): 192 return len(self._lanes) 193 194 def getLane(self, idx): 195 return self._lanes[idx] 196 197 def getLanes(self): 198 return self._lanes 199 200 def select(self, value=True): 201 self._selected = value 202 203 def isSelected(self): 204 return self._selected 205 206 def rebuildShape(self): 207 numLanes = len(self._lanes) 208 if numLanes % 2 == 1: 209 self._shape3D = self._lanes[int(numLanes / 2)].getShape3D() 210 else: 211 self._shape3D = [] 212 minLen = -1 213 for _lane in self._lanes: 214 if minLen == -1 or minLen > len(_lane.getShape()): 215 minLen = len(_lane.getShape()) 216 for i in range(minLen): 217 x = 0. 218 y = 0. 219 z = 0. 220 for _lane in self._lanes: 221 x += _lane.getShape3D()[i][0] 222 y += _lane.getShape3D()[i][1] 223 z += _lane.getShape3D()[i][2] 224 self._shape3D.append((x / float(numLanes), y / float(numLanes), z / float(numLanes))) 225 226 if self._function in ["crossing", "walkingarea"]: 227 self._shapeWithJunctions3D = self._shape3D 228 self._rawShape3D = self._shape3D 229 else: 230 self._shapeWithJunctions3D = addJunctionPos(self._shape3D, 231 self._from.getCoord3D(), self._to.getCoord3D()) 232 if self._rawShape3D == []: 233 self._rawShape3D = [self._from.getCoord3D(), self._to.getCoord3D()] 234 235 # 2d - versions 236 self._shape = [(x, y) for x, y, z in self._shape3D] # noqa 237 self._shapeWithJunctions = [(x, y) for x, y, z in self._shapeWithJunctions3D] # noqa 238 self._rawShape = [(x, y) for x, y, z in self._rawShape3D] # noqa 239 shapeLength = sumolib.geomhelper.polyLength(self.getShape()) 240 if shapeLength > 0: 241 self._lengthGeometryFactor = self.getLength() / shapeLength 242 243 def getLength(self): 244 return self._lanes[0].getLength() 245 246 def getLengthGeometryFactor(self): 247 return self._lengthGeometryFactor 248 249 def setTLS(self, tls): 250 self._tls = tls 251 252 def getFromNode(self): 253 return self._from 254 255 def getToNode(self): 256 return self._to 257 258 def getBidi(self): 259 return self._bidi 260 261 def is_fringe(self, connections=None, checkJunctions=False): 262 """true if this edge has no incoming or no outgoing connections (except turnarounds) 263 If connections is given, only those connections are considered""" 264 if connections is None: 265 return (self.is_fringe(self._incoming, checkJunctions) or 266 self.is_fringe(self._outgoing, checkJunctions)) 267 else: 268 if checkJunctions: 269 assert connections is not None 270 if connections == self._incoming: 271 return self.getFromNode().getFringe() is not None 272 elif connections == self._outgoing: 273 return self.getToNode().getFringe() is not None 274 cons = sum([c for c in connections.values()], []) 275 return len([c for c in cons if c._direction not in ( 276 Connection.LINKDIR_TURN, Connection.LINKDIR_TURN_LEFTHAND)]) == 0 277 278 def getPermissions(self): 279 """return the allowed vehicle classes for all lanes""" 280 allowed = set() 281 for lane in self._lanes: 282 allowed.update(lane.getPermissions()) 283 return list(allowed) 284 285 def allows(self, vClass): 286 """true if this edge has a lane which allows the given vehicle class""" 287 for lane in self._lanes: 288 if lane.allows(vClass): 289 return True 290 return False 291 292 def setParam(self, key, value): 293 self._params[key] = value 294 295 def getParam(self, key, default=None): 296 return self._params.get(key, default) 297 298 def getParams(self): 299 return self._params 300 301 def __repr__(self): 302 if self.getFunction() == '': 303 return '<edge id="%s" from="%s" to="%s"/>' % (self._id, self._from.getID(), self._to.getID()) 304 else: 305 return '<edge id="%s" function="%s"/>' % (self._id, self.getFunction())
Edges from a sumo network
31 def __init__(self, id, fromN, toN, prio, function, name, edgeType='', routingType=''): 32 self._id = id 33 self._from = fromN 34 self._to = toN 35 self._priority = prio 36 if fromN: 37 fromN.addOutgoing(self) 38 if toN: 39 toN.addIncoming(self) 40 self._lanes = [] 41 self._speed = None 42 self._length = None 43 self._incoming = {} 44 self._outgoing = {} 45 self._crossingEdges = [] 46 self._shape = None 47 self._shapeWithJunctions = None 48 self._shape3D = None 49 self._shapeWithJunctions3D = None 50 self._rawShape = None 51 self._rawShape3D = None 52 self._function = function 53 self._tls = None 54 self._name = name 55 self._type = edgeType 56 self._routingType = routingType 57 self._params = {} 58 self._bidi = None 59 self._selected = False 60 self._lengthGeometryFactor = 1
68 def isSpecial(self): 69 """ Check if the edge has a special function. 70 71 Returns False if edge's function is 'normal', else False, e.g. for 72 internal edges or connector edges """ 73 74 return self._function != ""
Check if the edge has a special function.
Returns False if edge's function is 'normal', else False, e.g. for internal edges or connector edges
85 def getRoutingType(self): 86 """ Return the effective routingType that would be used by duarouter or sumo""" 87 return self._routingType if self._routingType != "" else self._type
Return the effective routingType that would be used by duarouter or sumo
126 def getAllowedOutgoing(self, vClass): 127 if vClass is None or vClass == "ignoring": 128 return self._outgoing 129 else: 130 result = {} 131 for e, conns in self._outgoing.items(): 132 allowedConns = [c for c in conns if 133 c.getFromLane().allows(vClass) and 134 c.getToLane().allows(vClass) and 135 c.allows(vClass)] 136 if allowedConns: 137 result[e] = allowedConns 138 return result
140 def getConnections(self, toEdge): 141 """Returns all connections to the given target edge""" 142 return self._outgoing.get(toEdge, [])
Returns all connections to the given target edge
144 def getRawShape(self): 145 """Return the shape that was used in netconvert for building this edge (2D).""" 146 if self._shape is None: 147 self.rebuildShape() 148 return self._rawShape
Return the shape that was used in netconvert for building this edge (2D).
150 def getRawShape3D(self): 151 """Return the shape that was used in netconvert for building this edge (3D).""" 152 if self._shape is None: 153 self.rebuildShape() 154 return self._rawShape3D
Return the shape that was used in netconvert for building this edge (3D).
156 def getShape(self, includeJunctions=False): 157 """Return the 2D shape that is the average of all lane shapes (segment-wise)""" 158 if self._shape is None: 159 self.rebuildShape() 160 if includeJunctions: 161 return self._shapeWithJunctions 162 return self._shape
Return the 2D shape that is the average of all lane shapes (segment-wise)
176 def getClosestLanePosDist(self, point, perpendicular=False): 177 minDist = 1e400 178 minIdx = None 179 minPos = None 180 for i, l in enumerate(self._lanes): 181 pos, dist = l.getClosestLanePosAndDist(point, perpendicular) 182 if dist < minDist: 183 minDist = dist 184 minIdx = i 185 minPos = pos 186 return minIdx, minPos, minDist
206 def rebuildShape(self): 207 numLanes = len(self._lanes) 208 if numLanes % 2 == 1: 209 self._shape3D = self._lanes[int(numLanes / 2)].getShape3D() 210 else: 211 self._shape3D = [] 212 minLen = -1 213 for _lane in self._lanes: 214 if minLen == -1 or minLen > len(_lane.getShape()): 215 minLen = len(_lane.getShape()) 216 for i in range(minLen): 217 x = 0. 218 y = 0. 219 z = 0. 220 for _lane in self._lanes: 221 x += _lane.getShape3D()[i][0] 222 y += _lane.getShape3D()[i][1] 223 z += _lane.getShape3D()[i][2] 224 self._shape3D.append((x / float(numLanes), y / float(numLanes), z / float(numLanes))) 225 226 if self._function in ["crossing", "walkingarea"]: 227 self._shapeWithJunctions3D = self._shape3D 228 self._rawShape3D = self._shape3D 229 else: 230 self._shapeWithJunctions3D = addJunctionPos(self._shape3D, 231 self._from.getCoord3D(), self._to.getCoord3D()) 232 if self._rawShape3D == []: 233 self._rawShape3D = [self._from.getCoord3D(), self._to.getCoord3D()] 234 235 # 2d - versions 236 self._shape = [(x, y) for x, y, z in self._shape3D] # noqa 237 self._shapeWithJunctions = [(x, y) for x, y, z in self._shapeWithJunctions3D] # noqa 238 self._rawShape = [(x, y) for x, y, z in self._rawShape3D] # noqa 239 shapeLength = sumolib.geomhelper.polyLength(self.getShape()) 240 if shapeLength > 0: 241 self._lengthGeometryFactor = self.getLength() / shapeLength
261 def is_fringe(self, connections=None, checkJunctions=False): 262 """true if this edge has no incoming or no outgoing connections (except turnarounds) 263 If connections is given, only those connections are considered""" 264 if connections is None: 265 return (self.is_fringe(self._incoming, checkJunctions) or 266 self.is_fringe(self._outgoing, checkJunctions)) 267 else: 268 if checkJunctions: 269 assert connections is not None 270 if connections == self._incoming: 271 return self.getFromNode().getFringe() is not None 272 elif connections == self._outgoing: 273 return self.getToNode().getFringe() is not None 274 cons = sum([c for c in connections.values()], []) 275 return len([c for c in cons if c._direction not in ( 276 Connection.LINKDIR_TURN, Connection.LINKDIR_TURN_LEFTHAND)]) == 0
true if this edge has no incoming or no outgoing connections (except turnarounds) If connections is given, only those connections are considered
278 def getPermissions(self): 279 """return the allowed vehicle classes for all lanes""" 280 allowed = set() 281 for lane in self._lanes: 282 allowed.update(lane.getPermissions()) 283 return list(allowed)
return the allowed vehicle classes for all lanes
285 def allows(self, vClass): 286 """true if this edge has a lane which allows the given vehicle class""" 287 for lane in self._lanes: 288 if lane.allows(vClass): 289 return True 290 return False
true if this edge has a lane which allows the given vehicle class