-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnormalise-housenumbers.py
executable file
·112 lines (90 loc) · 3.97 KB
/
normalise-housenumbers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#!/usr/bin/env python
from xmltodict import parse, unparse
import re
import requests
"""
This program normalises housenumbers in an overpass query.
Upload the output with josm.
https://overpass-api.de/api/interpreter?data=%5Bout%3Axml%5D%5Btimeout%3A25%5D%3Barea%283602171347%29%2D%3E%2EsearchArea%3B%28node%5B%22addr%3Ahousenumber%22%5D%5B%22addr%3Ahousenumber%22%21%7E%22%5E%5B1%2D9%5D%5B0%2D9%5D%7B0%2C2%7D%5BA%2DZ%5D%7B0%2C3%7D%28%5B%2D%5D%2A%5B1%2D9%5D%5B0%2D9%5D%7B0%2C2%7D%5BA%2DZ%5D%7B0%2C3%7D%29%7B0%2C12%7D%24%22%5D%28area%2EsearchArea%29%3Bway%5B%22addr%3Ahousenumber%22%5D%5B%22addr%3Ahousenumber%22%21%7E%22%5E%5B1%2D9%5D%5B0%2D9%5D%7B0%2C2%7D%5BA%2DZ%5D%7B0%2C3%7D%28%5B%2D%5D%2A%5B1%2D9%5D%5B0%2D9%5D%7B0%2C2%7D%5BA%2DZ%5D%7B0%2C3%7D%29%7B0%2C12%7D%24%22%5D%28area%2EsearchArea%29%3Brelation%5B%22addr%3Ahousenumber%22%5D%5B%22addr%3Ahousenumber%22%21%7E%22%5E%5B1%2D9%5D%5B0%2D9%5D%7B0%2C2%7D%5BA%2DZ%5D%7B0%2C3%7D%28%5B%2D%5D%2A%5B1%2D9%5D%5B0%2D9%5D%7B0%2C2%7D%5BA%2DZ%5D%7B0%2C3%7D%29%7B0%2C12%7D%24%22%5D%28area%2EsearchArea%29%3B%29%3Bout%3B%3E%3Bout%20skel%20qt%3B%0A
"""
def handletags(taglist):
# Valid worst case: 1BIS-2BBB
re_validaddress = re.compile(
"^[1-9][0-9]{0,2}[A-Z]{0,3}([-][1-9][0-9]{0,2}[A-Z]{0,3}){0,1}$"
)
iterate = False
for tag in taglist:
if iterate == True:
break
if tag["@k"] == "addr:housenumber":
iterate = True # stop looping over tags after this one
# Don't touch if address already valid
if re_validaddress.match(tag["@v"]):
return False
# This is where the magic happens
# lowercase, e.g. "79a" becomes "79A"
# whitespace, e.g. "79 - 79A" becomes "79-79A"
re_whitespace = re.compile(" ")
# Bad connector, 91/93 or 25;26 or 12,14
re_badconnector = re.compile("[;/&.,]")
orig_v = tag["@v"]
tag["@v"] = re_whitespace.sub(
"", re_badconnector.sub("-", tag["@v"])
).upper()
# Sanity check
if not re_validaddress.match(tag["@v"]):
print("Oops! Weird address: {}".format(orig_v))
return False
return True
return False
overpass_query = """
[out:xml][timeout:25];
area(3602171347)->.searchArea;
(
node["addr:housenumber"]["ref:caclr"!~".*"]["addr:housenumber"!~"^[1-9][0-9]{0,2}[A-Z]{0,3}([-]*[1-9][0-9]{0,2}[A-Z]{0,3}){0,12}$"](area.searchArea);
way["addr:housenumber"]["ref:caclr"!~".*"]["addr:housenumber"!~"^[1-9][0-9]{0,2}[A-Z]{0,3}([-]*[1-9][0-9]{0,2}[A-Z]{0,3}){0,12}$"](area.searchArea);
relation["addr:housenumber"]["ref:caclr"!~".*"]["addr:housenumber"!~"^[1-9][0-9]{0,2}[A-Z]{0,3}([-]*[1-9][0-9]{0,2}[A-Z]{0,3}){0,12}$"](area.searchArea);
);
(._;>;);
out meta qt;
"""
overpass_interpreter = "https://overpass-api.de/api/interpreter"
# overpass_interpreter = 'https://overpass.openstreetmap.fr/api/interpreter'
# overpass_interpreter = 'https://stereo.lu/missing-streetname.osm'
osmdata = requests.get(overpass_interpreter, data=overpass_query).text
d = parse(osmdata, force_list=("tag", "node", "way", "relation"))
d["osm"]["@upload"] = "false"
# Ignore KeyError on tag if we have untagged node/way
try:
address_nodes = d["osm"]["node"]
except KeyError:
pass
else:
for a_n in address_nodes:
try:
if handletags(a_n["tag"]):
a_n["@action"] = "modify"
except KeyError:
pass
try:
address_ways = d["osm"]["way"]
except KeyError:
pass
else:
for a_w in address_ways:
try:
if handletags(a_w["tag"]):
a_w["@action"] = "modify"
except KeyError:
pass
try:
address_relations = d["osm"]["relation"]
except KeyError:
pass
else:
for a_r in address_relations:
if handletags(a_r["tag"]):
del a_r["center"]
a_r["@action"] = "modify"
with open("housenumber-normalised.osm", "w") as f:
f.write(unparse(d, pretty=True))