osmapi.relation

Relation operations for the OpenStreetMap API.

This module provides pythonic (snake_case) methods for working with OSM relations.

  1"""
  2Relation operations for the OpenStreetMap API.
  3
  4This module provides pythonic (snake_case) methods for working with OSM relations.
  5"""
  6
  7from typing import Any, Optional, TYPE_CHECKING, cast
  8from xml.dom.minidom import Element
  9
 10from . import dom, parser
 11
 12if TYPE_CHECKING:
 13    from .OsmApi import OsmApi
 14
 15
 16class RelationMixin:
 17    """Mixin providing relation-related operations with pythonic method names."""
 18
 19    def relation_get(
 20        self: "OsmApi", relation_id: int, relation_version: int = -1
 21    ) -> dict[str, Any]:
 22        """
 23        Returns relation with `relation_id` as a dict.
 24
 25        If `relation_version` is supplied, this specific version is returned,
 26        otherwise the latest version is returned.
 27
 28        If the requested element has been deleted,
 29        `OsmApi.ElementDeletedApiError` is raised.
 30
 31        If the requested element can not be found,
 32        `OsmApi.ElementNotFoundApiError` is raised.
 33        """
 34        uri = f"/api/0.6/relation/{relation_id}"
 35        if relation_version != -1:
 36            uri += f"/{relation_version}"
 37        data = self._session._get(uri)
 38        relation = cast(
 39            Element, dom.OsmResponseToDom(data, tag="relation", single=True)
 40        )
 41        return dom.dom_parse_relation(relation)
 42
 43    def relation_create(
 44        self: "OsmApi", relation_data: dict[str, Any]
 45    ) -> Optional[dict[str, Any]]:
 46        """
 47        Creates a relation based on the supplied `relation_data` dict.
 48
 49        If no authentication information are provided,
 50        `OsmApi.UsernamePasswordMissingError` is raised.
 51
 52        If the supplied information contain an existing relation,
 53        `OsmApi.OsmTypeAlreadyExistsError` is raised.
 54
 55        If there is no open changeset,
 56        `OsmApi.NoChangesetOpenError` is raised.
 57
 58        If the changeset is already closed,
 59        `OsmApi.ChangesetClosedApiError` is raised.
 60        """
 61        return self._do("create", "relation", relation_data)
 62
 63    def relation_update(
 64        self: "OsmApi", relation_data: dict[str, Any]
 65    ) -> Optional[dict[str, Any]]:
 66        """
 67        Updates relation with the supplied `relation_data` dict.
 68
 69        If no authentication information are provided,
 70        `OsmApi.UsernamePasswordMissingError` is raised.
 71
 72        If there is no open changeset,
 73        `OsmApi.NoChangesetOpenError` is raised.
 74
 75        If the changeset is already closed,
 76        `OsmApi.ChangesetClosedApiError` is raised.
 77        """
 78        return self._do("modify", "relation", relation_data)
 79
 80    def relation_delete(
 81        self: "OsmApi", relation_data: dict[str, Any]
 82    ) -> Optional[dict[str, Any]]:
 83        """
 84        Delete relation with `relation_data`.
 85
 86        If no authentication information are provided,
 87        `OsmApi.UsernamePasswordMissingError` is raised.
 88
 89        If there is no open changeset,
 90        `OsmApi.NoChangesetOpenError` is raised.
 91
 92        If the changeset is already closed,
 93        `OsmApi.ChangesetClosedApiError` is raised.
 94        """
 95        return self._do("delete", "relation", relation_data)
 96
 97    def relation_history(self: "OsmApi", relation_id: int) -> dict[int, dict[str, Any]]:
 98        """
 99        Returns dict with version as key.
100
101        If the requested element can not be found,
102        `OsmApi.ElementNotFoundApiError` is raised.
103        """
104        uri = f"/api/0.6/relation/{relation_id}/history"
105        data = self._session._get(uri)
106        relations = cast(list[Element], dom.OsmResponseToDom(data, tag="relation"))
107        result: dict[int, dict[str, Any]] = {}
108        for relation in relations:
109            relation_data = dom.dom_parse_relation(relation)
110            result[relation_data["version"]] = relation_data
111        return result
112
113    def relation_relations(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
114        """
115        Returns a list of dicts of relation data containing relation `relation_id`.
116
117        If the requested element can not be found,
118        `OsmApi.ElementNotFoundApiError` is raised.
119        """
120        uri = f"/api/0.6/relation/{relation_id}/relations"
121        data = self._session._get(uri)
122        relations = cast(
123            list[Element], dom.OsmResponseToDom(data, tag="relation", allow_empty=True)
124        )
125        result: list[dict[str, Any]] = []
126        for relation in relations:
127            relation_data = dom.dom_parse_relation(relation)
128            result.append(relation_data)
129        return result
130
131    def relation_full_recur(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
132        """
133        Returns the full data (all levels) for relation `relation_id` as list of dicts.
134
135        This function is useful for relations containing other relations.
136
137        If you don't need all levels, use `relation_full` instead,
138        which return only 2 levels.
139
140        If any relation (on any level) has been deleted,
141        `OsmApi.ElementDeletedApiError` is raised.
142
143        If the requested element can not be found,
144        `OsmApi.ElementNotFoundApiError` is raised.
145        """
146        data = []
147        todo = [relation_id]
148        done = []
149        while todo:
150            rid = todo.pop(0)
151            done.append(rid)
152            temp = self.relation_full(rid)
153            for item in temp:
154                if item["type"] != "relation":
155                    continue
156                if item["data"]["id"] in done:
157                    continue
158                todo.append(item["data"]["id"])
159            data += temp
160        return data
161
162    def relation_full(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
163        """
164        Returns the full data (two levels) for relation `relation_id` as list of dicts.
165
166        If you need all levels, use `relation_full_recur`.
167
168        If the requested element has been deleted,
169        `OsmApi.ElementDeletedApiError` is raised.
170
171        If the requested element can not be found,
172        `OsmApi.ElementNotFoundApiError` is raised.
173        """
174        uri = f"/api/0.6/relation/{relation_id}/full"
175        data = self._session._get(uri)
176        return parser.parse_osm(data)
177
178    def relations_get(
179        self: "OsmApi", relation_id_list: list[int]
180    ) -> dict[int, dict[str, Any]]:
181        """
182        Returns dict with the id of the relation as a key
183        for each relation in `relation_id_list`.
184
185        `relation_id_list` is a list containing unique identifiers
186        for multiple relations.
187        """
188        relation_list = ",".join([str(x) for x in relation_id_list])
189        uri = f"/api/0.6/relations?relations={relation_list}"
190        data = self._session._get(uri)
191        relations = cast(list[Element], dom.OsmResponseToDom(data, tag="relation"))
192        result: dict[int, dict[str, Any]] = {}
193        for relation in relations:
194            relation_data = dom.dom_parse_relation(relation)
195            result[relation_data["id"]] = relation_data
196        return result
class RelationMixin:
 17class RelationMixin:
 18    """Mixin providing relation-related operations with pythonic method names."""
 19
 20    def relation_get(
 21        self: "OsmApi", relation_id: int, relation_version: int = -1
 22    ) -> dict[str, Any]:
 23        """
 24        Returns relation with `relation_id` as a dict.
 25
 26        If `relation_version` is supplied, this specific version is returned,
 27        otherwise the latest version is returned.
 28
 29        If the requested element has been deleted,
 30        `OsmApi.ElementDeletedApiError` is raised.
 31
 32        If the requested element can not be found,
 33        `OsmApi.ElementNotFoundApiError` is raised.
 34        """
 35        uri = f"/api/0.6/relation/{relation_id}"
 36        if relation_version != -1:
 37            uri += f"/{relation_version}"
 38        data = self._session._get(uri)
 39        relation = cast(
 40            Element, dom.OsmResponseToDom(data, tag="relation", single=True)
 41        )
 42        return dom.dom_parse_relation(relation)
 43
 44    def relation_create(
 45        self: "OsmApi", relation_data: dict[str, Any]
 46    ) -> Optional[dict[str, Any]]:
 47        """
 48        Creates a relation based on the supplied `relation_data` dict.
 49
 50        If no authentication information are provided,
 51        `OsmApi.UsernamePasswordMissingError` is raised.
 52
 53        If the supplied information contain an existing relation,
 54        `OsmApi.OsmTypeAlreadyExistsError` is raised.
 55
 56        If there is no open changeset,
 57        `OsmApi.NoChangesetOpenError` is raised.
 58
 59        If the changeset is already closed,
 60        `OsmApi.ChangesetClosedApiError` is raised.
 61        """
 62        return self._do("create", "relation", relation_data)
 63
 64    def relation_update(
 65        self: "OsmApi", relation_data: dict[str, Any]
 66    ) -> Optional[dict[str, Any]]:
 67        """
 68        Updates relation with the supplied `relation_data` dict.
 69
 70        If no authentication information are provided,
 71        `OsmApi.UsernamePasswordMissingError` is raised.
 72
 73        If there is no open changeset,
 74        `OsmApi.NoChangesetOpenError` is raised.
 75
 76        If the changeset is already closed,
 77        `OsmApi.ChangesetClosedApiError` is raised.
 78        """
 79        return self._do("modify", "relation", relation_data)
 80
 81    def relation_delete(
 82        self: "OsmApi", relation_data: dict[str, Any]
 83    ) -> Optional[dict[str, Any]]:
 84        """
 85        Delete relation with `relation_data`.
 86
 87        If no authentication information are provided,
 88        `OsmApi.UsernamePasswordMissingError` is raised.
 89
 90        If there is no open changeset,
 91        `OsmApi.NoChangesetOpenError` is raised.
 92
 93        If the changeset is already closed,
 94        `OsmApi.ChangesetClosedApiError` is raised.
 95        """
 96        return self._do("delete", "relation", relation_data)
 97
 98    def relation_history(self: "OsmApi", relation_id: int) -> dict[int, dict[str, Any]]:
 99        """
100        Returns dict with version as key.
101
102        If the requested element can not be found,
103        `OsmApi.ElementNotFoundApiError` is raised.
104        """
105        uri = f"/api/0.6/relation/{relation_id}/history"
106        data = self._session._get(uri)
107        relations = cast(list[Element], dom.OsmResponseToDom(data, tag="relation"))
108        result: dict[int, dict[str, Any]] = {}
109        for relation in relations:
110            relation_data = dom.dom_parse_relation(relation)
111            result[relation_data["version"]] = relation_data
112        return result
113
114    def relation_relations(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
115        """
116        Returns a list of dicts of relation data containing relation `relation_id`.
117
118        If the requested element can not be found,
119        `OsmApi.ElementNotFoundApiError` is raised.
120        """
121        uri = f"/api/0.6/relation/{relation_id}/relations"
122        data = self._session._get(uri)
123        relations = cast(
124            list[Element], dom.OsmResponseToDom(data, tag="relation", allow_empty=True)
125        )
126        result: list[dict[str, Any]] = []
127        for relation in relations:
128            relation_data = dom.dom_parse_relation(relation)
129            result.append(relation_data)
130        return result
131
132    def relation_full_recur(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
133        """
134        Returns the full data (all levels) for relation `relation_id` as list of dicts.
135
136        This function is useful for relations containing other relations.
137
138        If you don't need all levels, use `relation_full` instead,
139        which return only 2 levels.
140
141        If any relation (on any level) has been deleted,
142        `OsmApi.ElementDeletedApiError` is raised.
143
144        If the requested element can not be found,
145        `OsmApi.ElementNotFoundApiError` is raised.
146        """
147        data = []
148        todo = [relation_id]
149        done = []
150        while todo:
151            rid = todo.pop(0)
152            done.append(rid)
153            temp = self.relation_full(rid)
154            for item in temp:
155                if item["type"] != "relation":
156                    continue
157                if item["data"]["id"] in done:
158                    continue
159                todo.append(item["data"]["id"])
160            data += temp
161        return data
162
163    def relation_full(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
164        """
165        Returns the full data (two levels) for relation `relation_id` as list of dicts.
166
167        If you need all levels, use `relation_full_recur`.
168
169        If the requested element has been deleted,
170        `OsmApi.ElementDeletedApiError` is raised.
171
172        If the requested element can not be found,
173        `OsmApi.ElementNotFoundApiError` is raised.
174        """
175        uri = f"/api/0.6/relation/{relation_id}/full"
176        data = self._session._get(uri)
177        return parser.parse_osm(data)
178
179    def relations_get(
180        self: "OsmApi", relation_id_list: list[int]
181    ) -> dict[int, dict[str, Any]]:
182        """
183        Returns dict with the id of the relation as a key
184        for each relation in `relation_id_list`.
185
186        `relation_id_list` is a list containing unique identifiers
187        for multiple relations.
188        """
189        relation_list = ",".join([str(x) for x in relation_id_list])
190        uri = f"/api/0.6/relations?relations={relation_list}"
191        data = self._session._get(uri)
192        relations = cast(list[Element], dom.OsmResponseToDom(data, tag="relation"))
193        result: dict[int, dict[str, Any]] = {}
194        for relation in relations:
195            relation_data = dom.dom_parse_relation(relation)
196            result[relation_data["id"]] = relation_data
197        return result

Mixin providing relation-related operations with pythonic method names.

def relation_get( self: osmapi.OsmApi.OsmApi, relation_id: int, relation_version: int = -1) -> dict[str, typing.Any]:
20    def relation_get(
21        self: "OsmApi", relation_id: int, relation_version: int = -1
22    ) -> dict[str, Any]:
23        """
24        Returns relation with `relation_id` as a dict.
25
26        If `relation_version` is supplied, this specific version is returned,
27        otherwise the latest version is returned.
28
29        If the requested element has been deleted,
30        `OsmApi.ElementDeletedApiError` is raised.
31
32        If the requested element can not be found,
33        `OsmApi.ElementNotFoundApiError` is raised.
34        """
35        uri = f"/api/0.6/relation/{relation_id}"
36        if relation_version != -1:
37            uri += f"/{relation_version}"
38        data = self._session._get(uri)
39        relation = cast(
40            Element, dom.OsmResponseToDom(data, tag="relation", single=True)
41        )
42        return dom.dom_parse_relation(relation)

Returns relation with relation_id as a dict.

If relation_version is supplied, this specific version is returned, otherwise the latest version is returned.

If the requested element has been deleted, OsmApi.ElementDeletedApiError is raised.

If the requested element can not be found, OsmApi.ElementNotFoundApiError is raised.

def relation_create( self: osmapi.OsmApi.OsmApi, relation_data: dict[str, typing.Any]) -> Optional[dict[str, Any]]:
44    def relation_create(
45        self: "OsmApi", relation_data: dict[str, Any]
46    ) -> Optional[dict[str, Any]]:
47        """
48        Creates a relation based on the supplied `relation_data` dict.
49
50        If no authentication information are provided,
51        `OsmApi.UsernamePasswordMissingError` is raised.
52
53        If the supplied information contain an existing relation,
54        `OsmApi.OsmTypeAlreadyExistsError` is raised.
55
56        If there is no open changeset,
57        `OsmApi.NoChangesetOpenError` is raised.
58
59        If the changeset is already closed,
60        `OsmApi.ChangesetClosedApiError` is raised.
61        """
62        return self._do("create", "relation", relation_data)

Creates a relation based on the supplied relation_data dict.

If no authentication information are provided, OsmApi.UsernamePasswordMissingError is raised.

If the supplied information contain an existing relation, OsmApi.OsmTypeAlreadyExistsError is raised.

If there is no open changeset, OsmApi.NoChangesetOpenError is raised.

If the changeset is already closed, OsmApi.ChangesetClosedApiError is raised.

def relation_update( self: osmapi.OsmApi.OsmApi, relation_data: dict[str, typing.Any]) -> Optional[dict[str, Any]]:
64    def relation_update(
65        self: "OsmApi", relation_data: dict[str, Any]
66    ) -> Optional[dict[str, Any]]:
67        """
68        Updates relation with the supplied `relation_data` dict.
69
70        If no authentication information are provided,
71        `OsmApi.UsernamePasswordMissingError` is raised.
72
73        If there is no open changeset,
74        `OsmApi.NoChangesetOpenError` is raised.
75
76        If the changeset is already closed,
77        `OsmApi.ChangesetClosedApiError` is raised.
78        """
79        return self._do("modify", "relation", relation_data)

Updates relation with the supplied relation_data dict.

If no authentication information are provided, OsmApi.UsernamePasswordMissingError is raised.

If there is no open changeset, OsmApi.NoChangesetOpenError is raised.

If the changeset is already closed, OsmApi.ChangesetClosedApiError is raised.

def relation_delete( self: osmapi.OsmApi.OsmApi, relation_data: dict[str, typing.Any]) -> Optional[dict[str, Any]]:
81    def relation_delete(
82        self: "OsmApi", relation_data: dict[str, Any]
83    ) -> Optional[dict[str, Any]]:
84        """
85        Delete relation with `relation_data`.
86
87        If no authentication information are provided,
88        `OsmApi.UsernamePasswordMissingError` is raised.
89
90        If there is no open changeset,
91        `OsmApi.NoChangesetOpenError` is raised.
92
93        If the changeset is already closed,
94        `OsmApi.ChangesetClosedApiError` is raised.
95        """
96        return self._do("delete", "relation", relation_data)

Delete relation with relation_data.

If no authentication information are provided, OsmApi.UsernamePasswordMissingError is raised.

If there is no open changeset, OsmApi.NoChangesetOpenError is raised.

If the changeset is already closed, OsmApi.ChangesetClosedApiError is raised.

def relation_history( self: osmapi.OsmApi.OsmApi, relation_id: int) -> dict[int, dict[str, typing.Any]]:
 98    def relation_history(self: "OsmApi", relation_id: int) -> dict[int, dict[str, Any]]:
 99        """
100        Returns dict with version as key.
101
102        If the requested element can not be found,
103        `OsmApi.ElementNotFoundApiError` is raised.
104        """
105        uri = f"/api/0.6/relation/{relation_id}/history"
106        data = self._session._get(uri)
107        relations = cast(list[Element], dom.OsmResponseToDom(data, tag="relation"))
108        result: dict[int, dict[str, Any]] = {}
109        for relation in relations:
110            relation_data = dom.dom_parse_relation(relation)
111            result[relation_data["version"]] = relation_data
112        return result

Returns dict with version as key.

If the requested element can not be found, OsmApi.ElementNotFoundApiError is raised.

def relation_relations( self: osmapi.OsmApi.OsmApi, relation_id: int) -> list[dict[str, typing.Any]]:
114    def relation_relations(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
115        """
116        Returns a list of dicts of relation data containing relation `relation_id`.
117
118        If the requested element can not be found,
119        `OsmApi.ElementNotFoundApiError` is raised.
120        """
121        uri = f"/api/0.6/relation/{relation_id}/relations"
122        data = self._session._get(uri)
123        relations = cast(
124            list[Element], dom.OsmResponseToDom(data, tag="relation", allow_empty=True)
125        )
126        result: list[dict[str, Any]] = []
127        for relation in relations:
128            relation_data = dom.dom_parse_relation(relation)
129            result.append(relation_data)
130        return result

Returns a list of dicts of relation data containing relation relation_id.

If the requested element can not be found, OsmApi.ElementNotFoundApiError is raised.

def relation_full_recur( self: osmapi.OsmApi.OsmApi, relation_id: int) -> list[dict[str, typing.Any]]:
132    def relation_full_recur(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
133        """
134        Returns the full data (all levels) for relation `relation_id` as list of dicts.
135
136        This function is useful for relations containing other relations.
137
138        If you don't need all levels, use `relation_full` instead,
139        which return only 2 levels.
140
141        If any relation (on any level) has been deleted,
142        `OsmApi.ElementDeletedApiError` is raised.
143
144        If the requested element can not be found,
145        `OsmApi.ElementNotFoundApiError` is raised.
146        """
147        data = []
148        todo = [relation_id]
149        done = []
150        while todo:
151            rid = todo.pop(0)
152            done.append(rid)
153            temp = self.relation_full(rid)
154            for item in temp:
155                if item["type"] != "relation":
156                    continue
157                if item["data"]["id"] in done:
158                    continue
159                todo.append(item["data"]["id"])
160            data += temp
161        return data

Returns the full data (all levels) for relation relation_id as list of dicts.

This function is useful for relations containing other relations.

If you don't need all levels, use relation_full instead, which return only 2 levels.

If any relation (on any level) has been deleted, OsmApi.ElementDeletedApiError is raised.

If the requested element can not be found, OsmApi.ElementNotFoundApiError is raised.

def relation_full( self: osmapi.OsmApi.OsmApi, relation_id: int) -> list[dict[str, typing.Any]]:
163    def relation_full(self: "OsmApi", relation_id: int) -> list[dict[str, Any]]:
164        """
165        Returns the full data (two levels) for relation `relation_id` as list of dicts.
166
167        If you need all levels, use `relation_full_recur`.
168
169        If the requested element has been deleted,
170        `OsmApi.ElementDeletedApiError` is raised.
171
172        If the requested element can not be found,
173        `OsmApi.ElementNotFoundApiError` is raised.
174        """
175        uri = f"/api/0.6/relation/{relation_id}/full"
176        data = self._session._get(uri)
177        return parser.parse_osm(data)

Returns the full data (two levels) for relation relation_id as list of dicts.

If you need all levels, use relation_full_recur.

If the requested element has been deleted, OsmApi.ElementDeletedApiError is raised.

If the requested element can not be found, OsmApi.ElementNotFoundApiError is raised.

def relations_get( self: osmapi.OsmApi.OsmApi, relation_id_list: list[int]) -> dict[int, dict[str, typing.Any]]:
179    def relations_get(
180        self: "OsmApi", relation_id_list: list[int]
181    ) -> dict[int, dict[str, Any]]:
182        """
183        Returns dict with the id of the relation as a key
184        for each relation in `relation_id_list`.
185
186        `relation_id_list` is a list containing unique identifiers
187        for multiple relations.
188        """
189        relation_list = ",".join([str(x) for x in relation_id_list])
190        uri = f"/api/0.6/relations?relations={relation_list}"
191        data = self._session._get(uri)
192        relations = cast(list[Element], dom.OsmResponseToDom(data, tag="relation"))
193        result: dict[int, dict[str, Any]] = {}
194        for relation in relations:
195            relation_data = dom.dom_parse_relation(relation)
196            result[relation_data["id"]] = relation_data
197        return result

Returns dict with the id of the relation as a key for each relation in relation_id_list.

relation_id_list is a list containing unique identifiers for multiple relations.