overturetoosm.objects

Pydantic models needed throughout the project.

  1"""Pydantic models needed throughout the project."""
  2
  3# ruff: noqa: D415
  4
  5from enum import Enum
  6from typing import Dict, List, Optional, Union
  7
  8try:
  9    from typing import Annotated
 10except ImportError:
 11    from typing_extensions import Annotated
 12
 13from pydantic import BaseModel, ConfigDict, Field, RootModel, field_validator
 14
 15from .resources import places_tags
 16
 17
 18class OvertureBaseModel(BaseModel):
 19    """Base model for Overture features."""
 20
 21    model_config = ConfigDict(extra="forbid")
 22
 23    version: int = Field(ge=0)
 24    theme: Optional[str] = None
 25    type: Optional[str] = None
 26    id: Optional[str] = Field(None, pattern=r"^(\S.*)?\S$")
 27
 28
 29class Wikidata(RootModel):
 30    """Model for transportation segment wikidata."""
 31
 32    root: str = Field(description="Wikidata ID.", pattern=r"^Q\d+")
 33
 34
 35class Sources(BaseModel):
 36    """Overture sources model."""
 37
 38    property: str
 39    dataset: str
 40    record_id: Optional[str] = None
 41    confidence: Optional[float] = Field(ge=0.0, le=1.0)
 42    update_time: Optional[str] = None
 43
 44    @field_validator("confidence")
 45    @classmethod
 46    def set_default_if_none(cls, v: float) -> float:
 47        """@private"""
 48        return v if v is not None else 0.0
 49
 50    def get_osm_link(self) -> Union[str, None]:
 51        """Return the OSM link for the source."""
 52        if (
 53            self.record_id
 54            and self.record_id.startswith(("n", "w", "r"))
 55            and self.dataset == "OpenStreetMap"
 56        ):
 57            type_dict = {"n": "node", "w": "way", "r": "relation"}
 58            return f"https://www.openstreetmap.org/{type_dict[self.record_id[0]]}/{self.record_id[1:]}"
 59
 60
 61class RulesVariant(str, Enum):
 62    """Overture name rules variant model."""
 63
 64    alternate = "alternate"
 65    common = "common"
 66    official = "official"
 67    short = "short"
 68
 69
 70class Between(RootModel):
 71    """Model for transportation segment between."""
 72
 73    root: Annotated[list, Field(float, min_length=2, max_length=2)]
 74
 75
 76class Rules(BaseModel):
 77    """Overture name rules model."""
 78
 79    variant: RulesVariant
 80    language: Optional[str] = None
 81    value: str
 82    between: Optional[Between] = None
 83    side: Optional[str] = None
 84
 85
 86class Names(BaseModel):
 87    """Overture names model."""
 88
 89    primary: str
 90    common: Optional[Dict[str, str]]
 91    rules: Optional[List[Rules]]
 92
 93
 94class PlaceAddress(BaseModel):
 95    """Overture addresses model."""
 96
 97    freeform: Optional[str]
 98    locality: Optional[str]
 99    postcode: Optional[str]
