monumenten 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
monumenten/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Package for retrieving monument data from various Dutch government APIs."""
2
+
3
+ from .client import MonumentenClient
4
+
5
+ __all__ = ["MonumentenClient"]
File without changes
@@ -0,0 +1,161 @@
1
+ import asyncio
2
+ import logging
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import aiohttp
6
+
7
+ # Create a module-level logger
8
+ logger = logging.getLogger("monumenten.api.cultureel_erfgoed")
9
+
10
+ _CULTUREEL_ERFGOED_SPARQL_ENDPOINT = (
11
+ "https://api.linkeddata.cultureelerfgoed.nl/datasets/rce/cho/sparql"
12
+ )
13
+
14
+ _RIJKSMONUMENTEN_QUERY_TEMPLATE = """
15
+ PREFIX ceo:<https://linkeddata.cultureelerfgoed.nl/def/ceo#>
16
+ PREFIX bag:<http://bag.basisregistraties.overheid.nl/bag/id/>
17
+ PREFIX rn:<https://data.cultureelerfgoed.nl/term/id/rn/>
18
+ SELECT ?identificatie (MAX(?nummer) as ?rijksmonument_nummer)
19
+ WHERE {{
20
+ ?monument ceo:heeftJuridischeStatus rn:b2d9a59a-fe1e-4552-9a05-3c2acddff864 ;
21
+ ceo:rijksmonumentnummer ?nummer ;
22
+ ceo:heeftBasisregistratieRelatie ?basisregistratieRelatie .
23
+ ?basisregistratieRelatie ceo:heeftBAGRelatie ?bagRelatie .
24
+ ?bagRelatie ceo:verblijfsobjectIdentificatie ?identificatie .
25
+ VALUES ?identificatie {{ {identificaties} }}
26
+ }}
27
+ GROUP BY ?identificatie
28
+ """
29
+
30
+ _BESCHERMDE_GEZICHTEN_QUERY = """
31
+ PREFIX ceo:<https://linkeddata.cultureelerfgoed.nl/def/ceo#>
32
+ PREFIX rn:<https://data.cultureelerfgoed.nl/term/id/rn/>
33
+ PREFIX geo: <http://www.opengis.net/ont/geosparql#>
34
+ SELECT DISTINCT ?gezicht ?beschermd_gezicht_naam ?gezichtWKT
35
+ WHERE {{
36
+ ?gezicht
37
+ ceo:heeftGeometrie ?gezichtGeometrie ;
38
+ ceo:heeftGezichtsstatus rn:fd968529-bf70-4afa-8564-7c6c2fcfcc54;
39
+ ceo:heeftNaam/ceo:naam ?beschermd_gezicht_naam.
40
+ ?gezichtGeometrie geo:asWKT ?gezichtWKT.
41
+ }}
42
+ """
43
+
44
+ _cultureel_erfgoed_semaphore: Optional[asyncio.Semaphore] = None
45
+
46
+
47
+ def _get_semaphore(loop: asyncio.AbstractEventLoop) -> asyncio.Semaphore:
48
+ """
49
+ Verkrijgt een semaphore voor het beperken van gelijktijdige aanvragen naar de Cultureel Erfgoed API.
50
+
51
+ Args:
52
+ loop (asyncio.AbstractEventLoop): De asyncio event loop
53
+
54
+ Returns:
55
+ asyncio.Semaphore: Een asyncio.Semaphore object dat het aantal gelijktijdige aanvragen beperkt tot 4
56
+ """
57
+ global _cultureel_erfgoed_semaphore
58
+ if _cultureel_erfgoed_semaphore is None:
59
+ _cultureel_erfgoed_semaphore = asyncio.Semaphore(4)
60
+ return _cultureel_erfgoed_semaphore
61
+
62
+
63
+ async def _query_rijksmonumenten(
64
+ session: aiohttp.ClientSession, identificaties: List[str]
65
+ ) -> List[Dict[str, Any]]:
66
+ """
67
+ Voert een SPARQL-query uit om rijksmonumenten op te halen voor gegeven BAG-identificaties.
68
+
69
+ Args:
70
+ session (aiohttp.ClientSession): De aiohttp ClientSession voor het uitvoeren van de HTTP-aanvraag
71
+ identificaties (List[str]): Lijst van BAG-identificaties waarvoor rijksmonumenten worden opgezocht
72
+
73
+ Returns:
74
+ List[Dict[str, Any]]: Lijst van dictionaries met informatie over gevonden rijksmonumenten
75
+
76
+ Raises:
77
+ aiohttp.ClientResponseError: Bij fouten in de HTTP-aanvraag na 3 pogingen
78
+ """
79
+ async with _get_semaphore(asyncio.get_running_loop()):
80
+ identificaties_str = " ".join(
81
+ f'"{identificatie}"' for identificatie in identificaties
82
+ )
83
+ query = _RIJKSMONUMENTEN_QUERY_TEMPLATE.format(
84
+ identificaties=identificaties_str
85
+ )
86
+ data = {"query": query, "format": "json"}
87
+ retries = 3
88
+ for poging in range(retries):
89
+ try:
90
+ async with session.post(
91
+ _CULTUREEL_ERFGOED_SPARQL_ENDPOINT, data=data
92
+ ) as response:
93
+ response.raise_for_status()
94
+ resultaat = await response.json()
95
+ if isinstance(resultaat, list):
96
+ return resultaat
97
+ else:
98
+ logger.warning(
99
+ "Unexpected response format on attempt %d: %s",
100
+ poging + 1,
101
+ resultaat,
102
+ )
103
+ except aiohttp.ClientResponseError as e:
104
+ if poging != retries - 1:
105
+ logger.warning(
106
+ "Poging %d/%d voor rijksmonumenten query mislukt: %s. Opnieuw proberen over 1 seconde...",
107
+ poging + 1,
108
+ retries,
109
+ str(e),
110
+ )
111
+ await asyncio.sleep(1)
112
+ else:
113
+ raise
114
+ return List[Dict[str, Any]]()
115
+
116
+
117
+ async def _query_beschermde_gebieden(
118
+ session: aiohttp.ClientSession,
119
+ ) -> List[Dict[str, Any]]:
120
+ """
121
+ Voert een SPARQL-query uit om beschermde stads- en dorpsgezichten op te halen.
122
+
123
+ Args:
124
+ session (aiohttp.ClientSession): De aiohttp ClientSession voor het uitvoeren van de HTTP-aanvraag
125
+
126
+ Returns:
127
+ List[Dict[str, Any]]: Lijst van dictionaries met informatie over beschermde stads- en dorpsgezichten
128
+
129
+ Raises:
130
+ aiohttp.ClientResponseError: Bij fouten in de HTTP-aanvraag na 3 pogingen
131
+ """
132
+ retries = 3
133
+ for poging in range(retries):
134
+ try:
135
+ data = {"query": _BESCHERMDE_GEZICHTEN_QUERY, "format": "json"}
136
+
137
+ async with session.post(
138
+ _CULTUREEL_ERFGOED_SPARQL_ENDPOINT, data=data
139
+ ) as response:
140
+ response.raise_for_status()
141
+ resultaat = await response.json()
142
+ if isinstance(resultaat, list):
143
+ return resultaat
144
+ else:
145
+ logger.warning(
146
+ "Onverwacht response formaat bij poging %d: %s",
147
+ poging + 1,
148
+ resultaat,
149
+ )
150
+ except aiohttp.ClientResponseError as e:
151
+ if poging != retries - 1:
152
+ logger.warning(
153
+ "Poging %d/%d voor beschermde gebieden query mislukt: %s. Opnieuw proberen over 1 seconde...",
154
+ poging + 1,
155
+ retries,
156
+ str(e),
157
+ )
158
+ await asyncio.sleep(1)
159
+ else:
160
+ raise
161
+ return List[Dict[str, Any]]()
@@ -0,0 +1,77 @@
1
+ import asyncio
2
+ import logging
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import aiohttp
6
+
7
+ _KADASTER_SPARQL_ENDPOINT = (
8
+ "https://api.labs.kadaster.nl/datasets/dst/kkg/services/default/sparql"
9
+ )
10
+
11
+ _VERBLIJFSOBJECTEN_QUERY_TEMPLATE = """
12
+ PREFIX sor: <https://data.kkg.kadaster.nl/sor/model/def/>
13
+ PREFIX nen3610: <https://data.kkg.kadaster.nl/nen3610/model/def/>
14
+ PREFIX geo: <http://www.opengis.net/ont/geosparql#>
15
+ SELECT DISTINCT ?identificatie ?verblijfsobjectWKT
16
+ WHERE {{
17
+ ?verblijfsobject sor:geregistreerdMet/nen3610:identificatie ?identificatie .
18
+ ?verblijfsobject geo:hasGeometry/geo:asWKT ?verblijfsobjectWKT.
19
+ FILTER (?identificatie IN ( {identificaties} ))
20
+ }}
21
+ """
22
+
23
+ _kadaster_semaphore: Optional[asyncio.Semaphore] = None
24
+
25
+ # Create a module-level logger
26
+ logger = logging.getLogger("monumenten.api.kadaster")
27
+
28
+
29
+ def _get_semaphore(loop: asyncio.AbstractEventLoop) -> asyncio.Semaphore:
30
+ global _kadaster_semaphore
31
+ if _kadaster_semaphore is None:
32
+ _kadaster_semaphore = asyncio.Semaphore(4)
33
+ return _kadaster_semaphore
34
+
35
+
36
+ async def _query_verblijfsobjecten(
37
+ session: aiohttp.ClientSession, identificaties: List[str]
38
+ ) -> List[Dict[str, Any]]:
39
+ async with _get_semaphore(asyncio.get_running_loop()):
40
+ identificaties_str = ", ".join(
41
+ f'"{identificatie}"' for identificatie in identificaties
42
+ )
43
+ query = _VERBLIJFSOBJECTEN_QUERY_TEMPLATE.format(
44
+ identificaties=identificaties_str
45
+ )
46
+ data = {"query": query, "format": "json"}
47
+ retries = 3
48
+ for poging in range(retries):
49
+ try:
50
+ async with session.post(
51
+ _KADASTER_SPARQL_ENDPOINT, data=data
52
+ ) as response:
53
+ response.raise_for_status()
54
+ resultaat = await response.json()
55
+ if isinstance(resultaat, list):
56
+ return resultaat
57
+ else:
58
+ logger.warning(
59
+ "Onverwacht response formaat bij poging %d: %s",
60
+ poging + 1,
61
+ resultaat,
62
+ )
63
+ except aiohttp.ClientResponseError as e:
64
+ if poging != retries - 1:
65
+ logger.warning(
66
+ "Poging %d/%d voor verblijfsobjecten query mislukt: %s. Opnieuw proberen over 1 seconde...",
67
+ poging + 1,
68
+ retries,
69
+ str(e),
70
+ )
71
+ await asyncio.sleep(1)
72
+ else:
73
+ logger.error(
74
+ "Alle pogingen voor verblijfsobjecten query mislukt: %s", str(e)
75
+ )
76
+ raise
77
+ return List[Dict[str, Any]]()
@@ -0,0 +1,151 @@
1
+ """Interne processing functies voor de monumenten package."""
2
+
3
+ import asyncio
4
+ from typing import Any, Dict, List, Tuple
5
+
6
+ import aiohttp
7
+ import geopandas as gpd
8
+ import pandas as pd
9
+ from tqdm.asyncio import tqdm_asyncio
10
+
11
+ from monumenten._api._cultureel_erfgoed import (
12
+ _query_beschermde_gebieden,
13
+ _query_rijksmonumenten,
14
+ )
15
+ from monumenten._api._kadaster import _query_verblijfsobjecten
16
+
17
+ _QUERY_BATCH_GROOTTE = 500 # lijkt meest optimaal qua performance
18
+
19
+
20
+ async def _verzamel_data(
21
+ session: aiohttp.ClientSession,
22
+ identificaties_batch: List[str],
23
+ ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
24
+ """Verzamel data van verschillende API's voor een batch verblijfsobjecten.
25
+
26
+ Args:
27
+ session (aiohttp.ClientSession): De sessie voor HTTP requests
28
+ identificaties_batch (List[str]): Lijst met verblijfsobject ID's
29
+
30
+ Returns:
31
+ Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: Tuple met rijksmonumenten en verblijfsobjecten data
32
+ """
33
+ rijksmonumenten_taak = _query_rijksmonumenten(session, identificaties_batch)
34
+ verblijfsobjecten_taak = _query_verblijfsobjecten(session, identificaties_batch)
35
+ return await asyncio.gather(rijksmonumenten_taak, verblijfsobjecten_taak)
36
+
37
+
38
+ async def _process_batch(
39
+ session: aiohttp.ClientSession, batch: List[str], bg_df: gpd.GeoDataFrame
40
+ ) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]], int]:
41
+ """Verwerk een batch verblijfsobjecten.
42
+
43
+ Args:
44
+ session (aiohttp.ClientSession): De sessie voor HTTP requests
45
+ batch (List[str]): Lijst met verblijfsobject ID's
46
+ bg_df (gpd.GeoDataFrame): GeoDataFrame met beschermde gezichten
47
+
48
+ Returns:
49
+ Tuple[List[Dict[str, Any]], List[Dict[str, Any]], int]: Tuple met rijksmonumenten,
50
+ beschermde gezichten en aantal verwerkte objecten
51
+ """
52
+ # Get the current event loop
53
+ loop = asyncio.get_running_loop()
54
+
55
+ # Create tasks using the current loop
56
+ rijksmonumenten_taak = loop.create_task(_query_rijksmonumenten(session, batch))
57
+ verblijfsobjecten_taak = loop.create_task(_query_verblijfsobjecten(session, batch))
58
+
59
+ # Wait for both tasks to complete
60
+ rijksmonumenten, verblijfsobjecten = await asyncio.gather(
61
+ rijksmonumenten_taak, verblijfsobjecten_taak
62
+ )
63
+
64
+ # Process 'verblijfsobjecten' into GeoDataFrame
65
+ if verblijfsobjecten:
66
+ vo_df = pd.DataFrame(verblijfsobjecten)
67
+ vo_df["geometry"] = gpd.GeoSeries.from_wkt(vo_df["verblijfsobjectWKT"])
68
+ vo_df = gpd.GeoDataFrame(
69
+ vo_df[["identificatie", "geometry"]], geometry="geometry"
70
+ )
71
+ else:
72
+ vo_df = gpd.GeoDataFrame(columns=["identificatie", "geometry"])
73
+
74
+ # Perform spatial join
75
+ if not vo_df.empty:
76
+ joined_df = gpd.sjoin(vo_df, bg_df, how="left", predicate="within")
77
+
78
+ verblijfsobjecten_in_beschermd_gezicht = joined_df[
79
+ ["identificatie", "beschermd_gezicht_naam"]
80
+ ].to_dict("records")
81
+ else:
82
+ verblijfsobjecten_in_beschermd_gezicht = List[Dict[str, Any]]()
83
+
84
+ return rijksmonumenten, verblijfsobjecten_in_beschermd_gezicht, len(batch)
85
+
86
+
87
+ async def _query(
88
+ session: aiohttp.ClientSession, verblijfsobject_ids: List[str]
89
+ ) -> pd.DataFrame:
90
+ """Voer queries uit voor een lijst verblijfsobjecten.
91
+
92
+ Args:
93
+ session (aiohttp.ClientSession): De sessie voor HTTP requests
94
+ verblijfsobject_ids (List[str]): Lijst met verblijfsobject ID's
95
+
96
+ Returns:
97
+ pd.DataFrame: DataFrame met monumentinformatie
98
+ """
99
+ # Load 'beschermde_gebieden' and convert to GeoDataFrame
100
+ beschermde_gebieden = await _query_beschermde_gebieden(session)
101
+ bg_df = gpd.GeoDataFrame()
102
+
103
+ if beschermde_gebieden:
104
+ bg_df = pd.DataFrame(beschermde_gebieden)
105
+ bg_df["geometry"] = gpd.GeoSeries.from_wkt(bg_df["gezichtWKT"])
106
+ bg_df = gpd.GeoDataFrame(
107
+ bg_df[["beschermd_gezicht_naam", "geometry"]], geometry="geometry"
108
+ )
109
+
110
+ # Ensure spatial index is built
111
+ bg_df.sindex
112
+
113
+ rijksmonumenten_result = list[Dict[str, Any]]()
114
+ verblijfsobjecten_in_beschermd_gezicht_result = list[Dict[str, Any]]()
115
+
116
+ # Prepare batches
117
+ batches = [
118
+ verblijfsobject_ids[i : i + _QUERY_BATCH_GROOTTE]
119
+ for i in range(0, len(verblijfsobject_ids), _QUERY_BATCH_GROOTTE)
120
+ ]
121
+
122
+ # Create tasks for each batch
123
+ tasks = [_process_batch(session, batch, bg_df) for batch in batches]
124
+
125
+ progress_bar = tqdm_asyncio(total=len(verblijfsobject_ids))
126
+
127
+ for task in asyncio.as_completed(tasks):
128
+ result = await task
129
+ rijksmonumenten_result.extend(result[0])
130
+ verblijfsobjecten_in_beschermd_gezicht_result.extend(result[1])
131
+ progress_bar.update(result[2])
132
+
133
+ progress_bar.close()
134
+
135
+ df_rijksmonumenten = (
136
+ pd.DataFrame(rijksmonumenten_result)
137
+ if rijksmonumenten_result
138
+ else pd.DataFrame({"identificatie": [], "rijksmonument_nummer": []})
139
+ )
140
+
141
+ df_beschermd_gezicht = (
142
+ pd.DataFrame(verblijfsobjecten_in_beschermd_gezicht_result)
143
+ if verblijfsobjecten_in_beschermd_gezicht_result
144
+ else pd.DataFrame({"identificatie": [], "beschermd_gezicht_naam": []})
145
+ )
146
+
147
+ result = pd.merge(
148
+ df_rijksmonumenten, df_beschermd_gezicht, on="identificatie", how="outer"
149
+ )
150
+
151
+ return result
monumenten/_version.py ADDED
@@ -0,0 +1,16 @@
1
+ # file generated by setuptools_scm
2
+ # don't change, don't track in version control
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '0.1.0'
16
+ __version_tuple__ = version_tuple = (0, 1, 0)
monumenten/client.py ADDED
@@ -0,0 +1,110 @@
1
+ """Client voor monumenten package."""
2
+
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ import aiohttp
6
+ import pandas as pd
7
+
8
+ from monumenten._processing import _query
9
+
10
+
11
+ class MonumentenClient:
12
+ """Client voor het ophalen van monumentgegevens van verschillende Nederlandse overheids-API's.
13
+
14
+ Args:
15
+ session (Optional[aiohttp.ClientSession]): Optionele aiohttp.ClientSession. Indien niet opgegeven wordt
16
+ een nieuwe sessie aangemaakt en beheerd door de client.
17
+ """
18
+
19
+ def __init__(self, session: Optional[aiohttp.ClientSession] = None) -> None:
20
+ self._session = session
21
+ self._owns_session = session is None
22
+
23
+ async def __aenter__(self) -> "MonumentenClient":
24
+ if self._owns_session:
25
+ self._session = aiohttp.ClientSession()
26
+ return self
27
+
28
+ async def __aexit__(
29
+ self,
30
+ exc_type: Optional[type[BaseException]],
31
+ exc_val: Optional[BaseException],
32
+ exc_tb: Optional[Any],
33
+ ) -> None:
34
+ if self._owns_session and self._session:
35
+ await self._session.close()
36
+
37
+ async def process_from_df(
38
+ self,
39
+ df: pd.DataFrame,
40
+ verblijfsobject_id_col: str,
41
+ ) -> pd.DataFrame:
42
+ """Verwerk een DataFrame met verblijfsobject ID's.
43
+
44
+ Args:
45
+ df (pd.DataFrame): Input DataFrame met verblijfsobject ID's
46
+ verblijfsobject_id_col (str): Naam van de kolom met de verblijfsobject ID's
47
+
48
+ Returns:
49
+ pd.DataFrame: DataFrame met toegevoegde monumentinformatie
50
+
51
+ Raises:
52
+ RuntimeError: Als de client niet als context manager wordt gebruikt
53
+ """
54
+ if not self._session:
55
+ raise RuntimeError("Client must be used as a context manager")
56
+
57
+ results = await _query(
58
+ self._session, df[verblijfsobject_id_col].drop_duplicates()
59
+ )
60
+ merged = pd.merge(
61
+ df, results, left_on=verblijfsobject_id_col, right_on="identificatie"
62
+ )
63
+
64
+ if "identificatie" not in df.columns:
65
+ merged = merged.drop(columns=["identificatie"])
66
+
67
+ rijksmonument_nummer_position = merged.columns.get_loc("rijksmonument_nummer")
68
+ merged.insert(
69
+ rijksmonument_nummer_position + 1,
70
+ "rijksmonument_url",
71
+ "https://monumenten.nl/monument/"
72
+ + merged["rijksmonument_nummer"]
73
+ .fillna("")
74
+ .astype(str)
75
+ .where(merged["rijksmonument_nummer"].notna(), None),
76
+ )
77
+
78
+ merged.insert(
79
+ rijksmonument_nummer_position,
80
+ "is_rijksmonument",
81
+ merged["rijksmonument_nummer"].notna(),
82
+ )
83
+
84
+ beschermd_gezicht_naam_position = merged.columns.get_loc(
85
+ "beschermd_gezicht_naam"
86
+ )
87
+
88
+ merged.insert(
89
+ beschermd_gezicht_naam_position,
90
+ "is_beschermd_gezicht",
91
+ merged["beschermd_gezicht_naam"].notna(),
92
+ )
93
+
94
+ return merged
95
+
96
+ async def process_from_list(
97
+ self, verblijfsobject_ids: List[str]
98
+ ) -> List[Dict[str, Any]]:
99
+ """Verwerk een lijst met verblijfsobject ID's.
100
+
101
+ Args:
102
+ verblijfsobject_ids (List[str]): Lijst met te verwerken ID's
103
+
104
+ Returns:
105
+ List[Dict[str, Any]]: Lijst met dictionaries met monumentinformatie
106
+ """
107
+ df = pd.DataFrame({"bag_verblijfsobject_id": verblijfsobject_ids})
108
+ result = await self.process_from_df(df, "bag_verblijfsobject_id")
109
+ result = result.replace({pd.NA: None, pd.NaT: None})
110
+ return list(result.to_dict(orient="records"))
monumenten/logging.py ADDED
File without changes
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Woonstad Rotterdam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.1
2
+ Name: monumenten
3
+ Version: 0.1.0
4
+ Summary: Checkt op basis van BAG verblijfsobject-ids of het verblijfsobject een monument en/of beschermd stadsgezicht is.
5
+ Author-email: Woonstad Rotterdam <info@woonstadrotterdam.nl>, Ben Verhees <ben.verhees@woonstadrotterdam.nl>, Tomer Gabay <tomer.gabay@woonstadrotterdam.nl>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2024 Woonstad Rotterdam
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/woonstadrotterdam/monumenten
29
+ Project-URL: Issues, https://github.com/woonstadrotterdam/monumenten/issues
30
+ Keywords: monument,bag,stadsgezicht,monumentenregister,beschermd
31
+ Classifier: Programming Language :: Python :: 3.9
32
+ Classifier: Programming Language :: Python :: 3.10
33
+ Classifier: Programming Language :: Python :: 3.11
34
+ Classifier: Programming Language :: Python :: 3.12
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Requires-Python: >=3.9.5
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: aiohttp>=3.10.10
41
+ Requires-Dist: Shapely>=2.0.6
42
+ Requires-Dist: tqdm>=4.66.5
43
+ Requires-Dist: pandas>=2.0.0
44
+ Requires-Dist: geopandas>=1.0.1
45
+ Provides-Extra: dev
46
+ Requires-Dist: monumenten[test]; extra == "dev"
47
+ Requires-Dist: ruff==0.4.8; extra == "dev"
48
+ Provides-Extra: test
49
+ Requires-Dist: pre-commit==3.*; extra == "test"
50
+ Requires-Dist: pytest==8.*; extra == "test"
51
+ Requires-Dist: pytest-cov==5.*; extra == "test"
52
+ Requires-Dist: pytest-asyncio==0.24.*; extra == "test"
53
+
54
+ # Monumenten
55
+
56
+ Een Python package voor het ophalen van monumentgegevens van Nederlandse overheids-API's. Momenteel is het mogelijk om rijksmonumenten en beschermd gezichten op te halen.
57
+
58
+ Door middel van dit package is het mogelijk om, indienst gewenst, voor tienduizenden verblijfsobjecten per seconde monumentgegevens op te halen. Er zijn geen API-keys nodig.
59
+
60
+ ## Installatie
61
+
62
+ `pip install monumenten`
63
+
64
+ ## Gebruik
65
+
66
+ ### Python-native 🐍
67
+
68
+ ```python
69
+ import asyncio
70
+
71
+ from monumenten import MonumentenClient
72
+
73
+ async def main():
74
+ bag_verblijfsobject_ids = [
75
+ "0599010000360091",
76
+ "0599010000486642",
77
+ "0599010000281115",
78
+ ]
79
+
80
+ async with MonumentenClient() as client:
81
+ result = await client.process_from_list(bag_verblijfsobject_ids)
82
+ print(result)
83
+
84
+ # in een .py file"
85
+ if __name__ == "__main__":
86
+ asyncio.run(main())
87
+
88
+ # in een .ipynb file (notebook):
89
+ await main()
90
+ ```
91
+
92
+ ```python
93
+ # OUTPUT
94
+ [
95
+ {
96
+ 'bag_verblijfsobject_id': '0599010000360091',
97
+ 'is_rijksmonument': True,
98
+ 'rijksmonument_nummer': '524327',
99
+ 'rijksmonument_url': 'https://monumenten.nl/monument/524327',
100
+ 'is_beschermd_gezicht': False,
101
+ 'beschermd_gezicht_naam': None
102
+ },
103
+ {
104
+ 'bag_verblijfsobject_id': '0599010000486642',
105
+ 'is_rijksmonument': False,
106
+ 'rijksmonument_nummer': None,
107
+ 'rijksmonument_url': None,
108
+ 'is_beschermd_gezicht': False,
109
+ 'beschermd_gezicht_naam': None
110
+ },
111
+ {
112
+ 'bag_verblijfsobject_id': '0599010000281115',
113
+ 'is_rijksmonument': False,
114
+ 'rijksmonument_nummer': None,
115
+ 'rijksmonument_url': None,
116
+ 'is_beschermd_gezicht': True,
117
+ 'beschermd_gezicht_naam': 'Kralingen - Midden'
118
+ }
119
+ ]
120
+ ```
121
+
122
+ ### Met pandas 🐼
123
+
124
+ ```python
125
+ import asyncio
126
+
127
+ import pandas as pd
128
+ from monumenten import MonumentenClient
129
+
130
+
131
+ async def main():
132
+
133
+ input_df = pd.DataFrame(
134
+ {
135
+ "bag_verblijfsobject_id": [
136
+ "0599010000360091",
137
+ "0599010000486642",
138
+ "0599010000360022",
139
+ "0599010000360096",
140
+ "0599010000183527",
141
+ "0599010400025880",
142
+ "0599010000281115",
143
+ ]
144
+ }
145
+ ) # of lees van een csv of een ander bestand in
146
+
147
+ async with MonumentenClient() as client:
148
+ result = await client.process_from_df(
149
+ df=input_df, verblijfsobject_id_col="bag_verblijfsobject_id"
150
+ )
151
+ result.to_csv("monumenten.csv", index=False)
152
+
153
+ # in een .py file"
154
+ if __name__ == "__main__":
155
+ asyncio.run(main())
156
+
157
+ # in een .ipynb file (notebook):
158
+ await main()
159
+ ```
160
+
161
+ | bag_verblijfsobject_id | is_rijksmonument | rijksmonument_nummer | rijksmonument_url | is_beschermd_gezicht | beschermd_gezicht_naam |
162
+ | ---------------------- | ---------------- | -------------------- | ------------------------------------- | -------------------- | ------------------------------- |
163
+ | 0599010000360091 | True | 524327 | https://monumenten.nl/monument/524327 | False | |
164
+ | 0599010000486642 | False | | | False | |
165
+ | 0599010000360022 | True | 524327 | https://monumenten.nl/monument/524327 | False | |
166
+ | 0599010000360096 | True | 524327 | https://monumenten.nl/monument/524327 | False | |
167
+ | 0599010000183527 | True | 32807 | https://monumenten.nl/monument/32807 | True | Rotterdam - Scheepvaartkwartier |
168
+ | 0599010400025880 | False | | | False | |
169
+ | 0599010000281115 | False | | | True | Kralingen - Midden |
@@ -0,0 +1,13 @@
1
+ monumenten/__init__.py,sha256=RsPjyAQJ3mi43ICefZQUEnqqWrwXk9xQXIq_VxnQwKk,149
2
+ monumenten/_processing.py,sha256=mSionFmOoIbQxmcASiDjRmCeOD_aib1EefDSwkFCsYg,5292
3
+ monumenten/_version.py,sha256=IMl2Pr_Sy4LVRKy_Sm4CdwUl1Gryous6ncL96EMYsnM,411
4
+ monumenten/client.py,sha256=6VsQbxpf_aMeEfYQVT3guFGSCOFGm6r4SGcqic0nd0Q,3585
5
+ monumenten/logging.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ monumenten/_api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ monumenten/_api/_cultureel_erfgoed.py,sha256=zJkkhM1xUrBFOJ16eVfrPv071vCHuc4IDXO_Dz4b8EI,5975
8
+ monumenten/_api/_kadaster.py,sha256=8aBJ4Et2QDpJuxq-CX2wX4z10MYsLhHhZHq5J2no6Sw,2808
9
+ monumenten-0.1.0.dist-info/LICENSE,sha256=XHXrWnYTM8HAM7azt1zAhsP6aD-sM-2DPoUAfoYx57Y,1075
10
+ monumenten-0.1.0.dist-info/METADATA,sha256=GRhssvq7fAODyiCGga_LflQoWmAL6xYY61HcKTZj55M,6795
11
+ monumenten-0.1.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
12
+ monumenten-0.1.0.dist-info/top_level.txt,sha256=evniMq34R8O_etXp8lHeRW7ESy32fbZvjUmDtMrLg2s,11
13
+ monumenten-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.3.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ monumenten