openstreetmap / iD

🆔 The easy-to-use OpenStreetMap editor in JavaScript.
https://www.openstreetmap.org/edit?editor=id
ISC License
3.35k stars 1.2k forks source link

Add relationships to presets #5326

Closed quincylvania closed 3 years ago

quincylvania commented 6 years ago

Presets in iD are currently defined in isolation. In the real world, we know that stop signs are found along roads and beaches are adjacent to water. iD makes no such assumptions, and will happily let users connect streams to high tension lines. iD could be a lot smarter if it was given information on how presets relate to each other. This data would enable many new features, among them:

My aim in abstracting this issue from those above is to ensure we build a thoughtful, general-purpose JSON schema for preset relationships.

This schema was inspired by the discussion in #4139, which also includes lots of great examples.

Design Goals

The relationship schema should be:

  1. Flexible: able to clearly describe any useful relationship between presets in iD, including future types of relationships
  2. Straightforward: there are countless possible preset relationships, so they should be human-friendly to write and read in JSON
  3. Non-Redundant: relationships naturally go both ways, but data shouldn't be duplicated

General Schema

Relationships are defined in the existing preset files under the top-level related property. related is an array of relationship objects. By using objects, we can fully describe the many complex ways presets are related. Further, they let us easily extend functionality in the future by simply adding properties.

The relationship's properties vary by its type. In each type, one or more properties reference the related, or "foreign" presets, by their file path strings (e.g. highway/service/driveway). These properties' values can be single presets or arrays of presets.

Note that none of the examples are meant to be exaustive definitions.

Which preset defines a relationship?

Relationships are bi-directional, but should only be defined once to avoid duplicate data. Where possible, keywords were chosen to discourage duplicate definitions. For example, there is a terminates keyword for vertices but no opposite keyword such as endpoints on lines.

Some relationship types, such as nearby and branches, do not naturally enforce a direction. In these cases, relationships should be defined on the higher-dimension, larger, wider, longer, more important, or generally "greater" preset. For example, an area/line relationship should be defined on the area, and a river/stream relationship should be defined on the river. When no such hierarchy is clear, arbitrary conventions should be established and kept.

Topology

The topolgy requirements ignore individual way features and operate only on the combined network of ways that match the preset. For example, consider a connects relationship with "endpoints": "only". If two residential road features are connected end-to-end, the no exit preset below only matches the two disconnected end vertices, not the connecting vertex.

Relationship Types

connects

This preset is any vertex of the foreign presets, which are ways.

Power Pole

{
    "geometry": [
        "point",
        "vertex"
    ],
    "tags": {
        "power": "pole"
    },
    "name": "Power Pole",
    "related": [
        {
            "connects": [
                "power/minor_line",
                "power/line"
            ]
        }
    ]
}

The optional endpoints property is a string that further limits the vertexes. Possible values:

No Exit

{
    "geometry": [
        "vertex"
    ],
    "tags": {
        "noexit": "yes"
    },
    "name": "No Exit",
    "related":[
        {
            "connects": [
                "highway/footway",
                "highway/path",
                "highway/residential",
                "highway/service"
            ],
            "endpoints": "only"
        }
    ]
}

Turning Circle

{
    "geometry": [
        "vertex"
    ],
    "tags": {
        "highway": "turning_circle"
    },
    "name": "Turning Circle",
    "related": [
        {
            "connects": [
                "highway/residential",
                "highway/service/driveway"
            ],
            "endpoints": "preferred"
        }
    ]
}

Speed Bump

{
    "geometry": [
        "vertex",
        "line"
    ],
    "tags": {
        "traffic_calming": "bump"
    },
    "name": "Speed Bump",
    "related": [
        {
            "connects": [
                "highway/residential",
                "highway/service"
            ],
            "endpoints": "never"
        }
    ]
}

If the to property is present, this preset is a vertex shared by at least one foreign preset way from connects and at least one foreign preset way from to.

endpoints should not be used with to. This may change in the future.

{
    "geometry": [
        "vertex"
    ],
    "tags": {
        "highway": "traffic_signals"
    },
    "name": "Traffic Signals",
    "related": [
        {
            "connects": [
                "highway/primary",
                "highway/secondary",
                "highway/tertiary",
                "highway/residential",
                "highway/service"
            ],
            "to": [
                "highway/primary",
                "highway/secondary",
                "highway/tertiary",
                "highway/residential",
                "highway/service"
            ]
        }
    ]
}

Street Crossing

{
    "geometry": [
        "vertex"
    ],
    "tags": {
        "highway": "crossing"
    },
    "name": "Street Crossing",
    "related": [
        {
            "connects": [
                "highway/footway",
                "highway/footway/sidewalk"
            ],
            "to": [
                "highway/primary",
                "highway/secondary",
                "highway/tertiary",
                "highway/residential",
                "highway/service"
            ]
        }
    ]
}

Motorway Junction / Exit

{
    "geometry": [
        "vertex"
    ],
    "tags": {
        "highway": "motorway_junction"
    },
    "name": "Motorway Junction / Exit",
    "related": [
        {
            "connects": "highway/motorway",
            "to": "highway/motorway_link"
        },
        {
            "connects": "highway/trunk",
            "to": "highway/trunk_link"
        },
        {
            "connects": "highway/primary",
            "to": "highway/primary_link"
        },
        {
            "connects": "highway/secondary",
            "to": "highway/secondary_link"
        },
        {
            "connects": "highway/tertiary",
            "to": "highway/tertiary_link"
        }
    ]
}

branches

This preset is a line or area. branches contains foreign preset ways that share at least one vertex with this preset.

River