100    region: Optional[str]
101    country: Optional[str] = Field(pattern=r"^[A-Z]{2}$")
102
103
104class Categories(BaseModel):
105    """Overture categories model."""
106
107    main: str
108    alternate: Optional[List[str]]
109
110
111class Brand(BaseModel):
112    """Overture brand model."""
113
114    wikidata: Optional[Wikidata] = None
115    names: Names
116
117    def to_osm(self) -> Dict[str, str]:
118        """Convert brand properties to OSM tags."""
119        osm = {"brand": self.names.primary}
120        if self.wikidata:
121            osm.update({"brand:wikidata": str(self.wikidata.root)})
122        return osm
123
124
125class Socials(RootModel):
126    """Overture socials model."""
127
128    root: List[str]
129
130    def to_osm(self) -> Dict[str, str]:
131        """Convert socials properties to OSM tags."""
132        new_props = {}
133        for social in self.root:
134            if "facebook" in social:
135                new_props["contact:facebook"] = social
136            elif "twitter" in str(social):
137                new_props["contact:twitter"] = social
138        return new_props
139
140
141class PlaceProps(OvertureBaseModel):
142    """Overture properties model.
143
144    Use this model directly if you want to manipulate the `place` properties yourself.
145    """
146
147    update_time: str
148    sources: List[Sources]
149    names: Names
150    brand: Optional[Brand] = None
151    categories: Optional[Categories] = None
152    confidence: float = Field(ge=0.0, le=1.0)
153    websites: Optional[List[str]] = None
154    socials: Optional[Socials] = None
155    emails: Optional[List[str]] = None
156    phones: Optional[List[str]] = None
157    addresses: List[PlaceAddress]
158
159    def to_osm(
160        self, confidence: float, region_tag: str, unmatched: str
161    ) -> Dict[str, str]:
162        """Convert Overture's place properties to OSM tags.
163
164        Used internally by the `overturetoosm.process_place` function.
165        """
166        new_props = {}
167        if self.confidence < confidence:
168            raise ConfidenceError(confidence, self.confidence)
169
170        if self.categories:
171            prim = places_tags.get(self.categories.main)
172            if prim:
173                new_props = {**new_props, **prim}
174            elif unmatched == "force":
175                new_props["type"] = self.categories.main
176            elif unmatched == "error":
177                raise UnmatchedError(self.categories.main)
178
179        if self.names.primary:
180            new_props["name"] = self.names.primary
181
182        if self.phones is not None:
183            new_props["phone"] = self.phones[0]
184
185        if self.websites is not None and self.websites[0]:
186            new_props["website"] = str(self.websites[0])
187
188        if add := self.addresses[0]:
189            if add.freeform:
190                new_props["addr:street_address"] = add.freeform
191            if add.country:
192                new_props["addr:country"] = add.country
193            if add.postcode:
194                new_props["addr:postcode"] = add.postcode
195            if add.locality:
196                new_props["addr:city"] = add.locality
197            if add.region:
198                new_props[region_tag] = add.region
199
200        if self.sources:
201            new_props["source"] = source_statement(self.sources)
202
203        if self.socials:
204            new_props.update(self.socials.to_osm())
205
206        if self.brand:
207            new_props.update(self.brand.to_osm())
208
209        return new_props
210
211
212class ConfidenceError(Exception):
213    """Confidence error exception.
214
215    This exception is raised when the confidence level of an item is below the
216    user-defined level. It contains the original confidence level and the confidence
217    level of the item.
218
219    Attributes:
220        confidence_level (float): The set confidence level.
221        confidence_item (float): The confidence of the item.
222        message (str): The error message.
223    """
224
225    def __init__(
226        self,
227        confidence_level: float,
228        confidence_item: float,
229        message: str = "Confidence in this item is too low.",
230    ) -> None:
231        """@private"""
232        self.confidence_level = confidence_level
233        self.confidence_item = confidence_item
234        self.message = message
235        super().__init__(message)
236
237    def __str__(self) -> str:
238        """@private"""
239        lev = f"confidence_level={self.confidence_level}"
240        item = f"confidence_item={self.confidence_item}"
241        return f"""{self.message} {lev}, {item}"""
242
243
244class UnmatchedError(Exception):
245    """Unmatched category error.
246
247    This exception is raised when an item's Overture category does not have a
248    corresponding OSM definition. Edit
249    [the OSM Wiki page](https://wiki.openstreetmap.org/wiki/Overture_categories)
250    to add a definition to this category.
251
252    Attributes:
253        category (str): The Overture category that is unmatched.
254        message (str): The error message.
255    """
256
257    def __init__(
258        self, category: str, message: str = "Overture category is unmatched."
259    ) -> None:
260        """@private"""
261        self.category = category
262        self.message = message
263        super().__init__(message)
264
265    def __str__(self) -> str:
266        """@private"""
267        return f"{self.message} {{category={self.category}}}"
268
269
270class BuildingProps(OvertureBaseModel):
271    """Overture building properties.
272
273    Use this model if you want to manipulate the `building` properties yourself.
274    """
275
276    has_parts: bool
277    sources: List[Sources]
278    class_: Optional[str] = Field(alias="class", default=None)
279    subtype: Optional[str] = None
280    names: Optional[Names] = None
281    level: Optional[int] = None
282    height: Optional[float] = None
283    is_underground: Optional[bool] = None
284    num_floors: Optional[int] = Field(
285        serialization_alias="building:levels", default=None
286    )
287    num_floors_underground: Optional[int] = Field(
288        serialization_alias="building:levels:underground", default=None
289    )
290    min_height: Optional[float] = None
291    min_floor: Optional[int] = Field(
292        serialization_alias="building:min_level", default=None
293    )
294    facade_color: Optional[str] = Field(
295        serialization_alias="building:colour", default=None
296    )
297    facade_material: Optional[str] = Field(
298        serialization_alias="building:material", default=None
299    )
300    roof_material: Optional[str] = Field(
301        serialization_alias="roof:material", default=None
302    )
303    roof_shape: Optional[str] = Field(serialization_alias="roof:shape", default=None)
304    roof_direction: Optional[str] = Field(
305        serialization_alias="roof:direction", default=None
306    )
307    roof_orientation: Optional[str] = Field(
308        serialization_alias="roof:orientation", default=None
309    )
310    roof_color: Optional[str] = Field(serialization_alias="roof:colour", default=None)
311    roof_height: Optional[float] = Field(
312        serialization_alias="roof:height", default=None
313    )
314
315    def to_osm(self, confidence: float) -> Dict[str, str]:
316        """Convert properties to OSM tags.
317
318        Used internally by`overturetoosm.process_building` function.
319        """
320        new_props = {}
321        confidences = {source.confidence for source in self.sources}
322        if any(conf and conf < confidence for conf in confidences):
323            raise ConfidenceError(confidence, max({i for i in confidences if i}))
324
325        new_props["building"] = self.class_ if self.class_ else "yes"
326
327        new_props["source"] = source_statement(self.sources)
328
329        prop_obj = self.model_dump(exclude_none=True, by_alias=True).items()
330        new_props.update(
331            {k: v for k, v in prop_obj if k.startswith(("roof", "building"))}
332        )
333        new_props.update({k: round(v, 2) for k, v in prop_obj if k.endswith("height")})
334
335        if self.is_underground:
336            new_props["location"] = "underground"
337        if self.names:
338            new_props["name"] = self.names.primary
339        return new_props
340
341
342class AddressLevel(BaseModel):
343    """Overture address level model."""
344
345    value: str
346
347
348class AddressProps(OvertureBaseModel):
349    """Overture address properties.
350
351    Use this model directly if you want to manipulate the `address` properties yourself.
352    """
353
354    number: Optional[str] = Field(serialization_alias="addr:housenumber")
355    street: Optional[str] = Field(serialization_alias="addr:street")
356    postcode: Optional[str] = Field(serialization_alias="addr:postcode")
357    country: Optional[str] = Field(serialization_alias="addr:country")
358    address_levels: Optional[
359        Annotated[List[AddressLevel], Field(min_length=1, max_length=5)]
360    ] = Field(default_factory=list)
361    sources: List[Sources]
362
363    def to_osm(self, style: str) -> Dict[str, str]:
364        """Convert properties to OSM tags.
365
366        Used internally by `overturetoosm.process_address`.
367        """
368        obj_dict = {
369            k: v
370            for k, v in self.model_dump(exclude_none=True, by_alias=True).items()
371            if k.startswith("addr:")
372        }
373        obj_dict["source"] = source_statement(self.sources)
374
375        if self.address_levels and len(self.address_levels) > 0 and style == "US":
376            obj_dict["addr:state"] = str(self.address_levels[0].value)
377
378        return obj_dict
379
380
381def source_statement(source: List[Sources]) -> str:
382    """Return a source statement from a list of sources."""
383    return (
384        ", ".join(sorted({i.dataset.strip(", ") for i in source}))
385        + " via overturetoosm"
386    )
class OvertureBaseModel(pydantic.main.BaseModel):
19class OvertureBaseModel(BaseModel):
20    """Base model for Overture features."""
21
22    model_config = ConfigDict(extra="forbid")
23
24    version: int = Field(ge=0)
25    theme: Optional[str] = None
26    type: Optional[str] = None
27    id: Optional[str] = Field(None, pattern=r"^(\S.*)?\S$")

