Skip to content

Project Card Data Model

Warning

These models are not the canonical schema and may or may not be completely accurate and up to date – although we try our best. The canonical schema is maintained in json-schema format and is documented on the schemas page – but this is much nicer to read so we recommend you stay here.

ProjectCard Data Model.

ProjectCards are a set of information that describe an infrastructure project or change to a network. They are used to document and manage changes to a network such as adding a new bus route, changing a roadway property, changing the number of lanes on a roadway, or increasing transit service frequency.

ProjectCards are composed of one or more changes. Each change is a specific type of change that happens to a specific portion, or selection of the network, such as a roadway property change, transit property change, or transit route addition. Each change type has its own set of parameters that describe the change in detail.

When more than one change is needed to describe a project, the changes field can be used to specify a list of changes. This is useful when a project requires multiple changes to be made to the network. For example, a project to add a new bus route may require adding new bus lanes to the roadway network. Or adding a new, mid-block bus-route may require adding a new bus stop as a node in the roadway network and splitting the link.

While the user can choose what changes should be grouped together into a single, “project”, they should be careful to ensure that the changes are logically related and would likely be implemented together.

Phased Projects:

In cases where a project maybe implemented in multiple phases, it is recommended to create separate project cards for each phase. This will help to ensure that the project can be in a network similar to how it would be implemented in the real world.

Dependencies:

If a project requires another project to be implemented first, the pre-requisites field of dependencies can be used. Similarly, if a project would be impossible to implement if another project is implemented, the conflicts field of dependencies can be used. corequisites can be used to specify projects that must be implemented at the same time - noting that if Project A specifies Project B as a corequisite, then Project B need not specify Project A as a corequisite (if they were dependent upon each other, then they should likely be combined into a single project).

Tags:

Tags can be used to categorize projects and make it easier to search for projects that are related to a specific topic or type of project. For example, a collection of projects that are considered to be in your committed future baseline because they have committed funding could be tagged as committed.

Formats + Serialization:

ProjectCards can be represented within memory as any structured data format such as JSON, Python Dictionaries, or a Struct. They can also be serialized to any nested file format that is compatible with JSON-schema such as YAML, TOML, or JSON. For the purposes of this documentation, we will use YAML as the serialization format for the examples becuase it is less verbose than JSON and python dictonaries and easier to read.

List of tools that support json-schema: https://json-schema.org/tools

Attributes:

Name Type Description
project str

The name of the project. This name must be unique within a set of projects being managed or applied to a network.

notes Optional[str]

Additional freeform notes about the project.

tags Optional[list[str]]

Tags for the project to associate the project card with a specific project or type of project.

dependencies Optional[Dependencies]

Dependencies for the project card: conflicts, prerequisites, and corequisites…each of which is a list of project names.

changes Optional[list[ChangeTypes]]

List of one or more changes. Must either have changes or a single change type (e.g. roadway_property_change, transit_property_change, etc.). If changes is provided, cannot specify another change outside of changes.

roadway_property_change Optional[RoadwayPropertyChanges]

A single roadway property change. Cannot be used with changes or another change type.

roadway_deletion Optional[RoadwayDeletion]

A single roadway deletion change. Cannot be used with changes or another change type.

roadway_addition Optional[RoadwayAddition]

A single roadway addition change. Cannot be used with changes or another change type.

transit_property_change Optional[TransitPropertyChange]

A single Transit property change. Cannot be used with changes or another change type.

transit_routing_change Optional[TransitRoutingChange]

A single Transit routing change. Cannot be used with changes or another change type.

transit_service_deletion Optional[TransitServiceDeletion]

A single Transit service deletion change. Cannot be used with changes or another change type.

transit_route_addition Optional[TransitRouteAddition]

A single transit route addition change. Cannot be used with changes or another change type.

pycode Optional[str]

A single pycode type change which uses python code for the project which refers to self as a transit or roadway network. Cannot be used with changes or another change type.

self_obj_type Optional[Literal['RoadwayNetwork', 'TransitNetwork']]

Type of object being changed in the pycode python code. Must be either TransitNetwork or RoadwayNetwork. Cannot be used with changes or another change type.

Example Project Card

project: "Add new bus route"
notes: "This project adds a new bus route to the network."
tags: ["bus", "transit"]
dependencies:
    conflicts: ["Remove bus route"]
    prerequisites: ["Add bus stop"]
    corequisites: ["Add bus route"]
transit_route_addition: ...
Source code in projectcard/models/project.py
class ProjectModel(BaseModel):
    """ProjectCard Data Model.

    ProjectCards are a set of information that describe an infrastructure project or change to a
    network. They are used to document and manage changes to a network such as adding a new bus
    route, changing a roadway property, changing the number of lanes on a roadway, or increasing
    transit service frequency.

    ProjectCards are composed of one or more **changes**. Each change is a specific type of change
    that happens to a specific portion, or **selection** of the network, such as a roadway
    property change, transit property change, or transit route addition. Each change type has
    its own set of parameters that describe the change in detail.

    #### Grouping Related Changes:
    When more than one change is needed to describe a project, the `changes` field can be used to
    specify a list of changes. This is useful when a project requires multiple changes to be made
    to the network. For example, a project to add a new bus route may require adding new bus lanes
    to the roadway network.  Or adding a new, mid-block bus-route may require adding a new bus stop
    as a node in the roadway network and splitting the link.

    While the user can choose what changes should be grouped together into a single,
    "project", they should be careful to ensure that the changes are logically related and would
    likely be implemented together.

    #### Phased Projects:
    In cases where a project maybe implemented in multiple phases, it is recommended to create
    separate project cards for each phase. This will help to ensure that the project can be
    in a network similar to how it would be implemented in the real world.

    #### Dependencies:
    If a project requires another project to be implemented first, the `pre-requisites` field
    of `dependencies` can be used.  Similarly, if a project would be impossible to implement
    if another project is implemented, the `conflicts` field of `dependencies` can be used.
    `corequisites` can be used to specify projects that must be implemented at the same time -
    noting that if Project A specifies Project B as a corequisite, then Project B need not specify
    Project A as a corequisite (if they were dependent upon each other, then they should likely
    be combined into a single project).

    #### Tags:
    Tags can be used to categorize projects and make it easier to search for projects that are
    related to a specific topic or type of project. For example, a collection of projects that are
    considered to be in your committed future baseline because they have committed funding could
    be tagged as `committed`.

    #### Formats + Serialization:
    ProjectCards can be represented within memory as any structured data format such as JSON,
    Python Dictionaries, or a Struct. They can also be serialized to any nested file format that
    is compatible with [JSON-schema ](https://json-schema.org) such as YAML, TOML, or JSON.
    For the purposes of this documentation, we will use YAML as the serialization format for
    the examples becuase it is less verbose than JSON and python dictonaries and easier to read.

    List of tools that support json-schema: <https://json-schema.org/tools>

    Attributes:
        project (str): The name of the project. This name must be unique within a set of projects
            being managed or applied to a network.
        notes (Optional[str]): Additional freeform notes about the project.
        tags (Optional[list[str]]): Tags for the project to associate the project card with a
            specific project or type of project.
        dependencies (Optional[Dependencies]): Dependencies for the project card: conflicts,
            prerequisites, and corequisites...each of which is a list of project names.
        changes (Optional[list[ChangeTypes]]): List of one or more changes. Must either have
            `changes` or a single change type (e.g. `roadway_property_change`,
            `transit_property_change`, etc.). If `changes` is provided, cannot specify another
            change outside of changes.
        roadway_property_change (Optional[RoadwayPropertyChanges]): A single roadway property
            change. Cannot be used with `changes` or another change type.
        roadway_deletion (Optional[RoadwayDeletion]): A single roadway deletion change. Cannot
            be used with `changes` or another change type.
        roadway_addition (Optional[RoadwayAddition]): A single roadway addition change. Cannot
            be used with `changes` or another change type.
        transit_property_change (Optional[TransitPropertyChange]): A single Transit property
            change. Cannot be used with `changes` or another change type.
        transit_routing_change (Optional[TransitRoutingChange]): A single Transit routing change.
            Cannot be used with `changes` or another change type.
        transit_service_deletion (Optional[TransitServiceDeletion]): A single Transit service
            deletion change. Cannot be used with `changes` or another change type.
        transit_route_addition (Optional[TransitRouteAddition]): A single transit route addition
            change. Cannot be used with `changes` or another change type.
        pycode (Optional[str]): A single pycode type change which uses python code for the project
            which refers to self as a transit or roadway network. Cannot be used with `changes` or
            another change type.
        self_obj_type (Optional[Literal["RoadwayNetwork", "TransitNetwork"]]): Type of object
            being changed in the pycode python code. Must be either TransitNetwork or
            RoadwayNetwork. Cannot be used with `changes` or another change type.

    !!! Example "Example Project Card"
        ```yaml
        project: "Add new bus route"
        notes: "This project adds a new bus route to the network."
        tags: ["bus", "transit"]
        dependencies:
            conflicts: ["Remove bus route"]
            prerequisites: ["Add bus stop"]
            corequisites: ["Add bus route"]
        transit_route_addition: ...
        ```
    """

    require_one_of: ClassVar = [
        "roadway_property_change",
        "roadway_deletion",
        "roadway_addition",
        "transit_property_change",
        "transit_routing_change",
        "transit_service_deletion",
        "transit_route_addition",
        ["pycode", "self_obj_type"],
        "changes",
    ]

    project: str
    notes: Optional[str]
    tags: Optional[list[str]]
    dependencies: Optional[Dependencies]
    changes: Optional[list[ChangeTypes]]
    roadway_property_change: Optional[RoadwayPropertyChanges]
    roadway_deletion: Optional[RoadwayDeletion]
    roadway_addition: Optional[RoadwayAddition]
    transit_property_change: Optional[TransitPropertyChange]
    transit_routing_change: Optional[TransitRoutingChange]
    transit_service_deletion: Optional[TransitServiceDeletion]
    transit_route_addition: Optional[TransitRouteAddition]
    pycode: Optional[str]
    self_obj_type: Optional[Literal["RoadwayNetwork", "TransitNetwork"]]

