Newer
Older
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
113
#!/usr/bin/env python3
# Copyright (c) 2024:
# Helmholtz-Zentrum Potsdam Deutsches GeoForschungsZentrum GFZ
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero
# General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
from rulelib import AbstractRule
class YearOfConstructionRule(AbstractRule):
def __call__(
self,
tags: dict,
relations: list,
year_of_construction_cadaster: int | None = None,
*args,
**kwargs
) -> dict[str, int | None]:
"""
Obtain the year of construction of a building from any available source, presently only
OSM and cadaster data. In OSM, the time of construction is stored in the `start_date`
tag of which only an explicit four-digit year with or without a leading `~` (signifying
an approximation) are considered. Priority is given to exact years of construction from
OSM, followed by those from the cadaster data and finally approximate dates from OSM.
Args:
tags (dict):
Building tags, such as the building levels or building type.
relations (list):
List of the attributes of all relations that the building is member of.
year_of_construction_cadaster (int, optional, default: None):
Year of construction taken from cadaster data.
Returns:
dict[str, int | None]:
A dictionary with the construction year of the building as an integer or None.
"""
all_building_tags = [tags] + [building["tags"] for building in relations]
year_of_construction_string_approximate = None
for tags in all_building_tags:
year_of_construction_string = self.get_start_date(tags)
if year_of_construction_string is None:
continue
else:
# Years with leading `~` are approximate dates, these values are set
# last in the priority list if other sources are available.
if (
year_of_construction_string[0] == "~"
and year_of_construction_string_approximate is None
):
year_of_construction_string_approximate = year_of_construction_string[1:5]
continue
# Years from OSM without a leading `~` are at the top of the priority list,
# and returned as soon as one is found.
else:
year_of_construction_string = year_of_construction_string[:4]
try:
year_of_construction = int(year_of_construction_string)
return {"year_of_construction": year_of_construction}
except ValueError:
continue
# If no unambiguous year is found from the OSM tags, the cadaster year of construction
# is returned, second in the priority list.
if year_of_construction_cadaster is not None:
return {"year_of_construction": year_of_construction_cadaster}
# If the there is no construction year from the cadaster data, the approximate year is
# returned.
elif year_of_construction_string_approximate is not None:
try:
year_of_construction = int(year_of_construction_string_approximate)
return {"year_of_construction": year_of_construction}
except ValueError:
return {"year_of_construction": None}
# If no source has a valid year of construction, None is returned.
else:
return {"year_of_construction": None}
@staticmethod
def get_start_date(tags: dict) -> str | None:
"""
Get the building year of construction based on the year in the `start_date` tag from
the OSM tags dictionary.
Args:
tags (dict):
Dictionary with building tags from OSM, which may include `start_date`, `name`,
`type`, etc.
Returns:
str | None:
String value of the tag `start_date` which represents the date in which a
building was completed, otherwise None. The date format may vary, as there are
many variations within the OSM `start_date` tag, for this rule, only the year is
relevant.
"""
return tags.get("start_date", None)