Base model for Overture features.

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

version: int
theme: Optional[str]
type: Optional[str]
id: Optional[str]
model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'version': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'theme': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'type': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, metadata=[_PydanticGeneralMetadata(pattern='^(\\S.*)?\\S$')])}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Wikidata(pydantic.main.BaseModel, typing.Generic[~RootModelRootType]):
30class Wikidata(RootModel):
31    """Model for transportation segment wikidata."""
32
33    root: str = Field(description="Wikidata ID.", pattern=r"^Q\d+")

Model for transportation segment wikidata.

root: str
Inherited Members
pydantic.main.BaseModel
BaseModel
model_config
model_fields
model_computed_fields
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Sources(pydantic.main.BaseModel):
36class Sources(BaseModel):
37    """Overture sources model."""
38
39    property: str
40    dataset: str
41    record_id: Optional[str] = None
42    confidence: Optional[float] = Field(ge=0.0, le=1.0)
43    update_time: Optional[str] = None
44
45    @field_validator("confidence")
46    @classmethod
47    def set_default_if_none(cls, v: float) -> float:
48        """@private"""
49        return v if v is not None else 0.0
50
51    def get_osm_link(self) -> Union[str, None]:
52        """Return the OSM link for the source."""
53        if (
54            self.record_id
55            and self.record_id.startswith(("n", "w", "r"))
56            and self.dataset == "OpenStreetMap"
57        ):
58            type_dict = {"n": "node", "w": "way", "r": "relation"}
59            return f"https://www.openstreetmap.org/{type_dict[self.record_id[0]]}/{self.record_id[1:]}"

Overture sources model.

property: str
dataset: str
record_id: Optional[str]
confidence: Optional[float]
update_time: Optional[str]
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'property': FieldInfo(annotation=str, required=True), 'dataset': FieldInfo(annotation=str, required=True), 'record_id': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'confidence': FieldInfo(annotation=Union[float, NoneType], required=True, metadata=[Ge(ge=0.0), Le(le=1.0)]), 'update_time': FieldInfo(annotation=Union[str, NoneType], required=False, default=None)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class RulesVariant(builtins.str, enum.Enum):
62class RulesVariant(str, Enum):
63    """Overture name rules variant model."""
64
65    alternate = "alternate"
66    common = "common"
67    official = "official"
68    short = "short"

Overture name rules variant model.