Change Types

Change type models for project card.

ChangeTypes

Union of all change types.

Source code in projectcard/models/changes.py
class ChangeTypes(BaseModel):
    """Union of all change types."""

    model_config = ConfigDict(extra="forbid")

    require_one_of: ClassVar = [
        [
            "roadway_property_change",
            "roadway_deletion",
            "roadway_addition",
            "transit_property_change",
            "transit_routing_change",
            "transit_service_deletion",
            "transit_route_addition",
            "pycode",
        ],
    ]

    roadway_property_change: Optional[RoadwayPropertyChanges]
    roadway_deletion: Optional[RoadwayDeletion]
    roadway_addition: Optional[RoadwayAddition]
    transit_property_change: Optional[TransitPropertyChange]
    transit_routing_change: Optional[TransitRoutingChange]
    transit_service_deletion: Optional[TransitServiceDeletion]
    transit_route_addition: Optional[TransitRouteAddition]
    pycode: Optional[str]

RoadwayAddition

Requirements for describing roadway addition project card.

Attributes:

Name Type Description
links Optional[list[RoadLink]]

Roadway links to add. Must have at least one link.

nodes Optional[list[RoadNode]]

Roadway nodes to add. Must have at least one node.

Example Roadway Addition

roadway_addition:
    links:
        - A: 269066
        B: 268932
        name: new neighborhood st
        roadway: residential
        lanes: 1
        model_link_id: 404982
        - A: 268932
        B: 269066
        name: new neighborhood st
        roadway: residential
        lanes: 1
        model_link_id: 407042
    nodes:
        - model_node_id: 268932
            latitude: 37.7749
            longitude: -122.4194
        - model_node_id: 269066
            latitude: 37.7749
            longitude: -122.4194
Source code in projectcard/models/changes.py
class RoadwayAddition(BaseModel):
    """Requirements for describing roadway addition project card.

    Attributes:
        links (Optional[list[RoadLink]]): Roadway links to add. Must have at least one link.
        nodes (Optional[list[RoadNode]]): Roadway nodes to add. Must have at least one node.

    !!! Example "Example Roadway Addition"
        ```yaml
        roadway_addition:
            links:
                - A: 269066
                B: 268932
                name: new neighborhood st
                roadway: residential
                lanes: 1
                model_link_id: 404982
                - A: 268932
                B: 269066
                name: new neighborhood st
                roadway: residential
                lanes: 1
                model_link_id: 407042
            nodes:
                - model_node_id: 268932
                    latitude: 37.7749
                    longitude: -122.4194
                - model_node_id: 269066
                    latitude: 37.7749
                    longitude: -122.4194
        ```
    """

    require_any_of: ClassVar = [["links", "nodes"]]
    model_config = ConfigDict(extra="forbid")

    links: Annotated[Optional[list[RoadLink]], Field(min_length=1)]
    nodes: Annotated[Optional[list[RoadNode]], Field(min_length=1)]

RoadwayDeletion

Requirements for describing roadway deletion project card (e.g. to delete).

Attributes:

Name Type Description
links Optional[SelectRoadLinks]

Roadway links to delete.

nodes Optional[SelectRoadNodes]

Roadway nodes to delete.

clean_shapes bool

If True, will clean unused roadway shapes associated with the deleted links if they are not otherwise being used. Defaults to False.

clean_nodes bool

If True, will clean unused roadway nodes associated with the deleted links if they are not otherwise being used. Defaults to False.

Example Roadway Deletion

roadway_deletion:
    links:
        model_link_id:
        - 281
        - 477533
    nodes:
        model_node_id:
        - 314159
    clean_shapes: true
    clean_nodes: false
Source code in projectcard/models/changes.py
class RoadwayDeletion(BaseModel):
    """Requirements for describing roadway deletion project card (e.g. to delete).

    Attributes:
        links (Optional[SelectRoadLinks]): Roadway links to delete.
        nodes (Optional[SelectRoadNodes]): Roadway nodes to delete.
        clean_shapes (bool): If True, will clean unused roadway shapes associated with the deleted links
            if they are not otherwise being used. Defaults to False.
        clean_nodes (bool): If True, will clean unused roadway nodes associated with the deleted links
            if they are not otherwise being used. Defaults to False.

    !!! Example "Example Roadway Deletion"
        ```yaml
        roadway_deletion:
            links:
                model_link_id:
                - 281
                - 477533
            nodes:
                model_node_id:
                - 314159
            clean_shapes: true
            clean_nodes: false
        ```
    """

    require_any_of: ClassVar = [["links", "nodes"]]
    model_config = ConfigDict(extra="forbid")

    links: Optional[SelectRoadLinks] = None
    nodes: Optional[SelectRoadNodes] = None
    clean_shapes: bool = False
    clean_nodes: bool = False

RoadwayPropertyChanges

Value for setting property changes for a time of day and category.

Attributes:

Name Type Description
facility SelectFacility

Selection of roadway links to change properties for.

property_changes dict[str, RoadwayPropertyChange]

Property changes to apply to the selection. Must have at least one property change.

Example Roadway Property Change

roadway_property_change:
    facility:
        links:
            modes: ['drive','bike']
            osm_link_id:
                - '1234'
                - '2345'
        from:
            osm_node_id: '4321'
        to:
            osm_node_id: '4322'
    property_changes:
        lanes:
            existing: 3
            change: -1
            existing_value_conflict: error
        bicycle_facility:
            existing: 1
            set: 3
            existing_value_conflict: skip
Source code in projectcard/models/changes.py
class RoadwayPropertyChanges(BaseModel):
    """Value for setting property changes for a time of day and category.

    Attributes:
        facility (SelectFacility): Selection of roadway links to change properties for.
        property_changes (dict[str, RoadwayPropertyChange]): Property changes to apply to the selection. Must have at least one property change.

    !!! Example "Example Roadway Property Change"
        ```yaml
        roadway_property_change:
            facility:
                links:
                    modes: ['drive','bike']
                    osm_link_id:
                        - '1234'
                        - '2345'
                from:
                    osm_node_id: '4321'
                to:
                    osm_node_id: '4322'
            property_changes:
                lanes:
                    existing: 3
                    change: -1
                    existing_value_conflict: error
                bicycle_facility:
                    existing: 1
                    set: 3
                    existing_value_conflict: skip
        ```
    """

    model_config = ConfigDict(extra="forbid")

    facility: SelectFacility
    property_changes: Annotated[dict[str, RoadwayPropertyChange], Field(min_length=1)]

TransitPropertyChange

Value for setting property change for a time of day and category.

Attributes:

Name Type Description
service SelectTransitTrips

Selection of transit trips to change properties for.

property_changes dict[str, TransitPropertyChange]

List of property changes to apply.

Example Transit Property Change

transit_property_change:
    service:
        trip_properties:
        trip_id:
            - 14940701-JUN19-MVS-BUS-Weekday-01
        timespans:
        - ['06:00:00', '09:00:00']
    property_changes:
        headway_secs:
            set: 900
Source code in projectcard/models/changes.py
class TransitPropertyChange(BaseModel):
    """Value for setting property change for a time of day and category.

    Attributes:
        service (SelectTransitTrips): Selection of transit trips to change properties for.
        property_changes (dict[str, TransitPropertyChange]): List of property changes to apply.

    !!! Example "Example Transit Property Change"
        ```yaml
        transit_property_change:
            service:
                trip_properties:
                trip_id:
                    - 14940701-JUN19-MVS-BUS-Weekday-01
                timespans:
                - ['06:00:00', '09:00:00']
            property_changes:
                headway_secs:
                    set: 900
        ```
    """

    model_config = ConfigDict(extra="forbid")

    service: SelectTransitTrips
    property_changes: Annotated[
        dict[str, TransitPropertyChange_PropertyChanges], Field(min_length=1)
    ]

TransitRouteAddition

Requirements for describing transit route addition project card.

Attributes:

Name Type Description
routes list[TransitRoute]

List of transit routes to be added. Must have at least one route.

Example Transit Route Addition

