diff --git a/.github/workflows/validate_config.yml b/.github/workflows/validate_config.yml new file mode 100644 index 0000000..02cf868 --- /dev/null +++ b/.github/workflows/validate_config.yml @@ -0,0 +1,54 @@ +name: Validate canton configuration + +# Guard rail: the cantonal geoservice configuration lives in per-canton YAML +# files (src/drillapi/cantons_configuration/data/*.yaml). A malformed config +# would break the running app, so this workflow validates every file against +# the Pydantic schema on every push and PR. A failure here blocks the merge / +# release before any deploy. + +on: + push: + branches: + - '**' + pull_request: + branches: + - '**' + +jobs: + validate-config: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.14] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache uv + uses: actions/cache@v4 + with: + path: ~/.cache/uv + key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }} + + - name: Install dependencies + run: uv sync --extra dev + + # Fast fail-fast load: validates every YAML file against the schema and + # exits non-zero with an aggregated error listing every problem found. + - name: Validate cantonal YAML config loads + run: | + uv run python -c "from drillapi.cantons_configuration.loader import load_cantons; c = load_cantons(); print(f'OK: {len(c)} canton configuration(s) validated')" + + # Full structural test suite for the configuration (schema invariants, + # ESRI id presence, name/filename agreement, fail-fast behaviour). + - name: Run configuration test suite + run: uv run python -m pytest tests/test_configuration_structure.py -v diff --git a/README.md b/README.md index a04b6ca..b79d802 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,34 @@ Run project uv run python -m drillapi ``` +## Cantonal configuration + +The per-canton geoservice configuration lives as one YAML file per canton under +`src/drillapi/cantons_configuration/data/.yaml` (e.g. `ZH.yaml`). Each file +is validated against a Pydantic schema (`cantons_configuration/schema.py`) when +the app starts, so an invalid config (unknown/typo'd key, missing field, wrong +type, filename/`name` mismatch) fails fast at startup — and in CI via the +`Validate canton configuration` workflow — rather than mid-request against a +live geoservice. + +To add or change a canton, edit the relevant `data/.yaml` file. The +filename (minus extension) must match the canton's `name` field. + +### Hot-reload the config in dev + +The config is loaded and cached once at startup, so a plain `--reload` server +(which watches only `*.py`) will **not** pick up YAML edits. For local work on +cantonal data, start the server watching the YAML files too (requires the dev +dependencies, which include `watchfiles`): + +```bash +uv run uvicorn drillapi.app:app --app-dir src --reload --reload-dir src/drillapi --reload-include "*.yaml" +``` + +Editing any `data/*.yaml` then restarts and re-validates the config +automatically. An invalid edit surfaces the validation error in the server log +and the app will not come back up until it is fixed. + ## Explore OpenAPI doc diff --git a/pyproject.toml b/pyproject.toml index 000e65c..728c99e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "mangum>=0.22.0", "owslib>=0.36.0", "pydantic-settings>=2.15.0", + "pyyaml>=6.0.3", "slowapi>=0.1.10", "uvicorn>=0.54.0", ] @@ -26,6 +27,7 @@ dev = [ "respx", "pytest-asyncio", "ruff>=0.16.10", + "watchfiles>=1.3.0", ] [tool.coverage.run] diff --git a/src/drillapi/cantons_configuration/cantons.py b/src/drillapi/cantons_configuration/cantons.py index 9c17a8a..9271c2e 100644 --- a/src/drillapi/cantons_configuration/cantons.py +++ b/src/drillapi/cantons_configuration/cantons.py @@ -1,1313 +1,22 @@ -CANTONS = { - "cantons_configurations": { - "JU": { - "active": True, - "name": "JU", - "ground_control_point": [ - [2574738, 1249285, 1, "Autorisé"], - [2576624, 1252365, 2, "A évaluer (prendre contact avec ENV)"], - [ - 2588623, - 1243355, - 2, - "Autorisé avec restriction de profondeur de 150m", - ], - [ - 2588245, - 1244475, - 2, - "Autorisé avec restriction de profondeur de 100m", - ], - [2590205, 1244035, 2, "Autorisé avec restriction de profondeur de 70m"], - [2573867, 1252854, 3, "Interdit"], - ], - "cantonal_energy_service_url": "https://www.jura.ch/DEN/ENV/Geothermie/Geothermie.html", - "wms_url": "https://geoservices.jura.ch/wms", - "query_url": "https://geoservices.jura.ch/wms", - "thematic_geoportal_url": "https://geo.jura.ch/theme/POI?lang=fr&map_x=2580425&map_y=1242775&map_zoom=1&baselayer_ref=sdt_01_03_mensuration_officielle&baselayer_opacity=0&tree_enable_env_18_06_cadastre_geologique=false&tree_enable_env_18_03_geothermie_limitation_forages_sondes_geothermiques=true&tree_groups=Geologie", - "legend_url": "https://geoservices.jura.ch/wms?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=ju.env_18_03_geothermie_limitation_forages_sondes_geothermiques&format=image/png&STYLE=default", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ju.env_18_03_geothermie_limitation_forages_sondes_geothermiques", - "property_name": "limitation_forage", - "property_values": [ - { - "name": "Autorisé", - "desc": "Autorisé", - "target_harmonized_value": 1, - }, - { - "name": "A évaluer (prendre contact avec ENV)", - "desc": "A évaluer (prendre contact avec ENV)", - "target_harmonized_value": 2, - }, - { - "name": "Autorisé avec restriction de profondeur de 150m", - "desc": "Autorisé avec restriction de profondeur de 150m", - "target_harmonized_value": 2, - }, - { - "name": "Autorisé avec restriction de profondeur de 100m", - "desc": "Autorisé avec restriction de profondeur de 100m", - "target_harmonized_value": 2, - }, - { - "name": "Autorisé avec restriction de profondeur de 70m", - "desc": "Autorisé avec restriction de profondeur de 70m", - "target_harmonized_value": 2, - }, - { - "name": "Interdit", - "desc": "Interdit", - "target_harmonized_value": 3, - }, - ], - }, - ], - }, - "ZH": { - "active": True, - "name": "ZH", - "ground_control_point": [ - [ - 2677790, - 1260185, - 3, - "Auflagen & waermenutzung-zone-d", - ], # Changed 28.01.2026, if Auflagen layer has a feature => 3 like on ZH geoportal - [2688561, 1274977, 3, "Zone A (Schutzzonen und Schutzareale)"], - [ - 2681563, - 1248410, - 2, - "Zone B (Schotter-Grundwasservorkommen, geeignet für Trinkwassergewinnung)", - ], - [ - 2680649, - 1246765, - 2, - "Zone C (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung)", - ], - [ - 2692350, - 1275334, - 2, - "Zone D (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung)", - ], - [ - 2693733, - 1238583, - 2, - "Zone E (Quellwassergebiete, geeignet für Trinkwassergewinnung)", - ], - ], - "cantonal_energy_service_url": "https://www.zh.ch/de/planen-bauen/bauvorschriften/energienutzung-untergrund-wasser.html", - "wms_url": "https://wms.zh.ch/AwelGSWaermewwwZHWMS", - "query_url": "https://wms.zh.ch/AwelGSWaermewwwZHWMS", - "thematic_geoportal_url": "https://maps.zh.ch/?topic=AwelGSWaermewwwZH&x=2685104.6444391827&y=1252283.9396742217&scale=70517.93063503089", - # ZH is a special case with multiple layers, but ZH WMS service is standard and does no support multiple layer in request. Will be fixed in v2 - "legend_url": "", # "https://wms.zh.ch/AwelGSWaermewwwZHWMS?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=erdwaermesonden-auflagen&format=image/png&STYLE=default", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 30, - "style": "", - "layers": [ - { - "name": "erdwaermesonden-auflagen", - "property_name": "name", - # WMS only return binary information: in perimeter or outside. If, in, we can't discriminate and therefore take the most conservative value - "property_values": [ - { - "name": "Auflagen für Erdwärmesonden", - "desc": "Verbot", - "target_harmonized_value": 3, - } - ], - }, - { - "name": "waermenutzung-zone-a", - "property_name": "name", - "target_harmonized_value": 2, - "property_values": [ - { - "name": "Zone A (Schutzzonen und Schutzareale)", - "desc": "Zone A (Schutzzonen und Schutzareale)", - "target_harmonized_value": 3, - }, - ], - }, - { - "name": "waermenutzung-zone-b", - "property_name": "name", - "target_harmonized_value": 2, - "property_values": [ - { - "name": "Zone B (Schotter-Grundwasservorkommen, geeignet für Trinkwassergewinnung)", - "desc": "Zone B (Schotter-Grundwasservorkommen, geeignet für Trinkwassergewinnung)", - "target_harmonized_value": 2, - }, - ], - }, - { - "name": "waermenutzung-zone-c", - "property_name": "name", - "target_harmonized_value": 2, - "property_values": [ - { - "name": "Zone C (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung)", - "desc": "Zone C (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung)", - "target_harmonized_value": 2, - }, - ], - }, - { - "name": "waermenutzung-zone-d", - "property_name": "name", - "target_harmonized_value": 2, - "property_values": [ - { - "name": "Zone D (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung)", - "desc": "Zone D (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung)", - "target_harmonized_value": 2, - }, - ], - }, - { - "name": "waermenutzung-zone-e", - "property_name": "name", - "target_harmonized_value": 2, - "property_values": [ - { - "name": "Zone E (Quellwassergebiete, geeignet für Trinkwassergewinnung)", - "desc": "Zone E (Quellwassergebiete, geeignet für Trinkwassergewinnung)", - "target_harmonized_value": 2, - }, - ], - }, - { - "name": "waermenutzung-zone-f", - "property_name": "name", - "target_harmonized_value": 2, - "property_values": [ - { - "name": "Zone F (Ausserhalb nutzbarer Grundwasservorkommen)", - "desc": "Zone F (Ausserhalb nutzbarer Grundwasservorkommen)", - "target_harmonized_value": 2, - }, - ], - }, - ], - }, - "ZG": { - "active": True, - "name": "ZG", - "ground_control_point": [ - [2676912, 1228091, 1, "mit Standardauflagen zulässig"], - [2684565, 1225437, 2, "mit spezifischen Auflagen zulässig"], - [2686680, 1226360, 3, "unzulässig"], - [2690680, 1219207, 4], # Middle of the lake => empty features - ], - "cantonal_energy_service_url": "https://zg.ch/de/planen-bauen/bauvorschriften/gebaeude-und-energie", - "wms_url": "https://services.geo.zg.ch/ows/Erdwaermenutzung", - "query_url": "https://services.geo.zg.ch/ows/Erdwaermenutzung", - "legend_url": "https://services.geo.zg.ch/ows/Erdwaermenutzung?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=zg.ews_zulaessigkeit&format=image/png&STYLE=default", - "thematic_geoportal_url": "https://zugmap.ch/", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "default", - "layers": [ - { - "name": "zg.ews_zulaessigkeit", - "property_name": "zulaessigkeit", - "property_values": [ - { - "name": "mit Standardauflagen zulässig", - "desc": "mit Standardauflagen zulässig", - "target_harmonized_value": 1, - }, - { - "name": "mit spezifischen Auflagen zulässig", - "desc": "mit spezifischen Auflagen zulässig", - "target_harmonized_value": 2, - }, - { - "name": "unzulässig", - "desc": "unzulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "VS": { - "active": True, - "name": "VS", - "ground_control_point": [ - [2596336, 1122359, 1, "CLASSGV 1"], - [2598146, 1124454, 1, "CLASSGV 2"], - [2593661, 1122203, 2, "CLASSGV 3"], - [ - 2594171, - 1121846, - 2, - "CLASSGV 4", - ], - [2598068.4, 1130927.1, 4, "CLASSGV 5"], - ], - "cantonal_energy_service_url": "https://www.vs.ch/web/sen/eaux-souterraines-et-constructions", - "wms_url": "https://sit.vs.ch/arcgis/services/ENVIRONNEMENT/MapServer/WMSServer", - "query_url": "https://sit.vs.ch/arcgis/services/ENVIRONNEMENT/MapServer/WMSServer", - "legend_url": "https://sit.vs.ch/arcgis/services/ENVIRONNEMENT/MapServer/WMSServer?request=GetLegendGraphic%26version=1.3.0%26format=image/png%26layer=29", - "thematic_geoportal_url": "https://sitonline.vs.ch/environnement/eso_admissibilite_SGV/#/?lang=fr", - "info_format": "application/geo+json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "29", - "property_name": "CLASSGV", - "property_values": [ - { - "name": "1", - "desc": "Sondes géothermiques admises (max. 200 m sans étude)", - "target_harmonized_value": 1, - }, - { - "name": "2", - "desc": "Sondes géothermiques limitées à 100 m de profondeur", - "target_harmonized_value": 1, - }, - { - "name": "3", - "desc": "Au cas par cas – Etude préalable (contexte hydrogéologique ou autres restrictions)", - "target_harmonized_value": 2, - }, - { - "name": "4", - "desc": "Au cas par cas – Etude préalable (danger de glissement de terrain ou effondrement)", - "target_harmonized_value": 2, - }, - { - "name": "5", - "desc": "Sondes géothermiques non admises", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "VD": { - "active": True, - "name": "VD", - "ground_control_point": [ - [2536682, 1156974, 1, "Admissible sous conditions"], - [2548704, 1168639, 2, "Limitation"], - [2535551, 1180721, 3, "Interdiction"], - [2535239, 1148966, 4], # Middle of the lake, empty feature - ], - "cantonal_energy_service_url": "https://www.vd.ch/themes/environnement/eaux/eaux-souterraines/pompes-a-chaleur-pac-sondes-geothermiques-verticales-et-pompage-a-la-nappe", - "wms_url": "https://www.ogc.vd.ch/public/services/OGC/wmsVD/Mapserver/WMSServer", - "query_url": "https://www.ogc.vd.ch/public/services/OGC/wmsVD/Mapserver/WMSServer", - "legend_url": "https://www.ogc.vd.ch/public/services/OGC/wmsVD/Mapserver/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&LAYER=vd.admissibilite_indicative_sonde_geothermique", - "thematic_geoportal_url": "https://www.geo.vd.ch/?visiblelayers=%7B%22GEO_THEME_ENERG%22%3A%5B%22Potentiel%20thermique%20global%22%2C%22Type%20de%20site%22%5D%7D&share=4c196dee-a011-479e-abd1-2a805f1b34a3", - "info_format": "application/geo+json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "vd.admissibilite_indicative_sonde_geothermique", - "property_name": "Type", - "property_values": [ - { - "name": "Admissible sous conditions", - "desc": "Admissible sous conditions", - "target_harmonized_value": 1, - }, - { - "name": "Admissible aux conditions standard, jusqu’à 300 mètres", - "desc": "Admissible aux conditions standard, jusqu’à 300 mètres", - "target_harmonized_value": 1, - }, - {"name": "Limitation", "target_harmonized_value": 2}, - { - "name": "Limitation, soumis à des conditions spéciales", - "desc": "Limitation, soumis à des conditions spéciales", - "target_harmonized_value": 2, - }, - { - "name": "Interdiction", - "desc": "Interdiction", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "UR": { - "active": True, - "name": "UR", - "ground_control_point": [ - [2703052, 1180378, 1, "B"], - [2699152, 1175536, 2, "C"], - [2689034, 1196007, 3, "A"], - ], - "cantonal_energy_service_url": "https://www.ur.ch/aemter/828", - "wms_url": "https://geo.ur.ch/webmercator/wms", - "query_url": "https://geo.ur.ch/webmercator/wms", - "legend_url": "https://geo.ur.ch/webmercator/wms?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=umwelt:wnk_zulaessigkeitsbereiche_erdsonden&format=image/png", - "thematic_geoportal_url": "https://geo.ur.ch/?center=962589%2C5922132&layers=Zul%C3%A4ssigkeit%20Grundwasserw%C3%A4rmepumpen&opacity=0.3&visibility=true&zoom=12", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "umwelt:wnk_zulaessigkeitsbereiche_erdsonden", - "property_name": "zulaessigkeit_ews", - "property_values": [ - { - "name": "B", - "desc": "Zulässig unter allgemeinen Auflagen", - "target_harmonized_value": 1, - }, - { - "name": "C", - "desc": "Zulässig unter zusätzlichen Schutzmassnahmen", - "target_harmonized_value": 2, - }, - { - "name": "A", - "desc": "roter Zulässigkeitsbereich / nicht zulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "TI": { - "active": True, - "name": "TI", - "ground_control_point": [[2715738, 1125797, 3], [2712602, 1133130, 1]], - "cantonal_energy_service_url": "https://www4.ti.ch/dt/da/spaas/upaai/temi/acqua-protezione-e-approvvigionamento/protezione-e-approvvigionamento/acque-sotterranee/utilizzo-termico-delle-acque-sotterranee-e-del-sottosuolo/sfruttamento-dellenergia-geotermica-e-dallacqua-sotterranea/", - "wms_url": "https://wms.geo.ti.ch/service", - "query_url": "https://wms.geo.ti.ch/service", - "legend_url": "https://www4.ti.ch/fileadmin/GENERALE/IGDAC/ccgeo/legends/ti_040_1_v1_0/ti_040_1_v1_0_sfruttamento_termico_acque_geotermia_sonda.png", - "thematic_geoportal_url": "https://map.geo.ti.ch/?lang=it&baselayer_ref=Carta%20Nazionale%20%28bianco%20e%20nero%29&tree_group_layers_Idoneit%C3%A0%20sonde%20geotermiche=Idoneit%C3%A0%20sonde%20geotermiche&tree_groups=Idoneit%C3%A0%20sonde%20geotermiche", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ti_040_1_v1_0_sfruttamento_termico_acque_geotermia_sonda", - "property_name": "idoneita_desc", - "property_values": [ - { - "name": "In linea di principio non permesso", - "desc": "nicht zulässig", - "target_harmonized_value": 3, - }, - { - "name": "In linea di principio permesso con condizioni", - "desc": "bedingt zulässig", - "target_harmonized_value": 1, - }, - ], - } - ], - }, - "TG": { - "active": True, - "name": "TG", - "ground_control_point": [ - [2747716.6, 1262056.4, 3, "EWS grundsätzlich unzulässig"], - [ - 2747720.5, - 1262436.9, - 1, - "EWS grundsätzlich zulässig mit Standardauflagen", - ], - [ - 2709370.0, - 1268975.0, - 2, - "EWS grundsätzlich zulässig mit zusätzlichen Auflagen", - ], - [2712450.0, 1269125.0, 1, "EWS grundsätzlich zulässig"], - ], - "cantonal_energy_service_url": "https://energie.tg.ch/erneuerbare-energien/umweltwaerme-waermepumpen.html/3437", - "wms_url": "https://ows.geo.tg.ch/geofy_access_proxy/erdwaerme", - "query_url": "https://ows.geo.tg.ch/geofy_access_proxy/erdwaerme", - "legend_url": "https://map.geo.tg.ch/services/geofy_chsdi3/static/images/legends/erdwaerme_eignung_de.png", - "thematic_geoportal_url": "https://map.geo.tg.ch/apps/mf-geoadmin3/?lang=de&topic=geologieboden&E=2718775.00&N=1270425.00&zoom=1&layers_opacity=1,1,0.9&layers=erdwaerme_eignung,erdwaerme_erdwaermesondenbohrungen,grundwasserkarte-fassung", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "Eignungszonen", - "property_name": "eignungszone", - "property_values": [ - { - "name": "1", - "desc": "EWS grundsätzlich zulässig mit Standardauflagen", - "target_harmonized_value": 1, - }, - { - "name": "2", - "desc": "EWS grundsätzlich zulässig mit Standardauflagen", - "target_harmonized_value": 2, - }, - { - "name": "3", - "desc": "EWS grundsätzlich unzulässig, Grundwasserschutzzone", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "SZ": { - "active": True, - "name": "SZ", - "ground_control_point": [ - [2707885, 1226453, 1], - [2707994, 1227010, 2], - [2708393, 1227843, 3], - ], - "cantonal_energy_service_url": "https://www.sz.ch/verwaltung/umweltdepartement/amt-fuer-umwelt-und-energie/energie-und-klima/waermenutzung.html/8756-8758-8802-9447-9453-10708-10760", - "wms_url": "https://map.geo.sz.ch/mapserv_proxy", - "query_url": "https://map.geo.sz.ch/mapserv_proxy", - "legend_url": "https://map.geo.sz.ch/mapserv_proxy?service=WMS&request=GetLegendGraphic&version=1.1.1&layer=ch.sz.a034c.waermenutzung.erdwaerme.technisch&format=image/png&FORMAT_OPTIONS=dpi:200", - "thematic_geoportal_url": "https://map.geo.sz.ch/?lang=fr&map_x=2697700&map_y=1213275&map_zoom=0&baselayer_ref=Landeskarte%20(farbig)&tree_groups=grp_Energie_Erdw%C3%A4rmenutzung%2Cgrp_Energie_Frei_Leitungskataster%2Cgrp_Energie_Netzgebiete%2Cgrp_Energie_Solarenergie%2Cgrp_Energie_Wasserw%C3%A4rmenutzung&tree_group_layers_grp_Energie_Erdw%C3%A4rmenutzung=ch.sz.a034c.waermenutzung.erdwaermeanlage.erdwaermesonde%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.erdwaermesonde_nicht_bewilligungsfaehig%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.energiepfahl%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.energiekorb%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.erdregister%2Cch.sz.a034c.waermenutzung.erdwaerme", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ch.sz.a034c.waermenutzung.erdwaerme.technisch", - "property_name": "zulaessigkeit_cd", - "property_values": [ - { - "name": "ja", - "desc": "zulässig", - "target_harmonized_value": 1, - }, - { - "name": "Abklaerung_noetig", - "desc": "bedingt zulässig", - "target_harmonized_value": 2, - }, - { - "name": "nein", - "desc": "nicht zulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "SH": { - "active": True, - "name": "SH", - "ground_control_point": [ - [2690343, 1289821, 1, "3"], - [2682889, 1292346, 2, "5"], - [2691554, 1293373, 3, "1"], - [2695085, 1290396, 2, "41"], - [2695476, 1286474, 2, "42"], - ], - "cantonal_energy_service_url": "https://sh.ch/CMS/Webseite/Kanton-Schaffhausen/Beh-rde/Verwaltung/Baudepartement/Tiefbau-Schaffhausen/Abteilung-Gew-sser-und-Materialabbau/Erdw-rme-1738469-DE.html", - "wms_url": "https://wms.geo.sh.ch/wms", - "query_url": "https://wms.geo.sh.ch/wms", - "legend_url": "https://wms.geo.sh.ch/wms?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=sh.energie.erdsonden.eignung&format=image/png", - "thematic_geoportal_url": "https://map.geo.sh.ch/geoportal/?project=Geoportal%20Schaffhausen&legend=Legende&rotation=0.00&scale=90202¢er=2692188,1282000&layers=0bf7129c-2c80-4371-941f-556c4a6ba1f1", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "sh.energie.erdsonden.eignung", - "property_name": "eignung_erdwaermesonden_code", - "property_values": [ - { - "name": "3", - "desc": "EWS bis 200 m Tiefe zulässig (über 200 m Vorabklärung notwendig)", - "target_harmonized_value": 1, - }, - { - "name": "5", - "desc": "EWS mit Vorabklärung und fallweise geologischer Begleitung zulässig", - "target_harmonized_value": 2, - }, - { - "name": "41", - "desc": "GWWN-Grossanlagen mit Gutachten zulässig (EWS auf Anfrage)", - "target_harmonized_value": 2, - }, - { - "name": "42", - "desc": "GWWN-Grossanlagen mit Gutachten zulässig (Kurzsonden auf Anfrage)", - "target_harmonized_value": 2, - }, - { - "name": "1", - "desc": "EWS und GWWN unzulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "SG": { - "active": True, - "name": "SG", - "ground_control_point": [ - [2733447, 1241435, 2, "1"], - [2735185, 1244058, 1, "3"], - [2742740, 1233995, 3, "2"], - ], - "cantonal_energy_service_url": "https://www.sg.ch/umwelt-natur/wasser/grundwasser---quellen-/geothermie0/erdwaermesonden.html", - "wms_url": "https://services.geo.sg.ch/wss/service/SG00025_WMS/guest", - "query_url": "https://services.geo.sg.ch/wss/service/SG00025_WMS/guest", - "legend_url": "https://services.geo.sg.ch/wss/service/SG00025_WMS/guest?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=Zulaessigkeitsbereich&format=image/png", - "thematic_geoportal_url": "https://www.geoportal.ch/ktsg/map/29?y=2743944.00&x=1231900.00&scale=300000&rotation=0", - "info_format": "application/geo+json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "Zulaessigkeitsbereich", - "property_name": "OBJECTID", - "property_values": [ - { - "name": "2", - "desc": "nicht zulässig (siehe Erläuterungen)", - "target_harmonized_value": 3, - }, - { - "name": "3", - "desc": "zulässig (siehe Erläuterungen)", - "target_harmonized_value": 1, - }, - { - "name": "1", - "desc": "bedingt zulässig (siehe Erläuterungen)", - "target_harmonized_value": 2, - }, - ], - } - ], - }, - "OW": { - "active": True, - "name": "OW", - "ground_control_point": [ - [2664351, 1193538, 1, "zulaessig"], - [2660898, 1191688, 1, "Auflage_GewaesserschutzbereichAu"], - [2660281, 1190191, 2, "Auflage_NutzungsgebietGrundwasser"], - [2661431, 1192803, 2, "Auflage_bedingtzulaessig"], - [2661534, 1193397, 3, "nichtzulaessig"], - [2657375.6, 1180366.4, 3, "Gebiete_mit_potentieller_Verkarstung"], - ], - "cantonal_energy_service_url": "https://www.ow.ch/dienstleistungen/2055", - "wms_url": "https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service", - "query_url": "https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service", - "legend_url": "https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ch.ow.ews_zulaessigkeit&format=image/png", - "thematic_geoportal_url": "https://www.gis-daten.ch/map/ow_waermenutzung", - "info_format": "application/json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ch.ow.ews_zulaessigkeit", - "property_name": "Zulaessigkeit", - "property_values": [ - { - "name": "zulaessig", - "desc": "Wärmenutzung zulässig (Bewilligungspflicht)", - "target_harmonized_value": 1, - }, - { - "name": "Auflage_GewaesserschutzbereichAu", - "desc": "Wärmenutzung zulässig (Bewilligungspflicht)", - "target_harmonized_value": 1, - }, - { - "name": "Auflage_NutzungsgebietGrundwasser", - "desc": "Wärmenutzung zulässig (Bewilligungspflicht); Geologische Begleitung während der Bohrung erforderlich", - "target_harmonized_value": 2, - }, - { - "name": "Auflage_bedingtzulaessig", - "desc": "Wärmenutzung bedingt zulässig (Bewilligungspflicht); vorgängiges geologisches Gutachten erforderlich als Grundlage zur Prüfung der Bewilligungsfähigkeit", - "target_harmonized_value": 2, - }, - { - "name": "nichtzulaessig", - "desc": "Wärmenutzung nicht zulässig", - "target_harmonized_value": 3, - }, - { - "name": "Gebiete_mit_potentieller_Verkarstung", - "desc": "Wärmenutzung nicht zulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "NW": { - "active": True, - "name": "NW", - "ground_control_point": [ - [2671744, 1196809, 3, "bautechnisch_bauverbot"], - [2675123, 1198890, 3, "Trinkwasserschutzgebiet"], - [2672327, 1202452, 3, "nutzbare_Grundwassergebiete"], - [2668633, 1202922, 2, "bebaubares_Grundwassergebiet"], - [2668267, 1201091, 2, "vermutlich_unproblematisch"], - [2668945, 1200200, 2, "problematisch"], - [2668854, 1199481, 2, "bautechnisch_problematisch"], - [2669798, 1200979, 1, "unproblematisch"], - ], - "cantonal_energy_service_url": "https://www.nw.ch/amtumweltdienste/4020", - "wms_url": "https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service", - "query_url": "https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service", - "legend_url": "https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ch.nw.waermenutzungsbereiche&format=image/png", - "thematic_geoportal_url": "https://www.gis-daten.ch/map/nw_waermenutzung", - "info_format": "application/json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ch.nw.waermenutzungsbereiche", - "property_name": "Art", - "property_values": [ - { - "name": "bautechnisch_bauverbot", - "desc": "Wärmenutzung aus dem Untergrund verboten", - "target_harmonized_value": 3, - }, - { - "name": "Trinkwasserschutzgebiet", - "target_harmonized_value": 3, - }, - { - "name": "nutzbare_Grundwassergebiete", - "desc": "Grundwassergebiet mit Erdsondenverbot", - "target_harmonized_value": 3, - }, - { - "name": "bebaubares_Grundwassergebiet", - "desc": "Grundwassergebiet ohne Erdsondenverbot", - "target_harmonized_value": 2, - }, - { - "name": "vermutlich_unproblematisch", - "desc": "Vermutlich unproblematische Untergrundverhältnisse", - "target_harmonized_value": 2, - }, - { - "name": "problematisch", - "desc": "Unsichere Untergrundverhältnisse", - "target_harmonized_value": 2, - }, - { - "name": "bautechnisch_problematisch", - "desc": "Bautechnisch problematische Untergrundverhältnisse", - "target_harmonized_value": 2, - }, - { - "name": "unproblematisch", - "desc": "Unproblematische Untergrundverhältnisse", - "target_harmonized_value": 1, - }, - ], - } - ], - }, - "LU": { - "active": True, - "name": "LU", - "ground_control_point": [ - [2665393, 1217714, 1, "ews_zulaessig"], - [2652462, 1196901, 2, "ews_zulaessig & ews_zulaessig_auflagen"], - [2651972, 1200000, 3, "ews_nicht_zulaessig & ews_zulaessig_auflagen"], - [ - 2645371, - 1200735, - 2, - "ews_vorabklaeren", - ], # TODO: check with LU how to map lanslide areas - [2645598, 1202446, 3, "ews_nicht_zulaessig"], - ], - "cantonal_energy_service_url": "https://uwe.lu.ch/themen/energie/erneuerbare_energien/erdwaerme", - "wms_url": "https://public.geo.lu.ch/ogd/services/managed/EWNUTZXX_COL_V3_MP/MapServer/WMSServer", - "query_url": "https://public.geo.lu.ch/ogd/services/managed/EWNUTZXX_COL_V3_MP/MapServer/WMSServer", - "legend_url": "https://github.com/SFOE/SuitabilityGeothermalDrillingSwitzerland/raw/main/images/legend_lu.png", - "thematic_geoportal_url": "https://map.geo.lu.ch/gebaeudeenergie/erdwaerme/", - "info_format": "application/geo+json", - "bbox_delta": 10, - "style": "", - "loopLayers": True, - # no property values for this canton, only layers are matched if request hits a polygon - "layers": [ - { - "name": "3", - "desc": "Erdwärmenutzung zulässig", - "property_name": "Erdwärmenutzung zulässig", - "target_harmonized_value": 1, - }, - { - "name": "1", - "desc": "Erdwärmenutzung zulässig mit Auflagen", - "property_name": "Erdwärmenutzung zulässig mit Auflagen", - "target_harmonized_value": 2, - }, - { - "name": "2", - "desc": "Erdwärmenutzung zulässig mit Auflagen", - "property_name": "Erdwärmenutzung zulässig mit Auflagen", - "target_harmonized_value": 2, - }, - { - "name": "0", - "desc": "Erdwärmenutzung nicht zulässig", - "property_name": "Erdwärmenutzung nicht zulässig", - "target_harmonized_value": 3, - }, - ], - "harmonyMap": [ - {"sum": 1, "value": 1}, - {"sum": 2, "value": 2}, - {"sum": 11, "value": 2}, - {"sum": 12, "value": 2}, - {"sum": 13, "value": 3}, - {"sum": 3, "value": 3}, - ], - }, - "GR": { - "active": True, - "name": "GR", - "ground_control_point": [ - [2745188, 1187042, 1, "zulässig"], - [2747613, 1190056, 2, "bedingt zulässig"], - [2730752, 1191438, 3, "nicht zulässig"], - ], - "cantonal_energy_service_url": "https://www.gr.ch/DE/institutionen/verwaltung/diem/aev/foerderprogramme/haustechnischeanlagen/waermepumpen/Seiten/default.aspx", - "wms_url": "https://wms.geo.gr.ch/erdwaermenutzung", - "query_url": "https://wms.geo.gr.ch/erdwaermenutzung", - "legend_url": "https://map.geo.gr.ch/mapserv_proxy?ogcserver=Kanton+Graub%C3%BCnden%2C+Energie&cache_version=6cf674ab2fb648a0ac60156e614e4bbe&FORMAT=image%2Fpng&TRANSPARENT=true&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYERTITLE=FALSE&Itemfontsize=10&LAYER=uGSP_Zulaessigkeit&SCALE=499998.9999999998", - "thematic_geoportal_url": "https://map.geo.gr.ch/public/theme/uGSP_Energie?tree_group_layers_layer4gmf24.uGSP_Grundstueckabfrage_Grundstueckabfrage_AGGR=layer4gmf24.uGSP_Grundstueckabfrage_AGGR&lang=fr&map_x=2760071&map_y=1169982&map_zoom=1&baselayer_ref=Karte%20grau&tree_group_layers_uGSP_Nutzungsplanung_Kommunaler_Darstellungsdienst.uGSP_Nutzungsplanung_Genereller_Erschliessungsplan=uGSP_Nutzungsplanung_Kommunaler_Darstellungsdienst.uGSP_GEP_Energie_AGGR&theme=uGSP_Energie&tree_groups=uGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Globalstrahlung_jaehrlich%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Globalstrahlung_Vollzug%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Netzgebiet_Elektroversorgung_AGGR%2CuGSP_Energie.uGSP_Erdwaermenutzung%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Waermepumpe_Luft_Wasser%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Wasserwerkkataster_AGGR%2Clayer4gmf24.uGSP_Grundstueckabfrage_Grundstueckabfrage_AGGR&tree_group_layers_uGSP_Energie.uGSP_Erdwaermenutzung=uGSP_Energie.uGSP_Zulaessigkeit", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "Erdwaermenutzung_Zulaessigkeit", - "property_name": "zulaessigkeit", - "property_values": [ - { - "name": "zulässig", - "desc": "zulässig", - "target_harmonized_value": 1, - }, - { - "name": "bedingt zulässig", - "desc": "bedingt zulässig", - "target_harmonized_value": 2, - }, - { - "name": "nicht zulässig", - "desc": "nicht zulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "GL": { - "active": True, - "name": "GL", - "ground_control_point": [ - [2724237, 1206147, 3, "Erdsondenausschlussgebiet"], - [2725831, 1215961, 4, "undefined"], - ], - "cantonal_energy_service_url": "https://www.gl.ch/verwaltung/bau-und-umwelt/umwelt-wald-und-energie/umweltschutz-und-energie/gewaesserschutz.html/752", - "wms_url": "https://wms.geo.gl.ch/", - "query_url": "https://wms.geo.gl.ch/", - "legend_url": "https://map.geo.gl.ch/api/v1/legend/mainmap?VERSION=1.3.0&SERVICE=WMS&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&CRS=EPSG%3A2056&SRS=EPSG%3A2056&SLD_VERSION=1.1.0&WIDTH=200&HEIGHT=200&LAYER=ch.gl.utilities.erdsondenausschlussbereich&FILTER=", - "thematic_geoportal_url": "https://map.geo.gl.ch/?t=default&l=ch.gl.utilities.erdsondenausschlussbereich%2Cch.gl.basemaps.kantonsmaske%5B40%5D&bl=pixelkarte&c=2726296%2C1208537&s=40000", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ch.gl.utilities.erdsondenausschlussbereich", - "property_name": "art", - "property_values": [ - { - "name": "undefined", - "desc": "undefined", - "target_harmonized_value": 2, - }, - { - "name": "Erdsondenausschlussgebiet", - "desc": "Erdsondenausschlussgebiet", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "GE": { - "active": True, - "name": "GE", - "ground_control_point": [ - [ - 2510785, - 1121097, - 1, - "Sondes géothermiques verticales (système fermé)", - ], - [2506500, 1118148, 2, "Demande de renseignement"], - [2502640, 1113946, 3, "Interdiction de géothermie"], - [2494984, 1116373, 3, "Géothermie sur nappe (système ouvert)"], - ], - "cantonal_energy_service_url": "https://www.ge.ch/gestion-du-sous-sol", - "wms_url": "https://app2.ge.ch/tergeoservices/rest/services/Hosted/GOL_EXPLOITATION_GEOTHERMIE/MapServer/WmsServer", - "query_url": "https://app2.ge.ch/tergeoservices/rest/services/Hosted/GOL_EXPLOITATION_GEOTHERMIE/FeatureServer", - "legend_url": "", - "style": "", - "thematic_geoportal_url": "https://map.sitg.ge.ch/app/?mapresources=GEOTHERMIE", - "info_format": "arcgis/json", - "bbox_delta": 10, - "layers": [ - { - "id": 0, - "name": "GOL_EXPLOITATION_GEOTHERMIE", - "property_name": "secteur", - "property_values": [ - { - "name": "Sondes géothermiques verticales (système fermé)", - "desc": "Sondes géothermiques verticales (système fermé)", - "target_harmonized_value": 1, - }, - { - "name": "Demande de renseignement", - "desc": "Demande de renseignement", - "target_harmonized_value": 2, - }, - { - "name": "Interdiction de géothermie", - "desc": "Interdiction de géothermie", - "target_harmonized_value": 3, - }, - { - "name": "Géothermie sur nappe (système ouvert)", - "desc": "Géothermie sur nappe (système ouvert)", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "FR": { - "active": True, - "name": "FR", - "ground_control_point": [ - [2582124, 1164966, 1, "SGV autorisées"], - [2582392, 1164789, 2, "SGV avec demande préalable obligatoire"], - [2582439, 1165031, 3, "SGV interdites"], - ], - "cantonal_energy_service_url": "https://www.fr.ch/energie-agriculture-et-environnement/energie", - "wms_url": "https://map.geo.fr.ch/arcgis/services/PortailCarto/Theme_environnement/MapServer/WmsServer", - "query_url": "https://map.geo.fr.ch/arcgis/rest/services/PortailCarto/Theme_environnement/MapServer", - "legend_url": "https://map.geo.fr.ch/arcgis/services/PortailCarto/Theme_environnement/MapServer/WmsServer?SERVICE=WMS&VERSION=1.1.3&REQUEST=GetLegendGraphic&FORMAT=image/png&LAYER=Admissibilite_des_sondes_geothermiques_SGV37370", - "thematic_geoportal_url": "https://map.geo.fr.ch/?share=a526a596-5cde-491e-b22d-6d692e78f25b", - "info_format": "arcgis/json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "id": 17, # Required: ESRI REST layer ID - "name": "Admissibilite_des_sondes_geothermiques_SGV37370", - "desc": "Admissibilite des sondes geothermiques SGV", - "property_name": "DA_SGV_DESC", - "property_values": [ - { - "name": "SGV autorisées", - "desc": "SGV autorisées", - "target_harmonized_value": 1, - }, - { - "name": "SGV avec demande préalable obligatoire", - "desc": "SGV avec demande préalable obligatoire", - "target_harmonized_value": 2, - }, - { - "name": "SGV interdites", - "desc": "SGV interdites", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "BL": { - "active": True, - "name": "BL", - "ground_control_point": [ - [2622256, 1258565, 1, "BC"], - [2623503, 1259120, 2, "B"], - [2620984, 1259551, 4, "nB"], - [2620314.1, 1263004.5, 1, "C"], - [2623369, 1261302, 3, "undefined", "Fall A"], - ], - "cantonal_energy_service_url": "https://www.baselland.ch/politik-und-behorden/direktionen/bau-und-umweltschutzdirektion/umweltschutz-energie", - "wms_url": "https://geowms.bl.ch/", - "query_url": "https://geowms.bl.ch/", - "legend_url": "https://geowms.bl.ch/?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=erdwaerme_uebersicht_ohne_a&format=image/png&sld_version=1.1.0", - "thematic_geoportal_url": "https://geoview.bl.ch/?map_x=2632500&map_y=1245475&map_zoom=2&tree_group_layers_Oberfl%C3%A4chennahe%20Erdw%C3%A4rme=erdwaerme_uebersicht_group&tree_groups=Oberfl%C3%A4chennahe%20Erdw%C3%A4rme", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "erdwaerme_uebersicht_ohne_a", - "property_name": "ov_kategor", - "property_values": [ - { - "name": "C", - "desc": "Fall C - Wärmenutzung durch Erdwärmesonden bis max. zulässige Bohrtiefe mit Standardauflagen möglich", - "target_harmonized_value": 1, - }, - { - "name": "B", - "desc": "Fall B - Wärmenutzung durch Erdwärmesonden mit speziellen Auflagen möglich", - "target_harmonized_value": 2, - }, - { - "name": "BC", - "desc": "Fall BC - Wärmenutzung durch Erdwärmesonden mit Standardauflagen für Schichten des Fall C, darunter bis max. zulässige Bohrtiefe Wärmenutzung mit spez. Auflagen", - "target_harmonized_value": 1, - }, - { - "name": "nB", - "desc": "Fall nB - Gebiet wurde noch nicht bearbeitet", - "target_harmonized_value": 4, - }, - { - "name": "undefined", - "desc": "Fall A - Wärmenutzung durch Erdwärmesonden unzulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "BE": { - "active": True, - "name": "BE", - "ground_control_point": [ - [2599525, 1201544, 1, "1"], - [2597320, 1198900, 1, "2"], - [2599516, 1200212, 2, "3"], - [2600318, 1199065, 3, "4"], - [2621040, 1171911, 4, "undefined", "im Thunersee"], - ], - "cantonal_energy_service_url": "https://www.bvd.be.ch/de/start/themen/wasser/wassernutzung/heizen-mit-einer-waermepumpe/bewilligung-fuer-eine-sole-wasserpumpe-beantragen.html", - "wms_url": "https://www.geoservice.apps.be.ch/geoservice3/services/a42geo/of_utilitiescommunication01_de_ms_wms/MapServer/WMSServer", - "query_url": "https://www.geoservice.apps.be.ch/geoservice3/services/a42geo/of_utilitiescommunication01_de_ms_wms/MapServer/WMSServer", - "legend_url": "https://www.geoservice.apps.be.ch/geoservice3/services/a42geo/of_utilitiescommunication01_de_ms_wms/MapServer/WMSServer?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ERDSOND_ERDSOND_VW_16828&format=image/png", - "thematic_geoportal_url": "https://www.topo.apps.be.ch/pub/map/?lang=de&gpk=ERDSOND_GPK", - "info_format": "application/geo+json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ERDSOND_ERDSOND_VW_16828", - "property_name": "CODE", - "property_values": [ - { - "name": "1", - "desc": "Erdwärmesonden erlaubt", - "target_harmonized_value": 1, - }, - { - "name": "2", - "desc": "Erdwärmesonden erlaubt - mit Tiefenbeschränkung", - "target_harmonized_value": 1, - }, - { - "name": "3", - "desc": "Erdwärmesonden erlaubt - mit Auflagen", - "target_harmonized_value": 2, - }, - { - "name": "4", - "desc": "Erdwärmesonden gem. BAFU-Richtlinie unzulässig (Erstbeurteilung)", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "AR": { - "active": True, - "name": "AR", - "ground_control_point": [ - [2755488, 1249846, 1, "zulässig (siehe Erläuterungen)"], - [2756050, 1251316, 2, "bedingt zulässig (siehe Erläuterungen)"], - [2755962, 1251037, 3, "nicht zulässig (siehe Erläuterungen)"], - ], - "cantonal_energy_service_url": "https://www.ar.ch/verwaltung/departement-bau-und-volkswirtschaft/amt-fuer-umwelt/energie/bauten-und-anlagen/waermepumpen/", - "wms_url": "https://www.geoportal.ch/services/wms/ktar", - "query_url": "https://www.geoportal.ch/services/wms/ktar", - # Legend not working due to force download - "legend_url": "", # "https://www.geoportal.ch/services/wms/ktar?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ch.geoportal.ver_entsorgung_kommunikation.29.0.erdwaermesonden_kt&format=image/png", - "thematic_geoportal_url": "https://www.geoportal.ch/ktar/map/29?y=2728824.00&x=1243710.00&scale=100000&rotation=0", - "info_format": "application/json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ch.geoportal.ver_entsorgung_kommunikation.29.0.erdwaermesonden_kt", - "property_name": "Info", - "property_values": [ - { - "name": "zulässig (siehe Erläuterungen)", - "desc": "zulässig (siehe Erläuterungen)", - "target_harmonized_value": 1, - }, - {"name": "zulässig", "target_harmonized_value": 1}, - { - "name": "bedingt zulässig (siehe Erläuterungen)", - "desc": "bedingt zulässig (siehe Erläuterungen)", - "target_harmonized_value": 2, - }, - { - "name": "bedingt zulässig aufgrund: Hydrogeologie", - "desc": "bedingt zulässig aufgrund: Hydrogeologie", - "target_harmonized_value": 2, - }, - { - "name": "bedingt zulässig aufgrund: Hydrogeologie, problematische Bohrung", - "desc": "bedingt zulässig aufgrund: Hydrogeologie, problematische Bohrung", - "target_harmonized_value": 2, - }, - { - "name": "nicht zulässig (siehe Erläuterungen)", - "desc": "nicht zulässig (siehe Erläuterungen)", - "target_harmonized_value": 3, - }, - { - "name": "unzulässig", - "desc": "unzulässig", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "AI": { - "active": True, - "name": "AI", - "ground_control_point": [ - [2751274, 1244545, 1, "zulässig (siehe Erläuterungen)"], - [2747293, 1243755, 2, "bedingt zulässig (siehe Erläuterungen)"], - [2748348, 1237931, 3, "nicht zulässig (siehe Erläuterungen)"], - ], - "cantonal_energy_service_url": "https://www.ai.ch/themen/natur-und-umwelt/umweltschutz/waermeerzeugungsanlagen/waermepumpen/erdsondenbohrungen", - "wms_url": "https://www.geoportal.ch/services/wms/ktai", - "query_url": "https://www.geoportal.ch/services/wms/ktai", - # Legend not working due to force download - "legend_url": "", # "https://www.geoportal.ch/services/wms/ktai?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ch.geoportal.ver_entsorgung_kommunikation.29.0.erdwaermesonden_kt&format=image/png" - "thematic_geoportal_url": "https://www.geoportal.ch/ktai/map/29?y=2728824.00&x=1243710.00&scale=100000&rotation=0", - "info_format": "application/json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "ch.geoportal.ver_entsorgung_kommunikation.29.0.erdwaermesonden_kt", - "property_name": "Info", - "property_values": [ - { - "name": "zulässig (siehe Erläuterungen)", - "desc": "zulässig (siehe Erläuterungen)", - "target_harmonized_value": 1, - }, - { - "name": "bedingt zulässig (siehe Erläuterungen)", - "desc": "bedingt zulässig (siehe Erläuterungen)", - "target_harmonized_value": 2, - }, - { - "name": "nicht zulässig (siehe Erläuterungen)", - "desc": "nicht zulässig (siehe Erläuterungen)", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - "AG": { - "active": True, - "name": "AG", - "ground_control_point": [ - [2655805, 1258983, 2, "3"], - [2653716, 1269536, 3, "6"], - [2657482, 1264764, 2, "2"], - [2658947, 1259532, 3, "4"], - [2658947, 1240238, 1, "1"], - ], - "cantonal_energy_service_url": "https://www.ag.ch/de/verwaltung/bvu/umwelt-natur-landschaft/umwelt/erdwaermenutzung/eignungskarte-erdwaerme", - "wms_url": "https://www.ag.ch/geoportal/services/afu_erdwaerme/MapServer/WMSServer", - "query_url": "https://www.ag.ch/geoportal/services/afu_erdwaerme/MapServer/WMSServer", - "legend_url": "https://www.ag.ch/geoportal/services/afu_erdwaerme/MapServer/WMSServer?SERVICE=WMS&REQUEST=GetLegendGraphic&VERSION=1.3.0&LAYER=Eignung_Erdwärmenutzung55223&FORMAT=image/png", - "thematic_geoportal_url": "https://www.ag.ch/geoportal/apps/onlinekarten/?basemap=base_landeskarten_sw::topicmaps.geo.ag.ch,1,true¢er=2652503.58,1250363.05&z=3&layers=afu_erdwaerme::topicmaps.geo.ag.ch;1;true", - "info_format": "application/geo+json", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "Eignung_Erdwärmenutzung55223", - "property_name": "G_Nutz_N", - "property_values": [ - { - "name": "1", - "desc": "EWS möglich", - "target_harmonized_value": 1, - }, - { - "name": "2", - "desc": "EWS mit geologischer Begleitung möglich", - "target_harmonized_value": 2, - }, - { - "name": "3", - "desc": "EWS mit geologischem Gutachten und geologischer Begleitung möglich", - "target_harmonized_value": 2, - }, - { - "name": "4", - "desc": "Nur Grundwasserwärmepumpe möglich", - "target_harmonized_value": 3, - }, - { - "name": "6", - "desc": "Keine Erdwärmenutzung möglich", - "target_harmonized_value": 3, - }, - ], - } - ], - }, - # CANTONS NOT YET AVAILABLE - "NE": { - "active": False, - "name": "NE", - "ground_control_point": [ - [2555501, 1206134, 1, "OK"], - [2556890, 1206292, 2, "RESTRICTION"], - ], - "cantonal_energy_service_url": "https://www.ne.ch/autorites/DDTE/SENE/energie/Pages/Loi-energie-et-remplacement-chauffage.aspx", - "wms_url": "https://sitn.ne.ch/mapserv_proxy?ogcserver=private-png", - "query_url": "https://sitn.ne.ch/mapserv_proxy?ogcserver=private-png", - "thematic_geoportal_url": "https://sitn.ne.ch/theme/environnement?lang=fr&map_x=2550050&map_y=1204950&map_zoom=1&baselayer_ref=blank&tree_group_layers_gp_photos_360=&theme=environnement&tree_groups=gp_mesures_meteo%2Cgp_mo_cadastre_partiel%2Cgp_antennes%2Cgp_environnement%2Cgp_base_layers&tree_enable_eg36_geotherm_carte_finale=true&tree_enable_eg37_geotherm_zones_exclues=true&tree_enable_eg38_geotherm_canepo_statut=true", - "legend_url": "https://sitn.ne.ch/mapserv_proxy?ogcserver=private-png&cache_version=ffa494693bec4c5bb5ba67f8641a590e&FORMAT=image%2Fpng&TRANSPARENT=true&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYER=eg36_geotherm_carte_finale&SCALE=357142.1428571427", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "TEST", - "property_name": "profondeur", - "property_values": [ - { - "name": "TEST", - "desc": "TEST", - "target_harmonized_value": 1, - }, - { - "name": "TEST", - "desc": "TEST", - "target_harmonized_value": 2, - }, - {"name": "TEST", "desc": "TEST", "target_harmonized_value": 3}, - ], - }, - ], - }, - "SO": { - "active": False, - "name": "NE", - "ground_control_point": [ - [2555501, 1206134, 1, "OK"], - [2556890, 1206292, 2, "RESTRICTION"], - ], - "cantonal_energy_service_url": "https://energie.so.ch/", - "wms_url": "https://so.ch/", - "query_url": "https://so.ch/", - "thematic_geoportal_url": "https://geo.so.ch/map/", - "legend_url": "https://so.ch/", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "test", - "property_name": "profondeur", - "property_values": [ - { - "name": "TEST", - "desc": "TEST", - "target_harmonized_value": 1, - }, - { - "name": "TEST", - "desc": "TEST", - "target_harmonized_value": 2, - }, - {"name": "TEST", "desc": "TEST", "target_harmonized_value": 3}, - ], - }, - ], - }, - "BS": { - "active": False, - "name": "NE", - "ground_control_point": [ - [2555501, 1206134, 1, "OK"], - [2556890, 1206292, 2, "RESTRICTION"], - ], - "cantonal_energy_service_url": "https://www.aue.bs.ch/wasser/grundwasser/bohrungen-in-das-grundwasser.html", - "wms_url": "https://bs.ch/", - "query_url": "https://bs.ch/", - "thematic_geoportal_url": "https://map.geo.bs.ch", - "legend_url": "https://map.geo.bs.ch", - "info_format": "application/vnd.ogc.gml", - "bbox_delta": 10, - "style": "", - "layers": [ - { - "name": "test", - "property_name": "profondeur", - "property_values": [ - { - "name": "TEST", - "desc": "TEST", - "target_harmonized_value": 1, - }, - { - "name": "TEST", - "desc": "TEST", - "target_harmonized_value": 2, - }, - {"name": "TEST", "desc": "TEST", "target_harmonized_value": 3}, - ], - }, - ], - }, - } -} +"""Backwards-compatible access to the cantonal configuration. + +The canonical source of truth is now the per-canton YAML files under ``data/``, +loaded and validated by :mod:`drillapi.cantons_configuration.loader`. + +This module is kept so existing imports (``from drillapi.cantons_configuration +import cantons`` / ``cantons.CANTONS``) continue to work. ``CANTONS`` is the +legacy-shaped dict (``{"cantons_configurations": {: {...}}}``) produced +from the validated YAML, so every consumer keeps receiving plain dicts with the +exact same keys and values as before. + +Importing this module triggers validation of all YAML files; a malformed +configuration raises +:class:`~drillapi.cantons_configuration.loader.ConfigError` at import time +(fail fast), so the app never starts serving broken data. +""" + +from __future__ import annotations + +from .loader import CANTONS + +__all__ = ["CANTONS"] diff --git a/src/drillapi/cantons_configuration/data/AG.yaml b/src/drillapi/cantons_configuration/data/AG.yaml new file mode 100644 index 0000000..24fc063 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/AG.yaml @@ -0,0 +1,50 @@ +active: true +name: AG +ground_control_point: +- - 2655805 + - 1258983 + - 2 + - '3' +- - 2653716 + - 1269536 + - 3 + - '6' +- - 2657482 + - 1264764 + - 2 + - '2' +- - 2658947 + - 1259532 + - 3 + - '4' +- - 2658947 + - 1240238 + - 1 + - '1' +cantonal_energy_service_url: https://www.ag.ch/de/verwaltung/bvu/umwelt-natur-landschaft/umwelt/erdwaermenutzung/eignungskarte-erdwaerme +wms_url: https://www.ag.ch/geoportal/services/afu_erdwaerme/MapServer/WMSServer +query_url: https://www.ag.ch/geoportal/services/afu_erdwaerme/MapServer/WMSServer +thematic_geoportal_url: https://www.ag.ch/geoportal/apps/onlinekarten/?basemap=base_landeskarten_sw::topicmaps.geo.ag.ch,1,true¢er=2652503.58,1250363.05&z=3&layers=afu_erdwaerme::topicmaps.geo.ag.ch;1;true +legend_url: https://www.ag.ch/geoportal/services/afu_erdwaerme/MapServer/WMSServer?SERVICE=WMS&REQUEST=GetLegendGraphic&VERSION=1.3.0&LAYER=Eignung_Erdwärmenutzung55223&FORMAT=image/png +info_format: application/geo+json +bbox_delta: 10 +style: '' +layers: +- name: Eignung_Erdwärmenutzung55223 + property_name: G_Nutz_N + property_values: + - name: '1' + target_harmonized_value: 1 + desc: EWS möglich + - name: '2' + target_harmonized_value: 2 + desc: EWS mit geologischer Begleitung möglich + - name: '3' + target_harmonized_value: 2 + desc: EWS mit geologischem Gutachten und geologischer Begleitung möglich + - name: '4' + target_harmonized_value: 3 + desc: Nur Grundwasserwärmepumpe möglich + - name: '6' + target_harmonized_value: 3 + desc: Keine Erdwärmenutzung möglich diff --git a/src/drillapi/cantons_configuration/data/AI.yaml b/src/drillapi/cantons_configuration/data/AI.yaml new file mode 100644 index 0000000..5e82136 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/AI.yaml @@ -0,0 +1,36 @@ +active: true +name: AI +ground_control_point: +- - 2751274 + - 1244545 + - 1 + - zulässig (siehe Erläuterungen) +- - 2747293 + - 1243755 + - 2 + - bedingt zulässig (siehe Erläuterungen) +- - 2748348 + - 1237931 + - 3 + - nicht zulässig (siehe Erläuterungen) +cantonal_energy_service_url: https://www.ai.ch/themen/natur-und-umwelt/umweltschutz/waermeerzeugungsanlagen/waermepumpen/erdsondenbohrungen +wms_url: https://www.geoportal.ch/services/wms/ktai +query_url: https://www.geoportal.ch/services/wms/ktai +thematic_geoportal_url: https://www.geoportal.ch/ktai/map/29?y=2728824.00&x=1243710.00&scale=100000&rotation=0 +legend_url: '' +info_format: application/json +bbox_delta: 10 +style: '' +layers: +- name: ch.geoportal.ver_entsorgung_kommunikation.29.0.erdwaermesonden_kt + property_name: Info + property_values: + - name: zulässig (siehe Erläuterungen) + target_harmonized_value: 1 + desc: zulässig (siehe Erläuterungen) + - name: bedingt zulässig (siehe Erläuterungen) + target_harmonized_value: 2 + desc: bedingt zulässig (siehe Erläuterungen) + - name: nicht zulässig (siehe Erläuterungen) + target_harmonized_value: 3 + desc: nicht zulässig (siehe Erläuterungen) diff --git a/src/drillapi/cantons_configuration/data/AR.yaml b/src/drillapi/cantons_configuration/data/AR.yaml new file mode 100644 index 0000000..e5b47da --- /dev/null +++ b/src/drillapi/cantons_configuration/data/AR.yaml @@ -0,0 +1,47 @@ +active: true +name: AR +ground_control_point: +- - 2755488 + - 1249846 + - 1 + - zulässig (siehe Erläuterungen) +- - 2756050 + - 1251316 + - 2 + - bedingt zulässig (siehe Erläuterungen) +- - 2755962 + - 1251037 + - 3 + - nicht zulässig (siehe Erläuterungen) +cantonal_energy_service_url: https://www.ar.ch/verwaltung/departement-bau-und-volkswirtschaft/amt-fuer-umwelt/energie/bauten-und-anlagen/waermepumpen/ +wms_url: https://www.geoportal.ch/services/wms/ktar +query_url: https://www.geoportal.ch/services/wms/ktar +thematic_geoportal_url: https://www.geoportal.ch/ktar/map/29?y=2728824.00&x=1243710.00&scale=100000&rotation=0 +legend_url: '' +info_format: application/json +bbox_delta: 10 +style: '' +layers: +- name: ch.geoportal.ver_entsorgung_kommunikation.29.0.erdwaermesonden_kt + property_name: Info + property_values: + - name: zulässig (siehe Erläuterungen) + target_harmonized_value: 1 + desc: zulässig (siehe Erläuterungen) + - name: zulässig + target_harmonized_value: 1 + - name: bedingt zulässig (siehe Erläuterungen) + target_harmonized_value: 2 + desc: bedingt zulässig (siehe Erläuterungen) + - name: 'bedingt zulässig aufgrund: Hydrogeologie' + target_harmonized_value: 2 + desc: 'bedingt zulässig aufgrund: Hydrogeologie' + - name: 'bedingt zulässig aufgrund: Hydrogeologie, problematische Bohrung' + target_harmonized_value: 2 + desc: 'bedingt zulässig aufgrund: Hydrogeologie, problematische Bohrung' + - name: nicht zulässig (siehe Erläuterungen) + target_harmonized_value: 3 + desc: nicht zulässig (siehe Erläuterungen) + - name: unzulässig + target_harmonized_value: 3 + desc: unzulässig diff --git a/src/drillapi/cantons_configuration/data/BE.yaml b/src/drillapi/cantons_configuration/data/BE.yaml new file mode 100644 index 0000000..de08c31 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/BE.yaml @@ -0,0 +1,48 @@ +active: true +name: BE +ground_control_point: +- - 2599525 + - 1201544 + - 1 + - '1' +- - 2597320 + - 1198900 + - 1 + - '2' +- - 2599516 + - 1200212 + - 2 + - '3' +- - 2600318 + - 1199065 + - 3 + - '4' +- - 2621040 + - 1171911 + - 4 + - undefined + - im Thunersee +cantonal_energy_service_url: https://www.bvd.be.ch/de/start/themen/wasser/wassernutzung/heizen-mit-einer-waermepumpe/bewilligung-fuer-eine-sole-wasserpumpe-beantragen.html +wms_url: https://www.geoservice.apps.be.ch/geoservice3/services/a42geo/of_utilitiescommunication01_de_ms_wms/MapServer/WMSServer +query_url: https://www.geoservice.apps.be.ch/geoservice3/services/a42geo/of_utilitiescommunication01_de_ms_wms/MapServer/WMSServer +thematic_geoportal_url: https://www.topo.apps.be.ch/pub/map/?lang=de&gpk=ERDSOND_GPK +legend_url: https://www.geoservice.apps.be.ch/geoservice3/services/a42geo/of_utilitiescommunication01_de_ms_wms/MapServer/WMSServer?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ERDSOND_ERDSOND_VW_16828&format=image/png +info_format: application/geo+json +bbox_delta: 10 +style: '' +layers: +- name: ERDSOND_ERDSOND_VW_16828 + property_name: CODE + property_values: + - name: '1' + target_harmonized_value: 1 + desc: Erdwärmesonden erlaubt + - name: '2' + target_harmonized_value: 1 + desc: Erdwärmesonden erlaubt - mit Tiefenbeschränkung + - name: '3' + target_harmonized_value: 2 + desc: Erdwärmesonden erlaubt - mit Auflagen + - name: '4' + target_harmonized_value: 3 + desc: Erdwärmesonden gem. BAFU-Richtlinie unzulässig (Erstbeurteilung) diff --git a/src/drillapi/cantons_configuration/data/BL.yaml b/src/drillapi/cantons_configuration/data/BL.yaml new file mode 100644 index 0000000..8096175 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/BL.yaml @@ -0,0 +1,51 @@ +active: true +name: BL +ground_control_point: +- - 2622256 + - 1258565 + - 1 + - BC +- - 2623503 + - 1259120 + - 2 + - B +- - 2620984 + - 1259551 + - 4 + - nB +- - 2620314.1 + - 1263004.5 + - 1 + - C +- - 2623369 + - 1261302 + - 3 + - undefined + - Fall A +cantonal_energy_service_url: https://www.baselland.ch/politik-und-behorden/direktionen/bau-und-umweltschutzdirektion/umweltschutz-energie +wms_url: https://geowms.bl.ch/ +query_url: https://geowms.bl.ch/ +thematic_geoportal_url: https://geoview.bl.ch/?map_x=2632500&map_y=1245475&map_zoom=2&tree_group_layers_Oberfl%C3%A4chennahe%20Erdw%C3%A4rme=erdwaerme_uebersicht_group&tree_groups=Oberfl%C3%A4chennahe%20Erdw%C3%A4rme +legend_url: https://geowms.bl.ch/?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=erdwaerme_uebersicht_ohne_a&format=image/png&sld_version=1.1.0 +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: erdwaerme_uebersicht_ohne_a + property_name: ov_kategor + property_values: + - name: C + target_harmonized_value: 1 + desc: Fall C - Wärmenutzung durch Erdwärmesonden bis max. zulässige Bohrtiefe mit Standardauflagen möglich + - name: B + target_harmonized_value: 2 + desc: Fall B - Wärmenutzung durch Erdwärmesonden mit speziellen Auflagen möglich + - name: BC + target_harmonized_value: 1 + desc: Fall BC - Wärmenutzung durch Erdwärmesonden mit Standardauflagen für Schichten des Fall C, darunter bis max. zulässige Bohrtiefe Wärmenutzung mit spez. Auflagen + - name: nB + target_harmonized_value: 4 + desc: Fall nB - Gebiet wurde noch nicht bearbeitet + - name: undefined + target_harmonized_value: 3 + desc: Fall A - Wärmenutzung durch Erdwärmesonden unzulässig diff --git a/src/drillapi/cantons_configuration/data/BS.yaml b/src/drillapi/cantons_configuration/data/BS.yaml new file mode 100644 index 0000000..3a97276 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/BS.yaml @@ -0,0 +1,32 @@ +active: false +name: BS +ground_control_point: +- - 2555501 + - 1206134 + - 1 + - OK +- - 2556890 + - 1206292 + - 2 + - RESTRICTION +cantonal_energy_service_url: https://www.aue.bs.ch/wasser/grundwasser/bohrungen-in-das-grundwasser.html +wms_url: https://bs.ch/ +query_url: https://bs.ch/ +thematic_geoportal_url: https://map.geo.bs.ch +legend_url: https://map.geo.bs.ch +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: test + property_name: profondeur + property_values: + - name: TEST + target_harmonized_value: 1 + desc: TEST + - name: TEST + target_harmonized_value: 2 + desc: TEST + - name: TEST + target_harmonized_value: 3 + desc: TEST diff --git a/src/drillapi/cantons_configuration/data/FR.yaml b/src/drillapi/cantons_configuration/data/FR.yaml new file mode 100644 index 0000000..24b71da --- /dev/null +++ b/src/drillapi/cantons_configuration/data/FR.yaml @@ -0,0 +1,38 @@ +active: true +name: FR +ground_control_point: +- - 2582124 + - 1164966 + - 1 + - SGV autorisées +- - 2582392 + - 1164789 + - 2 + - SGV avec demande préalable obligatoire +- - 2582439 + - 1165031 + - 3 + - SGV interdites +cantonal_energy_service_url: https://www.fr.ch/energie-agriculture-et-environnement/energie +wms_url: https://maps.fr.ch/ags/services/opendata/Admissibilite_des_sondes_geothermiques__SGV_/MapServer/WMSServer +query_url: https://maps.fr.ch/ags/rest/services/opendata/Admissibilite_des_sondes_geothermiques__SGV_/MapServer +thematic_geoportal_url: https://map.geo.fr.ch/?share=a526a596-5cde-491e-b22d-6d692e78f25b +legend_url: https://maps.fr.ch/ags/services/opendata/Admissibilite_des_sondes_geothermiques__SGV_/MapServer/WMSServer?request=GetLegendGraphic&version=1.3.0&format=image/png&layer=0 +info_format: arcgis/json +bbox_delta: 10 +style: '' +layers: +- name: Admissibilite_des_sondes_geothermiques__SGV_ + property_name: DA_SGV + property_values: + - name: SGVaut + target_harmonized_value: 1 + desc: SGV autorisées + - name: SGVdem + target_harmonized_value: 2 + desc: SGV avec demande préalable obligatoire + - name: SGVint + target_harmonized_value: 3 + desc: SGV interdites + id: 0 + desc: Admissibilité des sondes géothermiques (SGV) diff --git a/src/drillapi/cantons_configuration/data/GE.yaml b/src/drillapi/cantons_configuration/data/GE.yaml new file mode 100644 index 0000000..d4f6d8b --- /dev/null +++ b/src/drillapi/cantons_configuration/data/GE.yaml @@ -0,0 +1,44 @@ +active: true +name: GE +ground_control_point: +- - 2510785 + - 1121097 + - 1 + - Sondes géothermiques verticales (système fermé) +- - 2506500 + - 1118148 + - 2 + - Demande de renseignement +- - 2502640 + - 1113946 + - 3 + - Interdiction de géothermie +- - 2494984 + - 1116373 + - 3 + - Géothermie sur nappe (système ouvert) +cantonal_energy_service_url: https://www.ge.ch/gestion-du-sous-sol +wms_url: https://app2.ge.ch/tergeoservices/rest/services/Hosted/GOL_EXPLOITATION_GEOTHERMIE/MapServer/WmsServer +query_url: https://app2.ge.ch/tergeoservices/rest/services/Hosted/GOL_EXPLOITATION_GEOTHERMIE/FeatureServer +thematic_geoportal_url: https://map.sitg.ge.ch/app/?mapresources=GEOTHERMIE +legend_url: '' +info_format: arcgis/json +bbox_delta: 10 +style: '' +layers: +- name: GOL_EXPLOITATION_GEOTHERMIE + property_name: secteur + property_values: + - name: Sondes géothermiques verticales (système fermé) + target_harmonized_value: 1 + desc: Sondes géothermiques verticales (système fermé) + - name: Demande de renseignement + target_harmonized_value: 2 + desc: Demande de renseignement + - name: Interdiction de géothermie + target_harmonized_value: 3 + desc: Interdiction de géothermie + - name: Géothermie sur nappe (système ouvert) + target_harmonized_value: 3 + desc: Géothermie sur nappe (système ouvert) + id: 0 diff --git a/src/drillapi/cantons_configuration/data/GL.yaml b/src/drillapi/cantons_configuration/data/GL.yaml new file mode 100644 index 0000000..479f4f0 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/GL.yaml @@ -0,0 +1,29 @@ +active: true +name: GL +ground_control_point: +- - 2724237 + - 1206147 + - 3 + - Erdsondenausschlussgebiet +- - 2725831 + - 1215961 + - 4 + - undefined +cantonal_energy_service_url: https://www.gl.ch/verwaltung/bau-und-umwelt/umwelt-wald-und-energie/umweltschutz-und-energie/gewaesserschutz.html/752 +wms_url: https://wms.geo.gl.ch/ +query_url: https://wms.geo.gl.ch/ +thematic_geoportal_url: https://map.geo.gl.ch/?t=default&l=ch.gl.utilities.erdsondenausschlussbereich%2Cch.gl.basemaps.kantonsmaske%5B40%5D&bl=pixelkarte&c=2726296%2C1208537&s=40000 +legend_url: https://map.geo.gl.ch/api/v1/legend/mainmap?VERSION=1.3.0&SERVICE=WMS&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&CRS=EPSG%3A2056&SRS=EPSG%3A2056&SLD_VERSION=1.1.0&WIDTH=200&HEIGHT=200&LAYER=ch.gl.utilities.erdsondenausschlussbereich&FILTER= +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: ch.gl.utilities.erdsondenausschlussbereich + property_name: art + property_values: + - name: undefined + target_harmonized_value: 2 + desc: undefined + - name: Erdsondenausschlussgebiet + target_harmonized_value: 3 + desc: Erdsondenausschlussgebiet diff --git a/src/drillapi/cantons_configuration/data/GR.yaml b/src/drillapi/cantons_configuration/data/GR.yaml new file mode 100644 index 0000000..5d3f2db --- /dev/null +++ b/src/drillapi/cantons_configuration/data/GR.yaml @@ -0,0 +1,36 @@ +active: true +name: GR +ground_control_point: +- - 2745188 + - 1187042 + - 1 + - zulässig +- - 2747613 + - 1190056 + - 2 + - bedingt zulässig +- - 2730752 + - 1191438 + - 3 + - nicht zulässig +cantonal_energy_service_url: https://www.gr.ch/DE/institutionen/verwaltung/diem/aev/foerderprogramme/haustechnischeanlagen/waermepumpen/Seiten/default.aspx +wms_url: https://wms.geo.gr.ch/erdwaermenutzung +query_url: https://wms.geo.gr.ch/erdwaermenutzung +thematic_geoportal_url: https://map.geo.gr.ch/public/theme/uGSP_Energie?tree_group_layers_layer4gmf24.uGSP_Grundstueckabfrage_Grundstueckabfrage_AGGR=layer4gmf24.uGSP_Grundstueckabfrage_AGGR&lang=fr&map_x=2760071&map_y=1169982&map_zoom=1&baselayer_ref=Karte%20grau&tree_group_layers_uGSP_Nutzungsplanung_Kommunaler_Darstellungsdienst.uGSP_Nutzungsplanung_Genereller_Erschliessungsplan=uGSP_Nutzungsplanung_Kommunaler_Darstellungsdienst.uGSP_GEP_Energie_AGGR&theme=uGSP_Energie&tree_groups=uGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Globalstrahlung_jaehrlich%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Globalstrahlung_Vollzug%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Netzgebiet_Elektroversorgung_AGGR%2CuGSP_Energie.uGSP_Erdwaermenutzung%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Waermepumpe_Luft_Wasser%2CuGSP_Energie_Waermepumpen_Netzgebiete_Globalstrahlung_WWK.uGSP_Wasserwerkkataster_AGGR%2Clayer4gmf24.uGSP_Grundstueckabfrage_Grundstueckabfrage_AGGR&tree_group_layers_uGSP_Energie.uGSP_Erdwaermenutzung=uGSP_Energie.uGSP_Zulaessigkeit +legend_url: https://map.geo.gr.ch/mapserv_proxy?ogcserver=Kanton+Graub%C3%BCnden%2C+Energie&cache_version=6cf674ab2fb648a0ac60156e614e4bbe&FORMAT=image%2Fpng&TRANSPARENT=true&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYERTITLE=FALSE&Itemfontsize=10&LAYER=uGSP_Zulaessigkeit&SCALE=499998.9999999998 +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: Erdwaermenutzung_Zulaessigkeit + property_name: zulaessigkeit + property_values: + - name: zulässig + target_harmonized_value: 1 + desc: zulässig + - name: bedingt zulässig + target_harmonized_value: 2 + desc: bedingt zulässig + - name: nicht zulässig + target_harmonized_value: 3 + desc: nicht zulässig diff --git a/src/drillapi/cantons_configuration/data/JU.yaml b/src/drillapi/cantons_configuration/data/JU.yaml new file mode 100644 index 0000000..d60a90f --- /dev/null +++ b/src/drillapi/cantons_configuration/data/JU.yaml @@ -0,0 +1,57 @@ +active: true +name: JU +ground_control_point: +- - 2574738 + - 1249285 + - 1 + - Autorisé +- - 2576624 + - 1252365 + - 2 + - A évaluer (prendre contact avec ENV) +- - 2588623 + - 1243355 + - 2 + - Autorisé avec restriction de profondeur de 150m +- - 2588245 + - 1244475 + - 2 + - Autorisé avec restriction de profondeur de 100m +- - 2590205 + - 1244035 + - 2 + - Autorisé avec restriction de profondeur de 70m +- - 2573867 + - 1252854 + - 3 + - Interdit +cantonal_energy_service_url: https://www.jura.ch/DEN/ENV/Geothermie/Geothermie.html +wms_url: https://geoservices.jura.ch/wms +query_url: https://geoservices.jura.ch/wms +thematic_geoportal_url: https://geo.jura.ch/theme/POI?lang=fr&map_x=2580425&map_y=1242775&map_zoom=1&baselayer_ref=sdt_01_03_mensuration_officielle&baselayer_opacity=0&tree_enable_env_18_06_cadastre_geologique=false&tree_enable_env_18_03_geothermie_limitation_forages_sondes_geothermiques=true&tree_groups=Geologie +legend_url: https://geoservices.jura.ch/wms?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=ju.env_18_03_geothermie_limitation_forages_sondes_geothermiques&format=image/png&STYLE=default +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: ju.env_18_03_geothermie_limitation_forages_sondes_geothermiques + property_name: limitation_forage + property_values: + - name: Autorisé + target_harmonized_value: 1 + desc: Autorisé + - name: A évaluer (prendre contact avec ENV) + target_harmonized_value: 2 + desc: A évaluer (prendre contact avec ENV) + - name: Autorisé avec restriction de profondeur de 150m + target_harmonized_value: 2 + desc: Autorisé avec restriction de profondeur de 150m + - name: Autorisé avec restriction de profondeur de 100m + target_harmonized_value: 2 + desc: Autorisé avec restriction de profondeur de 100m + - name: Autorisé avec restriction de profondeur de 70m + target_harmonized_value: 2 + desc: Autorisé avec restriction de profondeur de 70m + - name: Interdit + target_harmonized_value: 3 + desc: Interdit diff --git a/src/drillapi/cantons_configuration/data/LU.yaml b/src/drillapi/cantons_configuration/data/LU.yaml new file mode 100644 index 0000000..b7ce6fe --- /dev/null +++ b/src/drillapi/cantons_configuration/data/LU.yaml @@ -0,0 +1,62 @@ +active: true +name: LU +ground_control_point: +- - 2665393 + - 1217714 + - 1 + - ews_zulaessig +- - 2652462 + - 1196901 + - 2 + - ews_zulaessig & ews_zulaessig_auflagen +- - 2651972 + - 1200000 + - 3 + - ews_nicht_zulaessig & ews_zulaessig_auflagen +- - 2645371 + - 1200735 + - 2 + - ews_vorabklaeren +- - 2645598 + - 1202446 + - 3 + - ews_nicht_zulaessig +cantonal_energy_service_url: https://uwe.lu.ch/themen/energie/erneuerbare_energien/erdwaerme +wms_url: https://public.geo.lu.ch/ogd/services/managed/EWNUTZXX_COL_V3_MP/MapServer/WMSServer +query_url: https://public.geo.lu.ch/ogd/services/managed/EWNUTZXX_COL_V3_MP/MapServer/WMSServer +thematic_geoportal_url: https://map.geo.lu.ch/gebaeudeenergie/erdwaerme/ +legend_url: https://github.com/SFOE/SuitabilityGeothermalDrillingSwitzerland/raw/main/images/legend_lu.png +info_format: application/geo+json +bbox_delta: 10 +style: '' +layers: +- name: '3' + property_name: Erdwärmenutzung zulässig + target_harmonized_value: 1 + desc: Erdwärmenutzung zulässig +- name: '1' + property_name: Erdwärmenutzung zulässig mit Auflagen + target_harmonized_value: 2 + desc: Erdwärmenutzung zulässig mit Auflagen +- name: '2' + property_name: Erdwärmenutzung zulässig mit Auflagen + target_harmonized_value: 2 + desc: Erdwärmenutzung zulässig mit Auflagen +- name: '0' + property_name: Erdwärmenutzung nicht zulässig + target_harmonized_value: 3 + desc: Erdwärmenutzung nicht zulässig +harmonyMap: +- sum: 1 + value: 1 +- sum: 2 + value: 2 +- sum: 11 + value: 2 +- sum: 12 + value: 2 +- sum: 13 + value: 3 +- sum: 3 + value: 3 +loopLayers: true diff --git a/src/drillapi/cantons_configuration/data/NE.yaml b/src/drillapi/cantons_configuration/data/NE.yaml new file mode 100644 index 0000000..47b926d --- /dev/null +++ b/src/drillapi/cantons_configuration/data/NE.yaml @@ -0,0 +1,32 @@ +active: false +name: NE +ground_control_point: +- - 2555501 + - 1206134 + - 1 + - OK +- - 2556890 + - 1206292 + - 2 + - RESTRICTION +cantonal_energy_service_url: https://www.ne.ch/autorites/DDTE/SENE/energie/Pages/Loi-energie-et-remplacement-chauffage.aspx +wms_url: https://sitn.ne.ch/mapserv_proxy?ogcserver=private-png +query_url: https://sitn.ne.ch/mapserv_proxy?ogcserver=private-png +thematic_geoportal_url: https://sitn.ne.ch/theme/environnement?lang=fr&map_x=2550050&map_y=1204950&map_zoom=1&baselayer_ref=blank&tree_group_layers_gp_photos_360=&theme=environnement&tree_groups=gp_mesures_meteo%2Cgp_mo_cadastre_partiel%2Cgp_antennes%2Cgp_environnement%2Cgp_base_layers&tree_enable_eg36_geotherm_carte_finale=true&tree_enable_eg37_geotherm_zones_exclues=true&tree_enable_eg38_geotherm_canepo_statut=true +legend_url: https://sitn.ne.ch/mapserv_proxy?ogcserver=private-png&cache_version=ffa494693bec4c5bb5ba67f8641a590e&FORMAT=image%2Fpng&TRANSPARENT=true&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetLegendGraphic&LAYER=eg36_geotherm_carte_finale&SCALE=357142.1428571427 +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: TEST + property_name: profondeur + property_values: + - name: TEST + target_harmonized_value: 1 + desc: TEST + - name: TEST + target_harmonized_value: 2 + desc: TEST + - name: TEST + target_harmonized_value: 3 + desc: TEST diff --git a/src/drillapi/cantons_configuration/data/NW.yaml b/src/drillapi/cantons_configuration/data/NW.yaml new file mode 100644 index 0000000..f7b6cbd --- /dev/null +++ b/src/drillapi/cantons_configuration/data/NW.yaml @@ -0,0 +1,70 @@ +active: true +name: NW +ground_control_point: +- - 2671744 + - 1196809 + - 3 + - bautechnisch_bauverbot +- - 2675123 + - 1198890 + - 3 + - Trinkwasserschutzgebiet +- - 2672327 + - 1202452 + - 3 + - nutzbare_Grundwassergebiete +- - 2668633 + - 1202922 + - 2 + - bebaubares_Grundwassergebiet +- - 2668267 + - 1201091 + - 2 + - vermutlich_unproblematisch +- - 2668945 + - 1200200 + - 2 + - problematisch +- - 2668854 + - 1199481 + - 2 + - bautechnisch_problematisch +- - 2669798 + - 1200979 + - 1 + - unproblematisch +cantonal_energy_service_url: https://www.nw.ch/amtumweltdienste/4020 +wms_url: https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service +query_url: https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service +thematic_geoportal_url: https://www.gis-daten.ch/map/nw_waermenutzung +legend_url: https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ch.nw.waermenutzungsbereiche&format=image/png +info_format: application/json +bbox_delta: 10 +style: '' +layers: +- name: ch.nw.waermenutzungsbereiche + property_name: Art + property_values: + - name: bautechnisch_bauverbot + target_harmonized_value: 3 + desc: Wärmenutzung aus dem Untergrund verboten + - name: Trinkwasserschutzgebiet + target_harmonized_value: 3 + - name: nutzbare_Grundwassergebiete + target_harmonized_value: 3 + desc: Grundwassergebiet mit Erdsondenverbot + - name: bebaubares_Grundwassergebiet + target_harmonized_value: 2 + desc: Grundwassergebiet ohne Erdsondenverbot + - name: vermutlich_unproblematisch + target_harmonized_value: 2 + desc: Vermutlich unproblematische Untergrundverhältnisse + - name: problematisch + target_harmonized_value: 2 + desc: Unsichere Untergrundverhältnisse + - name: bautechnisch_problematisch + target_harmonized_value: 2 + desc: Bautechnisch problematische Untergrundverhältnisse + - name: unproblematisch + target_harmonized_value: 1 + desc: Unproblematische Untergrundverhältnisse diff --git a/src/drillapi/cantons_configuration/data/OW.yaml b/src/drillapi/cantons_configuration/data/OW.yaml new file mode 100644 index 0000000..84a47b0 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/OW.yaml @@ -0,0 +1,57 @@ +active: true +name: OW +ground_control_point: +- - 2664351 + - 1193538 + - 1 + - zulaessig +- - 2660898 + - 1191688 + - 1 + - Auflage_GewaesserschutzbereichAu +- - 2660281 + - 1190191 + - 2 + - Auflage_NutzungsgebietGrundwasser +- - 2661431 + - 1192803 + - 2 + - Auflage_bedingtzulaessig +- - 2661534 + - 1193397 + - 3 + - nichtzulaessig +- - 2657375.6 + - 1180366.4 + - 3 + - Gebiete_mit_potentieller_Verkarstung +cantonal_energy_service_url: https://www.ow.ch/dienstleistungen/2055 +wms_url: https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service +query_url: https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service +thematic_geoportal_url: https://www.gis-daten.ch/map/ow_waermenutzung +legend_url: https://www.gis-daten.ch/wms/bfe_kann_ich_bohren/service?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=ch.ow.ews_zulaessigkeit&format=image/png +info_format: application/json +bbox_delta: 10 +style: '' +layers: +- name: ch.ow.ews_zulaessigkeit + property_name: Zulaessigkeit + property_values: + - name: zulaessig + target_harmonized_value: 1 + desc: Wärmenutzung zulässig (Bewilligungspflicht) + - name: Auflage_GewaesserschutzbereichAu + target_harmonized_value: 1 + desc: Wärmenutzung zulässig (Bewilligungspflicht) + - name: Auflage_NutzungsgebietGrundwasser + target_harmonized_value: 2 + desc: Wärmenutzung zulässig (Bewilligungspflicht); Geologische Begleitung während der Bohrung erforderlich + - name: Auflage_bedingtzulaessig + target_harmonized_value: 2 + desc: Wärmenutzung bedingt zulässig (Bewilligungspflicht); vorgängiges geologisches Gutachten erforderlich als Grundlage zur Prüfung der Bewilligungsfähigkeit + - name: nichtzulaessig + target_harmonized_value: 3 + desc: Wärmenutzung nicht zulässig + - name: Gebiete_mit_potentieller_Verkarstung + target_harmonized_value: 3 + desc: Wärmenutzung nicht zulässig diff --git a/src/drillapi/cantons_configuration/data/SG.yaml b/src/drillapi/cantons_configuration/data/SG.yaml new file mode 100644 index 0000000..ad209b3 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/SG.yaml @@ -0,0 +1,36 @@ +active: true +name: SG +ground_control_point: +- - 2733447 + - 1241435 + - 2 + - '1' +- - 2735185 + - 1244058 + - 1 + - '3' +- - 2742740 + - 1233995 + - 3 + - '2' +cantonal_energy_service_url: https://www.sg.ch/umwelt-natur/wasser/grundwasser---quellen-/geothermie0/erdwaermesonden.html +wms_url: https://services.geo.sg.ch/wss/service/SG00025_WMS/guest +query_url: https://services.geo.sg.ch/wss/service/SG00025_WMS/guest +thematic_geoportal_url: https://www.geoportal.ch/ktsg/map/29?y=2743944.00&x=1231900.00&scale=300000&rotation=0 +legend_url: https://services.geo.sg.ch/wss/service/SG00025_WMS/guest?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=Zulaessigkeitsbereich&format=image/png +info_format: application/geo+json +bbox_delta: 10 +style: '' +layers: +- name: Zulaessigkeitsbereich + property_name: OBJECTID + property_values: + - name: '2' + target_harmonized_value: 3 + desc: nicht zulässig (siehe Erläuterungen) + - name: '3' + target_harmonized_value: 1 + desc: zulässig (siehe Erläuterungen) + - name: '1' + target_harmonized_value: 2 + desc: bedingt zulässig (siehe Erläuterungen) diff --git a/src/drillapi/cantons_configuration/data/SH.yaml b/src/drillapi/cantons_configuration/data/SH.yaml new file mode 100644 index 0000000..fb5ff3c --- /dev/null +++ b/src/drillapi/cantons_configuration/data/SH.yaml @@ -0,0 +1,50 @@ +active: true +name: SH +ground_control_point: +- - 2690343 + - 1289821 + - 1 + - '3' +- - 2682889 + - 1292346 + - 2 + - '5' +- - 2691554 + - 1293373 + - 3 + - '1' +- - 2695085 + - 1290396 + - 2 + - '41' +- - 2695476 + - 1286474 + - 2 + - '42' +cantonal_energy_service_url: https://sh.ch/CMS/Webseite/Kanton-Schaffhausen/Beh-rde/Verwaltung/Baudepartement/Tiefbau-Schaffhausen/Abteilung-Gew-sser-und-Materialabbau/Erdw-rme-1738469-DE.html +wms_url: https://wms.geo.sh.ch/wms +query_url: https://wms.geo.sh.ch/wms +thematic_geoportal_url: https://map.geo.sh.ch/geoportal/?project=Geoportal%20Schaffhausen&legend=Legende&rotation=0.00&scale=90202¢er=2692188,1282000&layers=0bf7129c-2c80-4371-941f-556c4a6ba1f1 +legend_url: https://wms.geo.sh.ch/wms?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=sh.energie.erdsonden.eignung&format=image/png +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: sh.energie.erdsonden.eignung + property_name: eignung_erdwaermesonden_code + property_values: + - name: '3' + target_harmonized_value: 1 + desc: EWS bis 200 m Tiefe zulässig (über 200 m Vorabklärung notwendig) + - name: '5' + target_harmonized_value: 2 + desc: EWS mit Vorabklärung und fallweise geologischer Begleitung zulässig + - name: '41' + target_harmonized_value: 2 + desc: GWWN-Grossanlagen mit Gutachten zulässig (EWS auf Anfrage) + - name: '42' + target_harmonized_value: 2 + desc: GWWN-Grossanlagen mit Gutachten zulässig (Kurzsonden auf Anfrage) + - name: '1' + target_harmonized_value: 3 + desc: EWS und GWWN unzulässig diff --git a/src/drillapi/cantons_configuration/data/SO.yaml b/src/drillapi/cantons_configuration/data/SO.yaml new file mode 100644 index 0000000..a15032f --- /dev/null +++ b/src/drillapi/cantons_configuration/data/SO.yaml @@ -0,0 +1,32 @@ +active: false +name: SO +ground_control_point: +- - 2555501 + - 1206134 + - 1 + - OK +- - 2556890 + - 1206292 + - 2 + - RESTRICTION +cantonal_energy_service_url: https://energie.so.ch/ +wms_url: https://so.ch/ +query_url: https://so.ch/ +thematic_geoportal_url: https://geo.so.ch/map/ +legend_url: https://so.ch/ +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: test + property_name: profondeur + property_values: + - name: TEST + target_harmonized_value: 1 + desc: TEST + - name: TEST + target_harmonized_value: 2 + desc: TEST + - name: TEST + target_harmonized_value: 3 + desc: TEST diff --git a/src/drillapi/cantons_configuration/data/SZ.yaml b/src/drillapi/cantons_configuration/data/SZ.yaml new file mode 100644 index 0000000..a0baef7 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/SZ.yaml @@ -0,0 +1,33 @@ +active: true +name: SZ +ground_control_point: +- - 2707885 + - 1226453 + - 1 +- - 2707994 + - 1227010 + - 2 +- - 2708393 + - 1227843 + - 3 +cantonal_energy_service_url: https://www.sz.ch/verwaltung/umweltdepartement/amt-fuer-umwelt-und-energie/energie-und-klima/waermenutzung.html/8756-8758-8802-9447-9453-10708-10760 +wms_url: https://map.geo.sz.ch/mapserv_proxy +query_url: https://map.geo.sz.ch/mapserv_proxy +thematic_geoportal_url: https://map.geo.sz.ch/?lang=fr&map_x=2697700&map_y=1213275&map_zoom=0&baselayer_ref=Landeskarte%20(farbig)&tree_groups=grp_Energie_Erdw%C3%A4rmenutzung%2Cgrp_Energie_Frei_Leitungskataster%2Cgrp_Energie_Netzgebiete%2Cgrp_Energie_Solarenergie%2Cgrp_Energie_Wasserw%C3%A4rmenutzung&tree_group_layers_grp_Energie_Erdw%C3%A4rmenutzung=ch.sz.a034c.waermenutzung.erdwaermeanlage.erdwaermesonde%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.erdwaermesonde_nicht_bewilligungsfaehig%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.energiepfahl%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.energiekorb%2Cch.sz.a034c.waermenutzung.erdwaermeanlage.erdregister%2Cch.sz.a034c.waermenutzung.erdwaerme +legend_url: https://map.geo.sz.ch/mapserv_proxy?service=WMS&request=GetLegendGraphic&version=1.1.1&layer=ch.sz.a034c.waermenutzung.erdwaerme.technisch&format=image/png&FORMAT_OPTIONS=dpi:200 +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: ch.sz.a034c.waermenutzung.erdwaerme.technisch + property_name: zulaessigkeit_cd + property_values: + - name: ja + target_harmonized_value: 1 + desc: zulässig + - name: Abklaerung_noetig + target_harmonized_value: 2 + desc: bedingt zulässig + - name: nein + target_harmonized_value: 3 + desc: nicht zulässig diff --git a/src/drillapi/cantons_configuration/data/TG.yaml b/src/drillapi/cantons_configuration/data/TG.yaml new file mode 100644 index 0000000..b653346 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/TG.yaml @@ -0,0 +1,40 @@ +active: true +name: TG +ground_control_point: +- - 2747716.6 + - 1262056.4 + - 3 + - EWS grundsätzlich unzulässig +- - 2747720.5 + - 1262436.9 + - 1 + - EWS grundsätzlich zulässig mit Standardauflagen +- - 2709370.0 + - 1268975.0 + - 2 + - EWS grundsätzlich zulässig mit zusätzlichen Auflagen +- - 2712450.0 + - 1269125.0 + - 1 + - EWS grundsätzlich zulässig +cantonal_energy_service_url: https://energie.tg.ch/erneuerbare-energien/umweltwaerme-waermepumpen.html/3437 +wms_url: https://ows.geo.tg.ch/geofy_access_proxy/erdwaerme +query_url: https://ows.geo.tg.ch/geofy_access_proxy/erdwaerme +thematic_geoportal_url: https://map.geo.tg.ch/apps/mf-geoadmin3/?lang=de&topic=geologieboden&E=2718775.00&N=1270425.00&zoom=1&layers_opacity=1,1,0.9&layers=erdwaerme_eignung,erdwaerme_erdwaermesondenbohrungen,grundwasserkarte-fassung +legend_url: https://map.geo.tg.ch/services/geofy_chsdi3/static/images/legends/erdwaerme_eignung_de.png +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: Eignungszonen + property_name: eignungszone + property_values: + - name: '1' + target_harmonized_value: 1 + desc: EWS grundsätzlich zulässig mit Standardauflagen + - name: '2' + target_harmonized_value: 2 + desc: EWS grundsätzlich zulässig mit Standardauflagen + - name: '3' + target_harmonized_value: 3 + desc: EWS grundsätzlich unzulässig, Grundwasserschutzzone diff --git a/src/drillapi/cantons_configuration/data/TI.yaml b/src/drillapi/cantons_configuration/data/TI.yaml new file mode 100644 index 0000000..e5770c7 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/TI.yaml @@ -0,0 +1,27 @@ +active: true +name: TI +ground_control_point: +- - 2715738 + - 1125797 + - 3 +- - 2712602 + - 1133130 + - 1 +cantonal_energy_service_url: https://www4.ti.ch/dt/da/spaas/upaai/temi/acqua-protezione-e-approvvigionamento/protezione-e-approvvigionamento/acque-sotterranee/utilizzo-termico-delle-acque-sotterranee-e-del-sottosuolo/sfruttamento-dellenergia-geotermica-e-dallacqua-sotterranea/ +wms_url: https://wms.geo.ti.ch/service +query_url: https://wms.geo.ti.ch/service +thematic_geoportal_url: https://map.geo.ti.ch/?lang=it&baselayer_ref=Carta%20Nazionale%20%28bianco%20e%20nero%29&tree_group_layers_Idoneit%C3%A0%20sonde%20geotermiche=Idoneit%C3%A0%20sonde%20geotermiche&tree_groups=Idoneit%C3%A0%20sonde%20geotermiche +legend_url: https://www4.ti.ch/fileadmin/GENERALE/IGDAC/ccgeo/legends/ti_040_1_v1_0/ti_040_1_v1_0_sfruttamento_termico_acque_geotermia_sonda.png +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: ti_040_1_v1_0_sfruttamento_termico_acque_geotermia_sonda + property_name: idoneita_desc + property_values: + - name: In linea di principio non permesso + target_harmonized_value: 3 + desc: nicht zulässig + - name: In linea di principio permesso con condizioni + target_harmonized_value: 1 + desc: bedingt zulässig diff --git a/src/drillapi/cantons_configuration/data/UR.yaml b/src/drillapi/cantons_configuration/data/UR.yaml new file mode 100644 index 0000000..0b68583 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/UR.yaml @@ -0,0 +1,36 @@ +active: true +name: UR +ground_control_point: +- - 2703052 + - 1180378 + - 1 + - B +- - 2699152 + - 1175536 + - 2 + - C +- - 2689034 + - 1196007 + - 3 + - A +cantonal_energy_service_url: https://www.ur.ch/aemter/828 +wms_url: https://geo.ur.ch/webmercator/wms +query_url: https://geo.ur.ch/webmercator/wms +thematic_geoportal_url: https://geo.ur.ch/?center=962589%2C5922132&layers=Zul%C3%A4ssigkeit%20Grundwasserw%C3%A4rmepumpen&opacity=0.3&visibility=true&zoom=12 +legend_url: https://geo.ur.ch/webmercator/wms?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=umwelt:wnk_zulaessigkeitsbereiche_erdsonden&format=image/png +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: '' +layers: +- name: umwelt:wnk_zulaessigkeitsbereiche_erdsonden + property_name: zulaessigkeit_ews + property_values: + - name: B + target_harmonized_value: 1 + desc: Zulässig unter allgemeinen Auflagen + - name: C + target_harmonized_value: 2 + desc: Zulässig unter zusätzlichen Schutzmassnahmen + - name: A + target_harmonized_value: 3 + desc: roter Zulässigkeitsbereich / nicht zulässig diff --git a/src/drillapi/cantons_configuration/data/VD.yaml b/src/drillapi/cantons_configuration/data/VD.yaml new file mode 100644 index 0000000..0111fed --- /dev/null +++ b/src/drillapi/cantons_configuration/data/VD.yaml @@ -0,0 +1,44 @@ +active: true +name: VD +ground_control_point: +- - 2536682 + - 1156974 + - 1 + - Admissible sous conditions +- - 2548704 + - 1168639 + - 2 + - Limitation +- - 2535551 + - 1180721 + - 3 + - Interdiction +- - 2535239 + - 1148966 + - 4 +cantonal_energy_service_url: https://www.vd.ch/themes/environnement/eaux/eaux-souterraines/pompes-a-chaleur-pac-sondes-geothermiques-verticales-et-pompage-a-la-nappe +wms_url: https://www.ogc.vd.ch/public/services/OGC/wmsVD/Mapserver/WMSServer +query_url: https://www.ogc.vd.ch/public/services/OGC/wmsVD/Mapserver/WMSServer +thematic_geoportal_url: https://www.geo.vd.ch/?visiblelayers=%7B%22GEO_THEME_ENERG%22%3A%5B%22Potentiel%20thermique%20global%22%2C%22Type%20de%20site%22%5D%7D&share=4c196dee-a011-479e-abd1-2a805f1b34a3 +legend_url: https://www.ogc.vd.ch/public/services/OGC/wmsVD/Mapserver/WMSServer?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetLegendGraphic&FORMAT=image%2Fpng&LAYER=vd.admissibilite_indicative_sonde_geothermique +info_format: application/geo+json +bbox_delta: 10 +style: '' +layers: +- name: vd.admissibilite_indicative_sonde_geothermique + property_name: Type + property_values: + - name: Admissible sous conditions + target_harmonized_value: 1 + desc: Admissible sous conditions + - name: Admissible aux conditions standard, jusqu’à 300 mètres + target_harmonized_value: 1 + desc: Admissible aux conditions standard, jusqu’à 300 mètres + - name: Limitation + target_harmonized_value: 2 + - name: Limitation, soumis à des conditions spéciales + target_harmonized_value: 2 + desc: Limitation, soumis à des conditions spéciales + - name: Interdiction + target_harmonized_value: 3 + desc: Interdiction diff --git a/src/drillapi/cantons_configuration/data/VS.yaml b/src/drillapi/cantons_configuration/data/VS.yaml new file mode 100644 index 0000000..9b184e6 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/VS.yaml @@ -0,0 +1,50 @@ +active: true +name: VS +ground_control_point: +- - 2596336 + - 1122359 + - 1 + - CLASSGV 1 +- - 2598146 + - 1124454 + - 1 + - CLASSGV 2 +- - 2593661 + - 1122203 + - 2 + - CLASSGV 3 +- - 2594171 + - 1121846 + - 2 + - CLASSGV 4 +- - 2598068.4 + - 1130927.1 + - 4 + - CLASSGV 5 +cantonal_energy_service_url: https://www.vs.ch/web/sen/eaux-souterraines-et-constructions +wms_url: https://sit.vs.ch/arcgis/services/ENVIRONNEMENT/MapServer/WMSServer +query_url: https://sit.vs.ch/arcgis/services/ENVIRONNEMENT/MapServer/WMSServer +thematic_geoportal_url: https://sitonline.vs.ch/environnement/eso_admissibilite_SGV/#/?lang=fr +legend_url: https://sit.vs.ch/arcgis/services/ENVIRONNEMENT/MapServer/WMSServer?request=GetLegendGraphic%26version=1.3.0%26format=image/png%26layer=29 +info_format: application/geo+json +bbox_delta: 10 +style: '' +layers: +- name: '29' + property_name: CLASSGV + property_values: + - name: '1' + target_harmonized_value: 1 + desc: Sondes géothermiques admises (max. 200 m sans étude) + - name: '2' + target_harmonized_value: 1 + desc: Sondes géothermiques limitées à 100 m de profondeur + - name: '3' + target_harmonized_value: 2 + desc: Au cas par cas – Etude préalable (contexte hydrogéologique ou autres restrictions) + - name: '4' + target_harmonized_value: 2 + desc: Au cas par cas – Etude préalable (danger de glissement de terrain ou effondrement) + - name: '5' + target_harmonized_value: 3 + desc: Sondes géothermiques non admises diff --git a/src/drillapi/cantons_configuration/data/ZG.yaml b/src/drillapi/cantons_configuration/data/ZG.yaml new file mode 100644 index 0000000..7cc1036 --- /dev/null +++ b/src/drillapi/cantons_configuration/data/ZG.yaml @@ -0,0 +1,39 @@ +active: true +name: ZG +ground_control_point: +- - 2676912 + - 1228091 + - 1 + - mit Standardauflagen zulässig +- - 2684565 + - 1225437 + - 2 + - mit spezifischen Auflagen zulässig +- - 2686680 + - 1226360 + - 3 + - unzulässig +- - 2690680 + - 1219207 + - 4 +cantonal_energy_service_url: https://zg.ch/de/planen-bauen/bauvorschriften/gebaeude-und-energie +wms_url: https://services.geo.zg.ch/ows/Erdwaermenutzung +query_url: https://services.geo.zg.ch/ows/Erdwaermenutzung +thematic_geoportal_url: https://zugmap.ch/ +legend_url: https://services.geo.zg.ch/ows/Erdwaermenutzung?service=WMS&request=GetLegendGraphic&version=1.3.0&layer=zg.ews_zulaessigkeit&format=image/png&STYLE=default +info_format: application/vnd.ogc.gml +bbox_delta: 10 +style: default +layers: +- name: zg.ews_zulaessigkeit + property_name: zulaessigkeit + property_values: + - name: mit Standardauflagen zulässig + target_harmonized_value: 1 + desc: mit Standardauflagen zulässig + - name: mit spezifischen Auflagen zulässig + target_harmonized_value: 2 + desc: mit spezifischen Auflagen zulässig + - name: unzulässig + target_harmonized_value: 3 + desc: unzulässig diff --git a/src/drillapi/cantons_configuration/data/ZH.yaml b/src/drillapi/cantons_configuration/data/ZH.yaml new file mode 100644 index 0000000..44384df --- /dev/null +++ b/src/drillapi/cantons_configuration/data/ZH.yaml @@ -0,0 +1,84 @@ +active: true +name: ZH +ground_control_point: +- - 2677790 + - 1260185 + - 3 + - Auflagen & waermenutzung-zone-d +- - 2688561 + - 1274977 + - 3 + - Zone A (Schutzzonen und Schutzareale) +- - 2681563 + - 1248410 + - 2 + - Zone B (Schotter-Grundwasservorkommen, geeignet für Trinkwassergewinnung) +- - 2680649 + - 1246765 + - 2 + - Zone C (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung) +- - 2692350 + - 1275334 + - 2 + - Zone D (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung) +- - 2693733 + - 1238583 + - 2 + - Zone E (Quellwassergebiete, geeignet für Trinkwassergewinnung) +cantonal_energy_service_url: https://www.zh.ch/de/planen-bauen/bauvorschriften/energienutzung-untergrund-wasser.html +wms_url: https://wms.zh.ch/AwelGSWaermewwwZHWMS +query_url: https://wms.zh.ch/AwelGSWaermewwwZHWMS +thematic_geoportal_url: https://maps.zh.ch/?topic=AwelGSWaermewwwZH&x=2685104.6444391827&y=1252283.9396742217&scale=70517.93063503089 +legend_url: '' +info_format: application/vnd.ogc.gml +bbox_delta: 30 +style: '' +layers: +- name: erdwaermesonden-auflagen + property_name: name + property_values: + - name: Auflagen für Erdwärmesonden + target_harmonized_value: 3 + desc: Verbot +- name: waermenutzung-zone-a + property_name: name + property_values: + - name: Zone A (Schutzzonen und Schutzareale) + target_harmonized_value: 3 + desc: Zone A (Schutzzonen und Schutzareale) + target_harmonized_value: 2 +- name: waermenutzung-zone-b + property_name: name + property_values: + - name: Zone B (Schotter-Grundwasservorkommen, geeignet für Trinkwassergewinnung) + target_harmonized_value: 2 + desc: Zone B (Schotter-Grundwasservorkommen, geeignet für Trinkwassergewinnung) + target_harmonized_value: 2 +- name: waermenutzung-zone-c + property_name: name + property_values: + - name: Zone C (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung) + target_harmonized_value: 2 + desc: Zone C (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung) + target_harmonized_value: 2 +- name: waermenutzung-zone-d + property_name: name + property_values: + - name: Zone D (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung) + target_harmonized_value: 2 + desc: Zone D (Schotter-Grundwasservorkommen, ungeeignet für Trinkwassergewinnung) + target_harmonized_value: 2 +- name: waermenutzung-zone-e + property_name: name + property_values: + - name: Zone E (Quellwassergebiete, geeignet für Trinkwassergewinnung) + target_harmonized_value: 2 + desc: Zone E (Quellwassergebiete, geeignet für Trinkwassergewinnung) + target_harmonized_value: 2 +- name: waermenutzung-zone-f + property_name: name + property_values: + - name: Zone F (Ausserhalb nutzbarer Grundwasservorkommen) + target_harmonized_value: 2 + desc: Zone F (Ausserhalb nutzbarer Grundwasservorkommen) + target_harmonized_value: 2 diff --git a/src/drillapi/cantons_configuration/loader.py b/src/drillapi/cantons_configuration/loader.py new file mode 100644 index 0000000..b8652e3 --- /dev/null +++ b/src/drillapi/cantons_configuration/loader.py @@ -0,0 +1,161 @@ +"""Load and validate cantonal configuration from per-canton YAML files. + +Each canton lives in its own file at ``data/.yaml`` (e.g. ``data/ZH.yaml``). +Files are parsed with ``yaml.safe_load`` and validated against +:class:`~drillapi.cantons_configuration.schema.CantonConfig`. + +Design goals (fail fast, no silent breakage): + +* **Validate everything at import time.** :data:`CANTONS` is built when this + module is first imported (which happens during app startup via the route + modules). An invalid or missing config raises :class:`ConfigError` immediately, + so the process/container never comes up serving broken data. +* **Aggregate errors.** All files are checked and *every* problem is reported in + a single exception, rather than failing on the first bad file. +* **Load once.** Results are cached; warm Lambda invocations and repeated calls + pay nothing. +* **Package-relative paths.** The data directory is resolved from this file's + location, not the process CWD, so it works identically under uvicorn, pytest, + and AWS Lambda (Mangum). +""" + +from __future__ import annotations + +import logging +from functools import lru_cache +from pathlib import Path + +import yaml +from pydantic import ValidationError + +from .schema import CantonConfig, CantonsConfiguration + +logger = logging.getLogger(__name__) + +DATA_DIR = Path(__file__).resolve().parent / "data" + +# Canton code must be exactly two uppercase ASCII letters (matches the API). +_CODE_LENGTH = 2 + + +class ConfigError(RuntimeError): + """Raised when the cantonal configuration cannot be loaded or validated. + + Carries a human-readable, aggregated message listing every problem found so + the whole configuration can be fixed in one pass. + """ + + +def _parse_file(path: Path) -> dict: + """Read and YAML-parse a single file, returning the raw mapping.""" + try: + raw = yaml.safe_load(path.read_text(encoding="utf-8")) + except yaml.YAMLError as exc: # malformed YAML syntax + raise ConfigError(f"{path.name}: invalid YAML syntax: {exc}") from exc + except OSError as exc: # unreadable file + raise ConfigError(f"{path.name}: cannot read file: {exc}") from exc + + if not isinstance(raw, dict): + raise ConfigError( + f"{path.name}: expected a mapping at the top level, got {type(raw).__name__}" + ) + return raw + + +def _load_from_dir(data_dir: Path) -> dict[str, CantonConfig]: + """Load, validate and index every ``.yaml`` file in ``data_dir``. + + Raises :class:`ConfigError` with an aggregated message if the directory is + missing/empty or any file is invalid. + """ + if not data_dir.is_dir(): + raise ConfigError(f"canton config directory not found: {data_dir}") + + files = sorted(data_dir.glob("*.yaml")) + if not files: + raise ConfigError(f"no canton YAML files found in {data_dir}") + + cantons: dict[str, CantonConfig] = {} + errors: list[str] = [] + + for path in files: + code = path.stem + if len(code) != _CODE_LENGTH or not code.isascii() or not code.isupper(): + errors.append( + f"{path.name}: filename must be a 2-letter uppercase canton code " + f"(e.g. ZH.yaml), got {code!r}" + ) + continue + + try: + raw = _parse_file(path) + except ConfigError as exc: + errors.append(str(exc)) + continue + + try: + config = CantonConfig.model_validate(raw) + except ValidationError as exc: + errors.append(f"{path.name}: schema validation failed:\n{exc}") + continue + + if config.name != code: + # The in-file `name` must match the filename code so lookups by code + # and the serialized `name` never disagree. + errors.append( + f"{path.name}: 'name' is {config.name!r} but filename implies {code!r}" + ) + continue + + if code in cantons: + errors.append(f"{path.name}: duplicate canton code {code!r}") + continue + + cantons[code] = config + + if errors: + joined = "\n\n".join(f" - {e}" for e in errors) + raise ConfigError( + f"cantonal configuration failed validation ({len(errors)} problem(s)):\n{joined}" + ) + + return cantons + + +@lru_cache(maxsize=1) +def load_cantons() -> dict[str, CantonConfig]: + """Return the validated canton configs keyed by code, loading once. + + Raises :class:`ConfigError` on any problem. Cached: subsequent calls return + the same object without re-reading the filesystem. + """ + cantons = _load_from_dir(DATA_DIR) + logger.info("Loaded %d canton configuration(s) from %s", len(cantons), DATA_DIR) + return cantons + + +def get_cantons_configuration() -> CantonsConfiguration: + """Return the full validated configuration as a typed container.""" + return CantonsConfiguration(cantons_configurations=load_cantons()) + + +def as_legacy_dict() -> dict: + """Return the configuration in the legacy ``CANTONS`` dict shape. + + Produces ``{"cantons_configurations": {: {...}}}`` with absent optional + keys omitted, matching the exact payload the API has always returned. + """ + return { + "cantons_configurations": { + # mode="json" renders GroundSuitability enums as plain ints, matching + # the exact types the legacy CANTONS dict exposed to clients. + code: config.model_dump(exclude_none=True, mode="json") + for code, config in load_cantons().items() + } + } + + +# Validate at import time so a broken config fails fast at startup / in CI. +# `app.py` imports the route modules, which import this package, so any +# ConfigError surfaces before the server accepts traffic. +CANTONS: dict = as_legacy_dict() diff --git a/src/drillapi/cantons_configuration/schema.py b/src/drillapi/cantons_configuration/schema.py new file mode 100644 index 0000000..664b42f --- /dev/null +++ b/src/drillapi/cantons_configuration/schema.py @@ -0,0 +1,191 @@ +"""Pydantic schema for cantonal geoservice configuration. + +This module defines the typed contract for a single canton's configuration. +Each canton is stored as a YAML file under ``data/`` and validated against +:class:`CantonConfig` at load time (see :mod:`.loader`), so malformed data +fails fast at startup / in CI instead of mid-request against a live WMS. + +The schema is intentionally strict (``extra="forbid"``) so that a typo in a +field name is rejected rather than silently ignored. It is, however, lenient +exactly where the existing production data requires it (optional ``desc``, +layer-level ``target_harmonized_value`` / ``id``, empty-string URLs, and +ground-control-point rows of 3 to 5 elements). See the field docstrings for +why each allowance exists. +""" + +from __future__ import annotations + +from typing import Annotated + +from pydantic import ( + BaseModel, + ConfigDict, + Field, + field_validator, +) + +from drillapi.models.models import GroundSuitability + +# A permissive URL alias. We deliberately do NOT use pydantic's HttpUrl here: +# several production entries use an empty string to mean "no legend/geoportal" +# and HttpUrl would both reject that and rewrite/normalise valid URLs (adding +# trailing slashes), which would change the bytes we serialise back to clients. +# URL *shape* is validated by `_looks_like_url` below instead. +Url = Annotated[str, Field(description="Absolute http(s) URL, or empty string if none")] + + +def _looks_like_url(value: str) -> str: + """Validate that a non-empty URL field is an absolute http(s) URL.""" + if value == "": + return value + if not value.startswith(("http://", "https://")): + raise ValueError( + f"expected an absolute http(s) URL or empty string, got: {value!r}" + ) + return value + + +class PropertyValue(BaseModel): + """A single mappable attribute value within a layer. + + ``name`` is matched against the feature attribute returned by the + geoservice; ``target_harmonized_value`` is the normalised category it maps + to. ``desc`` is a human-readable label and is optional because a number of + cantons omit it (e.g. VD "Limitation", AR "zulässig"). + """ + + model_config = ConfigDict(extra="forbid") + + name: str + target_harmonized_value: GroundSuitability + # Optional: several cantons omit it entirely. Defaults to None (not "") so + # that serialising back excludes the key where it was absent, preserving the + # exact API response bytes. processing.py reads it via .get() and tolerates None. + desc: str | None = None + + +class Layer(BaseModel): + """A geoservice layer queried for a canton. + + Most cantons use ``property_values`` to map an attribute value to a + harmonized category. Some (ZH's zone layers) instead rely on mere feature + presence and carry a layer-level ``target_harmonized_value`` with no + ``property_values``. ESRI/arcgis cantons (GE, FR) additionally carry a + numeric ``id`` used to build the REST query URL. + """ + + model_config = ConfigDict(extra="forbid") + + name: str + property_name: str + property_values: list[PropertyValue] | None = None + # Layer-level fallback used when `property_values` is absent (ZH zones). + target_harmonized_value: GroundSuitability | None = None + # ESRI REST layer id (GE, FR). Required by processing for the ESRI branch. + id: int | None = None + # Optional human-readable layer description (FR only today). + desc: str | None = None + + +# A ground-control-point row: [east, north, expected_harmonized, label?, extra?] +# Only indices 0..2 are consumed (by the checker). Rows legally have 3, 4 or 5 +# elements and coordinates may be int or float, so we validate the invariant +# (length + leading numeric coords + integer category) without over-constraining. +GroundControlPoint = Annotated[ + list[float | int | str], + Field(min_length=3, max_length=5), +] + + +class HarmonyMapEntry(BaseModel): + """Legacy sum→value mapping entry (currently only present on LU). + + Not consumed by any code path today; retained so the configuration + round-trips exactly and no data is silently dropped during migration. + """ + + model_config = ConfigDict(extra="forbid") + + sum: int + value: GroundSuitability + + +class CantonConfig(BaseModel): + """Full configuration for one canton. + + ``extra="forbid"`` means any unknown key (typically a typo) is rejected at + load time. Every optional field below corresponds to a real variation in + the existing data; required fields are present for every canton. + """ + + model_config = ConfigDict(extra="forbid") + + active: bool + name: str = Field(min_length=2, max_length=2) + ground_control_point: list[GroundControlPoint] + + cantonal_energy_service_url: Url = "" + wms_url: Url + query_url: Url + thematic_geoportal_url: Url = "" + legend_url: Url = "" + + info_format: str + # Pixel/CRS buffer around the clicked point for WMS GetFeatureInfo bbox. + # All existing values are integers (10, 30); kept as int to preserve the + # exact JSON type served to clients. + bbox_delta: int = 10 + style: str = "" + layers: list[Layer] = Field(min_length=1) + + # Legacy / currently-unused fields (LU only). Retained for lossless + # round-tripping; see HarmonyMapEntry. + harmonyMap: list[HarmonyMapEntry] | None = None + loopLayers: bool | None = None + + @field_validator( + "cantonal_energy_service_url", + "wms_url", + "query_url", + "thematic_geoportal_url", + "legend_url", + ) + @classmethod + def _validate_url(cls, value: str) -> str: + return _looks_like_url(value) + + @field_validator("ground_control_point") + @classmethod + def _validate_gcp_rows( + cls, rows: list[list[float | int | str]] + ) -> list[list[float | int | str]]: + """Each row must start with two numeric coords and an int category.""" + # NOTE: pydantic field validators must raise ValueError (not TypeError) + # for the failure to be collected into a ValidationError, hence noqa TRY004. + for idx, row in enumerate(rows): + east, north, category = row[0], row[1], row[2] + if not isinstance(east, (int, float)) or isinstance(east, bool): + raise ValueError( # noqa: TRY004 + f"ground_control_point[{idx}][0] (east) must be numeric" + ) + if not isinstance(north, (int, float)) or isinstance(north, bool): + raise ValueError( # noqa: TRY004 + f"ground_control_point[{idx}][1] (north) must be numeric" + ) + if not isinstance(category, int) or isinstance(category, bool): + raise ValueError( # noqa: TRY004 + f"ground_control_point[{idx}][2] (category) must be an int" + ) + return rows + + +class CantonsConfiguration(BaseModel): + """Top-level container mirroring the legacy ``CANTONS`` dict shape. + + Keeps the public API contract intact: endpoints serialise this back to + ``{"cantons_configurations": {: {...}}}``. + """ + + model_config = ConfigDict(extra="forbid") + + cantons_configurations: dict[str, CantonConfig] diff --git a/tests/test_configuration_structure.py b/tests/test_configuration_structure.py index b5f73ee..4f3cad0 100644 --- a/tests/test_configuration_structure.py +++ b/tests/test_configuration_structure.py @@ -1,63 +1,161 @@ -from pydantic import BaseModel, HttpUrl, field_validator - -from drillapi.cantons_configuration.cantons import CANTONS - - -class PropertyValue(BaseModel): - name: str - target_harmonized_value: int - - -class Layer(BaseModel): - name: str - property_name: str - property_values: list[PropertyValue] | None = None - target_harmonized_value: int | None = None - - -class Cantonconfig(BaseModel): - active: bool - name: str - ground_control_point: list[list[int | float | str]] - wms_url: HttpUrl - query_url: HttpUrl - thematic_geoportal_url: HttpUrl | None - cantonal_energy_service_url: HttpUrl | None - legend_url: str - info_format: str - style: str | None - layers: list[Layer] - - @field_validator("layers") - @classmethod - def check_at_least_one_layer(cls, v): - if not v: - raise ValueError("There must be at least one layer") - return v - - @field_validator( - "wms_url", - "thematic_geoportal_url", - "cantonal_energy_service_url", - "query_url", - mode="before", +"""Validate the per-canton YAML configuration. + +These tests are the safety net that guarantees a malformed cantonal config can +never reach production: they load every ``data/.yaml`` file through the +real loader + Pydantic schema, assert the invariants the app relies on, and +confirm fail-fast behaviour on bad input. CI runs this suite, so a broken YAML +fails the pipeline before deploy. +""" + +from __future__ import annotations + +import tempfile +from pathlib import Path + +import pytest +import yaml +from pydantic import ValidationError + +from drillapi.cantons_configuration import loader +from drillapi.cantons_configuration.loader import ( + DATA_DIR, + ConfigError, + as_legacy_dict, + load_cantons, +) +from drillapi.cantons_configuration.schema import CantonConfig, CantonsConfiguration + +ALL_YAML_FILES = sorted(DATA_DIR.glob("*.yaml")) +ALL_CODES = [p.stem for p in ALL_YAML_FILES] + + +def test_data_dir_has_canton_files(): + assert ALL_YAML_FILES, f"no canton YAML files found in {DATA_DIR}" + + +def test_all_cantons_load_and_validate(): + """The whole configuration loads and validates without error.""" + cantons = load_cantons() + assert cantons, "expected at least one canton" + for code, config in cantons.items(): + assert isinstance(config, CantonConfig) + assert config.name == code + + +@pytest.mark.parametrize("yaml_file", ALL_YAML_FILES, ids=ALL_CODES) +def test_each_file_is_valid(yaml_file: Path): + """Each file individually parses as a mapping and validates against the schema.""" + raw = yaml.safe_load(yaml_file.read_text(encoding="utf-8")) + assert isinstance(raw, dict), f"{yaml_file.name}: top level must be a mapping" + config = CantonConfig.model_validate(raw) + # Filename code must agree with the in-file name. + assert config.name == yaml_file.stem + + +@pytest.mark.parametrize("yaml_file", ALL_YAML_FILES, ids=ALL_CODES) +def test_each_canton_has_usable_layers(yaml_file: Path): + """Every layer is usable: it has either property_values or a layer-level target.""" + config = CantonConfig.model_validate( + yaml.safe_load(yaml_file.read_text(encoding="utf-8")) ) - @classmethod - def allow_empty_urls(cls, v): - if v == "": - return None - return v + assert config.layers, f"{yaml_file.name}: must have at least one layer" + for layer in config.layers: + assert layer.property_values or layer.target_harmonized_value is not None, ( + f"{yaml_file.name}: layer {layer.name!r} has neither property_values " + f"nor a layer-level target_harmonized_value" + ) + + +@pytest.mark.parametrize("yaml_file", ALL_YAML_FILES, ids=ALL_CODES) +def test_esri_layers_have_id(yaml_file: Path): + """ESRI/arcgis cantons must provide a layer id (processing requires it).""" + config = CantonConfig.model_validate( + yaml.safe_load(yaml_file.read_text(encoding="utf-8")) + ) + if "arcgis" in config.info_format.lower(): + for layer in config.layers: + assert layer.id is not None, ( + f"{yaml_file.name}: ESRI layer {layer.name!r} is missing 'id'" + ) + + +def test_full_configuration_container_validates(): + """The aggregated container validates and preserves all codes.""" + container = CantonsConfiguration(cantons_configurations=load_cantons()) + assert set(container.cantons_configurations) == set(ALL_CODES) + + +def test_as_legacy_dict_shape(): + """The legacy-shaped dict is preserved for the public API contract.""" + legacy = as_legacy_dict() + assert set(legacy) == {"cantons_configurations"} + inner = legacy["cantons_configurations"] + assert set(inner) == set(ALL_CODES) + # absent optionals are omitted, not serialized as null + for canton in inner.values(): + assert "active" in canton + for layer in canton["layers"]: + for pv in layer.get("property_values") or []: + assert "desc" not in pv or isinstance(pv["desc"], str) + + +def test_names_match_filenames(): + for code, config in load_cantons().items(): + assert config.name == code, f"{code}.yaml declares name={config.name!r}" + + +# --- Fail-fast behaviour (the actual hardening guarantee) --- + + +def _write(dir_: Path, name: str, content: str) -> None: + (dir_ / name).write_text(content, encoding="utf-8") + + +def test_missing_directory_raises(): + with pytest.raises(ConfigError, match="not found"): + loader._load_from_dir(Path(tempfile.gettempdir()) / "definitely-not-here-xyz") + + +def test_empty_directory_raises(): + with ( + tempfile.TemporaryDirectory() as d, + pytest.raises(ConfigError, match="no canton YAML files"), + ): + loader._load_from_dir(Path(d)) + + +def test_invalid_yaml_syntax_raises(): + with tempfile.TemporaryDirectory() as d: + _write(Path(d), "ZH.yaml", "active: true\n bad: : indentation:") + with pytest.raises(ConfigError, match="invalid YAML syntax|ZH.yaml"): + loader._load_from_dir(Path(d)) + + +def test_unknown_field_is_rejected(): + """A typo'd/extra key must fail (extra='forbid').""" + valid = yaml.safe_load((DATA_DIR / "ZG.yaml").read_text(encoding="utf-8")) + valid["unexpected_typo_key"] = 1 + with pytest.raises(ValidationError, match="unexpected_typo_key"): + CantonConfig.model_validate(valid) -def test_cantons_configuration_integrity(): - """ - Ensure all cantons configuration entries respect the Region structure - and no structural damage has been caused. - """ - for canton_data in CANTONS["cantons_configurations"].values(): - # Pydantic validation - Cantonconfig(**canton_data) +def test_name_filename_mismatch_raises(): + with tempfile.TemporaryDirectory() as d: + # a syntactically valid canton, but name != filename + _write( + Path(d), + "ZH.yaml", + "active: true\nname: BE\nground_control_point: [[2600000, 1200000, 1]]\n" + "wms_url: https://x\nquery_url: https://x\ninfo_format: text/plain\n" + "layers:\n- {name: a, property_name: b, target_harmonized_value: 1}\n", + ) + with pytest.raises(ConfigError, match="filename implies"): + loader._load_from_dir(Path(d)) -if __name__ == "__main__": - test_cantons_configuration_integrity() +def test_bad_ground_control_point_raises(): + """A GCP row whose category is not an int must be rejected.""" + valid = yaml.safe_load((DATA_DIR / "ZG.yaml").read_text(encoding="utf-8")) + valid["ground_control_point"] = [[2600000, 1200000, "not-an-int"]] + with pytest.raises(ValidationError): + CantonConfig.model_validate(valid) diff --git a/tests/test_drill_suitability.py b/tests/test_drill_suitability.py index 4588c6b..f0a954e 100644 --- a/tests/test_drill_suitability.py +++ b/tests/test_drill_suitability.py @@ -70,7 +70,7 @@ def test_drill_category_esri_json(client): esri_json = f.read() respx.get( - "https://map.geo.fr.ch/arcgis/rest/services/PortailCarto/Theme_environnement/MapServer/17/query" + "https://maps.fr.ch/ags/rest/services/opendata/Admissibilite_des_sondes_geothermiques__SGV_/MapServer/0/query" ).mock( return_value=httpx.Response( 200, diff --git a/tests/test_geoservice_unavailable.py b/tests/test_geoservice_unavailable.py index 334cec7..521800b 100644 --- a/tests/test_geoservice_unavailable.py +++ b/tests/test_geoservice_unavailable.py @@ -65,7 +65,7 @@ def test_esri_connection_error_returns_structured_response(client): _mock_canton_identify("FR") respx.get( - "https://map.geo.fr.ch/arcgis/rest/services/PortailCarto/Theme_environnement/MapServer/17/query" + "https://maps.fr.ch/ags/rest/services/opendata/Admissibilite_des_sondes_geothermiques__SGV_/MapServer/0/query" ).mock(side_effect=httpx.ConnectError("Connection refused")) response = client.get(f"/v1/drill-category/{coord_x}/{coord_y}") diff --git a/uv.lock b/uv.lock index 1752a9b..f3fc9f7 100644 --- a/uv.lock +++ b/uv.lock @@ -246,6 +246,7 @@ dependencies = [ { name = "mangum" }, { name = "owslib" }, { name = "pydantic-settings" }, + { name = "pyyaml" }, { name = "slowapi" }, { name = "uvicorn" }, ] @@ -259,6 +260,7 @@ dev = [ { name = "requests" }, { name = "respx" }, { name = "ruff" }, + { name = "watchfiles" }, ] [package.metadata] @@ -273,11 +275,13 @@ requires-dist = [ { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest-asyncio", marker = "extra == 'dev'" }, { name = "pytest-cov", marker = "extra == 'dev'" }, + { name = "pyyaml", specifier = ">=6.0.3" }, { name = "requests", marker = "extra == 'dev'" }, { name = "respx", marker = "extra == 'dev'" }, { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.16.10" }, { name = "slowapi", specifier = ">=0.1.10" }, { name = "uvicorn", specifier = ">=0.54.0" }, + { name = "watchfiles", marker = "extra == 'dev'", specifier = ">=1.3.0" }, ] provides-extras = ["dev"] @@ -884,6 +888,45 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/38/0c/b54a4fdd7f90a3af8b02ebc9ce6712c2c208b7926a2f7bad95c33ebbe943/uvicorn-0.54.0-py3-none-any.whl", hash = "sha256:505bdb0f318731d45f1f712071fc781a8981f6847a31c902c9f5e652d4f67faf", size = 87427, upload-time = "2026-09-25T06:52:35.829Z" }, ] +[[package]] +name = "watchfiles" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/68/e6aa0b77d217b31f8f486ec0cdfe5e00e6e38dc0be657e7d85819b9faf0a/watchfiles-1.3.0.tar.gz", hash = "sha256:99aee4a07847c06820765fd7b1b49ceac4f3f711ccb7d104655a33231de1c207", size = 108119, upload-time = "2026-09-21T09:08:55.664Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/fa/c0b840d5d8bafe640925408ce11095948c8945bb61be1e67d0ab629b872a/watchfiles-1.3.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:000b9688fc8133037a8b075c8ebf98f32844ff8964dda61e85c1db547dafc441", size = 399703, upload-time = "2026-09-21T09:08:13.518Z" }, + { url = "https://files.pythonhosted.org/packages/c7/8a/894799b485fe9473ad10422a0d9668e53fb78e3a2b5cc6061159572844ad/watchfiles-1.3.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:bbc1198edfdc90fda0600f825aa94150f428dfcbf8138746f55998e0e660d64c", size = 397647, upload-time = "2026-09-21T09:08:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/5a/61/f277cd5ead05f5d37b98b41fbdd0c943ee0c04d49738f62e902cb291cf43/watchfiles-1.3.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4af9c464d410e8c44b58ddf1cbec7c3a02c1cb2c6c80df9c74659ba2fbda0ab5", size = 454994, upload-time = "2026-09-21T09:08:16.587Z" }, + { url = "https://files.pythonhosted.org/packages/46/66/ea4c01382975e53b1ac1b0a0ea1b353416b8127fb6e04f5c1c313119aab6/watchfiles-1.3.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb94e40b9a0db19636b5e3fed0a9803452ecc7381df13b385272abb779ee83ca", size = 460105, upload-time = "2026-09-21T09:08:17.961Z" }, + { url = "https://files.pythonhosted.org/packages/31/c3/1a501b597817096c9831c255f6270c124ce0e2faeb74124be24dcc93b86d/watchfiles-1.3.0-cp310-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7398930a2b76b9dc67a4e6b8bfc7baf30946892a86f417a3920c07bdd6febc30", size = 493761, upload-time = "2026-09-21T09:08:19.327Z" }, + { url = "https://files.pythonhosted.org/packages/5a/16/77352d05e152c7786a016e9f1decdb4507e8aa5bcf3343c72142a0413d14/watchfiles-1.3.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:025b108f2d5cc2799cb941f79380df3e8b22bfb589c218e801767fe78d598c95", size = 577500, upload-time = "2026-09-21T09:08:20.723Z" }, + { url = "https://files.pythonhosted.org/packages/95/fe/81001c55b24466fe9911135e50269fd3a29f43e446627a92adfab54553b6/watchfiles-1.3.0-cp310-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:be9ef3cd403d756a304a0a08a112163c63da7ec72c8c9d083c98818291ff8f29", size = 469209, upload-time = "2026-09-21T09:08:22.346Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cf/e3ad894ed5909a7c44b0707e89bc25d19a3d78b3a7aec846119443b6ae56/watchfiles-1.3.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5768b49e426fd5b550b012c866db347cdf15c398ef98dc557b6e6b72fa74cd1", size = 458353, upload-time = "2026-09-21T09:08:23.7Z" }, + { url = "https://files.pythonhosted.org/packages/7f/f5/2f106829e6e2bc91ad74614730f6df484202b9a4d20d927dbf2f08d37d44/watchfiles-1.3.0-cp310-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:d36c72fcdc08f143d87316a4c4c3b064e340da2c7383d39803950740ed418935", size = 468707, upload-time = "2026-09-21T09:08:25.167Z" }, + { url = "https://files.pythonhosted.org/packages/15/37/2741ab776b978669c1f3bba1e573f1a184f727ab8b4d3a6d9c61090800ed/watchfiles-1.3.0-cp310-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f0c9865240e065a2247f4b5248528ae3d01e0e8ae071462c250c884dc16e3ee3", size = 632510, upload-time = "2026-09-21T09:08:26.532Z" }, + { url = "https://files.pythonhosted.org/packages/48/64/db4a4684275fe9b696ee5118c194705c1423b3d42181c3f8ee70194d5c73/watchfiles-1.3.0-cp310-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:380f513d26cc2e598266b88d66456e07d67ef4be8f0f435a1154d9c44b43d509", size = 661723, upload-time = "2026-09-21T09:08:28.004Z" }, + { url = "https://files.pythonhosted.org/packages/52/80/95ddb24f6a2f595c031c24432ab7b8aa7d6d1ada0c6d63a552166e06e8d1/watchfiles-1.3.0-cp310-abi3-win32.whl", hash = "sha256:509d9f74d2bec5c1f4cd868ddcfe0c9601f7ea53066dfcb19d8ede742c4de661", size = 277761, upload-time = "2026-09-21T09:08:29.335Z" }, + { url = "https://files.pythonhosted.org/packages/48/a1/1c4a1b3c8030cd03206232612be8a6e8799093490c220b0fcb935d02b75a/watchfiles-1.3.0-cp310-abi3-win_amd64.whl", hash = "sha256:1acabde19b67e673274e89a04d613b7fb1f122e2c4b8ec9a581e59c149264b61", size = 290643, upload-time = "2026-09-21T09:08:30.45Z" }, + { url = "https://files.pythonhosted.org/packages/82/e0/d2db577c632d26d08078c4390dd870b8555c4e558df25d22270f0166c71f/watchfiles-1.3.0-cp310-abi3-win_arm64.whl", hash = "sha256:d978cc1dd7ba44f5590d7de74f7a9c8be7262d9e469c2c0efb8bbfc2574321dc", size = 285510, upload-time = "2026-09-21T09:08:31.561Z" }, + { url = "https://files.pythonhosted.org/packages/4f/55/a122743607939429ab6a7272672f49303aa02bbd34c3f06676765a3861de/watchfiles-1.3.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:e0204f90677b7fb8d3a824279a71a01166bea9f6e53923b8056592d87b33b0b6", size = 395978, upload-time = "2026-09-21T09:08:32.747Z" }, + { url = "https://files.pythonhosted.org/packages/3e/9d/0ee1b0ea42acfe72afa2484f0222e2386dd09992e9034a81e63cc3cdab0a/watchfiles-1.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7c1125e99f84934a92996ae343501285617373acd66f16b59fd5654b27ad8d80", size = 387997, upload-time = "2026-09-21T09:08:33.953Z" }, + { url = "https://files.pythonhosted.org/packages/b7/f2/4319c6b683e8bd28465dfc53c750617a2a07f072eb2e3ab0182ccf812229/watchfiles-1.3.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a85e243c87109d9f48a2e0d10d6abdee8768cc114848b32281ec83d7f81d86ef", size = 448067, upload-time = "2026-09-21T09:08:35.104Z" }, + { url = "https://files.pythonhosted.org/packages/b7/71/9f37ac192a51a24de3a2d7fe9aac1e99b2b2668300674ff7a9f85d95beb1/watchfiles-1.3.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd538c71766c59c732e2c5cedc39323c99c11897b99f93eac773438e54af206a", size = 452881, upload-time = "2026-09-21T09:08:36.238Z" }, + { url = "https://files.pythonhosted.org/packages/34/07/1224667c32a4a96876ddd642da69733cbfe6a15897db0865aec5a882cebc/watchfiles-1.3.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da213822ec9082b62cadf3008e07a8c690ec7a3c89f6dc5e55a8cf07b56a1b26", size = 484802, upload-time = "2026-09-21T09:08:37.717Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4d/7f29cd87e1f826df86fc0418d0b8f370ca0311c29c618406dc492aad9951/watchfiles-1.3.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4593392e87669836670f87d62fc754059eaeb0e6157af44060a46558cebe5a3", size = 567522, upload-time = "2026-09-21T09:08:38.94Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ac/3aa1f84abd4dbbfbae99a817dd931f77b439261c911189cad4c30fdfd470/watchfiles-1.3.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26b81bc515a0f03f66a69a6fea86eea54c8eca4173c46dd7222569f79dd2f977", size = 461997, upload-time = "2026-09-21T09:08:40.142Z" }, + { url = "https://files.pythonhosted.org/packages/82/1c/27c66764d5b0dbb70a36770a32eca217eba739c28b24e8583a035087a3d7/watchfiles-1.3.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca39661934749df580d89f3dde266f2af7c6968f4c21c72ce2dc387d299e6aa7", size = 451856, upload-time = "2026-09-21T09:08:41.305Z" }, + { url = "https://files.pythonhosted.org/packages/1e/b3/a45827084d953c1d0dd3d75abf85dcac26a0f5d115ad2fe0b7e9e931573b/watchfiles-1.3.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:04c5500faa725d69a99b0f63750e80795e455bb951849e9c98a811b4324997c4", size = 459889, upload-time = "2026-09-21T09:08:42.58Z" }, + { url = "https://files.pythonhosted.org/packages/7a/3d/07740d23ce2bb23419bb24055c435a9abe5fe03efdaea1ba0720b831aab6/watchfiles-1.3.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:ec3cd4bb181a7b6329134204c2277c7144ab5f62cce9c5335d6f901ee7ce8d6a", size = 624482, upload-time = "2026-09-21T09:08:44.064Z" }, + { url = "https://files.pythonhosted.org/packages/91/46/9540f8123eaec86df989f26d7704cad0448d9fb7271763a4f8f9fe23617d/watchfiles-1.3.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e5871d4a7f788a9f64fb06f5793389d29d171e5c32d37c3ebfd520b017cc7694", size = 653724, upload-time = "2026-09-21T09:08:45.257Z" }, + { url = "https://files.pythonhosted.org/packages/25/9d/b9deb52f1e4528ffe44338c5ebab95c32ed79203082da661de50288e130a/watchfiles-1.3.0-cp314-cp314t-win32.whl", hash = "sha256:81c989d7267bfb7b676cd32c83bec97914e396870983cbf493e29415037bff5e", size = 270863, upload-time = "2026-09-21T09:08:46.518Z" }, + { url = "https://files.pythonhosted.org/packages/59/59/8b1881afccbafda06248be4185e28034dd73c80c85e4da24f2cb0c1321eb/watchfiles-1.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d754e049006e81a9be49dc78ba7802081cee426e968cbc63ee19e164fa2d0e39", size = 286879, upload-time = "2026-09-21T09:08:47.874Z" }, + { url = "https://files.pythonhosted.org/packages/46/dd/41c12ae9745d5d72f709333287679d71cbba68ad7678fed872207bba94f4/watchfiles-1.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a5b631db08fd3032db57ec6a92d1777cd60f88803287e4e941a4858c53aab1e0", size = 276924, upload-time = "2026-09-21T09:08:48.976Z" }, +] + [[package]] name = "wrapt" version = "2.5.0"