alternate = <RulesVariant.alternate: 'alternate'>
common = <RulesVariant.common: 'common'>
official = <RulesVariant.official: 'official'>
short = <RulesVariant.short: 'short'>
Inherited Members
enum.Enum
name
value
builtins.str
encode
replace
split
rsplit
join
capitalize
casefold
title
center
count
expandtabs
find
partition
index
ljust
lower
lstrip
rfind
rindex
rjust
rstrip
rpartition
splitlines
strip
swapcase
translate
upper
startswith
endswith
removeprefix
removesuffix
isascii
islower
isupper
istitle
isspace
isdecimal
isdigit
isnumeric
isalpha
isalnum
isidentifier
isprintable
zfill
format
format_map
maketrans
class Between(pydantic.main.BaseModel, typing.Generic[~RootModelRootType]):
71class Between(RootModel):
72    """Model for transportation segment between."""
73
74    root: Annotated[list, Field(float, min_length=2, max_length=2)]

Model for transportation segment between.

root: Annotated[list, FieldInfo(annotation=NoneType, required=False, default=<class 'float'>, metadata=[MinLen(min_length=2), MaxLen(max_length=2)])]
Inherited Members
pydantic.main.BaseModel
BaseModel
model_config
model_fields
model_computed_fields
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Rules(pydantic.main.BaseModel):
77class Rules(BaseModel):
78    """Overture name rules model."""
79
80    variant: RulesVariant
81    language: Optional[str] = None
82    value: str
83    between: Optional[Between] = None
84    side: Optional[str] = None

Overture name rules model.

variant: RulesVariant
language: Optional[str]
value: str
between: Optional[Between]
side: Optional[str]
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'variant': FieldInfo(annotation=RulesVariant, required=True), 'language': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'value': FieldInfo(annotation=str, required=True), 'between': FieldInfo(annotation=Union[Between, NoneType], required=False, default=None), 'side': FieldInfo(annotation=Union[str, NoneType], required=False, default=None)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Names(pydantic.main.BaseModel):
87class Names(BaseModel):
88    """Overture names model."""
89
90    primary: str
91    common: Optional[Dict[str, str]]
92    rules: Optional[List[Rules]]

Overture names model.