transit_route_addition:
    routes:
        - route_id: abc
        route_long_name: green_line
        route_short_name: green
        route_type: 3
        agency_id: The Bus
        trips:
            - direction_id: 0
            headway_secs:
                - ('6:00','12:00'): 600
                - ('12:00','13:00'): 900
            routing:
                - 1:
                    stop: true #when stop is set to True, defaults to allow both boarding and alighting
                - 2
                - 3
                - 4:
                    stop: true # default to False, specify only when stopping
                    alight: false  # default to True, specify only when not default
                - 5
                - 6:
                    stop: true
Source code in projectcard/models/changes.py
class TransitRouteAddition(BaseModel):
    """Requirements for describing transit route addition project card.

    Attributes:
        routes (list[TransitRoute]): List of transit routes to be added. Must have at least one route.

    !!! Example "Example Transit Route Addition"
        ```yaml
        transit_route_addition:
            routes:
                - route_id: abc
                route_long_name: green_line
                route_short_name: green
                route_type: 3
                agency_id: The Bus
                trips:
                    - direction_id: 0
                    headway_secs:
                        - ('6:00','12:00'): 600
                        - ('12:00','13:00'): 900
                    routing:
                        - 1:
                            stop: true #when stop is set to True, defaults to allow both boarding and alighting
                        - 2
                        - 3
                        - 4:
                            stop: true # default to False, specify only when stopping
                            alight: false  # default to True, specify only when not default
                        - 5
                        - 6:
                            stop: true

        ```
    """

    model_config = ConfigDict(extra="forbid")

    routes: Annotated[list[TransitRoute], Field(min_length=1)]

TransitRoutingChange

Value for setting routing change for transit.

Attributes:

Name Type Description
service SelectTransitTrips

Selection of transit trips to change routing for.

transit_routing_change TransitRoutingChange

Existing and changed routing as denoted as a list of nodes with nodes where the route doesn’t stop noted as negative integers.

Example Transit Routing Change

transit_routing_change:
    service:
        trip_properties:
        route_id:
            - 294-111
        direction_id: 1
    routing:
        existing:
        - 1
        - 2
        set:
        - 1
        - -11
        - -12
        - 2
Source code in projectcard/models/changes.py
class TransitRoutingChange(BaseModel):
    """Value for setting routing change for transit.

    Attributes:
        service (SelectTransitTrips): Selection of transit trips to change routing for.
        transit_routing_change (TransitRoutingChange): Existing and changed routing as denoted as a list of nodes with
            nodes where the route doesn't stop noted as negative integers.

    !!! Example "Example Transit Routing Change"
        ```yaml
        transit_routing_change:
            service:
                trip_properties:
                route_id:
                    - 294-111
                direction_id: 1
            routing:
                existing:
                - 1
                - 2
                set:
                - 1
                - -11
                - -12
                - 2
        ```
    """

    model_config = ConfigDict(extra="forbid")

    service: SelectTransitTrips
    routing: TransitRoutingChange_Routing

TransitServiceDeletion

Requirements for describing transit service deletion project card (e.g. to delete).

Attributes:

Name Type Description
service SelectTransitTrips

Selection of transit trips to delete.

clean_shapes Optional[bool]

If True, will clean unused transit shapes associated with the deleted trips if they are not otherwise being used. Defaults to False.

clean_routes Optional[bool]

If True, will clean unused routes associated with the deleted trips if they are not otherwise being used. Defaults to False.

Example Transit Service Deletion

transit_service_deletion:
    service:
        trip_properties:
        route_short_name: "green"
        timespans:
        - ['06:00:00', '09:00:00']
    clean_shapes: false
    clean_routes: true
Source code in projectcard/models/changes.py
class TransitServiceDeletion(BaseModel):
    """Requirements for describing transit service deletion project card (e.g. to delete).

    Attributes:
        service (SelectTransitTrips): Selection of transit trips to delete.
        clean_shapes (Optional[bool]): If True, will clean unused transit shapes associated with the deleted trips
            if they are not otherwise being used. Defaults to False.
        clean_routes (Optional[bool]): If True, will clean unused routes associated with the deleted trips if they
            are not otherwise being used. Defaults to False.

    !!! Example "Example Transit Service Deletion"
        ```yaml
        transit_service_deletion:
            service:
                trip_properties:
                route_short_name: "green"
                timespans:
                - ['06:00:00', '09:00:00']
            clean_shapes: false
            clean_routes: true
        ```
    """

    model_config = ConfigDict(extra="forbid")

    service: SelectTransitTrips
    clean_shapes: Optional[bool] = False
    clean_routes: Optional[bool] = False

Selections

Data models for selecting nodes, links, and trips in a project card.

SelectFacility

Roadway Facility Selection.

Each selection must have at either: links, nodes, or links and from and to.

Specifying links, from, and to will attempt to select a continuous path between the two nodes which as much as possible follows the initial link selection that is provided (e.g. name, osm_link_id, model_link_id, ref) using the specified modes. Secondary selection parameters (e.g. lanes, price) will be used to filter the continuous path - reulting in a final selection of links that may or may not connect the two nodes.

Attributes:

Name Type Description
links Optional[SelectRoadLinks]

Selection of roadway links.

nodes Optional[SelectRoadNodes]

Selection of roadway nodes.

from Optional[SelectRoadNode]

Selection of the origin node.

to Optional[SelectRoadNode]

Selection of the destination node.

Example: Select all links between osm nodes 1 and 2 along main.

facility:
    links:
        name: ["main"]
    from:
        osm_node_id: "1"
    to:
        osm_node_id: "2"

Example: Select links between model nodes 4 and 5 along I-95 that are 3 lanes.

facility:
    links:
        ref: ["I-95"]
        lanes: [3]
    from:
        model_node_id: 4
    to:
        model_node_id: 5

Example: Select all links on SR320 which have 1 or 2 managed lanes.

facility:
    links:
        ref: ["SR320"]
        ML_lanes: [1, 2]
Source code in projectcard/models/selections.py
class SelectFacility(RecordModel):
    """Roadway Facility Selection.

    Each selection must have at either: `links`, `nodes`, or `links` and `from` and `to`.

    Specifying `links`, `from`, and `to` will attempt to select a continuous path between the
        two nodes which as much as possible follows the initial link selection that is provided
        (e.g. `name`, `osm_link_id`, `model_link_id`, `ref`) using the specified `modes`.
        Secondary selection parameters (e.g. `lanes`, `price`) will be used to filter the
        continuous path - reulting in a final selection of links that may or may not connect
        the two nodes.

    Attributes:
        links (Optional[SelectRoadLinks]): Selection of roadway links.
        nodes (Optional[SelectRoadNodes]): Selection of roadway nodes.
        from (Optional[SelectRoadNode]): Selection of the origin node.
        to (Optional[SelectRoadNode]): Selection of the destination node.

    !!! Example "Example: Select all links between osm nodes 1 and 2 along `main`."
        ```yaml
        facility:
            links:
                name: ["main"]
            from:
                osm_node_id: "1"
            to:
                osm_node_id: "2"
        ```

    !!! Example "Example: Select links between model nodes 4 and 5 along I-95 that are 3 lanes."
        ```yaml
        facility:
            links:
                ref: ["I-95"]
                lanes: [3]
            from:
                model_node_id: 4
            to:
                model_node_id: 5
        ```

    !!! Example "Example: Select all links on SR320 which have 1 or 2 managed lanes."
        ```yaml
        facility:
            links:
                ref: ["SR320"]
                ML_lanes: [1, 2]
        ```
    """

    require_one_of: ClassVar[OneOf] = [
        ["links", "nodes", ["links", "from", "to"]],
    ]
    model_config = ConfigDict(extra="forbid")

    links: Optional[SelectRoadLinks] = None
    nodes: Optional[SelectRoadNodes] = None
    from_: Annotated[Optional[SelectRoadNode], Field(None, alias="from")]
    to: Optional[SelectRoadNode] = None

Requirements for describing links in the facility section of a project card.

Must have one of all, name, osm_link_id, or model_link_id. Additional fields to select on may be provided and will be treated as an AND condition after the primary selection from all, name, osm_link_id, or model_link_id.

Attributes:

Name Type Description
all bool

If True, select all links.

name Optional[list[str]]

List of names to select. If multiple provided will be treated as an OR condition.

ref Optional[list[str]]

Open Street Map ref which usually refers to a route or exit number. See: https://wiki.openstreetmap.org/wiki/Key:ref. If multiple provided will be treated as an OR condition.

osm_link_id Optional[list[str]]

List of OSM link IDs to select. If multiple provided will be treated as an OR condition.

model_link_id Optional[list[int]]

List of model link IDs to select. If multiple provided will be treated as an OR condition.

modes Optional[Modes]

List of modes to select. If multiple provided will be treated as an OR condition.

ignore_missing bool

If True, ignore missing links. Otherwise, raise an error if they are not found. Defaults to True.

Example: 2 and 3 lane drivable links named ‘Main St’ or ‘Broadway’.

links:
    name: ["Main St", "Broadway"]
    all: true
    modes: ["drive"]
    lanes: [2, 3]

Example: Links with model_link_id 12345 or 67890.

