def __attrs_post_init__(self): super().__attrs_post_init__() # Check that left and right edges lie inside the element. # (don't check centerline here since it can lie inside a median, for example) # (TODO reconsider the decision to have polygon only include drivable areas?) assert self.containsRegion(self.leftEdge, tolerance=0.5) assert self.containsRegion(self.rightEdge, tolerance=0.5) if self.orientation is None: self.orientation = VectorField(self.name, self._defaultHeadingAt)
def __init__(self, path, n=20, tolerance=None): '''Initialize from OpenDRIVE file at @path, with @n points per lane section reference line.''' self.road_map = RoadMap(tolerance=tolerance) self.road_map.parse(path) self.road_map.calculate_geometry(n, calc_intersect=True) drivable_poly = self.road_map.drivable_region sidewalk_poly = self.road_map.sidewalk_region intersect_poly = self.road_map.intersection_region self.road_direction = VectorField('Road Direction', self.road_map.heading_at) self.drivable_region = regionFromShapelyObject( drivable_poly, orientation=self.road_direction) self.sidewalk_region = regionFromShapelyObject(sidewalk_poly) self.intersection_region = regionFromShapelyObject( intersect_poly, orientation=self.road_direction) super().__init__() # lane_sec_dict is dict of road id to list of dict of lane id to Region. self.lane_sec_dict = {} for id_ in self.road_map.roads: lane_dicts = [] for d in self.road_map.roads[id_].sec_lane_polys: lane_dicts.append({ i: regionFromShapelyObject(d[i], orientation=self.road_direction) for i in d.keys() }) self.lane_sec_dict[id_] = lane_dicts
def union(self, other, triedReversed=False, buf=0): poly = toPolygon(other) if not poly: return super().union(other, triedReversed) union = polygonUnion((self.polygons, poly), buf=buf) orientation = VectorField.forUnionOf((self, other)) return PolygonalRegion(polygon=union, orientation=orientation)
def __init__(self, proj, data): super().__init__(proj, data) self.id = data['id'] self.direction = data['direction'] self.origin_lane = data['origin_lane']['lane_id'] medianPoints = localize(data['median'], proj) self.median = PolylineRegion(medianPoints) self.medianPoints = np.array(medianPoints) self.region.orientation = VectorField(f'Road{self.id}Direction', self.directionAt)
def __init__(self, points=None, polyline=None, orientation=True, name=None): if orientation is True: orientation = VectorField('Polyline', self.defaultOrientation) self.usingDefaultOrientation = True else: self.usingDefaultOrientation = False super().__init__(name, orientation=orientation) if points is not None: points = tuple(points) if len(points) < 2: raise RuntimeError( 'tried to create PolylineRegion with < 2 points') self.points = points self.lineString = shapely.geometry.LineString(points) elif polyline is not None: if isinstance(polyline, shapely.geometry.LineString): if len(polyline.coords) < 2: raise RuntimeError( 'tried to create PolylineRegion with <2-point LineString' ) elif isinstance(polyline, shapely.geometry.MultiLineString): if len(polyline) == 0: raise RuntimeError( 'tried to create PolylineRegion from empty MultiLineString' ) for line in polyline: assert len(line.coords) >= 2 else: raise RuntimeError( 'tried to create PolylineRegion from non-LineString') self.lineString = polyline self.points = None else: raise RuntimeError( 'must specify points or polyline for PolylineRegion') if not self.lineString.is_valid: raise RuntimeError('tried to create PolylineRegion with ' f'invalid LineString {self.lineString}') self.segments = self.segmentsOf(self.lineString) cumulativeLengths = [] total = 0 for p, q in self.segments: dx, dy = p[0] - q[0], p[1] - q[1] total += math.hypot(dx, dy) cumulativeLengths.append(total) self.cumulativeLengths = cumulativeLengths if self.points is None: pts = [] for p, q in self.segments: pts.append(p) pts.append(q) self.points = pts
def __init__(self, intersection): self.intersection = intersection drivablePoly = polygonUnion(gw.region.polygons for gw in intersection.vehicleGuideways) walkablePoly = polygonUnion(cw.region.polygons for cw in intersection.crosswalks) workspacePoly = polygonUnion((drivablePoly, walkablePoly)) self.roadDirection = VectorField('RoadDirection', intersection.directionAt) self.drivableRegion = PolygonalRegion(polygon=drivablePoly, orientation=self.roadDirection) self.workspaceRegion = PolygonalRegion(polygon=workspacePoly) super().__init__(self.workspaceRegion)
def unionAll(regions, buf=0): regs, polys = [], [] for reg in regions: if reg != nowhere: regs.append(reg) polys.append(toPolygon(reg)) if not polys: return nowhere if any(not poly for poly in polys): raise RuntimeError(f'cannot take union of regions {regions}') union = polygonUnion(polys, buf=buf) orientation = VectorField.forUnionOf(regs) return PolygonalRegion(polygon=union, orientation=orientation)
def __attrs_post_init__(self): proxy = weakref.proxy(self) for uid, elem in self.elements.items(): assert elem.uid == uid elem.network = proxy self.allRoads = self.roads + self.connectingRoads self.roadSections = tuple(sec for road in self.roads for sec in road.sections) self.laneSections = tuple(sec for lane in self.lanes for sec in lane.sections) if self.roadRegion is None: self.roadRegion = PolygonalRegion.unionAll(self.roads) if self.laneRegion is None: self.laneRegion = PolygonalRegion.unionAll(self.lanes) if self.intersectionRegion is None: self.intersectionRegion = PolygonalRegion.unionAll( self.intersections) if self.crossingRegion is None: self.crossingRegion = PolygonalRegion.unionAll(self.crossings) if self.sidewalkRegion is None: self.sidewalkRegion = PolygonalRegion.unionAll(self.sidewalks) if self.shoulderRegion is None: self.shoulderRegion = PolygonalRegion.unionAll(self.shoulders) if self.drivableRegion is None: self.drivableRegion = self.laneRegion.union( self.intersectionRegion) if self.walkableRegion is None: self.walkableRegion = self.sidewalkRegion.union( self.crossingRegion) if self.curbRegion is None: edges = [] for road in self.roads: # only include curbs of ordinary roads if road.forwardLanes: edges.append(road.forwardLanes.curb) if road.backwardLanes: edges.append(road.backwardLanes.curb) self.curbRegion = PolylineRegion.unionAll(edges) if self.roadDirection is None: # TODO replace with a PolygonalVectorField for better pruning self.roadDirection = VectorField('roadDirection', self._defaultRoadDirection)
def roadDirection(self): return VectorField('roadDirection', self.roadHeadingAt)
class LinearElement(NetworkElement): """LinearElement() A part of a road network with (mostly) linear 1- or 2-way flow. Includes roads, lane groups, lanes, sidewalks, and pedestrian crossings, but not intersections. LinearElements have a direction, namely from the first point on their centerline to the last point. This is called 'forward', even for 2-way roads. The 'left' and 'right' edges are interpreted with respect to this direction. The left/right edges are oriented along the direction of traffic near them; so for 2-way roads they will point opposite directions. """ # Geometric info (on top of the overall polygon from PolygonalRegion) centerline: PolylineRegion leftEdge: PolylineRegion rightEdge: PolylineRegion # Links to next/previous element _successor: Union[NetworkElement, None] = None # going forward _predecessor: Union[NetworkElement, None] = None # going backward @property def successor(self): return _rejectIfNonexistent(self._successor, 'successor') @property def predecessor(self): return _rejectIfNonexistent(self._predecessor, 'predecessor') def __attrs_post_init__(self): super().__attrs_post_init__() # Check that left and right edges lie inside the element. # (don't check centerline here since it can lie inside a median, for example) # (TODO reconsider the decision to have polygon only include drivable areas?) assert self.containsRegion(self.leftEdge, tolerance=0.5) assert self.containsRegion(self.rightEdge, tolerance=0.5) if self.orientation is None: self.orientation = VectorField(self.name, self._defaultHeadingAt) def _defaultHeadingAt(self, point): """Default orientation for this LinearElement. In general, we align along the nearest segment of the centerline. For roads, lane groups, etc., we align along the orientation of the lane containing the point. :meta private: """ point = _toVector(point) start, end = self.centerline.nearestSegmentTo(point) return start.angleTo(end) @distributionFunction def flowFrom(self, point: Vectorlike, distance: float, steps: Union[int, None] = None, stepSize: float = 5) -> Vector: """Advance a point along this element by a given distance. Equivalent to ``follow element.orientation from point for distance``, but possibly more accurate. The default implementation uses the forward Euler approximation with a step size of 5 meters; subclasses may ignore the **steps** and **stepSize** parameters if they can compute the flow exactly. Arguments: point: point to start from. distance: distance to travel. steps: number of steps to take, or :obj:`None` to compute the number of steps based on the distance (default :obj:`None`). stepSize: length used to compute how many steps to take, if **steps** is not specified (default 5 meters). """ return self.orientation.followFrom(_toVector(point), distance, steps=steps, stepSize=stepSize)