primary: str
common: Optional[Dict[str, str]]
rules: Optional[List[Rules]]
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'primary': FieldInfo(annotation=str, required=True), 'common': FieldInfo(annotation=Union[Dict[str, str], NoneType], required=True), 'rules': FieldInfo(annotation=Union[List[Rules], NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class PlaceAddress(pydantic.main.BaseModel):
 95class PlaceAddress(BaseModel):
 96    """Overture addresses model."""
 97
 98    freeform: Optional[str]
 99    locality: Optional[str]
100    postcode: Optional[str]
101    region: Optional[str]
102    country: Optional[str] = Field(pattern=r"^[A-Z]{2}$")

Overture addresses model.

freeform: Optional[str]
locality: Optional[str]
postcode: Optional[str]
region: Optional[str]
country: Optional[str]
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'freeform': FieldInfo(annotation=Union[str, NoneType], required=True), 'locality': FieldInfo(annotation=Union[str, NoneType], required=True), 'postcode': FieldInfo(annotation=Union[str, NoneType], required=True), 'region': FieldInfo(annotation=Union[str, NoneType], required=True), 'country': FieldInfo(annotation=Union[str, NoneType], required=True, metadata=[_PydanticGeneralMetadata(pattern='^[A-Z]{2}$')])}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Categories(pydantic.main.BaseModel):
105class Categories(BaseModel):
106    """Overture categories model."""
107
108    main: str
109    alternate: Optional[List[str]]

Overture categories model.

main: str
alternate: Optional[List[str]]
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'main': FieldInfo(annotation=str, required=True), 'alternate': FieldInfo(annotation=Union[List[str], NoneType], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Brand(pydantic.main.BaseModel):
112class Brand(BaseModel):
113    """Overture brand model."""
114
115    wikidata: Optional[Wikidata] = None
116    names: Names
117
118    def to_osm(self) -> Dict[str, str]:
119        """Convert brand properties to OSM tags."""
120        osm = {"brand": self.names.primary}
121        if self.wikidata:
122            osm.update({"brand:wikidata": str(self.wikidata.root)})
123        return osm

Overture brand model.

wikidata: Optional[Wikidata]
names: Names
def to_osm(self) -> Dict[str, str]:
118    def to_osm(self) -> Dict[str, str]:
119        """Convert brand properties to OSM tags."""
120        osm = {"brand": self.names.primary}
121        if self.wikidata:
122            osm.update({"brand:wikidata": str(self.wikidata.root)})
123        return osm

Convert brand properties to OSM tags.

model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'wikidata': FieldInfo(annotation=Union[Wikidata, NoneType], required=False, default=None), 'names': FieldInfo(annotation=Names, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class Socials(pydantic.main.BaseModel, typing.Generic[~RootModelRootType]):
126class Socials(RootModel):
127    """Overture socials model."""
128
129    root: List[str]
130
131    def to_osm(self) -> Dict[str, str]:
132        """Convert socials properties to OSM tags."""
133        new_props = {}
134        for social in self.root:
135            if "facebook" in social:
136                new_props["contact:facebook"] = social
137            elif "twitter" in str(social):
138                new_props["contact:twitter"] = social
139        return new_props

Overture socials model.

root: List[str]
def to_osm(self) -> Dict[str, str]:
131    def to_osm(self) -> Dict[str, str]:
132        """Convert socials properties to OSM tags."""
133        new_props = {}
134        for social in self.root:
135            if "facebook" in social:
136                new_props["contact:facebook"] = social
137            elif "twitter" in str(social):
138                new_props["contact:twitter"] = social
139        return new_props

Convert socials properties to OSM tags.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_config
model_fields
model_computed_fields
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class PlaceProps(OvertureBaseModel):
142class PlaceProps(OvertureBaseModel):
143    """Overture properties model.
144
145    Use this model directly if you want to manipulate the `place` properties yourself.
146    """
147
148    update_time: str
149    sources: List[Sources]
150    names: Names
151    brand: Optional[Brand] = None
152    categories: Optional[Categories] = None
153    confidence: float = Field(ge=0.0, le=1.0)
154    websites: Optional[List[str]] = None
155    socials: Optional[Socials] = None
156    emails: Optional[List[str]] = None
157    phones: Optional[List[str]] = None
158    addresses: List[PlaceAddress]
159
160    def to_osm(
161        self, confidence: float, region_tag: str, unmatched: str
162    ) -> Dict[str, str]:
163        """Convert Overture's place properties to OSM tags.
164
165        Used internally by the `overturetoosm.process_place` function.
166        """
167        new_props = {}
168        if self.confidence < confidence:
169            raise ConfidenceError(confidence, self.confidence)
170
171        if self.categories:
172            prim = places_tags.get(self.categories.main)
173            if prim:
174                new_props = {**new_props, **prim}
175            elif unmatched == "force":
176                new_props["type"] = self.categories.main
177            elif unmatched == "error":
178                raise UnmatchedError(self.categories.main)
179
180        if self.names.primary:
181            new_props["name"] = self.names.primary
182
183        if self.phones is not None:
184            new_props["phone"] = self.phones[0]
185
186        if self.websites is not None and self.websites[0]:
187            new_props["website"] = str(self.websites[0])
188
189        if add := self.addresses[0]:
190            if add.freeform:
191                new_props["addr:street_address"] = add.freeform
192            if add.country:
193                new_props["addr:country"] = add.country
194            if add.postcode:
195                new_props["addr:postcode"] = add.postcode
196            if add.locality:
197                new_props["addr:city"] = add.locality
198            if add.region:
199                new_props[region_tag] = add.region
200
201        if self.sources:
202            new_props["source"] = source_statement(self.sources)
203
204        if self.socials:
205            new_props.update(self.socials.to_osm())
206
207        if self.brand:
208            new_props.update(self.brand.to_osm())
209
210        return new_props

Overture properties model.

Use this model directly if you want to manipulate the place properties yourself.

update_time: str
sources: List[Sources]
names: Names
brand: Optional[Brand]
categories: Optional[Categories]
confidence: float
websites: Optional[List[str]]
socials: Optional[Socials]
emails: Optional[List[str]]
phones: Optional[List[str]]
addresses: List[PlaceAddress]
def to_osm( self, confidence: float, region_tag: str, unmatched: str) -> Dict[str, str]:
160    def to_osm(
161        self, confidence: float, region_tag: str, unmatched: str
162    ) -> Dict[str, str]:
163        """Convert Overture's place properties to OSM tags.
164
165        Used internally by the `overturetoosm.process_place` function.
166        """
167        new_props = {}
168        if self.confidence < confidence:
169            raise ConfidenceError(confidence, self.confidence)
170
171        if self.categories:
172            prim = places_tags.get(self.categories.main)
173            if prim:
174                new_props = {**new_props, **prim}
175            elif unmatched == "force":
176                new_props["type"] = self.categories.main
177            elif unmatched == "error":
178                raise UnmatchedError(self.categories.main)
179
180        if self.names.primary:
181            new_props["name"] = self.names.primary
182
183        if self.phones is not None:
184            new_props["phone"] = self.phones[0]
185
186        if self.websites is not None and self.websites[0]:
187            new_props["website"] = str(self.websites[0])
188
189        if add := self.addresses[0]:
190            if add.freeform:
191                new_props["addr:street_address"] = add.freeform
192            if add.country:
193                new_props["addr:country"] = add.country
194            if add.postcode:
195                new_props["addr:postcode"] = add.postcode
196            if add.locality:
197                new_props["addr:city"] = add.locality
198            if add.region:
199                new_props[region_tag] = add.region
200
201        if self.sources:
202            new_props["source"] = source_statement(self.sources)
203
204        if self.socials:
205            new_props.update(self.socials.to_osm())
206
207        if self.brand:
208            new_props.update(self.brand.to_osm())
209
210        return new_props

Convert Overture's place properties to OSM tags.

Used internally by the overturetoosm.process_place function.

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'version': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'theme': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'type': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, metadata=[_PydanticGeneralMetadata(pattern='^(\\S.*)?\\S$')]), 'update_time': FieldInfo(annotation=str, required=True), 'sources': FieldInfo(annotation=List[Sources], required=True), 'names': FieldInfo(annotation=Names, required=True), 'brand': FieldInfo(annotation=Union[Brand, NoneType], required=False, default=None), 'categories': FieldInfo(annotation=Union[Categories, NoneType], required=False, default=None), 'confidence': FieldInfo(annotation=float, required=True, metadata=[Ge(ge=0.0), Le(le=1.0)]), 'websites': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'socials': FieldInfo(annotation=Union[Socials, NoneType], required=False, default=None), 'emails': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'phones': FieldInfo(annotation=Union[List[str], NoneType], required=False, default=None), 'addresses': FieldInfo(annotation=List[PlaceAddress], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
OvertureBaseModel
version
theme
type
id
class ConfidenceError(builtins.Exception):
213class ConfidenceError(Exception):
214    """Confidence error exception.
215
216    This exception is raised when the confidence level of an item is below the
217    user-defined level. It contains the original confidence level and the confidence
218    level of the item.
219
220    Attributes:
221        confidence_level (float): The set confidence level.
222        confidence_item (float): The confidence of the item.
223        message (str): The error message.
224    """
225
226    def __init__(
227        self,
228        confidence_level: float,
229        confidence_item: float,
230        message: str = "Confidence in this item is too low.",
231    ) -> None:
232        """@private"""
233        self.confidence_level = confidence_level
234        self.confidence_item = confidence_item
235        self.message = message
236        super().__init__(message)
237
238    def __str__(self) -> str:
239        """@private"""
240        lev = f"confidence_level={self.confidence_level}"
241        item = f"confidence_item={self.confidence_item}"
242        return f"""{self.message} {lev}, {item}"""

Confidence error exception.

This exception is raised when the confidence level of an item is below the user-defined level. It contains the original confidence level and the confidence level of the item.

Attributes:
  • confidence_level (float): The set confidence level.
  • confidence_item (float): The confidence of the item.
  • message (str): The error message.
confidence_level
confidence_item
message
Inherited Members
builtins.BaseException
with_traceback
add_note
args
class UnmatchedError(builtins.Exception):
245class UnmatchedError(Exception):
246    """Unmatched category error.
247
248    This exception is raised when an item's Overture category does not have a
249    corresponding OSM definition. Edit
250    [the OSM Wiki page](https://wiki.openstreetmap.org/wiki/Overture_categories)
251    to add a definition to this category.
252
253    Attributes:
254        category (str): The Overture category that is unmatched.
255        message (str): The error message.
256    """
257
258    def __init__(
259        self, category: str, message: str = "Overture category is unmatched."
260    ) -> None:
261        """@private"""
262        self.category = category
263        self.message = message
264        super().__init__(message)
265
266    def __str__(self) -> str:
267        """@private"""
268        return f"{self.message} {{category={self.category}}}"

Unmatched category error.

This exception is raised when an item's Overture category does not have a corresponding OSM definition. Edit the OSM Wiki page to add a definition to this category.

Attributes:
  • category (str): The Overture category that is unmatched.
  • message (str): The error message.
category
message
Inherited Members
builtins.BaseException
with_traceback
add_note
args
class BuildingProps(OvertureBaseModel):
271class BuildingProps(OvertureBaseModel):
272    """Overture building properties.
273
274    Use this model if you want to manipulate the `building` properties yourself.
275    """
276
277    has_parts: bool
278    sources: List[Sources]
279    class_: Optional[str] = Field(alias="class", default=None)
280    subtype: Optional[str] = None
281    names: Optional[Names] = None
282    level: Optional[int] = None
283    height: Optional[float] = None
284    is_underground: Optional[bool] = None
285    num_floors: Optional[int] = Field(
286        serialization_alias="building:levels", default=None
287    )
288    num_floors_underground: Optional[int] = Field(
289        serialization_alias="building:levels:underground", default=None
290    )
291    min_height: Optional[float] = None
292    min_floor: Optional[int] = Field(
293        serialization_alias="building:min_level", default=None
294    )
295    facade_color: Optional[str] = Field(
296        serialization_alias="building:colour", default=None
297    )
298    facade_material: Optional[str] = Field(
299        serialization_alias="building:material", default=None
300    )
301    roof_material: Optional[str] = Field(
302        serialization_alias="roof:material", default=None
303    )
304    roof_shape: Optional[str] = Field(serialization_alias="roof:shape", default=None)
305    roof_direction: Optional[str] = Field(
306        serialization_alias="roof:direction", default=None
307    )
308    roof_orientation: Optional[str] = Field(
309        serialization_alias="roof:orientation", default=None
310    )
311    roof_color: Optional[str] = Field(serialization_alias="roof:colour", default=None)
312    roof_height: Optional[float] = Field(
313        serialization_alias="roof:height", default=None
314    )
315
316    def to_osm(self, confidence: float) -> Dict[str, str]:
317        """Convert properties to OSM tags.
318
319        Used internally by`overturetoosm.process_building` function.
320        """
321        new_props = {}
322        confidences = {source.confidence for source in self.sources}
323        if any(conf and conf < confidence for conf in confidences):
324            raise ConfidenceError(confidence, max({i for i in confidences if i}))
325
326        new_props["building"] = self.class_ if self.class_ else "yes"
327
328        new_props["source"] = source_statement(self.sources)
329
330        prop_obj = self.model_dump(exclude_none=True, by_alias=True).items()
331        new_props.update(
332            {k: v for k, v in prop_obj if k.startswith(("roof", "building"))}
333        )
334        new_props.update({k: round(v, 2) for k, v in prop_obj if k.endswith("height")})
335
336        if self.is_underground:
337            new_props["location"] = "underground"
338        if self.names:
339            new_props["name"] = self.names.primary
340        return new_props

Overture building properties.

Use this model if you want to manipulate the building properties yourself.

has_parts: bool
sources: List[Sources]
class_: Optional[str]
subtype: Optional[str]
names: Optional[Names]
level: Optional[int]
height: Optional[float]
is_underground: Optional[bool]
num_floors: Optional[int]
num_floors_underground: Optional[int]
min_height: Optional[float]
min_floor: Optional[int]
facade_color: Optional[str]
facade_material: Optional[str]
roof_material: Optional[str]
roof_shape: Optional[str]
roof_direction: Optional[str]
roof_orientation: Optional[str]
roof_color: Optional[str]
roof_height: Optional[float]
def to_osm(self, confidence: float) -> Dict[str, str]:
316    def to_osm(self, confidence: float) -> Dict[str, str]:
317        """Convert properties to OSM tags.
318
319        Used internally by`overturetoosm.process_building` function.
320        """
321        new_props = {}
322        confidences = {source.confidence for source in self.sources}
323        if any(conf and conf < confidence for conf in confidences):
324            raise ConfidenceError(confidence, max({i for i in confidences if i}))
325
326        new_props["building"] = self.class_ if self.class_ else "yes"
327
328        new_props["source"] = source_statement(self.sources)
329
330        prop_obj = self.model_dump(exclude_none=True, by_alias=True).items()
331        new_props.update(
332            {k: v for k, v in prop_obj if k.startswith(("roof", "building"))}
333        )
334        new_props.update({k: round(v, 2) for k, v in prop_obj if k.endswith("height")})
335
336        if self.is_underground:
337            new_props["location"] = "underground"
338        if self.names:
339            new_props["name"] = self.names.primary
340        return new_props

Convert properties to OSM tags.

Used internally byoverturetoosm.process_building function.

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'version': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'theme': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'type': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, metadata=[_PydanticGeneralMetadata(pattern='^(\\S.*)?\\S$')]), 'has_parts': FieldInfo(annotation=bool, required=True), 'sources': FieldInfo(annotation=List[Sources], required=True), 'class_': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias='class', alias_priority=2), 'subtype': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'names': FieldInfo(annotation=Union[Names, NoneType], required=False, default=None), 'level': FieldInfo(annotation=Union[int, NoneType], required=False, default=None), 'height': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'is_underground': FieldInfo(annotation=Union[bool, NoneType], required=False, default=None), 'num_floors': FieldInfo(annotation=Union[int, NoneType], required=False, default=None, alias_priority=2, serialization_alias='building:levels'), 'num_floors_underground': FieldInfo(annotation=Union[int, NoneType], required=False, default=None, alias_priority=2, serialization_alias='building:levels:underground'), 'min_height': FieldInfo(annotation=Union[float, NoneType], required=False, default=None), 'min_floor': FieldInfo(annotation=Union[int, NoneType], required=False, default=None, alias_priority=2, serialization_alias='building:min_level'), 'facade_color': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='building:colour'), 'facade_material': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='building:material'), 'roof_material': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='roof:material'), 'roof_shape': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='roof:shape'), 'roof_direction': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='roof:direction'), 'roof_orientation': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='roof:orientation'), 'roof_color': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, alias_priority=2, serialization_alias='roof:colour'), 'roof_height': FieldInfo(annotation=Union[float, NoneType], required=False, default=None, alias_priority=2, serialization_alias='roof:height')}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
OvertureBaseModel
version
theme
type
id
class AddressLevel(pydantic.main.BaseModel):
343class AddressLevel(BaseModel):
344    """Overture address level model."""
345
346    value: str

