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