links:
    model_link_id: [12345, 67890]

Example: Links where biking is allowed but driving is not.

links:
    all: true
    bike_allowed: true
    drive_allowed: false
Source code in projectcard/models/selections.py
class SelectRoadLinks(RecordModel):
    """Requirements for describing links in the `facility` section of a project card.

    Must have one of `all`, `name`, `osm_link_id`, or `model_link_id`.
    Additional fields to select on may be provided and will be treated as an AND condition after
        the primary selection from `all`, `name`, `osm_link_id`, or `model_link_id`.

    Attributes:
        all (bool): If True, select all links.
        name (Optional[list[str]]): List of names to select. If multiple provided will
            be treated as an OR condition.
        ref (Optional[list[str]]): Open Street Map `ref` which usually refers to a route
            or exit number.  See: <https://wiki.openstreetmap.org/wiki/Key:ref>. If multiple
            provided will be treated as an OR condition.
        osm_link_id (Optional[list[str]]): List of OSM link IDs to select. If multiple provided
            will be treated as an OR condition.
        model_link_id (Optional[list[int]]): List of model link IDs to select. If multiple provided
            will be treated as an OR condition.
        modes (Optional[Modes]): List of modes to select. If multiple provided will be
            treated as an OR condition.
        ignore_missing (bool): If True, ignore missing links. Otherwise, raise an error
            if they are not found. Defaults to True.

    !!! Example "Example: 2 and 3 lane drivable links named 'Main St' or 'Broadway'."
        ```yaml
        links:
            name: ["Main St", "Broadway"]
            all: true
            modes: ["drive"]
            lanes: [2, 3]
        ```

    !!! Example "Example: Links with model_link_id 12345 or 67890."
        ```yaml
        links:
            model_link_id: [12345, 67890]
        ```

    !!! Example "Example: Links where biking is allowed but driving is not."
        ```yaml
        links:
            all: true
            bike_allowed: true
            drive_allowed: false
        ```
    """

    require_conflicts: ClassVar[ConflictsWith] = [
        ["all", "osm_link_id"],
        ["all", "model_link_id"],
        ["osm_link_id", "name"],
        ["model_link_id", "name"],
    ]
    require_any_of: ClassVar[AnyOf] = [["name", "ref", "osm_link_id", "model_link_id", "all"]]

    model_config = ConfigDict(extra="allow", coerce_numbers_to_str=True)

    all: Optional[bool] = False
    name: Annotated[Optional[list[str]], Field(None, min_length=1)]
    ref: Annotated[Optional[list[str]], Field(None, min_length=1)]
    osm_link_id: Annotated[Optional[list[str]], Field(None, min_length=1)]
    model_link_id: Annotated[Optional[list[int]], Field(None, min_length=1)]
    modes: Annotated[Optional[list[Mode]], Field(None, min_length=1)]
    ignore_missing: Optional[bool] = True

    _examples: ClassVar[list[dict]] = [
        {"name": ["Main St"], "modes": ["drive"]},
        {"osm_link_id": ["123456789"]},
        {"model_link_id": [123456789], "modes": ["walk"]},
        {"all": True, "modes": ["transit"]},
    ]

SelectRoadNodes

Requirements for describing multiple nodes of a project card (e.g. to delete).

Attributes:

Name Type Description
all bool

If True, select all nodes. Must have either all, osm_node_id or model_node_id.

osm_node_id Optional[list[str]]

List of OSM node IDs to select. Must have either all, osm_node_id or model_node_id.

model_node_id Optional[list[int]]

List of model node IDs to select. Must have either all, osm_node_id or model_node_id.

ignore_missing bool

If True, ignore missing nodes. Otherwise, raise an error if they are not found. Defaults to True.

Example Roadway Nodes

nodes:
    model_node_id: [12345, 67890]
Source code in projectcard/models/selections.py
class SelectRoadNodes(RecordModel):
    """Requirements for describing multiple nodes of a project card (e.g. to delete).

    Attributes:
        all (bool): If True, select all nodes. Must have either `all`, `osm_node_id` or
            `model_node_id`.
        osm_node_id (Optional[list[str]]): List of OSM node IDs to select. Must have either
            `all`, `osm_node_id` or `model_node_id`.
        model_node_id (Optional[list[int]]): List of model node IDs to select. Must have either
            `all`, `osm_node_id` or `model_node_id`.
        ignore_missing (bool): If True, ignore missing nodes. Otherwise, raise an error
            if they are not found. Defaults to True.

    !!! Example "Example Roadway Nodes"
        ```yaml
        nodes:
            model_node_id: [12345, 67890]
        ```
    """

    require_any_of: ClassVar[AnyOf] = [["osm_node_id", "model_node_id"]]
    model_config = ConfigDict(extra="forbid", coerce_numbers_to_str=True)

    all: Optional[bool] = False
    osm_node_id: Annotated[Optional[list[str]], Field(None, min_length=1)]
    model_node_id: Annotated[Optional[list[int]], Field(min_length=1)]
    ignore_missing: Optional[bool] = True

SelectTransitTrips

Selection of transit trips.

Each selection must have at least one of trip_properties, route_properties, nodes, or links.

Multiple requirements are treated as an AND condition.

Attributes:

Name Type Description
trip_properties Optional[SelectTripProperties]

Selection based on trip properties.

route_properties Optional[SelectRouteProperties]

Selection based on route properties.

timespans List[Timespan]

List of timespans to select. Multiple timespans are treated as an OR condition.

nodes Optional[SelectTransitNodes]

Transit nodes to select. Useful for querying stops that might be moved/deleted when a roadway is changed.

links Optional[SelectTransitLinks]

Selection of transit links. Useful for querying links that might be moved/deleted when a roadway is changed.

Example: Select trips on route 1 or 2 that run between 12:00 and 19:45.

service:
    route_properties:
        route_id: ["1", "2"]
    timespans:
        - ["12:00", "19:45"]

Example: Select express route trips that goes through nodes 1, 2, and 3.

service:
    route_properties:
        route_short_name: ["EXP*"]
    nodes:
        stop_id: [1, 2, 3]
        require: "all"
Source code in projectcard/models/selections.py
class SelectTransitTrips(RecordModel):
    """Selection of transit trips.

    Each selection must have at least one of `trip_properties`, `route_properties`, `nodes`,
    or `links`.

    Multiple requirements are treated as an AND condition.

    Attributes:
        trip_properties (Optional[SelectTripProperties]): Selection based on trip properties.
        route_properties (Optional[SelectRouteProperties]): Selection based on route properties.
        timespans (List[Timespan]): List of timespans to select. Multiple timespans are treated
            as an OR condition.
        nodes (Optional[SelectTransitNodes]): Transit nodes to select. Useful for querying
            stops that might be moved/deleted when a roadway is changed.
        links (Optional[SelectTransitLinks]): Selection of transit links. Useful for querying
            links that might be moved/deleted when a roadway is changed.

    !!! Example "Example: Select trips on route 1 or 2 that run between 12:00 and 19:45."
        ```yaml
        service:
            route_properties:
                route_id: ["1", "2"]
            timespans:
                - ["12:00", "19:45"]
        ```

    !!! Example "Example: Select express route trips that goes through nodes 1, 2, and 3."
        ```yaml
        service:
            route_properties:
                route_short_name: ["EXP*"]
            nodes:
                stop_id: [1, 2, 3]
                require: "all"
        ```
    """

    trip_properties: Annotated[Optional[SelectTripProperties], Field(None)]
    route_properties: Annotated[Optional[SelectRouteProperties], Field(None)]
    timespans: Annotated[Optional[list[Timespan]], Field(None, min_length=1)]
    nodes: Annotated[Optional[SelectTransitNodes], Field(None)]
    links: Annotated[Optional[SelectTransitLinks], Field(None)]

    model_config = ConfigDict(
        extra="forbid",
        protected_namespaces=(),
    )

Supporting Models

Structures (lists and objects) used in project card models.

Dependencies

Dependencies for a project card.

Source code in projectcard/models/structs.py
class Dependencies(BaseModel):
    """Dependencies for a project card."""

    model_config = ConfigDict(extra="forbid")
    conflicts: Optional[list[str]]
    prerequisites: Optional[list[str]]
    corequisites: Optional[list[str]]
    _examples: ClassVar[list[dict]] = [
        {"conflicts": ["a", "b"], "prerequisites": ["c", "d"], "corequisites": ["e", "f"]},
    ]

IndivScopedPropertySetItem

Value for setting property value for a single time of day and category.

Must have at least one of set or change.

Attributes:

Name Type Description
category Optional[Union[str, int]]

Optional[Union[str, int]]: Category for the property change. If not provided, the change will be applied to all categories minus any other categories that are provided.

timespan Optional[Timespan]

Optional[Timespan]: Timespan for the property change. If not provided, the change will be applied to the entire timespan minus any other timespans that are provided.

set Optional[Any]

Optional[Any]: Value to set for the property change.

existing Optional[Any]

Optional[Any]: Existing value for the property change.

change Optional[Union[int, float]]

Optional[Union[int, float]]: Change value for the property change.

Example Scoped Property Set

