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

1"""Convert Overture's `places` features to OSM tags.""" 

2 

3from typing import Literal, Dict 

4 

5from .objects import PlaceProps, UnmatchedError, ConfidenceError 

6from .resources import places_tags 

7 

8 

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. 

16 

17 Example usage: 

18 ```python 

19 import json 

20 from overturetoosm.places import process_place 

21 

22 with open("overture.geojson", "r", encoding="utf-8") as f: 

23 contents: dict = json.load(f) 

24 

25 for feature in contents["features"]: 

26 feature["properties"] = process_place(feature["properties"]) 

27 

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". 

40 

41 Returns: 

42 dict[str, str]: The reshaped and converted properties in OSM's flat str:str schema. 

43 

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) 

53 

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) 

62 

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 

65 

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] 

68 

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] 

71 

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 

83 

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 ) 

88 

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 

95 

96 if prop_obj.brand: 

97 new_props["brand"] = prop_obj.brand.names.primary 

98 new_props["brand:wikidata"] = prop_obj.brand.wikidata 

99 

100 return new_props