{
    "geometry": [
        "line"
    ],
    "tags": {
        "waterway": "river"
    },
    "name": "River",
    "related":[
        {
            "branches": [
                "waterway/stream",
                "waterway/canal",
                "waterway/drain",
                "waterway/dam",
                "waterway/weir"
            ]
        }
    ]
}

House

{
    "geometry": [
        "area"
    ],
    "tags": {
        "building": "house"
    },
    "name": "House",
    "related": [
        {
            "branches": [
                "highway/footway",
                "highway/corridor",
                "highway/service/driveway",
                "barrier/fence",
                "barrier/wall"
            ]
        }
    ]
}

nearby

nearby is an array of foreign presets commonly found within, or within a short distance of this preset.

This is the least specific relationship type and should only be used when no other type describes how the presets relate. See also: "Which preset defines a relationship?"

Residential Area

{
    "geometry": [
        "area"
    ],
    "tags": {
        "landuse": "residential"
    },
    "name": "Residential Area",
    "related": [
        {
            "nearby": [
                "building/house",
                "building/apartments",
                "building/garage",
                "highway/residential",
                "highway/service/driveway"
            ]
        }
    ]
}

Park

{
    "geometry": [
        "point",
        "area"
    ],
    "tags": {
        "leisure": "park"
    },
    "name": "Park",
    "related": [
        {
            "nearby": [
                "amenity/bench",
                "amenity/bbq",
                "amenity/fountain",
                "highway/footway",
                "landuse/grass"
            ]
        }
    ]
}

members

This preset is a relation. members contains the foreign presets that are the relation members of this preset.

If provided, role contains the satisfactory role strings for members. If role is an array, it should be ordered by priority with the first as the default role.

Transit Stop Area

{
    "geometry": [
        "relation"
    ],
    "tags": {
        "type": "public_transport",
        "public_transport": "stop_area"
    },
    "name": "Transit Stop Area",
    "related": [
        {
            "members": "public_transport/stop_position",
            "role": "stop"
        },
        {
            "members": "public_transport/platform",
            "role": "platform"
        },
        {
            "members":[
                "public_transport/station",
                "amenity/bench",
                "amenity/shelter"
            ]
        }
    ]
}

Bus Route

{
    "geometry": [
        "relation"
    ],
    "tags": {
        "type": "route",
        "route": "bus"
    },
    "name": "Bus Route",
    "related": [
        {
            "members": "public_transport/stop_position",
            "role": [
                "stop",
                "stop_exit_only",
                "stop_entry_only"
            ]
        },
        {
            "members": "public_transport/platform",
            "role": [
                "platform",
                "platform_exit_only",
                "platform_entry_only"
            ]
        },
        {
            "members": [
                "highway/motorway",
                "highway/trunk",
                "highway/primary",
                "highway/secondary",
                "highway/tertiary",
                "highway/motorway_link",
                "highway/trunk_link",
                "highway/primary_link",
                "highway/secondary_link",
                "highway/tertiary_link",
                "highway/bus_guideway"
            ]
        }
    ]
}

Future Considerations

Here are some additions which are not currently baked into the above schema, but could be added later based on actual needs and usage.

Feedback

Please let me know what you think! I'm more than happy to clarify items, consider changes, and discuss the issue further.

Changes since original posting

quincylvania commented 6 years ago

EDIT: I made the changes described below to the original posting.

I'm increasingly conflicted about using the lines keyword since line already has a different meaning in the schema. Also, the foreign presets can be line or area geometry. One option would be to rename it. Another option is to use the connects keyword without to to mean any vertex is allowed, and require to for any intersections. The downside of this approach is that lists of presets that all can intersect with each other would have to be written twice:

{
    "geometry": [
        "vertex"
    ],
    "tags": {
        "highway": "traffic_signals"
    },
    "name": "Traffic Signals",
    "related": [
        {
            "connects": [
                "highway/primary",
                "highway/secondary",
                "highway/tertiary",
                "highway/residential",
                "highway/service"
            ],
            "to": [
                "highway/primary",
                "highway/secondary",
                "highway/tertiary",
                "highway/residential",
                "highway/service"
            ]
        }
    ]
}

I would be in favor of the second option. It reduces the number of keywords and the duplicate listing case doesn't seem that common.

slhh commented 6 years ago

Assuming relationships to be bidirectional seem to be a potential issue. E.g. a nuclear power plant does likely have a highway, a railway and a waterway nearby. This doesn't make it likely for any highway, railway, or waterway to have a nuclear power plant nearby.

quincylvania commented 6 years ago

@slhh Good point! I would say that those sort of relationships are still bidirectional, but not symmetrical. The two directions carry different weights. One makes a strong implication, while the other is only vaguely useful. We'll have to keep that in mind and see what difference it makes in practice.

slhh commented 6 years ago

We do likely need more specific relationships to support different usecases in ID.

What does a relationship "A" connects "B" to "C" really mean?

  1. "A" can be a vertex connecting lines "B" and "C"?
  2. "A" must be the vertex connecting lines "B" and "C"?
  3. "A" is often tagged on the vertex connecting lines "B" and "C"?
  4. A vertex connecting lines "B" and "C" must be tagged as "A"?
  5. A vertex connecting lines "B" and "C" is often tagged as "A"?

Different usecases like preventing wrong connections, or suggesting presets seem to need different variants of the relationship.

What would the "A" connects "B" to "C" relationship mean for a vertex connecting lines of type"B", "C", and "D"?

quincylvania commented 3 years ago

Closing this since:

We probably still need to define tag relationships, so I might reopen something later in a different form, but it'd help to start fresh.