scoped:
- category: hov3
  timespan: ['6:00', '9:00']
  set: 2.0
- category: hov2
  change: 1
Source code in projectcard/models/structs.py
class IndivScopedPropertySetItem(BaseModel):
    """Value for setting property value for a single time of day and category.

    Must have at least one of `set` or `change`.

    Attributes:
        category: Optional[Union[str, int]]: Category for the property change. If not provided,
            the change will be applied to all categories minus any other categories that are
            provided.
        timespan: Optional[Timespan]: Timespan for the property change. If not provided, the
            change will be applied to the entire timespan minus any other timespans that are
            provided.
        set: Optional[Any]: Value to set for the property change.
        existing: Optional[Any]: Existing value for the property change.
        change: Optional[Union[int, float]]: Change value for the property change.

    !!! Example "Example Scoped Property Set"
        ```yaml
        scoped:
        - category: hov3
          timespan: ['6:00', '9:00']
          set: 2.0
        - category: hov2
          change: 1
        ```
    """

    model_config = ConfigDict(extra="forbid", exclude_none=True)

    category: Optional[Union[str, int]]
    timespan: Optional[Timespan]
    set: Optional[Any] = None
    existing: Optional[Any] = None
    change: Optional[Union[int, float]] = None

Requirements for describing new roadway links of a project card.

The following fields may NOT be specified in a ProjectCard as they are calculated or managed by Wrangler: model_link_id_idx, managed, geometry, projects, ML_geometry, ML_A, ML_B, ML_projects.

Attributes:

Name Type Description
model_link_id int
A int
B int
name str
roadway OsmRoadwayType
lanes Annotated[int, Field(ge=0)]
price Optional[float]

Optional[float]: Price for the link.

rail_only Optional[bool]

Optional[bool]: True if the link is rail only.

bus_only Optional[bool]

Optional[bool]: True if the link is bus only.

drive_access Optional[bool]

Optional[bool]: True if the link is drive accessible.

bike_access Optional[bool]

Optional[bool]: True if the link is bike accessible.

walk_access Optional[bool]

Optional[bool]: True if the link is walk accessible.

truck_access Optional[bool]

Optional[bool]: True if the link is truck accessible.

distance Annotated[float, Field(ge=0)]
shape_id Optional[str]

Optional[str]: Shape ID for the link used as a foreign key to a roadway shape table.

osm_link_id Optional[str]

Optional[str]: OSM link ID.

access Optional[Any]

Optional[Any]: Access for the link.

sc_lanes Optional[list[dict]]

Optional[list[dict]]: List of values of the lane property as it changes with timespans and/or categories (e.g. HOV, truck, etc.).

sc_price Optional[list[dict]]

Optional[list[dict]]: List of values of the price property as it changes with timespans and/or categories (e.g. HOV, truck, etc.).

ML_access_point Optional[MLAccessEgress]

Optional[MLAccessEgress]: Access point for parallel managed lanes.

ML_egress_point Optional[MLAccessEgress]

Optional[MLAccessEgress]: Egress point for parallel managed lanes.

ML_lanes Optional[Annotated[int, Field(0, ge=0)]]

Optional[int]: Number of lanes for parallel managed lanes. Must be greater than or equal to 0.

ML_price Optional[Annotated[float, Field(0)]]

Optional[float]: Price for parallel managed lanes.

ML_access Optional[Any]

Optional[Any]: Access for parallel managed lanes.

sc_ML_lanes Optional[list[dict]]

Optional[list[dict]]: List of values of the lane property for parallel managed lanes as it changes with timespans and/or categories (e.g. HOV, truck, etc.).

sc_ML_price Optional[list[dict]]

Optional[list[dict]]: List of values of the price property for parallel managed lanes as it changes with timespans and/or categories (e.g. HOV, truck, etc.).

Example Roadway Link

- model_link_id: 404982
  A: 269066
  B: 268932
  name: new freeway
  roadway: motorway
  lanes: 3
  distance: 0.5
  sc_lanes:
    - timespan: ['6:00', '9:00']
      value: 2
    - timespan: ['9:00', '15:00']
      value: 4
  sc_price:
    - timespan: ['6:00', '9:00']
      value: 2.0
    - timespan: ['9:00', '15:00']
      value: 4.0
  ML_access_point: 'all'
  ML_egress_point: 'all'
  ML_lanes: 1
  sc_ML_lanes:
    - timespan: ['6:00', '9:00']
      value: 2
    - timespan: ['9:00', '15:00']
      value: 4
  sc_ML_price:
    - timespan: ['6:00', '9:00']
      value: 2.0
    - timespan: ['9:00', '15:00']
      value: 4.0
Source code in projectcard/models/structs.py
class RoadLink(BaseModel):
    """Requirements for describing new roadway links of a project card.

    The following fields may NOT be specified in a ProjectCard as they are calculated or managed
    by Wrangler: `model_link_id_idx`, `managed`, `geometry`, `projects`, `ML_geometry`, `ML_A`,
    `ML_B`, `ML_projects`.

    Attributes:
        model_link_id: int: Model link ID.
        A: int: `model_node_id` for the A (from) node.
        B: int: `model_node_id` for the B (to) node.
        name: str: Name of the link.
        roadway: OsmRoadwayType: Roadway facility type based on Open Street Map (OSM) roadway types.
            See: <https://wiki.openstreetmap.org/wiki/Key:highway>.
        lanes: int: Number of lanes. Must be greater than or equal to 0.
        price: Optional[float]: Price for the link.
        rail_only: Optional[bool]: True if the link is rail only.
        bus_only: Optional[bool]: True if the link is bus only.
        drive_access: Optional[bool]: True if the link is drive accessible.
        bike_access: Optional[bool]: True if the link is bike accessible.
        walk_access: Optional[bool]: True if the link is walk accessible.
        truck_access: Optional[bool]: True if the link is truck accessible.
        distance: float: Distance of the link in miles. Must be greater than or equal to 0.
        shape_id: Optional[str]: Shape ID for the link used as a foreign key to a
            roadway shape table.
        osm_link_id: Optional[str]: OSM link ID.
        access: Optional[Any]: Access for the link.
        sc_lanes: Optional[list[dict]]: List of values of the lane property as it changes with
            timespans and/or categories (e.g. HOV, truck, etc.).
        sc_price: Optional[list[dict]]: List of values of the price property as it changes with
            timespans and/or categories (e.g. HOV, truck, etc.).
        ML_access_point: Optional[MLAccessEgress]: Access point for parallel managed lanes.
        ML_egress_point: Optional[MLAccessEgress]: Egress point for parallel managed lanes.
        ML_lanes: Optional[int]: Number of lanes for parallel managed lanes. Must be greater than
            or equal to 0.
        ML_price: Optional[float]: Price for parallel managed lanes.
        ML_access: Optional[Any]: Access for parallel managed lanes.
        sc_ML_lanes: Optional[list[dict]]: List of values of the lane property for parallel managed
            lanes as it changes with timespans and/or categories (e.g. HOV, truck, etc.).
        sc_ML_price: Optional[list[dict]]: List of values of the price property for parallel managed
            lanes as it changes with timespans and/or categories (e.g. HOV, truck, etc.).

    !!! Example "Example Roadway Link"
        ```yaml
        - model_link_id: 404982
          A: 269066
          B: 268932
          name: new freeway
          roadway: motorway
          lanes: 3
          distance: 0.5
          sc_lanes:
            - timespan: ['6:00', '9:00']
              value: 2
            - timespan: ['9:00', '15:00']
              value: 4
          sc_price:
            - timespan: ['6:00', '9:00']
              value: 2.0
            - timespan: ['9:00', '15:00']
              value: 4.0
          ML_access_point: 'all'
          ML_egress_point: 'all'
          ML_lanes: 1
          sc_ML_lanes:
            - timespan: ['6:00', '9:00']
              value: 2
            - timespan: ['9:00', '15:00']
              value: 4
          sc_ML_price:
            - timespan: ['6:00', '9:00']
              value: 2.0
            - timespan: ['9:00', '15:00']
              value: 4.0
        ```
    """

    model_config = ConfigDict(coerce_numbers_to_str=True)
    PROTECTED_FIELDS: ClassVar = [
        "model_link_id_idx",
        "managed",
        "geometry",
        "projects",
        "ML_geometry",
        "ML_A",
        "ML_B",
        "ML_projects",
    ]
    model_link_id: int
    A: int
    B: int
    name: str
    roadway: OsmRoadwayType
    lanes: Annotated[int, Field(ge=0)]

    # Fields Wrangler has defaults for that are optional to specify in ProjectCard
    price: Optional[float]
    rail_only: Optional[bool]
    bus_only: Optional[bool]
    drive_access: Optional[bool]
    bike_access: Optional[bool]
    walk_access: Optional[bool]
    truck_access: Optional[bool]
    distance: Annotated[float, Field(ge=0)]

    # Optional Fields for Wrangler
    shape_id: Optional[str]
    osm_link_id: Optional[str]
    access: Optional[Any]
    sc_lanes: Optional[list[dict]]
    sc_price: Optional[list[dict]]

    # Fields for parallel managed lanes properties
    ML_access_point: Optional[MLAccessEgress]
    ML_egress_point: Optional[MLAccessEgress]

    ML_lanes: Optional[Annotated[int, Field(0, ge=0)]]
    ML_price: Optional[Annotated[float, Field(0)]]
    ML_access: Optional[Any]

    sc_ML_lanes: Optional[list[dict]]
    sc_ML_price: Optional[list[dict]]
    sc_ML_access: Optional[list[dict]]
    ML_shape_id: Optional[str]

    @model_validator(mode="before")
    @classmethod
    def check_protected_omitted(cls, data: Any) -> Any:
        """Check that protected fields are omitted."""
        if isinstance(data, dict):
            protected_present = [k for k in cls.PROTECTED_FIELDS if k in data]
            if protected_present:
                msg = f"Protected fields {cls.PROTECTED_FIELDS} must be omitted."
                CardLogger.error(msg + f" Found: {protected_present}")
                raise ValueError(msg)
        return data

