Coverage for src/overturetoosm/places.py: 86%
45 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-07-28 10:12 -0400
« prev ^ index » next coverage.py v7.6.0, created at 2024-07-28 10:12 -0400
1"""Convert Overture's `places` features to OSM tags."""
3from typing import Literal, Dict
5from .objects import PlaceProps, UnmatchedError, ConfidenceError
6from .resources import places_tags
9def process_place(
10 props: dict,
11 confidence: float = 0.0,
12 region_tag: str = "addr:state",
13 unmatched: Literal["error", "force", "ignore"] = "ignore",
14) -> Dict[str, str]:
15 """Convert Overture's places properties to OSM tags.
17 Example usage:
18 ```python
19 import json
20 from overturetoosm.places import process_place
22 with open("overture.geojson", "r", encoding="utf-8") as f:
23 contents: dict = json.load(f)
25 for feature in contents["features"]:
26 feature["properties"] = process_place(feature["properties"])
28 with open("overture_out.geojson", "w+", encoding="utf-8") as x:
29 json.dump(contents, x, indent=4)
30 ```
31 Args:
32 props (dict): The feature properties from the Overture GeoJSON.
33 region_tag (str, optional): What tag to convert Overture's `region` tag to.
34 Defaults to `addr:state`.
35 confidence (float, optional): The minimum confidence level. Defaults to 0.0.
36 unmatched (Literal["error", "force", "ignore"], optional): How to handle
37 unmatched Overture categories. The "error" option raises an UnmatchedError
38 exception, "force" puts the category into the `type` key, and "ignore"
39 only returns other properties. Defaults to "ignore".
41 Returns:
42 dict[str, str]: The reshaped and converted properties in OSM's flat str:str schema.
44 Raises:
45 `UnmatchedError`: Raised if `unmatched` is set to `error` and the Overture category
46 has no OSM definition.
47 `ConfidenceError`: Raised if the confidence level is set above a feature's confidence.
48 """
49 new_props = {}
50 prop_obj = PlaceProps(**props)
51 if prop_obj.confidence < confidence:
52 raise ConfidenceError(confidence, prop_obj.confidence)
54 if prop_obj.categories:
55 prim = places_tags.get(prop_obj.categories.main)
56 if prim:
57 new_props = {**new_props, **prim}
58 elif unmatched == "force":
59 new_props["type"] = prop_obj.categories.main
60 elif unmatched == "error":
61 raise UnmatchedError(prop_obj.categories.main)
63 if prop_obj.names.primary: 63 ↛ 66line 63 didn't jump to line 66 because the condition on line 63 was always true
64 new_props["name"] = prop_obj.names.primary
66 if prop_obj.phones is not None: 66 ↛ 69line 66 didn't jump to line 69 because the condition on line 66 was always true
67 new_props["phone"] = prop_obj.phones[0]
69 if prop_obj.websites is not None: 69 ↛ 72line 69 didn't jump to line 72 because the condition on line 69 was always true
70 new_props["website"] = prop_obj.websites[0]
72 if add := prop_obj.addresses[0]: 72 ↛ 84line 72 didn't jump to line 84 because the condition on line 72 was always true
73 if add.freeform: 73 ↛ 75line 73 didn't jump to line 75 because the condition on line 73 was always true
74 new_props["addr:street_address"] = add.freeform
75 if add.country: 75 ↛ 77line 75 didn't jump to line 77 because the condition on line 75 was always true
76 new_props["addr:country"] = add.country
77 if add.postcode: 77 ↛ 79line 77 didn't jump to line 79 because the condition on line 77 was always true
78 new_props["addr:postcode"] = add.postcode
79 if add.locality: 79 ↛ 81line 79 didn't jump to line 81 because the condition on line 79 was always true
80 new_props["addr:city"] = add.locality
81 if add.region: 81 ↛ 84line 81 didn't jump to line 84 because the condition on line 81 was always true
82 new_props[region_tag] = add.region
84 if prop_obj.sources: 84 ↛ 89line 84 didn't jump to line 89 because the condition on line 84 was always true
85 new_props["source"] = (
86 ", ".join({i.dataset for i in prop_obj.sources}) + " via overturetoosm"
87 )
89 if prop_obj.socials is not None: 89 ↛ 96line 89 didn't jump to line 96 because the condition on line 89 was always true
90 for social in prop_obj.socials:
91 if "facebook" in social:
92 new_props["contact:facebook"] = social
93 elif "twitter" in social: 93 ↛ 90line 93 didn't jump to line 90 because the condition on line 93 was always true
94 new_props["contact:twitter"] = social
96 if prop_obj.brand:
97 new_props["brand"] = prop_obj.brand.names.primary
98 new_props["brand:wikidata"] = prop_obj.brand.wikidata
100 return new_props