Overture address level model.

value: str
model_config: ClassVar[pydantic.config.ConfigDict] = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'value': FieldInfo(annotation=str, required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
class AddressProps(OvertureBaseModel):
349class AddressProps(OvertureBaseModel):
350    """Overture address properties.
351
352    Use this model directly if you want to manipulate the `address` properties yourself.
353    """
354
355    number: Optional[str] = Field(serialization_alias="addr:housenumber")
356    street: Optional[str] = Field(serialization_alias="addr:street")
357    postcode: Optional[str] = Field(serialization_alias="addr:postcode")
358    country: Optional[str] = Field(serialization_alias="addr:country")
359    address_levels: Optional[
360        Annotated[List[AddressLevel], Field(min_length=1, max_length=5)]
361    ] = Field(default_factory=list)
362    sources: List[Sources]
363
364    def to_osm(self, style: str) -> Dict[str, str]:
365        """Convert properties to OSM tags.
366
367        Used internally by `overturetoosm.process_address`.
368        """
369        obj_dict = {
370            k: v
371            for k, v in self.model_dump(exclude_none=True, by_alias=True).items()
372            if k.startswith("addr:")
373        }
374        obj_dict["source"] = source_statement(self.sources)
375
376        if self.address_levels and len(self.address_levels) > 0 and style == "US":
377            obj_dict["addr:state"] = str(self.address_levels[0].value)
378
379        return obj_dict

Overture address properties.

Use this model directly if you want to manipulate the address properties yourself.

number: Optional[str]
street: Optional[str]
postcode: Optional[str]
country: Optional[str]
address_levels: Optional[Annotated[List[AddressLevel], FieldInfo(annotation=NoneType, required=True, metadata=[MinLen(min_length=1), MaxLen(max_length=5)])]]
sources: List[Sources]
def to_osm(self, style: str) -> Dict[str, str]:
364    def to_osm(self, style: str) -> Dict[str, str]:
365        """Convert properties to OSM tags.
366
367        Used internally by `overturetoosm.process_address`.
368        """
369        obj_dict = {
370            k: v
371            for k, v in self.model_dump(exclude_none=True, by_alias=True).items()
372            if k.startswith("addr:")
373        }
374        obj_dict["source"] = source_statement(self.sources)
375
376        if self.address_levels and len(self.address_levels) > 0 and style == "US":
377            obj_dict["addr:state"] = str(self.address_levels[0].value)
378
379        return obj_dict

Convert properties to OSM tags.

Used internally by overturetoosm.process_address.

model_config = {'extra': 'forbid'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

model_fields: ClassVar[Dict[str, pydantic.fields.FieldInfo]] = {'version': FieldInfo(annotation=int, required=True, metadata=[Ge(ge=0)]), 'theme': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'type': FieldInfo(annotation=Union[str, NoneType], required=False, default=None), 'id': FieldInfo(annotation=Union[str, NoneType], required=False, default=None, metadata=[_PydanticGeneralMetadata(pattern='^(\\S.*)?\\S$')]), 'number': FieldInfo(annotation=Union[str, NoneType], required=True, alias_priority=2, serialization_alias='addr:housenumber'), 'street': FieldInfo(annotation=Union[str, NoneType], required=True, alias_priority=2, serialization_alias='addr:street'), 'postcode': FieldInfo(annotation=Union[str, NoneType], required=True, alias_priority=2, serialization_alias='addr:postcode'), 'country': FieldInfo(annotation=Union[str, NoneType], required=True, alias_priority=2, serialization_alias='addr:country'), 'address_levels': FieldInfo(annotation=Union[Annotated[List[AddressLevel], FieldInfo(annotation=NoneType, required=True, metadata=[MinLen(min_length=1), MaxLen(max_length=5)])], NoneType], required=False, default_factory=list), 'sources': FieldInfo(annotation=List[Sources], required=True)}

Metadata about the fields defined on the model, mapping of field names to [FieldInfo][pydantic.fields.FieldInfo] objects.

This replaces Model.__fields__ from Pydantic V1.

model_computed_fields: ClassVar[Dict[str, pydantic.fields.ComputedFieldInfo]] = {}

A dictionary of computed field names and their corresponding ComputedFieldInfo objects.

Inherited Members
pydantic.main.BaseModel
BaseModel
model_extra
model_fields_set
model_construct
model_copy
model_dump
model_dump_json
model_json_schema
model_parametrized_name
model_post_init
model_rebuild
model_validate
model_validate_json
model_validate_strings
dict
json
parse_obj
parse_raw
parse_file
from_orm
construct
copy
schema
schema_json
validate
update_forward_refs
OvertureBaseModel
version
theme
type
id
def source_statement(source: List[Sources]) -> str:
382def source_statement(source: List[Sources]) -> str:
383    """Return a source statement from a list of sources."""
384    return (
385        ", ".join(sorted({i.dataset.strip(", ") for i in source}))
386        + " via overturetoosm"
387    )

Return a source statement from a list of sources.