check_protected_omitted(data) classmethod

Check that protected fields are omitted.

Source code in projectcard/models/structs.py
@model_validator(mode="before")
@classmethod
def check_protected_omitted(cls, data: Any) -> Any:
    """Check that protected fields are omitted."""
    if isinstance(data, dict):
        protected_present = [k for k in cls.PROTECTED_FIELDS if k in data]
        if protected_present:
            msg = f"Protected fields {cls.PROTECTED_FIELDS} must be omitted."
            CardLogger.error(msg + f" Found: {protected_present}")
            raise ValueError(msg)
    return data

RoadNode

Requirements for describing new roadway nodes of a project card.

Attributes:

Name Type Description
model_node_id int
X Longitude
Y Latitude
Source code in projectcard/models/structs.py
class RoadNode(BaseModel):
    """Requirements for describing new roadway nodes of a project card.

    Attributes:
        model_node_id: int: Model node ID.
        X: Longitude: Longitude of the node.
        Y: Latitude: Latitude of the node.
    """

    model_node_id: int
    X: Longitude
    Y: Latitude

RoadwayPropertyChange

Value for setting a single property value.

Must have at least one of set or change.

Attributes:

Name Type Description
existing Optional[Any]

Optional[Any]: Existing value for the property change. Assumption about the existing (default, not scoped) value for this property.

change Optional[Union[int, float]]

Optional[Union[int, float]]: Change value for the property change. If scoped is provided, this value will be used as the default value when no scoped value matches. Should not be provided if set is provided. This value is assumed to be a scalar difference to be applied to the existing value.

set Optional[Any]

Optional[Any]: Value to set the property to. If scoped is provided, this value will be used as the default value when no scoped value matches. Should not be provided if change is provided.

scoped Optional[Union[None, ScopedPropertySetList]]

Optional[Union[None, ScopedPropertySetList]]: List of values for the property for various category (e.g. HOV, truck, etc.) and timespan (e.g. 6:00-9:00, 9:00-15:00) combinations. When provided, the set (or applied change) value will be used as the default value when no scoped value matches.

overwrite_scoped Optional[Literal['conflicting', 'all', 'error']]

Optional[Literal[“conflicting”, “all”, “error”]]: How to handle conflicting scoped property sets. If conflicting, conflicting scoped property sets will be overwritten. If all, all existing scoped property sets will be overwritten. If error, conflicting scoped property sets will raise an error.

existing_value_conflict Optional[Literal['error', 'warn', 'skip']]

Optional[Literal[“error”, “warn”, “skip”]]: How to handle conflicting existing values. If error, conflicting existing values will raise an error. If warn, conflicting existing values will raise a warning. If skip, property change will be skipped.

Example: Reduce lanes by 1…but only if the existing value is 3.

lanes:
    existing: 3
    change: -1
    existing_value_conflict: skip

Example: Set Peak Hour tolls for HOV3 and HOV2.

price:
    overwrite_scoped: all
    change: 0
    scoped:
    - category: hov3
      timespan: ['6:00', '9:00']
      set: 2.0
    - category: hov2
      timespan: ['6:00', '9:00']
      set: 3.0
Source code in projectcard/models/structs.py
class RoadwayPropertyChange(BaseModel):
    """Value for setting a single property value.

    Must have at least one of `set` or `change`.

    Attributes:
        existing: Optional[Any]: Existing value for the property change. Assumption about the
            existing (default, not scoped) value for this property.
        change: Optional[Union[int, float]]: Change value for the property change.  If `scoped` is
            provided, this value will be used as the default value when no scoped value matches.
            Should not be provided if `set` is provided. This value is assumed to be a scalar
            difference to be applied to the existing value.
        set: Optional[Any]: Value to set the property to. If `scoped` is provided, this value
            will be used as the default value when no scoped value matches. Should not be provided
            if `change` is provided.
        scoped: Optional[Union[None, ScopedPropertySetList]]: List of values for the property for
            various `category` (e.g. HOV, truck, etc.) and `timespan` (e.g. 6:00-9:00, 9:00-15:00)
            combinations. When provided, the `set` (or applied `change`) value will be used as
            the default value when no scoped value matches.
        overwrite_scoped: Optional[Literal["conflicting", "all", "error"]]: How to handle
            conflicting scoped property sets. If `conflicting`, conflicting scoped property sets
            will be overwritten. If `all`, all existing scoped property sets will be
            overwritten. If `error`, conflicting scoped property sets will raise an error.
        existing_value_conflict: Optional[Literal["error", "warn", "skip"]]: How to handle
            conflicting existing values. If `error`, conflicting existing values will raise an
            error. If `warn`, conflicting existing values will raise a warning. If `skip`,
            property change will be skipped.

    !!! Example "Example: Reduce lanes by 1...but only if the existing value is 3."
        ```yaml
        lanes:
            existing: 3
            change: -1
            existing_value_conflict: skip
        ```

    !!! Example "Example: Set Peak Hour tolls for HOV3 and HOV2."
        ```yaml
        price:
            overwrite_scoped: all
            change: 0
            scoped:
            - category: hov3
              timespan: ['6:00', '9:00']
              set: 2.0
            - category: hov2
              timespan: ['6:00', '9:00']
              set: 3.0
        ```
    """

    model_config = ConfigDict(extra="forbid", exclude_none=True)

    existing: Optional[Any] = None
    change: Optional[Union[int, float]] = None
    set: Optional[Any] = None
    scoped: Optional[Union[None, ScopedPropertySetList]] = None
    overwrite_scoped: Optional[Literal["conflicting", "all", "error"]] = None
    existing_value_conflict: Optional[Literal["error", "warn", "skip"]] = None

    require_one_of: ClassVar = [
        ["change", "set"],
    ]

SelectRoadNode

Selection of a single roadway node in the facility section of a project card.

Additional properties may be used if they are defined in the roadway network node table.

Attributes:

Name Type Description
model_node_id Optional[int]

Optional[int]: Model node ID to select. Must have either this or osm_node_id.

osm_node_id Optional[str]

Optional[str]: OSM node ID to select. Must have either this or model_node_id.

Example: Select roadway node with model node ID 12345.

model_node_id: 12345
Source code in projectcard/models/structs.py
class SelectRoadNode(BaseModel):
    """Selection of a single roadway node in the `facility` section of a project card.

    Additional properties may be used if they are defined in the roadway network node table.

    Attributes:
        model_node_id: Optional[int]: Model node ID to select. Must have either this or
            `osm_node_id`.
        osm_node_id: Optional[str]: OSM node ID to select. Must have either this or
            `model_node_id`.

    !!! Example "Example: Select roadway node with model node ID 12345."
        ```yaml
        model_node_id: 12345
        ```
    """

    require_one_of: ClassVar = [["osm_node_id", "model_node_id"]]
    model_config = ConfigDict(extra="allow", coerce_numbers_to_str=True)

    osm_node_id: Optional[str]
    model_node_id: Optional[int]

SelectRouteProperties

Selection proeprties for transit routes.

Assumed to be an AND condition if more than one property is provided.

Additional properties may be used if they are defined in the transit route table.

Attributes:

Name Type Description
route_short_name Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List of Route short names to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

route_long_name Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List of Route long names to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

agency_id Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List of Agency IDs to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

route_type Annotated[Optional[list[int]], Field(None, min_length=1)]

