diff --git a/PYDANTIC_GUIDE.md b/PYDANTIC_GUIDE.md index 487168b2a..57e7d3979 100644 --- a/PYDANTIC_GUIDE.md +++ b/PYDANTIC_GUIDE.md @@ -850,11 +850,11 @@ Instead of making classes abstract, we use **entry point registration** where on ```python # In packages/overture-schema-transportation-theme/pyproject.toml [project.entry-points."overture.models"] -"transportation.connector" = "overture.schema.transportation.connector.models:Connector" -"transportation.segment" = "overture.schema.transportation.segment.models:Segment" +connector = "overture.schema.transportation:Connector" +segment = "overture.schema.transportation:Segment" ``` -**Real example:** See [`packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py`](packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py) where: +**Real example:** See [`packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/__init__.py`](packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/__init__.py) where: - **`Segment`** is a discriminated union: `RoadSegment | RailSegment | WaterSegment` - **`TransportationSegment`** is the concrete base class that all segment types inherit from @@ -1095,7 +1095,7 @@ class Address(BaseModel): freeform: str | None = None locality: str | None = None -# In overture-schema-buildings-theme/src/overture/schema/buildings/building/models.py +# In overture-schema-buildings-theme/src/overture/schema/buildings/building.py class Building(OvertureFeature): address: Address | None = None ``` diff --git a/examples/transportation/segment/road/road.yaml b/examples/transportation/segment/road/road.yaml index 4ac081556..eac92f24e 100644 --- a/examples/transportation/segment/road/road.yaml +++ b/examples/transportation/segment/road/road.yaml @@ -28,7 +28,6 @@ properties: - value: gravel road_flags: - values: [is_link, is_tunnel] # Note: `is_link` is deprecated and will be removed in a future release in favor of the link subclass - level: -1 level_rules: - value: -1 width_rules: diff --git a/packages/overture-schema-codegen/tests/test_example_loader.py b/packages/overture-schema-codegen/tests/test_example_loader.py index 6db506b0b..039bead9b 100644 --- a/packages/overture-schema-codegen/tests/test_example_loader.py +++ b/packages/overture-schema-codegen/tests/test_example_loader.py @@ -23,7 +23,7 @@ ) from overture.schema.system.geometric import BBox, Geometry from overture.schema.transportation import Segment -from overture.schema.transportation.segment.models import ( +from overture.schema.transportation.segment import ( RoadSegment, TransportationSegment, ) diff --git a/packages/overture-schema-codegen/tests/test_integration_real_models.py b/packages/overture-schema-codegen/tests/test_integration_real_models.py index 99953711c..a78ae6af5 100644 --- a/packages/overture-schema-codegen/tests/test_integration_real_models.py +++ b/packages/overture-schema-codegen/tests/test_integration_real_models.py @@ -18,8 +18,7 @@ from overture.schema.codegen.markdown.renderer import render_model from overture.schema.codegen.spec_discovery import extract_model_spec from overture.schema.system.discovery import discover_models -from overture.schema.transportation import Segment -from overture.schema.transportation.segment.models import RoadSegment +from overture.schema.transportation import RoadSegment, Segment from pydantic import BaseModel diff --git a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary.py b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary.py index 5c7d29a95..b78ee33e7 100644 --- a/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary.py +++ b/packages/overture-schema-divisions-theme/src/overture/schema/divisions/division_boundary.py @@ -70,7 +70,7 @@ class DivisionBoundary( Some boundaries may be disputed by the divisions on one or both sides. """ - model_config = ConfigDict(title="boundary") + model_config = ConfigDict(title="division_boundary") # Core geometry: Annotated[ diff --git a/packages/overture-schema-divisions-theme/tests/division_boundary_baseline_schema.json b/packages/overture-schema-divisions-theme/tests/division_boundary_baseline_schema.json index ba17bf758..43bb98b03 100644 --- a/packages/overture-schema-divisions-theme/tests/division_boundary_baseline_schema.json +++ b/packages/overture-schema-divisions-theme/tests/division_boundary_baseline_schema.json @@ -564,6 +564,6 @@ "geometry", "properties" ], - "title": "boundary", + "title": "division_boundary", "type": "object" } \ No newline at end of file diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/__init__.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/__init__.py index ed70f708b..4300f8a12 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/__init__.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/__init__.py @@ -1,15 +1,75 @@ """Transportation theme. -Transportation infrastructure including network segments (roads, rail, water) and -connection points between segments. +Transportation infrastructure including network segments (road, rail, water) and the +connection points that join them. """ __path__ = __import__("pkgutil").extend_path(__path__, __name__) from .connector import Connector -from .segment import Segment +from .segment import ( + AccessRule, + AccessType, + ConnectorReference, + DestinationLabels, + DestinationLabelType, + DestinationRule, + DestinationSignSymbol, + LevelRule, + ProhibitedTransitionRule, + ProhibitedTransitionSequenceEntry, + RailClass, + RailFlag, + RailFlagRule, + RailSegment, + RoadClass, + RoadFlag, + RoadFlagRule, + RoadSegment, + RoadSubclass, + RoadSubclassRule, + RoadSurface, + RoadSurfaceRule, + RouteReference, + Segment, + SegmentSubtype, + Speed, + SpeedLimitRule, + TransportationSegment, + WaterSegment, + WidthRule, +) __all__ = [ + "AccessRule", + "AccessType", "Connector", + "ConnectorReference", + "DestinationLabelType", + "DestinationLabels", + "DestinationRule", + "DestinationSignSymbol", + "LevelRule", + "ProhibitedTransitionRule", + "ProhibitedTransitionSequenceEntry", + "RailClass", + "RailFlag", + "RailFlagRule", + "RailSegment", + "RoadClass", + "RoadFlag", + "RoadFlagRule", + "RoadSegment", + "RoadSubclass", + "RoadSubclassRule", + "RoadSurface", + "RoadSurfaceRule", + "RouteReference", "Segment", + "SegmentSubtype", + "Speed", + "SpeedLimitRule", + "TransportationSegment", + "WaterSegment", + "WidthRule", ] diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector.py similarity index 100% rename from packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/models.py rename to packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector.py diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/__init__.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/__init__.py deleted file mode 100644 index 9767f6e05..000000000 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/connector/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .models import Connector - -__all__ = ["Connector"] diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/enums.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/enums.py deleted file mode 100644 index 98c797ff6..000000000 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/enums.py +++ /dev/null @@ -1,177 +0,0 @@ -"""Transportation theme enums.""" - -from enum import Enum - -from overture.schema.system.doc import DocumentedEnum - - -class Subtype(str, Enum): - """Transportation segment subtype classification.""" - - ROAD = "road" - RAIL = "rail" - WATER = "water" - - -class DestinationLabelType(str, Enum): - """ - Indicates what special symbol/icon is present on a signpost, visible as road marking or - similar. - """ - - STREET = "street" - COUNTRY = "country" - ROUTE_REF = "route_ref" - TOWARD_ROUTE_REF = "toward_route_ref" - UNKNOWN = "unknown" - - -class RoadClass(str, DocumentedEnum): - """Captures the kind of road and its position in the road network hierarchy.""" - - MOTORWAY = "motorway" - PRIMARY = "primary" - SECONDARY = "secondary" - TERTIARY = "tertiary" - RESIDENTIAL = "residential" - LIVING_STREET = ( - "living_street", - "Similar to residential but has implied legal restriction for motor vehicles (which can vary country by country)", - ) - TRUNK = "trunk" - UNCLASSIFIED = ( - "unclassified", - "Known roads, paved, but subordinate to all of: motorway, trunk, primary, secondary, tertiary", - ) - SERVICE = ( - "service", - "Provides vehicle access to a feature (such as a building), typically not part of the public street network", - ) - PEDESTRIAN = "pedestrian" - FOOTWAY = ("footway", "Minor segments mainly used by pedestrians") - STEPS = "steps" - PATH = "path" - TRACK = "track" - CYCLEWAY = "cycleway" - BRIDLEWAY = ("bridleway", "Similar to track but has implied access only for horses") - UNKNOWN = "unknown" - - -class RailClass(str, DocumentedEnum): - """Captures the kind of rail segment.""" - - FUNICULAR = ("funicular", "Inclined plane / cliff railway") - LIGHT_RAIL = ( - "light_rail", - "Higher-standard tram system, falls between 'tram' and 'rail'", - ) - MONORAIL = "monorail" - NARROW_GAUGE = "narrow_gauge" - STANDARD_GAUGE = ( - "standard_gauge", - "Standard-gauge rail, equivalent to OSM's railway=rail tag", - ) - SUBWAY = ("subway", "City passenger rail, often underground") - TRAM = ( - "tram", - "1-2 carriage rail vehicle tracks, often sharing road with vehicles", - ) - UNKNOWN = "unknown" - - -class DestinationSignSymbol(str, DocumentedEnum): - """ - Indicates what special symbol/icon is present on a signpost, visible as road marking or - similar. - """ - - MOTORWAY = "motorway" - AIRPORT = "airport" - HOSPITAL = "hospital" - CENTER = ( - "center", - "Center of a locality, city center or downtown, from centre in raw OSM value", - ) - INDUSTRIAL = "industrial" - PARKING = "parking" - BUS = "bus" - TRAIN_STATION = "train_station" - REST_AREA = "rest_area" - FERRY = "ferry" - MOTORROAD = "motorroad" - FUEL = "fuel" - VIEWPOINT = "viewpoint" - FUEL_DIESEL = "fuel_diesel" - FOOD = ("food", "'food', 'restaurant' in OSM") - LODGING = "lodging" - INFO = "info" - CAMP_SITE = "camp_site" - INTERCHANGE = "interchange" - RESTROOMS = ("restrooms", "'toilets' in OSM") - - -class RoadFlag(str, DocumentedEnum): - """Simple flags that can be on or off for a road segment. - - Specifies physical characteristics and can overlap. - """ - - IS_BRIDGE = "is_bridge" - IS_LINK = ( - "is_link", - "Deprecated: will be removed in a future release in favor of the `link` subclass", - ) - IS_TUNNEL = "is_tunnel" - IS_UNDER_CONSTRUCTION = "is_under_construction" - IS_ABANDONED = "is_abandoned" - IS_COVERED = "is_covered" - IS_INDOOR = "is_indoor" - - -class RailFlag(str, DocumentedEnum): - """Simple flags that can be on or off for a railway segment. - - Specifies physical characteristics and can overlap. - """ - - IS_BRIDGE = "is_bridge" - IS_TUNNEL = ( - "is_tunnel", - "Note: You may also be looking for the 'subway' class (though subways are occasionally above-ground)", - ) - IS_UNDER_CONSTRUCTION = "is_under_construction" - IS_ABANDONED = "is_abandoned" - IS_COVERED = "is_covered" - IS_PASSENGER = "is_passenger" - IS_FREIGHT = "is_freight" - IS_DISUSED = "is_disused" - - -class RoadSurface(str, Enum): - """Physical surface of the road.""" - - UNKNOWN = "unknown" - PAVED = "paved" - UNPAVED = "unpaved" - GRAVEL = "gravel" - DIRT = "dirt" - PAVING_STONES = "paving_stones" - METAL = "metal" - - -class Subclass(str, DocumentedEnum): - """Refines expected usage of the segment, must not overlap.""" - - LINK = ("link", "Connecting stretch (sliproad or ramp) between two road types") - SIDEWALK = ("sidewalk", "Footway that lies along a road") - CROSSWALK = ("crosswalk", "Footway that intersects other roads") - PARKING_AISLE = ("parking_aisle", "Service road intended for parking") - DRIVEWAY = ("driveway", "Service road intended for deliveries") - ALLEY = ("alley", "Service road intended for rear entrances, fire exits") - CYCLE_CROSSING = ("cycle_crossing", "Cycleway that intersects with other roads") - - -class AccessType(str, Enum): - ALLOWED = "allowed" - DENIED = "denied" - DESIGNATED = "designated" diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py deleted file mode 100644 index 4f1727a3a..000000000 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/models.py +++ /dev/null @@ -1,319 +0,0 @@ -"""Transportation theme models.""" - -from typing import Annotated, NewType - -from pydantic import BaseModel, ConfigDict, Field - -from overture.schema.common import OvertureFeature -from overture.schema.common.level import ( - Level, -) -from overture.schema.common.scoping import Heading, Scope, scoped -from overture.schema.common.unit import SpeedUnit -from overture.schema.system.field_constraint import UniqueItemsConstraint -from overture.schema.system.model_constraint import ( - no_extra_fields, - require_any_of, -) -from overture.schema.system.numeric import float64, int32 -from overture.schema.system.ref import Id, Reference, Relationship -from overture.schema.system.string import StrippedString, WikidataId - -from .enums import ( - AccessType, - DestinationLabelType, - DestinationSignSymbol, - RailFlag, - RoadFlag, - RoadSurface, - Subclass, -) - -SpeedValue = NewType( - "SpeedValue", Annotated[int32, Field(ge=1, le=350, description="Speed value")] -) - -Width = NewType("Width", Annotated[float64, Field(gt=0)]) - - -def _connector_type() -> type[OvertureFeature]: - from .connector import Connector # noqa: PLC0415 - - return Connector - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_POSITION) -class ConnectorReference(BaseModel): - """ - Contains the GERS ID and relative position between 0 and 1 of a connector feature along the - segment. - """ - - model_config = ConfigDict(frozen=True) - - # Required - - connector_id: Annotated[ - Id, Reference(Relationship.ASSOCIATION, _connector_type(), role="connects_to") - ] - - -@no_extra_fields -class DestinationLabels(BaseModel): - """The type of object of the destination label.""" - - model_config = ConfigDict(frozen=True) - - # Required - - value: Annotated[ - StrippedString, - Field(min_length=1, description="Names the object that is reached"), - ] - type: DestinationLabelType - - -@require_any_of("labels", "symbols") -@no_extra_fields -@scoped(Scope.HEADING) -class DestinationRule(BaseModel): - # Required - - from_connector_id: Annotated[ - Id, - Field( - description="Identifies the point of physical connection on this segment before which the destination sign or marking is visible.", - ), - ] - to_connector_id: Annotated[ - Id, - Field( - description="Identifies the point of physical connection on the segment identified by 'to_segment_id' to transition to for reaching the destination(s).", - ), - ] - to_segment_id: Annotated[ - Id, - Field( - description="Identifies the segment to transition to reach the destination(s) labeled on the sign or marking.", - ), - ] - final_heading: Annotated[ - Heading, - Field( - description="Direction of travel on the segment identified by 'to_segment_id' that leads to the destination.", - ), - ] - - # Optional - - labels: Annotated[ - list[DestinationLabels] | None, - Field( - min_length=1, - description="Labeled destinations that can be reached by following the segment.", - ), - UniqueItemsConstraint(), - ] = None - symbols: Annotated[ - list[DestinationSignSymbol] | None, - Field( - description="A collection of symbols or icons present on the sign next to current destination label." - ), - UniqueItemsConstraint(), - ] = None - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class RouteReference(BaseModel): - """Route reference with linear referencing support.""" - - # Optional - - name: Annotated[ - StrippedString | None, Field(min_length=1, description="Full name of the route") - ] = None - network: Annotated[ - StrippedString | None, - Field( - min_length=1, - description="Name of the highway system this route belongs to", - ), - ] = None - ref: Annotated[ - StrippedString | None, - Field(min_length=1, description="Code or number used to reference the route"), - ] = None - symbol: Annotated[ - StrippedString | None, - Field(min_length=1, description="URL or description of route signage"), - ] = None - wikidata: WikidataId | None = None - - -@no_extra_fields -class Speed(BaseModel): - """A speed value, i.e. a certain number of distance units travelled per unit time.""" - - model_config = ConfigDict(frozen=True) - - # Required - - value: SpeedValue - unit: SpeedUnit - - -@no_extra_fields -class SequenceEntry(BaseModel): - """A segment/connector pair in a prohibited transition sequence.""" - - model_config = ConfigDict(frozen=True) - - # Required - - connector_id: Annotated[ - Id, - Field( - description="Identifies the point of physical connection between the previous segment in the sequence and the segment in this sequence entry.", - ), - ] - segment_id: Annotated[ - Id, - Field( - description="Identifies the segment that the previous segment in the sequence is physically connected to via the sequence entry's connector.", - ), - ] - - -@no_extra_fields -@require_any_of("max_speed", "min_speed") -@scoped( - Scope.GEOMETRIC_RANGE, - Scope.HEADING, - Scope.PURPOSE_OF_USE, - Scope.RECOGNIZED_STATUS, - Scope.TEMPORAL, - Scope.TRAVEL_MODE, - Scope.VEHICLE, -) -class SpeedLimitRule(BaseModel): - """An individual speed limit rule.""" - - # Optional - - max_speed: Speed | None = None - min_speed: Speed | None = None - is_max_speed_variable: Annotated[ - bool | None, - Field( - description="Indicates a variable speed corridor", - strict=True, - ), - ] = False - - -@no_extra_fields -@scoped( - Scope.GEOMETRIC_RANGE, - Scope.HEADING, - Scope.PURPOSE_OF_USE, - Scope.RECOGNIZED_STATUS, - Scope.TEMPORAL, - Scope.TRAVEL_MODE, - Scope.VEHICLE, -) -class AccessRestrictionRule(BaseModel): - model_config = ConfigDict(frozen=True) - - # Required - - access_type: AccessType - - -@no_extra_fields -@scoped( - Scope.GEOMETRIC_RANGE, - Scope.HEADING, - Scope.PURPOSE_OF_USE, - Scope.RECOGNIZED_STATUS, - Scope.TEMPORAL, - Scope.TRAVEL_MODE, - Scope.VEHICLE, -) -class ProhibitedTransitionRule(BaseModel): - # Required - - sequence: Annotated[ - list[SequenceEntry], - Field( - min_length=1, - description="Ordered sequence of connector/segment pairs that it is prohibited to follow from this segment.", - ), - UniqueItemsConstraint(), - ] - final_heading: Annotated[ - Heading, - Field( - description="Direction of travel that is prohibited on the destination segment of the sequence.", - ), - ] - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class RoadFlagRule(BaseModel): - """Road-specific flag rule with geometric scoping only.""" - - # Required - - values: Annotated[list[RoadFlag], Field(min_length=1), UniqueItemsConstraint()] - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class RailFlagRule(BaseModel): - """Rail-specific flag rule with geometric scoping only.""" - - # Required - - values: Annotated[list[RailFlag], Field(min_length=1), UniqueItemsConstraint()] - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class LevelRule(BaseModel): - """ - A single level rule defining the Z-order, i.e. stacking order, applicable within a given scope - on the road segment. - """ - - # Required - - value: Level - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class SubclassRule(BaseModel): - """Set of subclasses scoped along segment.""" - - # Required - - value: Subclass - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class SurfaceRule(BaseModel): - # Required - - value: RoadSurface - - -@no_extra_fields -@scoped(Scope.GEOMETRIC_RANGE) -class WidthRule(BaseModel): - # Required - - value: Width diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/__init__.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/__init__.py index 8ca0a63ee..3bd356872 100644 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/__init__.py +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/__init__.py @@ -1,3 +1,97 @@ -from .models import Segment +"""Transportation segment feature.""" -__all__ = ["Segment"] +__path__ = __import__("pkgutil").extend_path(__path__, __name__) + +from typing import Annotated + +from pydantic import Field, Tag + +from overture.schema.system.feature import Feature + +from ._common import ( + AccessRule, + AccessType, + ConnectorReference, + LevelRule, + ProhibitedTransitionRule, + ProhibitedTransitionSequenceEntry, + SegmentSubtype, + Speed, + SpeedLimitRule, + TransportationSegment, + WidthRule, +) +from .rail import RailClass, RailFlag, RailFlagRule, RailSegment +from .road import ( + DestinationLabels, + DestinationLabelType, + DestinationRule, + DestinationSignSymbol, + RoadClass, + RoadFlag, + RoadFlagRule, + RoadSegment, + RoadSubclass, + RoadSubclassRule, + RoadSurface, + RoadSurfaceRule, + RouteReference, +) +from .water import WaterSegment + +# The public `Segment` type: a discriminated union over the three subtype models, keyed on the +# `subtype` field. Assembled here because it depends on the subtype classes imported above. +Segment = Annotated[ + Annotated[RoadSegment, Tag(SegmentSubtype.ROAD)] + | Annotated[RailSegment, Tag(SegmentSubtype.RAIL)] + | Annotated[WaterSegment, Tag(SegmentSubtype.WATER)], + Field( + discriminator=Feature.field_discriminator( + "subtype", RoadSegment, RailSegment, WaterSegment + ) + ), +] + +# Explicitly assign docstring to the Segment type alias +Segment.__doc__ = """Transportation segment model representing linear travel infrastructure. + +Encompasses road, rail, and water transportation segments. Models linear features that enable +movement of people, goods, and vehicles through structured networks. Each segment type provides +specialized attributes for its respective transportation mode. + +Supports routing, mapping, navigation, and transportation network analysis through rich geometric +and attribute data. +""" + +__all__ = [ + "AccessRule", + "AccessType", + "ConnectorReference", + "DestinationLabelType", + "DestinationLabels", + "DestinationRule", + "DestinationSignSymbol", + "LevelRule", + "ProhibitedTransitionRule", + "ProhibitedTransitionSequenceEntry", + "RailClass", + "RailFlag", + "RailFlagRule", + "RailSegment", + "RoadClass", + "RoadFlag", + "RoadFlagRule", + "RoadSegment", + "RoadSubclass", + "RoadSubclassRule", + "RoadSurface", + "RoadSurfaceRule", + "RouteReference", + "Segment", + "SegmentSubtype", + "Speed", + "SpeedLimitRule", + "TransportationSegment", + "WaterSegment", + "WidthRule", +] diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/_common.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/_common.py new file mode 100644 index 000000000..8c21c8317 --- /dev/null +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/_common.py @@ -0,0 +1,373 @@ +"""Shared, mode-independent segment models for the Overture Maps transportation theme. + +This private module holds the pieces of the segment feature that are common to every segment +subtype (road, rail, water): the subtype discriminator, the base `TransportationSegment` +feature class, and the rule/reference models and type aliases used across subtypes. It mirrors +the `_common.py` convention used by the other refactored themes, scoped to the segment package. + +The `Segment` discriminated union is intentionally NOT defined here; it must be assembled in +`segment/__init__.py` after the subtype modules (`.road`/`.rail`/`.water`) are imported, +because those modules import their base class from here. Keeping the base in `_common` and the +union in `__init__` is what breaks the import cycle. +""" + +import textwrap +from typing import Annotated, Literal, NewType + +from pydantic import BaseModel, ConfigDict, Field + +from overture.schema.common import OvertureFeature +from overture.schema.common.level import Level +from overture.schema.common.names import Named +from overture.schema.common.scoping import Heading, Scope, scoped +from overture.schema.common.unit import SpeedUnit +from overture.schema.system.doc import DocumentedEnum +from overture.schema.system.field_constraint import UniqueItemsConstraint +from overture.schema.system.geometric import ( + Geometry, + GeometryType, + GeometryTypeConstraint, +) +from overture.schema.system.model_constraint import no_extra_fields, require_any_of +from overture.schema.system.numeric import float64, int32 +from overture.schema.system.ref import Id, Reference, Relationship + +# Import `Connector` from its sibling module (not the theme package root) so this import does +# not depend on the transportation package's `__init__` having finished executing. +from ..connector import Connector + + +class SegmentSubtype(str, DocumentedEnum): + """Transportation segment subtype classification.""" + + ROAD = ( + "road", + "A segment of the road network, travelled by motor vehicles, cyclists, pedestrians, etc.", + ) + RAIL = ( + "rail", + "A segment of the railway network, travelled by trains, trams, and other rail vehicles.", + ) + WATER = ( + "water", + "A segment of the navigable water network, travelled by vessels such as ferries and boats.", + ) + + +class AccessType(str, DocumentedEnum): + """Whether access is allowed, denied, or allowed as designated.""" + + ALLOWED = ( + "allowed", + "Access is permitted.", + ) + DENIED = ( + "denied", + "Access is prohibited.", + ) + DESIGNATED = ( + "designated", + "Access is positively designated, typically by signage or regulation, for the relevant travel mode(s).", + ) + + +@no_extra_fields +@scoped( + Scope.GEOMETRIC_RANGE, + Scope.HEADING, + Scope.PURPOSE_OF_USE, + Scope.RECOGNIZED_STATUS, + Scope.TEMPORAL, + Scope.TRAVEL_MODE, + Scope.VEHICLE, +) +class AccessRule(BaseModel): + """ + A single scoped rule about who or what can use a segment (or a sub-segment of a segment). + + This rule can be scoped to apply only to linearly-referenced subsegment, a travel mode such as + motor vehicle, a heading such as forward or backward, and to various other scopes as well. See + the fields for the full list of available scopes. + """ + + model_config = ConfigDict(frozen=True) + + # Required + + access_type: AccessType + + +AccessRules = NewType( + "AccessRules", + Annotated[ + list[AccessRule], + Field(min_length=1, description="Rules governing access to this road segment"), + UniqueItemsConstraint(), + ], +) + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_POSITION) +class ConnectorReference(BaseModel): + """ + Contains the GERS ID and relative position between 0 and 1 of a connector feature along the + segment. + """ + + model_config = ConfigDict(frozen=True) + + # Required + + connector_id: Annotated[ + Id, Reference(Relationship.ASSOCIATION, Connector, role="connects_to") + ] + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class LevelRule(BaseModel): + """ + A single level rule defining the Z-order, i.e. stacking order, applicable within a given scope + on the road segment. + """ + + value: Level + + +LevelRules = NewType( + "LevelRules", + Annotated[ + list[LevelRule], + Field( + description="Defines the Z-order, i.e. stacking order, of the road segment." + ), + ], +) + + +class TransportationSegment( + OvertureFeature[Literal["transportation"], Literal["segment"]], Named +): + """Common Segment Properties.""" + + model_config = ConfigDict(title="segment") + + # Core + geometry: Annotated[ + Geometry, + GeometryTypeConstraint(GeometryType.LINE_STRING), + Field(description="Segment centerline"), + ] + + # Required + + # Should not be confused with a transport mode. A segment kind has an (implied) set of default + # transport modes. + subtype: Annotated[ + SegmentSubtype, Field(description="Broad category of transportation segment.") + ] + + # Optional + + access_restrictions: AccessRules | None = None + # Contains the GERS ID and relative position between 0 and 1 of a connector feature along the segment. + connectors: Annotated[ + list[ConnectorReference] | None, + Field( + min_length=2, + description=textwrap.dedent(""" + List of connectors which this segment is physically connected to and their + relative location. Each connector is a possible routing decision point, + meaning it defines a place along the segment in which there is possibility to + transition to other segments which share the same connector. + """).strip(), + ), + UniqueItemsConstraint(), + ] = [] + level_rules: LevelRules | None = None + + +@no_extra_fields +class ProhibitedTransitionSequenceEntry(BaseModel): + """A segment/connector pair in a prohibited transition sequence.""" + + model_config = ConfigDict(frozen=True) + + # Required + + connector_id: Annotated[ + Id, + Reference(Relationship.ASSOCIATION, Connector, role="transitions_through"), + Field( + description=textwrap.dedent(""" + Identifies the point of physical connection between the previous segment in the + sequence and the segment in this sequence entry. + """).strip(), + ), + ] + segment_id: Annotated[ + Id, + Reference( + Relationship.ASSOCIATION, TransportationSegment, role="transitions_to" + ), + Field( + description=textwrap.dedent(""" + Identifies the segment that the previous segment in the sequence is physically + connected to via the sequence entry's connector. + """).strip(), + ), + ] + + +@no_extra_fields +@scoped( + Scope.GEOMETRIC_RANGE, + Scope.HEADING, + Scope.PURPOSE_OF_USE, + Scope.RECOGNIZED_STATUS, + Scope.TEMPORAL, + Scope.TRAVEL_MODE, + Scope.VEHICLE, +) +class ProhibitedTransitionRule(BaseModel): + """ + A single scoped rule identifying a transition from the current segment (or a sub-segment of it) + to a connected segment that is not allowed. + + This rule can be scoped to apply only to linearly-referenced subsegment, a travel mode such as + motor vehicle, a heading such as forward or backward, and to various other scopes as well. See + the fields for the full list of available scopes. + + Terminology note: Prohibited transitions are more commonly referred to as turn restrictions. + While the term turn restriction is simpler and more relatable, it is less precise because + transitions between segments don't always require maneuvers that a human would describe as being + a turn. For this reason, the Overture schema has opted for the more general and precise term + prohibited transitions. + """ + + # Required + + sequence: Annotated[ + list[ProhibitedTransitionSequenceEntry], + Field( + min_length=1, + description=textwrap.dedent(""" + Ordered sequence of connector/segment pairs that it is prohibited to follow + from this segment. + """).strip(), + ), + UniqueItemsConstraint(), + ] + final_heading: Annotated[ + Heading, + Field( + description=textwrap.dedent(""" + Direction of travel that is prohibited on the destination segment of the sequence. + """).strip(), + ), + ] + + +ProhibitedTransitions = NewType( + "ProhibitedTransitions", + Annotated[ + list[ProhibitedTransitionRule], + Field( + description="Rules preventing transitions from this segment to another segment." + ), + ], +) + + +SpeedValue = NewType( + "SpeedValue", Annotated[int32, Field(ge=1, le=350, description="Speed value")] +) + + +@no_extra_fields +class Speed(BaseModel): + """A speed value, i.e. a certain number of distance units travelled per unit time.""" + + model_config = ConfigDict(frozen=True) + + # Required + + value: SpeedValue + unit: SpeedUnit + + +@no_extra_fields +@require_any_of("max_speed", "min_speed") +@scoped( + Scope.GEOMETRIC_RANGE, + Scope.HEADING, + Scope.PURPOSE_OF_USE, + Scope.RECOGNIZED_STATUS, + Scope.TEMPORAL, + Scope.TRAVEL_MODE, + Scope.VEHICLE, +) +class SpeedLimitRule(BaseModel): + """An individual speed limit rule.""" + + # Optional + + max_speed: Speed | None = None + min_speed: Speed | None = None + is_max_speed_variable: Annotated[ + bool | None, + Field( + description="Indicates a variable speed corridor", + strict=True, + ), + ] = False + + +SpeedLimits = NewType( + "SpeedLimits", + Annotated[ + list[SpeedLimitRule], + Field(min_length=1, description="Rules governing speed on this road segment"), + UniqueItemsConstraint(), + ], +) + + +Width = NewType("Width", Annotated[float64, Field(gt=0)]) + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class WidthRule(BaseModel): + """ + Specifies the width of a segment or a linearly-referenced subsegment of a segment. + """ + + value: Annotated[ + Width, + Field( + description="Edge-to-edge width of the feature modeled by this segment, in meters." + ), + ] + + +WidthRules = NewType( + "WidthRules", + Annotated[ + list[WidthRule], + Field( + min_length=1, + description=textwrap.dedent(""" + Edge-to-edge width of the feature modeled by this segment, in meters. + + Examples: (1) If this segment models a carriageway without sidewalk, this value + represents the edge-to-edge width of the carriageway, inclusive of any shoulder. + (2) If this segment models a sidewalk by itself, this value represents the + edge-to-edge width of the sidewalk. (3) If this segment models a combined sidewalk + and carriageway, this value represents the edge-to-edge width inclusive of sidewalk. + """).strip(), + ), + UniqueItemsConstraint(), + ], +) diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py deleted file mode 100644 index 6a7ad9b5f..000000000 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/models.py +++ /dev/null @@ -1,149 +0,0 @@ -"""Segment feature models for Overture Maps transportation theme.""" - -from typing import Annotated, Literal - -from pydantic import ConfigDict, Field, Tag - -from overture.schema.common import ( - OvertureFeature, -) -from overture.schema.common.names import ( - Named, -) -from overture.schema.system.feature import Feature -from overture.schema.system.field_constraint import UniqueItemsConstraint -from overture.schema.system.geometric import ( - Geometry, - GeometryType, - GeometryTypeConstraint, -) - -from ..enums import RailClass, RoadClass, Subclass, Subtype -from ..models import ConnectorReference -from ..types import ( - AccessRules, - Destinations, - LevelRules, - ProhibitedTransitions, - RailFlags, - RoadFlags, - Routes, - SpeedLimits, - SubclassRules, - Surfaces, - WidthRules, -) - - -class TransportationSegment( - OvertureFeature[Literal["transportation"], Literal["segment"]], Named -): - """Common Segment Properties.""" - - model_config = ConfigDict(title="segment") - - # Core - geometry: Annotated[ - Geometry, - GeometryTypeConstraint(GeometryType.LINE_STRING), - Field(description="Segment centerline"), - ] - - # Required - - # Should not be confused with a transport mode. A segment kind has an (implied) set of default - # transport modes. - subtype: Annotated[ - Subtype, Field(description="Broad category of transportation segment.") - ] - - # Optional - - access_restrictions: AccessRules | None = None - # Contains the GERS ID and relative position between 0 and 1 of a connector feature along the segment. - connectors: Annotated[ - list[ConnectorReference] | None, - Field( - min_length=2, - description="List of connectors which this segment is physically connected to and their relative location. Each connector is a possible routing decision point, meaning it defines a place along the segment in which there is possibility to transition to other segments which share the same connector.", - ), - UniqueItemsConstraint(), - ] = [] - level_rules: LevelRules | None = None - routes: Routes | None = None - subclass_rules: SubclassRules | None = None - - -class RoadSegment(TransportationSegment): - """Road Segment Properties.""" - - model_config = ConfigDict(title="Road-Specific Properties") - - # Discriminator - - subtype: Literal[Subtype.ROAD] - - # Required - - class_: Annotated[RoadClass, Field(alias="class")] - - # Optional - - destinations: Destinations | None = None - prohibited_transitions: ProhibitedTransitions | None = None - road_flags: RoadFlags | None = None - road_surface: Surfaces | None = None - speed_limits: SpeedLimits | None = None - subclass: Subclass | None = None - width_rules: WidthRules | None = None - - -class RailSegment(TransportationSegment): - """Rail Segment Properties.""" - - model_config = ConfigDict(title="Rail-Specific Properties") - - # Discriminator - - subtype: Literal[Subtype.RAIL] - - # Required - - class_: Annotated[RailClass, Field(alias="class")] - - # Optional - - rail_flags: RailFlags | None = None - - -class WaterSegment(TransportationSegment): - """Water Segment Properties.""" - - model_config = ConfigDict(title="Water-Specific Properties") - - # Discriminator - - subtype: Literal[Subtype.WATER] - - -Segment = Annotated[ - Annotated[RoadSegment, Tag(Subtype.ROAD)] - | Annotated[RailSegment, Tag(Subtype.RAIL)] - | Annotated[WaterSegment, Tag(Subtype.WATER)], - Field( - discriminator=Feature.field_discriminator( - "subtype", RoadSegment, RailSegment, WaterSegment - ) - ), -] - -# Explicitly assign docstring to the Segment type alias -Segment.__doc__ = """Transportation segment model representing linear travel infrastructure. - -Encompasses road, rail, and water transportation segments. Models linear features that enable -movement of people, goods, and vehicles through structured networks. Each segment type provides -specialized attributes for its respective transportation mode. - -Supports routing, mapping, navigation, and transportation network analysis through rich geometric -and attribute data. -""" diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/rail.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/rail.py new file mode 100644 index 000000000..bd841c891 --- /dev/null +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/rail.py @@ -0,0 +1,96 @@ +"""Rail segment and its supporting types.""" + +import textwrap +from typing import Annotated, Literal, NewType + +from pydantic import BaseModel, ConfigDict, Field + +from overture.schema.common.scoping import Scope, scoped +from overture.schema.system.doc import DocumentedEnum +from overture.schema.system.field_constraint import UniqueItemsConstraint +from overture.schema.system.model_constraint import no_extra_fields + +from ._common import SegmentSubtype, TransportationSegment + + +class RailClass(str, DocumentedEnum): + """Captures the kind of rail segment.""" + + FUNICULAR = ("funicular", "Inclined plane / cliff railway") + LIGHT_RAIL = ( + "light_rail", + "Higher-standard tram system, falls between 'tram' and 'rail'", + ) + MONORAIL = "monorail" + NARROW_GAUGE = "narrow_gauge" + STANDARD_GAUGE = ( + "standard_gauge", + "Standard-gauge rail, equivalent to OSM's railway=rail tag", + ) + SUBWAY = ("subway", "City passenger rail, often underground") + TRAM = ( + "tram", + "1-2 carriage rail vehicle tracks, often sharing road with vehicles", + ) + UNKNOWN = "unknown" + + +class RailFlag(str, DocumentedEnum): + """Simple flags that can be on or off for a railway segment. + + Specifies physical characteristics and can overlap. + """ + + IS_BRIDGE = "is_bridge" + IS_TUNNEL = ( + "is_tunnel", + "Note: You may also be looking for the 'subway' class (though subways are occasionally above-ground)", + ) + IS_UNDER_CONSTRUCTION = "is_under_construction" + IS_ABANDONED = "is_abandoned" + IS_COVERED = "is_covered" + IS_PASSENGER = "is_passenger" + IS_FREIGHT = "is_freight" + IS_DISUSED = "is_disused" + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class RailFlagRule(BaseModel): + """Rail-specific flag rule with geometric scoping only.""" + + values: Annotated[list[RailFlag], Field(min_length=1), UniqueItemsConstraint()] + + +RailFlags = NewType( + "RailFlags", + Annotated[ + list[RailFlagRule], + Field( + min_length=1, + description=textwrap.dedent(""" + Set of boolean attributes applicable to railways. May be specified either as a + single flag array of flag values, or as an array of flag rules. + """).strip(), + ), + UniqueItemsConstraint(), + ], +) + + +class RailSegment(TransportationSegment): + """Rail segment properties.""" + + model_config = ConfigDict(title="segment (rail)") + + # Discriminator + + subtype: Literal[SegmentSubtype.RAIL] + + # Required + + class_: Annotated[RailClass, Field(alias="class")] + + # Optional + + rail_flags: RailFlags | None = None diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/road.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/road.py new file mode 100644 index 000000000..d29f3d9a7 --- /dev/null +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/road.py @@ -0,0 +1,397 @@ +"""Road segment and its supporting types.""" + +import textwrap +from enum import Enum +from typing import Annotated, Literal, NewType + +from pydantic import BaseModel, ConfigDict, Field + +from overture.schema.common.scoping import Heading, Scope, scoped +from overture.schema.system.doc import DocumentedEnum +from overture.schema.system.field_constraint import UniqueItemsConstraint +from overture.schema.system.model_constraint import no_extra_fields, require_any_of +from overture.schema.system.ref import Id, Reference, Relationship +from overture.schema.system.string import StrippedString, WikidataId + +from ..connector import Connector +from ._common import ( + ProhibitedTransitions, + SegmentSubtype, + SpeedLimits, + TransportationSegment, + WidthRules, +) + + +class RoadClass(str, DocumentedEnum): + """Captures the kind of road and its position in the road network hierarchy.""" + + MOTORWAY = "motorway" + PRIMARY = "primary" + SECONDARY = "secondary" + TERTIARY = "tertiary" + RESIDENTIAL = "residential" + LIVING_STREET = ( + "living_street", + "Similar to residential but has implied legal restriction for motor vehicles (which can vary country by country)", + ) + TRUNK = "trunk" + UNCLASSIFIED = ( + "unclassified", + "Known roads, paved, but subordinate to all of: motorway, trunk, primary, secondary, tertiary", + ) + SERVICE = ( + "service", + "Provides vehicle access to a feature (such as a building), typically not part of the public street network", + ) + PEDESTRIAN = "pedestrian" + FOOTWAY = ("footway", "Minor segments mainly used by pedestrians") + STEPS = "steps" + PATH = "path" + TRACK = "track" + CYCLEWAY = "cycleway" + BRIDLEWAY = ("bridleway", "Similar to track but has implied access only for horses") + UNKNOWN = "unknown" + + +class RoadSubclass(str, DocumentedEnum): + """Refines expected usage of the segment, must not overlap.""" + + LINK = ("link", "Connecting stretch (sliproad or ramp) between two road types") + SIDEWALK = ("sidewalk", "Footway that lies along a road") + CROSSWALK = ("crosswalk", "Footway that intersects other roads") + PARKING_AISLE = ("parking_aisle", "Service road intended for parking") + DRIVEWAY = ("driveway", "Service road intended for deliveries") + ALLEY = ("alley", "Service road intended for rear entrances, fire exits") + CYCLE_CROSSING = ("cycle_crossing", "Cycleway that intersects with other roads") + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class RoadSubclassRule(BaseModel): + """Set of subclasses scoped along segment.""" + + value: RoadSubclass + + +RoadSubclassRules = NewType( + "RoadSubclassRules", + Annotated[ + list[RoadSubclassRule], + Field(description="Set of subclasses scoped along segment"), + ], +) + + +class RoadFlag(str, DocumentedEnum): + """Simple flags that can be on or off for a road segment. + + Specifies physical characteristics and can overlap. + """ + + IS_BRIDGE = "is_bridge" + IS_LINK = ( + "is_link", + "Deprecated: will be removed in a future release in favor of the `link` subclass", + ) + IS_TUNNEL = "is_tunnel" + IS_UNDER_CONSTRUCTION = "is_under_construction" + IS_ABANDONED = "is_abandoned" + IS_COVERED = "is_covered" + IS_INDOOR = "is_indoor" + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class RoadFlagRule(BaseModel): + """Road-specific flag rule with geometric scoping only.""" + + values: Annotated[list[RoadFlag], Field(min_length=1), UniqueItemsConstraint()] + + +RoadFlags = NewType( + "RoadFlags", + Annotated[ + list[RoadFlagRule], + Field( + min_length=1, + description=textwrap.dedent(""" + Set of boolean attributes applicable to roads. May be specified either as a + single flag array of flag values, or as an array of flag rules. + """).strip(), + ), + UniqueItemsConstraint(), + ], +) + + +class RoadSurface(str, Enum): + """Physical surface of the road.""" + + UNKNOWN = "unknown" + PAVED = "paved" + UNPAVED = "unpaved" + GRAVEL = "gravel" + DIRT = "dirt" + PAVING_STONES = "paving_stones" + METAL = "metal" + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class RoadSurfaceRule(BaseModel): + """ + Specifies the physical surface of a road segment or a linearly-referenced subsegment of the road + segment. + """ + + value: RoadSurface + + +# We should likely restrict the available surface types to the subset of the common OSM surface=* tag values that are useful both for routing and for map tile rendering. +RoadSurfaces = NewType( + "RoadSurfaces", + Annotated[ + list[RoadSurfaceRule], + Field( + min_length=1, + description=textwrap.dedent(""" + Physical surface of the road. May either be specified as a single global value + for the segment, or as an array of surface rules. + """).strip(), + ), + UniqueItemsConstraint(), + ], +) + + +class DestinationLabelType(str, Enum): + """ + Indicates what special symbol/icon is present on a signpost, visible as road marking or + similar. + """ + + STREET = "street" + COUNTRY = "country" + ROUTE_REF = "route_ref" + TOWARD_ROUTE_REF = "toward_route_ref" + UNKNOWN = "unknown" + + +@no_extra_fields +class DestinationLabels(BaseModel): + """The type of object of the destination label.""" + + model_config = ConfigDict(frozen=True) + + # Required + + value: Annotated[ + StrippedString, + Field(min_length=1, description="Names the object that is reached"), + ] + type: DestinationLabelType + + +class DestinationSignSymbol(str, DocumentedEnum): + """ + Indicates what special symbol/icon is present on a signpost, visible as road marking or + similar. + """ + + MOTORWAY = "motorway" + AIRPORT = "airport" + HOSPITAL = "hospital" + CENTER = ( + "center", + "Center of a locality, city center or downtown, from centre in raw OSM value", + ) + INDUSTRIAL = "industrial" + PARKING = "parking" + BUS = "bus" + TRAIN_STATION = "train_station" + REST_AREA = "rest_area" + FERRY = "ferry" + MOTORROAD = "motorroad" + FUEL = "fuel" + VIEWPOINT = "viewpoint" + FUEL_DIESEL = "fuel_diesel" + FOOD = ("food", "'food', 'restaurant' in OSM") + LODGING = "lodging" + INFO = "info" + CAMP_SITE = "camp_site" + INTERCHANGE = "interchange" + RESTROOMS = ("restrooms", "'toilets' in OSM") + + +@require_any_of("labels", "symbols") +@no_extra_fields +@scoped(Scope.HEADING) +class DestinationRule(BaseModel): + """ + A single scoped rule describing the destinations reachable by following a transition from the + current segment onto the segment `to_segment_id` via the connector `to_connector_id` in + the `final_heading` direction. + + The destination is described as it appears in real-world signage through text `labels` and/or + pictographic `symbols`. Heading-scoped to the approach direction on the current segment. + """ + + # Required + + from_connector_id: Annotated[ + Id, + Reference(Relationship.ASSOCIATION, Connector, role="signposted_from"), + Field( + description=textwrap.dedent(""" + Identifies the point of physical connection on this segment before which the + destination sign or marking is visible. + """).strip(), + ), + ] + to_connector_id: Annotated[ + Id, + Reference(Relationship.ASSOCIATION, Connector, role="transitions_through"), + Field( + description=textwrap.dedent(""" + Identifies the point of physical connection on the segment identified by + 'to_segment_id' to transition to for reaching the destination(s). + """).strip(), + ), + ] + to_segment_id: Annotated[ + Id, + Reference( + Relationship.ASSOCIATION, TransportationSegment, role="transitions_to" + ), + Field( + description=textwrap.dedent(""" + Identifies the segment to transition to reach the destination(s) labeled on the + sign or marking. + """).strip(), + ), + ] + final_heading: Annotated[ + Heading, + Field( + description=textwrap.dedent(""" + Direction of travel on the segment identified by 'to_segment_id' that leads to + the destination. + """).strip(), + ), + ] + + # Optional + + labels: Annotated[ + list[DestinationLabels] | None, + Field( + min_length=1, + description="Labeled destinations that can be reached by following the segment.", + ), + UniqueItemsConstraint(), + ] = None + symbols: Annotated[ + list[DestinationSignSymbol] | None, + Field( + description=textwrap.dedent(""" + A collection of symbols or icons present on the sign next to current destination + label. + """).strip() + ), + UniqueItemsConstraint(), + ] = None + + +Destinations = NewType( + "Destinations", + Annotated[ + list[DestinationRule], + Field( + description=textwrap.dedent(""" + Describes objects that can be reached by following a transportation segment in the + same way those objects are described on signposts or ground writing that a + traveller following the segment would observe in the real world. This allows + navigation systems to refer to signs and observable writing that a traveller + actually sees. + """).strip() + ), + ], +) + + +@no_extra_fields +@scoped(Scope.GEOMETRIC_RANGE) +class RouteReference(BaseModel): + """Route reference with linear referencing support.""" + + # Optional + + name: Annotated[ + StrippedString | None, Field(min_length=1, description="Full name of the route") + ] = None + network: Annotated[ + StrippedString | None, + Field( + min_length=1, + description="Name of the highway system this route belongs to", + ), + ] = None + ref: Annotated[ + StrippedString | None, + Field(min_length=1, description="Code or number used to reference the route"), + ] = None + symbol: Annotated[ + StrippedString | None, + Field(min_length=1, description="URL or description of route signage"), + ] = None + wikidata: WikidataId | None = None + + +Routes = NewType( + "Routes", + Annotated[ + list[RouteReference], Field(description="Routes this segment belongs to") + ], +) + + +class RoadSegment(TransportationSegment): + """Road segment properties.""" + + model_config = ConfigDict(title="segment (road)") + + # Discriminator + + subtype: Literal[SegmentSubtype.ROAD] + + # Required + + class_: Annotated[RoadClass, Field(alias="class")] + + # Optional + + destinations: Destinations | None = None + prohibited_transitions: Annotated[ + ProhibitedTransitions | None, + Field( + description=textwrap.dedent(""" + Rules for when transition to a connected segment is not allowed (commonly known as turn + restrictions). + """).strip() + ), + ] = None + road_flags: RoadFlags | None = None + road_surface: Annotated[ + RoadSurfaces | None, + Field( + description=textwrap.dedent(""" + Rules describing the physical surface of the road. + """).strip() + ), + ] = None + routes: Routes | None = None + speed_limits: SpeedLimits | None = None + subclass: RoadSubclass | None = None + subclass_rules: RoadSubclassRules | None = None + width_rules: WidthRules | None = None diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/water.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/water.py new file mode 100644 index 000000000..ed68b4b98 --- /dev/null +++ b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/segment/water.py @@ -0,0 +1,17 @@ +"""Water segment and its supporting types.""" + +from typing import Literal + +from pydantic import ConfigDict + +from ._common import SegmentSubtype, TransportationSegment + + +class WaterSegment(TransportationSegment): + """Water segment properties.""" + + model_config = ConfigDict(title="segment (water)") + + # Discriminator + + subtype: Literal[SegmentSubtype.WATER] diff --git a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py b/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py deleted file mode 100644 index 6f8818db7..000000000 --- a/packages/overture-schema-transportation-theme/src/overture/schema/transportation/types.py +++ /dev/null @@ -1,135 +0,0 @@ -"""Transportation theme type aliases and annotated types.""" - -from typing import Annotated, NewType - -from pydantic import Field - -from overture.schema.system.field_constraint import UniqueItemsConstraint - -from .models import ( - AccessRestrictionRule, - DestinationRule, - LevelRule, - ProhibitedTransitionRule, - RailFlagRule, - RoadFlagRule, - RouteReference, - SpeedLimitRule, - SubclassRule, - SurfaceRule, - WidthRule, -) - -Destinations = NewType( - "Destinations", - Annotated[ - list[DestinationRule], - Field( - description="Describes objects that can be reached by following a transportation segment in the same way those objects are described on signposts or ground writing that a traveller following the segment would observe in the real world. This allows navigation systems to refer to signs and observable writing that a traveller actually sees." - ), - ], -) - -Routes = NewType( - "Routes", - Annotated[ - list[RouteReference], Field(description="Routes this segment belongs to") - ], -) - -AccessRules = NewType( - "AccessRules", - Annotated[ - list[AccessRestrictionRule], - Field(min_length=1, description="Rules governing access to this road segment"), - UniqueItemsConstraint(), - ], -) - - -SpeedLimits = NewType( - "SpeedLimits", - Annotated[ - list[SpeedLimitRule], - Field(min_length=1, description="Rules governing speed on this road segment"), - UniqueItemsConstraint(), - ], -) - -ProhibitedTransitions = NewType( - "ProhibitedTransitions", - Annotated[ - list[ProhibitedTransitionRule], - Field( - description="Rules preventing transitions from this segment to another segment." - ), - ], -) - -RoadFlags = NewType( - "RoadFlags", - Annotated[ - list[RoadFlagRule], - Field( - min_length=1, - description="Set of boolean attributes applicable to roads. May be specified either as a single flag array of flag values, or as an array of flag rules.", - ), - UniqueItemsConstraint(), - ], -) - -RailFlags = NewType( - "RailFlags", - Annotated[ - list[RailFlagRule], - Field( - min_length=1, - description="Set of boolean attributes applicable to railways. May be specified either as a single flag array of flag values, or as an array of flag rules.", - ), - UniqueItemsConstraint(), - ], -) - -LevelRules = NewType( - "LevelRules", - Annotated[ - list[LevelRule], - Field( - description="Defines the Z-order, i.e. stacking order, of the road segment." - ), - ], -) - -SubclassRules = NewType( - "SubclassRules", - Annotated[ - list[SubclassRule], Field(description="Set of subclasses scoped along segment") - ], -) - -# We should likely restrict the available surface types to the subset of the common OSM surface=* tag values that are useful both for routing and for map tile rendering. -Surfaces = NewType( - "Surfaces", - Annotated[ - list[SurfaceRule], - Field( - min_length=1, - description="Physical surface of the road. May either be specified as a single global value for the segment, or as an array of surface rules.", - ), - UniqueItemsConstraint(), - ], -) - -WidthRules = NewType( - "WidthRules", - Annotated[ - list[WidthRule], - Field( - min_length=1, - description="""Edge-to-edge width of the road modeled by this segment, in meters. - - Examples: (1) If this segment models a carriageway without sidewalk, this value represents the edge-to-edge width of the carriageway, inclusive of any shoulder. (2) If this segment models a sidewalk by itself, this value represents the edge-to-edge width of the sidewalk. (3) If this segment models a combined sidewalk and carriageway, this value represents the edge-to-edge width inclusive of sidewalk.""", - ), - UniqueItemsConstraint(), - ], -) diff --git a/packages/overture-schema-transportation-theme/tests/segment_baseline_schema.json b/packages/overture-schema-transportation-theme/tests/segment_baseline_schema.json index 1a0bfc017..0bf9d1962 100644 --- a/packages/overture-schema-transportation-theme/tests/segment_baseline_schema.json +++ b/packages/overture-schema-transportation-theme/tests/segment_baseline_schema.json @@ -1,13 +1,14 @@ { "$defs": { - "AccessRestrictionRule": { + "AccessRule": { "additionalProperties": false, + "description": "A single scoped rule about who or what can use a segment (or a sub-segment of a segment).\n\nThis rule can be scoped to apply only to linearly-referenced subsegment, a travel mode such as\nmotor vehicle, a heading such as forward or backward, and to various other scopes as well. See\nthe fields for the full list of available scopes.", "properties": { "access_type": { "$ref": "#/$defs/AccessType" }, "between": { - "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing AccessRestrictionRule applies to.", + "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing AccessRule applies to.", "items": { "maximum": 1.0, "minimum": 0.0, @@ -19,16 +20,17 @@ "type": "array" }, "when": { - "$ref": "#/$defs/overture__schema__transportation__models__AccessRestrictionRule__When" + "$ref": "#/$defs/overture__schema__transportation__segment___common__AccessRule__When" } }, "required": [ "access_type" ], - "title": "AccessRestrictionRule", + "title": "AccessRule", "type": "object" }, "AccessType": { + "description": "Whether access is allowed, denied, or allowed as designated.", "enum": [ "allowed", "denied", @@ -124,13 +126,14 @@ ] } ], + "description": "A single scoped rule describing the destinations reachable by following a transition from the\ncurrent segment onto the segment `to_segment_id` via the connector `to_connector_id` in\nthe `final_heading` direction.\n\nThe destination is described as it appears in real-world signage through text `labels` and/or\npictographic `symbols`. Heading-scoped to the approach direction on the current segment.", "properties": { "final_heading": { "$ref": "#/$defs/Heading", - "description": "Direction of travel on the segment identified by 'to_segment_id' that leads to the destination." + "description": "Direction of travel on the segment identified by 'to_segment_id' that leads to\nthe destination." }, "from_connector_id": { - "description": "Identifies the point of physical connection on this segment before which the destination sign or marking is visible.", + "description": "Identifies the point of physical connection on this segment before which the\ndestination sign or marking is visible.", "minLength": 1, "pattern": "^\\S+$", "title": "From Connector Id", @@ -147,7 +150,7 @@ "uniqueItems": true }, "symbols": { - "description": "A collection of symbols or icons present on the sign next to current destination label.", + "description": "A collection of symbols or icons present on the sign next to current destination\nlabel.", "items": { "$ref": "#/$defs/DestinationSignSymbol" }, @@ -156,21 +159,21 @@ "uniqueItems": true }, "to_connector_id": { - "description": "Identifies the point of physical connection on the segment identified by 'to_segment_id' to transition to for reaching the destination(s).", + "description": "Identifies the point of physical connection on the segment identified by\n'to_segment_id' to transition to for reaching the destination(s).", "minLength": 1, "pattern": "^\\S+$", "title": "To Connector Id", "type": "string" }, "to_segment_id": { - "description": "Identifies the segment to transition to reach the destination(s) labeled on the sign or marking.", + "description": "Identifies the segment to transition to reach the destination(s) labeled on the\nsign or marking.", "minLength": 1, "pattern": "^\\S+$", "title": "To Segment Id", "type": "string" }, "when": { - "$ref": "#/$defs/overture__schema__transportation__models__DestinationRule__When" + "$ref": "#/$defs/overture__schema__transportation__segment__road__DestinationRule__When" } }, "required": [ @@ -404,6 +407,7 @@ }, "ProhibitedTransitionRule": { "additionalProperties": false, + "description": "A single scoped rule identifying a transition from the current segment (or a sub-segment of it)\nto a connected segment that is not allowed.\n\nThis rule can be scoped to apply only to linearly-referenced subsegment, a travel mode such as\nmotor vehicle, a heading such as forward or backward, and to various other scopes as well. See\nthe fields for the full list of available scopes.\n\nTerminology note: Prohibited transitions are more commonly referred to as turn restrictions.\nWhile the term turn restriction is simpler and more relatable, it is less precise because\ntransitions between segments don't always require maneuvers that a human would describe as being\na turn. For this reason, the Overture schema has opted for the more general and precise term\nprohibited transitions.", "properties": { "between": { "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing ProhibitedTransitionRule applies to.", @@ -422,9 +426,9 @@ "description": "Direction of travel that is prohibited on the destination segment of the sequence." }, "sequence": { - "description": "Ordered sequence of connector/segment pairs that it is prohibited to follow from this segment.", + "description": "Ordered sequence of connector/segment pairs that it is prohibited to follow\nfrom this segment.", "items": { - "$ref": "#/$defs/SequenceEntry" + "$ref": "#/$defs/ProhibitedTransitionSequenceEntry" }, "minItems": 1, "title": "Sequence", @@ -432,7 +436,7 @@ "uniqueItems": true }, "when": { - "$ref": "#/$defs/overture__schema__transportation__models__ProhibitedTransitionRule__When" + "$ref": "#/$defs/overture__schema__transportation__segment___common__ProhibitedTransitionRule__When" } }, "required": [ @@ -442,6 +446,32 @@ "title": "ProhibitedTransitionRule", "type": "object" }, + "ProhibitedTransitionSequenceEntry": { + "additionalProperties": false, + "description": "A segment/connector pair in a prohibited transition sequence.", + "properties": { + "connector_id": { + "description": "Identifies the point of physical connection between the previous segment in the\nsequence and the segment in this sequence entry.", + "minLength": 1, + "pattern": "^\\S+$", + "title": "Connector Id", + "type": "string" + }, + "segment_id": { + "description": "Identifies the segment that the previous segment in the sequence is physically\nconnected to via the sequence entry's connector.", + "minLength": 1, + "pattern": "^\\S+$", + "title": "Segment Id", + "type": "string" + } + }, + "required": [ + "connector_id", + "segment_id" + ], + "title": "ProhibitedTransitionSequenceEntry", + "type": "object" + }, "PurposeOfUse": { "description": "Reason why a person or entity travelling on the transportation network is using a particular\nlocation.", "enum": [ @@ -518,7 +548,7 @@ }, "RailSegment": { "additionalProperties": false, - "description": "Rail Segment Properties.", + "description": "Rail segment properties.", "properties": { "bbox": { "description": "An optional bounding box for the feature", @@ -589,7 +619,7 @@ "access_restrictions": { "description": "Rules governing access to this road segment", "items": { - "$ref": "#/$defs/AccessRestrictionRule" + "$ref": "#/$defs/AccessRule" }, "minItems": 1, "title": "Access Restrictions", @@ -601,7 +631,7 @@ }, "connectors": { "default": [], - "description": "List of connectors which this segment is physically connected to and their relative location. Each connector is a possible routing decision point, meaning it defines a place along the segment in which there is possibility to transition to other segments which share the same connector.", + "description": "List of connectors which this segment is physically connected to and their\nrelative location. Each connector is a possible routing decision point,\nmeaning it defines a place along the segment in which there is possibility to\ntransition to other segments which share the same connector.", "items": { "$ref": "#/$defs/ConnectorReference" }, @@ -623,7 +653,7 @@ "description": "All known names by which the feature is called" }, "rail_flags": { - "description": "Set of boolean attributes applicable to railways. May be specified either as a single flag array of flag values, or as an array of flag rules.", + "description": "Set of boolean attributes applicable to railways. May be specified either as a\nsingle flag array of flag values, or as an array of flag rules.", "items": { "$ref": "#/$defs/RailFlagRule" }, @@ -632,14 +662,6 @@ "type": "array", "uniqueItems": true }, - "routes": { - "description": "Routes this segment belongs to", - "items": { - "$ref": "#/$defs/RouteReference" - }, - "title": "Routes", - "type": "array" - }, "sources": { "description": "Information about the source data used to assemble the feature.", "items": { @@ -650,14 +672,6 @@ "type": "array", "uniqueItems": true }, - "subclass_rules": { - "description": "Set of subclasses scoped along segment", - "items": { - "$ref": "#/$defs/SubclassRule" - }, - "title": "Subclass Rules", - "type": "array" - }, "subtype": { "const": "rail", "title": "Subtype", @@ -701,7 +715,7 @@ "geometry", "properties" ], - "title": "Rail-Specific Properties", + "title": "segment (rail)", "type": "object" }, "RecognizedStatus": { @@ -788,7 +802,7 @@ }, "RoadSegment": { "additionalProperties": false, - "description": "Road Segment Properties.", + "description": "Road segment properties.", "properties": { "bbox": { "description": "An optional bounding box for the feature", @@ -859,7 +873,7 @@ "access_restrictions": { "description": "Rules governing access to this road segment", "items": { - "$ref": "#/$defs/AccessRestrictionRule" + "$ref": "#/$defs/AccessRule" }, "minItems": 1, "title": "Access Restrictions", @@ -871,7 +885,7 @@ }, "connectors": { "default": [], - "description": "List of connectors which this segment is physically connected to and their relative location. Each connector is a possible routing decision point, meaning it defines a place along the segment in which there is possibility to transition to other segments which share the same connector.", + "description": "List of connectors which this segment is physically connected to and their\nrelative location. Each connector is a possible routing decision point,\nmeaning it defines a place along the segment in which there is possibility to\ntransition to other segments which share the same connector.", "items": { "$ref": "#/$defs/ConnectorReference" }, @@ -881,7 +895,7 @@ "uniqueItems": true }, "destinations": { - "description": "Describes objects that can be reached by following a transportation segment in the same way those objects are described on signposts or ground writing that a traveller following the segment would observe in the real world. This allows navigation systems to refer to signs and observable writing that a traveller actually sees.", + "description": "Describes objects that can be reached by following a transportation segment in the\nsame way those objects are described on signposts or ground writing that a\ntraveller following the segment would observe in the real world. This allows\nnavigation systems to refer to signs and observable writing that a traveller\nactually sees.", "items": { "$ref": "#/$defs/DestinationRule" }, @@ -901,7 +915,7 @@ "description": "All known names by which the feature is called" }, "prohibited_transitions": { - "description": "Rules preventing transitions from this segment to another segment.", + "description": "Rules for when transition to a connected segment is not allowed (commonly known as turn\nrestrictions).", "items": { "$ref": "#/$defs/ProhibitedTransitionRule" }, @@ -909,7 +923,7 @@ "type": "array" }, "road_flags": { - "description": "Set of boolean attributes applicable to roads. May be specified either as a single flag array of flag values, or as an array of flag rules.", + "description": "Set of boolean attributes applicable to roads. May be specified either as a\nsingle flag array of flag values, or as an array of flag rules.", "items": { "$ref": "#/$defs/RoadFlagRule" }, @@ -919,9 +933,9 @@ "uniqueItems": true }, "road_surface": { - "description": "Physical surface of the road. May either be specified as a single global value for the segment, or as an array of surface rules.", + "description": "Rules describing the physical surface of the road.", "items": { - "$ref": "#/$defs/SurfaceRule" + "$ref": "#/$defs/RoadSurfaceRule" }, "minItems": 1, "title": "Road Surface", @@ -957,12 +971,12 @@ "uniqueItems": true }, "subclass": { - "$ref": "#/$defs/Subclass" + "$ref": "#/$defs/RoadSubclass" }, "subclass_rules": { "description": "Set of subclasses scoped along segment", "items": { - "$ref": "#/$defs/SubclassRule" + "$ref": "#/$defs/RoadSubclassRule" }, "title": "Subclass Rules", "type": "array" @@ -990,7 +1004,7 @@ "type": "integer" }, "width_rules": { - "description": "Edge-to-edge width of the road modeled by this segment, in meters.\n\n Examples: (1) If this segment models a carriageway without sidewalk, this value represents the edge-to-edge width of the carriageway, inclusive of any shoulder. (2) If this segment models a sidewalk by itself, this value represents the edge-to-edge width of the sidewalk. (3) If this segment models a combined sidewalk and carriageway, this value represents the edge-to-edge width inclusive of sidewalk.", + "description": "Edge-to-edge width of the feature modeled by this segment, in meters.\n\nExamples: (1) If this segment models a carriageway without sidewalk, this value\nrepresents the edge-to-edge width of the carriageway, inclusive of any shoulder.\n(2) If this segment models a sidewalk by itself, this value represents the\nedge-to-edge width of the sidewalk. (3) If this segment models a combined sidewalk\nand carriageway, this value represents the edge-to-edge width inclusive of sidewalk.", "items": { "$ref": "#/$defs/WidthRule" }, @@ -1020,7 +1034,47 @@ "geometry", "properties" ], - "title": "Road-Specific Properties", + "title": "segment (road)", + "type": "object" + }, + "RoadSubclass": { + "description": "Refines expected usage of the segment, must not overlap.", + "enum": [ + "link", + "sidewalk", + "crosswalk", + "parking_aisle", + "driveway", + "alley", + "cycle_crossing" + ], + "title": "RoadSubclass", + "type": "string" + }, + "RoadSubclassRule": { + "additionalProperties": false, + "description": "Set of subclasses scoped along segment.", + "properties": { + "between": { + "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing RoadSubclassRule applies to.", + "items": { + "maximum": 1.0, + "minimum": 0.0, + "type": "number" + }, + "maxItems": 2, + "minItems": 2, + "title": "Between", + "type": "array" + }, + "value": { + "$ref": "#/$defs/RoadSubclass" + } + }, + "required": [ + "value" + ], + "title": "RoadSubclassRule", "type": "object" }, "RoadSurface": { @@ -1037,6 +1091,32 @@ "title": "RoadSurface", "type": "string" }, + "RoadSurfaceRule": { + "additionalProperties": false, + "description": "Specifies the physical surface of a road segment or a linearly-referenced subsegment of the road\nsegment.", + "properties": { + "between": { + "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing RoadSurfaceRule applies to.", + "items": { + "maximum": 1.0, + "minimum": 0.0, + "type": "number" + }, + "maxItems": 2, + "minItems": 2, + "title": "Between", + "type": "array" + }, + "value": { + "$ref": "#/$defs/RoadSurface" + } + }, + "required": [ + "value" + ], + "title": "RoadSurfaceRule", + "type": "object" + }, "RouteReference": { "additionalProperties": false, "description": "Route reference with linear referencing support.", @@ -1091,32 +1171,6 @@ "title": "RouteReference", "type": "object" }, - "SequenceEntry": { - "additionalProperties": false, - "description": "A segment/connector pair in a prohibited transition sequence.", - "properties": { - "connector_id": { - "description": "Identifies the point of physical connection between the previous segment in the sequence and the segment in this sequence entry.", - "minLength": 1, - "pattern": "^\\S+$", - "title": "Connector Id", - "type": "string" - }, - "segment_id": { - "description": "Identifies the segment that the previous segment in the sequence is physically connected to via the sequence entry's connector.", - "minLength": 1, - "pattern": "^\\S+$", - "title": "Segment Id", - "type": "string" - } - }, - "required": [ - "connector_id", - "segment_id" - ], - "title": "SequenceEntry", - "type": "object" - }, "Side": { "description": "The side, left or right, on which something appears relative to a facing or heading direction\n(*e.g.*, the side of a road relative to the road orientation), or relative to the direction of\ntravel of a person or vehicle.", "enum": [ @@ -1282,7 +1336,7 @@ "$ref": "#/$defs/Speed" }, "when": { - "$ref": "#/$defs/overture__schema__transportation__models__SpeedLimitRule__When" + "$ref": "#/$defs/overture__schema__transportation__segment___common__SpeedLimitRule__When" } }, "title": "SpeedLimitRule", @@ -1297,71 +1351,6 @@ "title": "SpeedUnit", "type": "string" }, - "Subclass": { - "description": "Refines expected usage of the segment, must not overlap.", - "enum": [ - "link", - "sidewalk", - "crosswalk", - "parking_aisle", - "driveway", - "alley", - "cycle_crossing" - ], - "title": "Subclass", - "type": "string" - }, - "SubclassRule": { - "additionalProperties": false, - "description": "Set of subclasses scoped along segment.", - "properties": { - "between": { - "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing SubclassRule applies to.", - "items": { - "maximum": 1.0, - "minimum": 0.0, - "type": "number" - }, - "maxItems": 2, - "minItems": 2, - "title": "Between", - "type": "array" - }, - "value": { - "$ref": "#/$defs/Subclass" - } - }, - "required": [ - "value" - ], - "title": "SubclassRule", - "type": "object" - }, - "SurfaceRule": { - "additionalProperties": false, - "properties": { - "between": { - "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing SurfaceRule applies to.", - "items": { - "maximum": 1.0, - "minimum": 0.0, - "type": "number" - }, - "maxItems": 2, - "minItems": 2, - "title": "Between", - "type": "array" - }, - "value": { - "$ref": "#/$defs/RoadSurface" - } - }, - "required": [ - "value" - ], - "title": "SurfaceRule", - "type": "object" - }, "TravelMode": { "description": "Enumerates possible travel modes.\n\nSome modes represent groups of modes.", "enum": [ @@ -1551,7 +1540,7 @@ }, "WaterSegment": { "additionalProperties": false, - "description": "Water Segment Properties.", + "description": "Water segment properties.", "properties": { "bbox": { "description": "An optional bounding box for the feature", @@ -1622,7 +1611,7 @@ "access_restrictions": { "description": "Rules governing access to this road segment", "items": { - "$ref": "#/$defs/AccessRestrictionRule" + "$ref": "#/$defs/AccessRule" }, "minItems": 1, "title": "Access Restrictions", @@ -1631,7 +1620,7 @@ }, "connectors": { "default": [], - "description": "List of connectors which this segment is physically connected to and their relative location. Each connector is a possible routing decision point, meaning it defines a place along the segment in which there is possibility to transition to other segments which share the same connector.", + "description": "List of connectors which this segment is physically connected to and their\nrelative location. Each connector is a possible routing decision point,\nmeaning it defines a place along the segment in which there is possibility to\ntransition to other segments which share the same connector.", "items": { "$ref": "#/$defs/ConnectorReference" }, @@ -1652,14 +1641,6 @@ "$ref": "#/$defs/Names", "description": "All known names by which the feature is called" }, - "routes": { - "description": "Routes this segment belongs to", - "items": { - "$ref": "#/$defs/RouteReference" - }, - "title": "Routes", - "type": "array" - }, "sources": { "description": "Information about the source data used to assemble the feature.", "items": { @@ -1670,14 +1651,6 @@ "type": "array", "uniqueItems": true }, - "subclass_rules": { - "description": "Set of subclasses scoped along segment", - "items": { - "$ref": "#/$defs/SubclassRule" - }, - "title": "Subclass Rules", - "type": "array" - }, "subtype": { "const": "water", "title": "Subtype", @@ -1720,7 +1693,7 @@ "geometry", "properties" ], - "title": "Water-Specific Properties", + "title": "segment (water)", "type": "object" }, "WeightUnit": { @@ -1739,6 +1712,7 @@ }, "WidthRule": { "additionalProperties": false, + "description": "Specifies the width of a segment or a linearly-referenced subsegment of a segment.", "properties": { "between": { "description": "The linearly-referenced sub-segment of the geometry, specified as a range (pair) of percentage displacements from the start of the geometry, that the containing WidthRule applies to.", @@ -1753,6 +1727,7 @@ "type": "array" }, "value": { + "description": "Edge-to-edge width of the feature modeled by this segment, in meters.", "exclusiveMinimum": 0, "title": "Value", "type": "number" @@ -1764,7 +1739,7 @@ "title": "WidthRule", "type": "object" }, - "overture__schema__transportation__models__AccessRestrictionRule__When": { + "overture__schema__transportation__segment___common__AccessRule__When": { "additionalProperties": false, "anyOf": [ { @@ -1840,19 +1815,19 @@ ] } ], - "description": "Scopes for AccessRestrictionRule: Scope.HEADING, Scope.TEMPORAL, Scope.TRAVEL MODE, Scope.PURPOSE OF USE, Scope.RECOGNIZED STATUS and Scope.VEHICLE", + "description": "Scopes for AccessRule: Scope.HEADING, Scope.TEMPORAL, Scope.TRAVEL MODE, Scope.PURPOSE OF USE, Scope.RECOGNIZED STATUS and Scope.VEHICLE", "properties": { "during": { - "description": "The recurring time span, in the OpenStreetMap opening hours format, that the containing AccessRestrictionRule applies to. For the OSM opening hours specification, see https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification.", + "description": "The recurring time span, in the OpenStreetMap opening hours format, that the containing AccessRule applies to. For the OSM opening hours specification, see https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification.", "title": "During", "type": "string" }, "heading": { "$ref": "#/$defs/Heading", - "description": "The heading, either forward or backward, that the containing AccessRestrictionRule applies to." + "description": "The heading, either forward or backward, that the containing AccessRule applies to." }, "mode": { - "description": "A list of one or more travel modes, such as car, truck, or foot, that the containing AccessRestrictionRule applies to.", + "description": "A list of one or more travel modes, such as car, truck, or foot, that the containing AccessRule applies to.", "items": { "$ref": "#/$defs/TravelMode" }, @@ -1862,7 +1837,7 @@ "uniqueItems": true }, "recognized": { - "description": "A list of one or more recognized status values, such as employee or student, that the containing AccessRestrictionRule applies to.", + "description": "A list of one or more recognized status values, such as employee or student, that the containing AccessRule applies to.", "items": { "$ref": "#/$defs/RecognizedStatus" }, @@ -1872,7 +1847,7 @@ "uniqueItems": true }, "using": { - "description": "A list of one or more usage purposes, such as delivery or arrival at final destination, that the containing AccessRestrictionRule applies to.", + "description": "A list of one or more usage purposes, such as delivery or arrival at final destination, that the containing AccessRule applies to.", "items": { "$ref": "#/$defs/PurposeOfUse" }, @@ -1882,7 +1857,7 @@ "uniqueItems": true }, "vehicle": { - "description": "A list of one or more vehicle parameters that limit the vehicles the containing AccessRestrictionRule applies to.", + "description": "A list of one or more vehicle parameters that limit the vehicles the containing AccessRule applies to.", "items": { "description": "Selects vehicles that a scope applies to based on criteria such as height, weight, or axle count.", "discriminator": { @@ -1919,25 +1894,10 @@ "uniqueItems": true } }, - "title": "AccessRestrictionRule.When", + "title": "AccessRule.When", "type": "object" }, - "overture__schema__transportation__models__DestinationRule__When": { - "additionalProperties": false, - "description": "Scope for DestinationRule: ", - "properties": { - "heading": { - "$ref": "#/$defs/Heading", - "description": "The heading, either forward or backward, that the containing DestinationRule applies to." - } - }, - "required": [ - "heading" - ], - "title": "DestinationRule.When", - "type": "object" - }, - "overture__schema__transportation__models__ProhibitedTransitionRule__When": { + "overture__schema__transportation__segment___common__ProhibitedTransitionRule__When": { "additionalProperties": false, "anyOf": [ { @@ -2095,7 +2055,7 @@ "title": "ProhibitedTransitionRule.When", "type": "object" }, - "overture__schema__transportation__models__SpeedLimitRule__When": { + "overture__schema__transportation__segment___common__SpeedLimitRule__When": { "additionalProperties": false, "anyOf": [ { @@ -2252,6 +2212,21 @@ }, "title": "SpeedLimitRule.When", "type": "object" + }, + "overture__schema__transportation__segment__road__DestinationRule__When": { + "additionalProperties": false, + "description": "Scope for DestinationRule: ", + "properties": { + "heading": { + "$ref": "#/$defs/Heading", + "description": "The heading, either forward or backward, that the containing DestinationRule applies to." + } + }, + "required": [ + "heading" + ], + "title": "DestinationRule.When", + "type": "object" } }, "oneOf": [ diff --git a/schema/transportation/segment.yaml b/schema/transportation/segment.yaml index b12a560d0..4a959a1a6 100644 --- a/schema/transportation/segment.yaml +++ b/schema/transportation/segment.yaml @@ -22,9 +22,7 @@ properties: - title: "Common Segment Properties" properties: # Classes are per-subtype following the addition of rail classes - subclass_rules: { "$ref": "#/$defs/propertyContainers/subclassRulesContainer" } access_restrictions: { "$ref": "#/$defs/propertyContainers/accessContainer" } - level: { "$ref": "../defs.yaml#/$defs/propertyDefinitions/level" } level_rules: { "$ref": "#/$defs/propertyContainers/levelRulesContainer" } - "$ref": ../defs.yaml#/$defs/propertyContainers/overtureFeaturePropertiesContainer - "$ref": ../defs.yaml#/$defs/propertyContainers/namesContainer @@ -41,6 +39,8 @@ properties: speed_limits: { "$ref": "#/$defs/propertyContainers/speedLimitsContainer" } width_rules: { "$ref": "#/$defs/propertyContainers/widthRulesContainer" } subclass: { "$ref": "#/$defs/propertyDefinitions/subclass" } + subclass_rules: { "$ref": "#/$defs/propertyContainers/subclassRulesContainer" } + routes: { "$ref": "#/$defs/propertyDefinitions/routes" } - title: "Rail-Specific Properties" required: [class] properties: @@ -77,7 +77,6 @@ properties: uniqueItems: true minItems: 2 default: [] - routes: { "$ref": "#/$defs/propertyDefinitions/routes" } "$defs": propertyDefinitions: destinationLabelType: