Skip to content

Commit fab7601

Browse files
committed
Create kinda nice app to select an area from which you want to download CPTs
1 parent 819c37a commit fab7601

File tree

1 file changed

+204
-19
lines changed

1 file changed

+204
-19
lines changed

examples/nl_amsterdam/nl_amsterdam_bro.py

Lines changed: 204 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,42 @@
1616
app = marimo.App(width="medium")
1717

1818

19+
@app.cell
20+
def _():
21+
import datetime
22+
import json
23+
import xml.dom.minidom as minidom
24+
from io import BytesIO
25+
from urllib.request import Request, urlopen
26+
27+
import folium
28+
import geopandas as gpd
29+
import marimo as mo
30+
import xmltodict
31+
from folium.plugins import Draw
32+
from lxml import etree
33+
from pygef import read_cpt
34+
from pygef.plotting import plot_cpt
35+
36+
cwd = mo.notebook_location()
37+
return (
38+
BytesIO,
39+
Draw,
40+
Request,
41+
datetime,
42+
etree,
43+
folium,
44+
gpd,
45+
json,
46+
minidom,
47+
mo,
48+
plot_cpt,
49+
read_cpt,
50+
urlopen,
51+
xmltodict,
52+
)
53+
54+
1955
@app.cell
2056
def _(mo):
2157
mo.md(
@@ -28,47 +64,196 @@ def _(mo):
2864
2. [REST](https://www.bro-productomgeving.nl/bpo/latest/url-s-publieke-rest-services)
2965
1. CPT: <https://publiek.broservices.nl/sr/cpt/v1>
3066
2. Goetechnical boreholes: <https://publiek.broservices.nl/sr/bhrgt/v2>
31-
3. [PDOK](https://app.pdok.nl/viewer) - WMS
32-
67+
3. [PDOK](https://app.pdok.nl/viewer)
68+
1. WMS - This is useful for quickly viewing the location of historic CPTs or geotechnical boreholes.
69+
2. ATOM feed - For downloading the whole dataset, i.e. to download all CPTs or all geotechnical boreholes in BRO.
3370
"""
3471
)
3572
return
3673

3774

75+
@app.cell(hide_code=True)
76+
def _(Draw, buffer, folium, geojson_text_area, gpd, json, site_geojson):
77+
geojson = geojson_text_area.value if geojson_text_area.value else site_geojson
78+
# Create a folium interactive map (leaflet.js maps)
79+
site = gpd.GeoDataFrame.from_features(
80+
{
81+
"type": "FeatureCollection",
82+
"name": "site",
83+
"features": [json.loads(geojson)],
84+
},
85+
crs=4326,
86+
).to_crs(28992)
87+
buffered_site = site.geometry.buffer(buffer.value)
88+
bounds = buffered_site.to_crs(4326).bounds.to_numpy()[0]
89+
folium_map = buffered_site.explore(
90+
tiles="https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}",
91+
style_kwds={"fillOpacity": 0.1},
92+
attr=("Esri.WorldStreetMap"),
93+
)
94+
site.explore(m=folium_map, color="red", style_kwds={"fill": False})
95+
folium.Rectangle(
96+
bounds=[[bounds[1], bounds[0]], [bounds[3], bounds[2]]],
97+
color="black",
98+
weight=0.5,
99+
).add_to(folium_map)
100+
101+
# Add PDOK's CPT WMS layer
102+
folium.WmsTileLayer(
103+
url="https://service.pdok.nl/bzk/geologie/bro-geotechnisch-sondeeronderzoek/wms/v1_0?request=GetCapabilities&service=WMS",
104+
name="BRO CPT",
105+
fmt="image/png",
106+
layers="GE.conePenetrationTest",
107+
transparent=True,
108+
).add_to(folium_map)
109+
110+
# Add PDOK's geotechnical borehole WMS layer
111+
folium.WmsTileLayer(
112+
url="https://service.pdok.nl/bzk/geologie/bro-geotechnisch-booronderzoek/wms/v1_0?request=getcapabilities&service=wms",
113+
name="BRO BHR-GT",
114+
fmt="image/png",
115+
layers="GE.Borehole",
116+
transparent=True,
117+
).add_to(folium_map)
118+
119+
# Add drawing widget
120+
draw = Draw(
121+
export=True,
122+
filename="site.geojson",
123+
position="topleft",
124+
draw_options={
125+
"polyline": True,
126+
"polygon": True,
127+
"circle": False,
128+
"rectangle": False,
129+
"marker": True,
130+
"circlemarker": False,
131+
},
132+
).add_to(folium_map)
133+
134+
folium_map
135+
return (bounds,)
136+
137+
38138
@app.cell
39139
def _(mo):
40-
cwd = mo.notebook_location()
41-
return (cwd,)
140+
site_geojson = '{"type":"Feature","properties":{},"geometry":{"type":"Polygon","coordinates":[[[4.902889698063478,52.385435257999404],[4.902923632305726,52.385509906165986],[4.902901716440955,52.385619936470341],[4.903006347021203,52.38569026942325],[4.90389499998988,52.385854235381281],[4.904091535809526,52.385902562073795],[4.904285950739031,52.385988859607401],[4.904484607448818,52.38602683046868],[4.904532680958658,52.386042363993511],[4.904790722592364,52.386094573856099],[4.904799206152918,52.386073862513044],[4.904974533071164,52.386108812898868],[4.904999276789463,52.386100183176488],[4.905287541107636,52.385853480276289],[4.903576336246269,52.385074312916224],[4.903554420381488,52.385093082996256],[4.903498923756159,52.38509416173622],[4.903484784488561,52.385100202679702],[4.903473119592793,52.385110990076754],[4.903287895187223,52.385027927051986],[4.903256788798504,52.38504302943177],[4.903150390809821,52.38513472234078],[4.903119637902792,52.385127171167227],[4.903098782483085,52.38514421524286],[4.903084996697174,52.385139037296511],[4.90302349088312,52.385198152147602],[4.902889698063478,52.385435257999404]]]}}'
141+
geojson_text_area = mo.ui.text_area(
142+
placeholder="1. Draw a shape on the map.\n2. Click it.\n3. Copy the GeoJSON from the pop-up.\n4. Paste GeoJSON here.",
143+
debounce=5,
144+
full_width=True,
145+
)
146+
buffer = mo.ui.slider(start=0, stop=200, label="Buffer", value=100, step=10)
147+
mo.hstack(
148+
[geojson_text_area, buffer],
149+
widths=[1, 0],
150+
)
151+
return buffer, geojson_text_area, site_geojson
42152

43153

44154
@app.cell
45-
def _(cwd, gpd):
46-
site_plot = gpd.read_file(cwd / "context.gpkg")
47-
site_plot.explore()
155+
def _(buffer):
156+
# EPSG:4258 is the European Terrestrial Reference System 1989 (ETR)
157+
# ETR89 coordinates are in [Longitude (x), Latitude (y)]
158+
lonlat_bounds = buffer.to_crs(4258).bounds
159+
lonlat_bounds
48160
return
49161

50162

51163
@app.cell
52-
def _(cwd, read_cpt):
53-
cpt_data = read_cpt(cwd / "data" / "CPT000000061548.xml")
54-
cpt_data.data
55-
return (cpt_data,)
164+
def _(Request, bounds, datetime, json, minidom, mo, urlopen, xmltodict):
165+
cpt_search_url = "https://publiek.broservices.nl/sr/cpt/v1/characteristics/searches"
166+
api_request_data = json.dumps(
167+
{
168+
"registrationPeriod": {
169+
"beginDate": "2017-01-01",
170+
"endDate": datetime.date.today().isoformat(),
171+
},
172+
"area": {
173+
"boundingBox": {
174+
"lowerCorner": {
175+
"lon": bounds[0],
176+
"lat": bounds[1],
177+
},
178+
"upperCorner": {
179+
"lon": bounds[2],
180+
"lat": bounds[3],
181+
},
182+
}
183+
},
184+
}
185+
).encode("utf-8")
186+
187+
cpt_search_req = Request(
188+
cpt_search_url,
189+
data=api_request_data,
190+
headers={"Content-Type": "application/json"},
191+
method="POST",
192+
)
193+
with urlopen(cpt_search_req, timeout=30) as cpt_search_resp:
194+
xml = cpt_search_resp.read()
195+
196+
cpt_search = xmltodict.parse(
197+
xml,
198+
xml_attribs=False,
199+
process_namespaces=True,
200+
namespaces={
201+
"http://www.broservices.nl/xsd/dscpt/1.1": None,
202+
"http://www.broservices.nl/xsd/brocommon/3.0": None,
203+
},
204+
)
205+
206+
tabs = mo.ui.tabs(
207+
{
208+
"XML": mo.md(
209+
f"```xml\n{minidom.parseString(xml).toprettyxml(indent=' ')}\n```"
210+
),
211+
"JSON": xmltodict.parse(xml),
212+
"JSON, no XML attributes": cpt_search,
213+
}
214+
)
215+
216+
tabs
217+
return
218+
219+
220+
app._unparsable_cell(
221+
r"""
222+
for cpt in cpt_search
223+
""",
224+
name="_",
225+
)
56226

57227

58228
@app.cell
59-
def _(cpt_data, plot_cpt):
60-
plot_cpt(cpt_data)
229+
def _(Request, etree, urlopen):
230+
cpt_get_url = "https://publiek.broservices.nl/sr/cpt/v1/objects/CPT000000198164"
231+
cpt_get_req = Request(cpt_get_url, method="GET")
232+
with urlopen(cpt_get_req, timeout=30) as resp:
233+
cpt_str = resp.read()
234+
cpt_xml = etree.fromstring(cpt_str)
235+
236+
print(etree.tostring(cpt_xml, pretty_print=True, encoding="unicode"))
237+
return (cpt_str,)
238+
239+
240+
@app.cell
241+
def _(cpt_str):
242+
cpt_str
61243
return
62244

63245

64246
@app.cell
65-
def _():
66-
import geopandas as gpd
67-
import marimo as mo
68-
from pygef import read_cpt
69-
from pygef.plotting import plot_cpt
247+
def _(BytesIO, cpt_str, read_cpt):
248+
cpt_data = read_cpt(BytesIO(cpt_str))
249+
cpt_data.__dict__
250+
return (cpt_data,)
251+
70252

71-
return gpd, mo, plot_cpt, read_cpt
253+
@app.cell
254+
def _(cpt_data, plot_cpt):
255+
plot_cpt(cpt_data)
256+
return
72257

73258

74259
if __name__ == "__main__":

0 commit comments

Comments
 (0)