Optional[List[int]: List of Route types to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

Source code in projectcard/models/structs.py
class SelectRouteProperties(BaseModel):
    """Selection proeprties for transit routes.

    Assumed to be an AND condition if more than one property is provided.

    Additional properties may be used if they are defined in the transit route table.

    Attributes:
        route_short_name: Optional[List[str]: List of Route short names to select. If more than one
            is provided, the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
        route_long_name: Optional[List[str]: List of Route long names to select. If more than one
            is provided, the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
        agency_id: Optional[List[str]: List of Agency IDs to select. If more than one is provided,
            the selection will be based on an OR condition. Can also select based on a partial match
            using the '*' wildcard character.
        route_type: Optional[List[int]: List of Route types to select. If more than one is provided,
            the selection will be based on an OR condition. Can also select based on a partial match
            using the '*' wildcard character.
    """

    model_config = ConfigDict(extra="allow", coerce_numbers_to_str=True)
    route_short_name: Annotated[Optional[list[str]], Field(None, min_length=1)]
    route_long_name: Annotated[Optional[list[str]], Field(None, min_length=1)]
    agency_id: Annotated[Optional[list[str]], Field(None, min_length=1)]
    route_type: Annotated[Optional[list[int]], Field(None, min_length=1)]

    class ConfigDict:
        """Config for the model."""

        protected_namespaces = ()

ConfigDict

Config for the model.

Source code in projectcard/models/structs.py
class ConfigDict:
    """Config for the model."""

    protected_namespaces = ()

Requirements for describing multiple transit links of a project card.

Attributes:

Name Type Description
model_link_id Annotated[Optional[list[int]], Field(min_length=1)]

Optional[List[int]]: List of model link IDs to select. If more than one is provided, the selection will be OR or AND based on the require attribute. Must be provided if ab_nodes is not.

ab_nodes Annotated[Optional[list[TransitABNodes]], Field(min_length=1)]

Optional[List[TransitABNodes]]: List of AB nodes to select. If more than one is provided, the selection will be OR or AND based on the require attribute. Must be provided if model_link_id is not.

require Optional[Literal['any', 'all']]

Optional[Literal[“any”, “all”]]: Require either any or all of the selected links to meet the selection criteria.

Example: Select transit trips with links using nodes 1-2 OR 3-4.

ab_nodes:
    - A: 1
      B: 2
    - A: 3
      B: 4
require: any

Example: Select transit trips with links using model link IDs 123 AND 321.

model_link_id: [123, 321]
require: all
Source code in projectcard/models/structs.py
class SelectTransitLinks(BaseModel):
    """Requirements for describing multiple transit links of a project card.

    Attributes:
        model_link_id: Optional[List[int]]: List of model link IDs to select. If more than one is
            provided, the selection will be OR or AND based on the `require` attribute. Must
            be provided if `ab_nodes` is not.
        ab_nodes: Optional[List[TransitABNodes]]: List of AB nodes to select. If more than one is
            provided, the selection will be OR or AND based on the `require` attribute. Must
            be provided if `model_link_id` is not.
        require: Optional[Literal["any", "all"]]: Require either any or all of the selected
            links to meet the selection criteria.

    !!! Example "Example: Select transit trips with links using nodes 1-2 OR 3-4."
        ```yaml
        ab_nodes:
            - A: 1
              B: 2
            - A: 3
              B: 4
        require: any
        ```

    !!! Example "Example: Select transit trips with links using model link IDs 123 AND 321."
        ```yaml
        model_link_id: [123, 321]
        require: all
        ```
    """

    require_one_of: ClassVar = [
        ["ab_nodes", "model_link_id"],
    ]

    model_link_id: Annotated[Optional[list[int]], Field(min_length=1)]
    ab_nodes: Annotated[Optional[list[TransitABNodes]], Field(min_length=1)]
    require: Optional[Literal["any", "all"]]

    model_config = ConfigDict(
        extra="forbid",
        protected_namespaces=(),
    )
    _examples: ClassVar[list[dict]] = [
        {
            "ab_nodes": [{"A": 75520, "B": 66380}, {"A": 66380, "B": 75520}],
            "type": "any",
        },
        {
            "model_link_id": [123, 321],
            "type": "all",
        },
    ]

SelectTransitNodes

Selecting trips that use transit nodes.

Attributes:

Name Type Description
stop_id Annotated[list[int], Field(min_length=1)]

List[int]: List of model node IDs to select. Must have at least one node. Multiple nodes are treated as an OR or AND based on the require attribute.

require Optional[Literal['any', 'all']]

Optional[Literal[“any”, “all”]]: Require either any or all of the selected nodes to meet the selection criteria.

Example: Select transit trips that use model node IDs 1 AND 2.

stop_id: [1, 2]
require: all
Source code in projectcard/models/structs.py
class SelectTransitNodes(BaseModel):
    """Selecting trips that use transit nodes.

    Attributes:
        stop_id: List[int]: List of model node IDs to select. Must have at least one node.
            Multiple nodes are treated as an OR or AND based on the `require` attribute.
        require: Optional[Literal["any", "all"]]: Require either any or all of the selected
            nodes to meet the selection criteria.

    !!! Example "Example: Select transit trips that use model node IDs 1 AND 2."
        ```yaml
        stop_id: [1, 2]
        require: all
        ```
    """

    stop_id: Annotated[list[int], Field(min_length=1)]
    require: Optional[Literal["any", "all"]] = "any"

    model_config = ConfigDict(
        extra="forbid",
    )

SelectTripProperties

Selection properties for transit trips.

Assumed to be an AND condition if more than one property is provided.

Additional properties may be used if they are defined in the transit trip table.

Attributes:

Name Type Description
trip_id Annotated[Optional[list[str]], Field(None, misn_length=1)]

Optional[List[str]: List of Trip IDs to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

shape_id Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List of Shape IDs to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

direction_id Optional[Literal[0, 1]]

Optional[Literal[0, 1]]: Direction ID to select.

service_id Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List Service IDs to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

route_id Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List of Route IDs to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

trip_short_name Annotated[Optional[list[str]], Field(None, min_length=1)]

Optional[List[str]: List Trip short names to select. If more than one is provided, the selection will be based on an OR condition. Can also select based on a partial match using the ‘*’ wildcard character.

Source code in projectcard/models/structs.py
class SelectTripProperties(BaseModel):
    """Selection properties for transit trips.

    Assumed to be an AND condition if more than one property is provided.

    Additional properties may be used if they are defined in the transit trip table.

    Attributes:
        trip_id: Optional[List[str]: List of Trip IDs to select. If more than one is provided,
            the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
        shape_id: Optional[List[str]: List of Shape IDs to select. If more than one is
            provided, the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
        direction_id: Optional[Literal[0, 1]]: Direction ID to select.
        service_id: Optional[List[str]: List Service IDs to select. If more than one is
            provided, the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
        route_id: Optional[List[str]: List of Route IDs to select. If more than one is
            provided, the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
        trip_short_name: Optional[List[str]: List Trip short names to select. If more than one
            is provided, the selection will be based on an OR condition. Can also select based on
            a partial match using the '*' wildcard character.
    """

    model_config = ConfigDict(extra="allow", coerce_numbers_to_str=True)

    trip_id: Annotated[Optional[list[str]], Field(None, misn_length=1)]
    shape_id: Annotated[Optional[list[str]], Field(None, min_length=1)]
    direction_id: Optional[Literal[0, 1]] = None
    service_id: Annotated[Optional[list[str]], Field(None, min_length=1)]
    route_id: Annotated[Optional[list[str]], Field(None, min_length=1)]
    trip_short_name: Annotated[Optional[list[str]], Field(None, min_length=1)]

    class ConfigDict:
        """Config for the model."""

        protected_namespaces = ()

ConfigDict

Config for the model.

Source code in projectcard/models/structs.py
class ConfigDict:
    """Config for the model."""

    protected_namespaces = ()

TransitABNodes

Single transit link model.

Source code in projectcard/models/structs.py
class TransitABNodes(BaseModel):
    """Single transit link model."""

    A: Optional[int] = None  # model_node_id
    B: Optional[int] = None  # model_node_id

    model_config = ConfigDict(
        extra="forbid",
    )

TransitPropertyChange_PropertyChanges

Value for setting a single property value for transit.

Source code in projectcard/models/structs.py
class TransitPropertyChange_PropertyChanges(BaseModel):
    """Value for setting a single property value for transit."""

    model_config = ConfigDict(extra="forbid", exclude_none=True)

    existing: Optional[Any] = None
    change: Optional[Union[int, float]] = None
    set: Optional[Any] = None
    existing_value_conflict: Optional[Literal["error", "warn", "skip"]] = None

    require_one_of: ClassVar = [
        ["change", "set"],
    ]

TransitRoute

Description of a new transit route.

Attributes:

Name Type Description
route_id str
agency_id str
route_short_name Optional[str]

Optional[str]: Route short name for the route. See GTFS for more information: https://gtfs.org/reference/static/#routes.txt.

route_long_name Optional[str]

Optional[str]: Route long name for the route. See GTFS for more information: https://gtfs.org/reference/static/#routes.txt.

route_type int
trips Annotated[list[TransitTrip], Field(min_length=1)]

list[TransitTrip]: List of trips for the route. Must have at least one trip.

Source code in projectcard/models/structs.py
class TransitRoute(BaseModel):
    """Description of a new transit route.

    Attributes:
        route_id: str: Route ID for the route.
        agency_id: str: Agency ID for the route. See GTFS for more information:
            <https://gtfs.org/reference/static/#routes.txt>.
        route_short_name: Optional[str]: Route short name for the route. See GTFS for more
            information: <https://gtfs.org/reference/static/#routes.txt>.
        route_long_name: Optional[str]: Route long name for the route.  See GTFS for more
            information: <https://gtfs.org/reference/static/#routes.txt>.
        route_type: int: Route type for the route. See GTFS for more information:
            <https://gtfs.org/reference/static/#routes.txt>.
        trips: list[TransitTrip]: List of trips for the route. Must have at least one trip.
    """

    model_config = ConfigDict(coerce_numbers_to_str=True)
    route_id: str
    agency_id: str
    route_short_name: Optional[str]
    route_long_name: Optional[str]
    route_type: int
    trips: Annotated[list[TransitTrip], Field(min_length=1)]

TransitRoutingChange_Routing

Value for setting routing change for transit.

Attributes:

Name Type Description
existing list[int]

list[int]: list of model_node_id for the extent of the existing nodes to change.

set list[int]

list[int]: list of model_node_id for the extent of the new nodes to set. Nodes which are negative will be treated as non-stops.

Example: Reroute around node 2.

routing:
    existing: [1, -2, 3]
    set: [1, -4, -5, -6,  3]
Source code in projectcard/models/structs.py
class TransitRoutingChange_Routing(BaseModel):
    """Value for setting routing change for transit.

    Attributes:
        existing: list[int]: list of `model_node_id` for the extent of the existing nodes
            to change.
        set: list[int]: list of `model_node_id` for the extent of the new nodes to set.
            Nodes which are negative will be treated as non-stops.

    !!! Example "Example: Reroute around node 2."
        ```yaml
        routing:
            existing: [1, -2, 3]
            set: [1, -4, -5, -6,  3]
        ```
    """

    model_config = ConfigDict(extra="forbid")
    existing: list[int]
    set: list[int]

TransitStopProps

Representation of details about a single transit stop for a new route.

Must have at at least either:

  • stop
  • board and alight

  • If stop is True, then board and alight are assumed to be True unless otherwise specified.

  • If stop is False, then board and alight are assumed to be False unless otherwise specified.

Attributes:

Name Type Description
stop Optional[bool]

Optional[bool]: True if the stop is a stop on the route.

dwell_secs Optional[PositiveInt]

Optional[PositiveInt]: Dwell time in seconds at the stop.

time_to_next_node_sec Optional[PositiveInt]

Optional[PositiveInt]: Time in seconds to the next node.

board Optional[bool]

Optional[bool]: True if the stop is a boarding stop.

alight Optional[bool]

Optional[bool]: True if the stop is an alighting stop.

Example: Stop with only boarding.

alight: false
board: true

Example: Stop with no boarding or alighting.

stop: false

Example: Stop with boarding and alighting.

stop: true
time_to_next_node_sec: 68
Source code in projectcard/models/structs.py
class TransitStopProps(BaseModel):
    """Representation of details about a single transit stop for a new route.

    Must have at at least either:

    - `stop`
    - `board` and `alight`

    - If `stop` is True, then `board` and `alight` are assumed to be True unless otherwise
    specified.
    - If `stop` is False, then `board` and `alight` are assumed to be False unless otherwise
    specified.

    Attributes:
        stop: Optional[bool]: True if the stop is a stop on the route.
        dwell_secs: Optional[PositiveInt]: Dwell time in seconds at the stop.
        time_to_next_node_sec: Optional[PositiveInt]: Time in seconds to the next node.
        board: Optional[bool]: True if the stop is a boarding stop.
        alight: Optional[bool]: True if the stop is an alighting stop.

    !!! Example "Example: Stop with only boarding."
        ```yaml
        alight: false
        board: true
        ```

    !!! Example "Example: Stop with no boarding or alighting."
        ```yaml
        stop: false
        ```

    !!! Example "Example: Stop with boarding and alighting."
        ```yaml
        stop: true
        time_to_next_node_sec: 68
        ```
    """

    require_one_of: ClassVar = [
        ["stop", "board"],
        ["stop", "alight"],
        ["stop"],
        ["board", "alight"],
    ]
    stop: Optional[bool]
    dwell_secs: Optional[PositiveInt]
    time_to_next_node_sec: Optional[PositiveInt]
    board: Optional[bool]
    alight: Optional[bool]

TransitTrip

Description of a new transit trip.

Additional properties may be used so long as they do not conflict with the fields: trip_id, shape_id, and route_id. route_id is provided at the route-level and trip_id, shape_id are managed by Wrangler in order to ensure primary and foreign key constraints are met.

Attributes:

Name Type Description
headway_secs Annotated[list[TransitHeadways], Field(min_length=1)]

list[TransitHeadways]: List of headways for the trip - each of which is a dictionary of a single timespans and headway value. Must have at least one headway. Example: {"('7:00', '9:00')": 600}.

routing TransitRouting

stop properties.

service_id Optional[str]

Optional[str]: Service ID for the trip. See GTFS for more information: https://gtfs.org/reference/static/#trips.txt.

trip_headsign Optional[str]

Optional[str]: Trip headsign. See GTFS for more information: https://gtfs.org/reference/static/#trips.txt.

trip_short_name Optional[str]

Optional[str]: Trip short name. See GTFS for more information: https://gtfs.org/reference/static/#trips.txt.

direction_id Optional[int]

Optional[int]: Direction ID for the trip. Must be either 0 or 1. See GTFS for more information: https://gtfs.org/reference/static/#trips.txt.

Example: New Transit Trip

- route_id: 1
  service_id: weekday
  trip_headsign: downtown
  trip_short_name: 1
  direction_id: 0
  headway_secs:
    - ('6:00', '12:00'): 600
    - ('12:00', '13:00'): 900
  routing:
    - 1:
        stop: true
    - 2
    - 3
    - 4:
        stop: true
        alight: false
    - 5
    - 6:
        stop: true
Source code in projectcard/models/structs.py
class TransitTrip(BaseModel):
    """Description of a new transit trip.

    Additional properties may be used so long as they do not conflict with the fields:
    `trip_id`, `shape_id`, and `route_id`. `route_id` is provided at the route-level and
    `trip_id`, `shape_id` are managed by Wrangler in order to ensure primary and foreign
    key constraints are met.

    Attributes:
        headway_secs: list[TransitHeadways]: List of headways for the trip - each of which is a
            dictionary of a single timespans and headway value. Must have at least one headway.
            Example: `{"('7:00', '9:00')": 600}`.
        routing: TransitRouting: Routing for the trip which is a list of nodes or detailed
            stop properties.
        service_id: Optional[str]: Service ID for the trip. See GTFS for more information:
            <https://gtfs.org/reference/static/#trips.txt>.
        trip_headsign: Optional[str]: Trip headsign. See GTFS for more information:
            <https://gtfs.org/reference/static/#trips.txt>.
        trip_short_name: Optional[str]: Trip short name. See GTFS for more information:
            <https://gtfs.org/reference/static/#trips.txt>.
        direction_id: Optional[int]: Direction ID for the trip. Must be either 0 or 1. See
            GTFS for more information: <https://gtfs.org/reference/static/#trips.txt>.

    !!! Example "Example: New Transit Trip"
        ```yaml
        - route_id: 1
          service_id: weekday
          trip_headsign: downtown
          trip_short_name: 1
          direction_id: 0
          headway_secs:
            - ('6:00', '12:00'): 600
            - ('12:00', '13:00'): 900
          routing:
            - 1:
                stop: true
            - 2
            - 3
            - 4:
                stop: true
                alight: false
            - 5
            - 6:
                stop: true
        ```
    """

    model_config = ConfigDict(coerce_numbers_to_str=True)
    PROTECTED_FIELDS: ClassVar = ["trip_id", "shape_id", "route_id"]
    headway_secs: Annotated[list[TransitHeadways], Field(min_length=1)]
    routing: TransitRouting
    service_id: Optional[str]
    trip_headsign: Optional[str]
    trip_short_name: Optional[str]
    direction_id: Optional[int]

Supporting Types

Field types for the projectcard models.

MLAccessEgress = Union[Literal['all'], list[int]] module-attribute

Determines how managed lanes can be accessed from the general purpose lanes as represented by connector links. If all is specied, all managed lanes will be able to access managed lanes. Otherwise, a list of node IDs where access is allowed is provided. If nothing is specified for a continuous managed lane, access is assumed to be allowed at all nodes.

Mode = Literal['drive', 'walk', 'bike', 'transit', 'any'] module-attribute

Which modes are searched for. If any is specified, all modes are searched for.

OsmRoadwayType = Literal['taz', 'motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'unclassified', 'residential', 'motorway_link', 'trunk_link', 'primary_link', 'secondary_link', 'tertiary_link', 'living_street', 'service', 'pedestrian', 'footway', 'steps', 'cycleway', 'track', 'bus_guideway', 'road'] module-attribute

Open Street Map Roadway Types. See: https://wiki.openstreetmap.org/wiki/Key:highway.

TimeString = Annotated[str, Field(description='A time string in the format HH:MM or HH:MM:SS', pattern='^(\\d+):([0-5]\\d)(:[0-5]\\d)?$')] module-attribute

A HH:MM or HH:MM:SS time string. Example: "23:44".

Timespan = Annotated[list[TimeString], Field(examples=[['12:00', '19:45:00']], max_length=2, min_length=2)] module-attribute

A list of two time strings representing a start and end time for a time span. Example: ["12:00", "19:45:00"].