Spaces:
Sleeping
Sleeping
Upload 11 files
Browse files- README.md +2 -8
- app.py +63 -0
- bez_dict.json +255 -0
- flurstueck.py +213 -0
- guby.py +131 -0
- kat.py +92 -0
- main.py +42 -0
- nutflu.py +29 -0
- nutzung.py +115 -0
- requirements.txt +77 -0
- ver.py +159 -0
README.md
CHANGED
@@ -1,12 +1,6 @@
|
|
1 |
---
|
2 |
-
title: NAS
|
3 |
-
|
4 |
-
colorFrom: gray
|
5 |
-
colorTo: gray
|
6 |
sdk: gradio
|
7 |
sdk_version: 4.44.0
|
8 |
-
app_file: app.py
|
9 |
-
pinned: false
|
10 |
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
---
|
2 |
+
title: NAS-ALKIS_CONVERSION
|
3 |
+
app_file: app.py
|
|
|
|
|
4 |
sdk: gradio
|
5 |
sdk_version: 4.44.0
|
|
|
|
|
6 |
---
|
|
|
|
app.py
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import subprocess
|
3 |
+
import os
|
4 |
+
import time
|
5 |
+
|
6 |
+
def run_script(script_name, *args):
|
7 |
+
command = ['python', script_name] + list(args)
|
8 |
+
start_time = time.time()
|
9 |
+
result = subprocess.run(command, capture_output=True, text=True)
|
10 |
+
end_time = time.time()
|
11 |
+
elapsed_time = end_time - start_time
|
12 |
+
if result.returncode != 0:
|
13 |
+
return f"Error running {script_name}: {result.stderr}"
|
14 |
+
else:
|
15 |
+
pass
|
16 |
+
|
17 |
+
def process_files(xml_file, output_path):
|
18 |
+
# Define file paths
|
19 |
+
bez_dict_file = "bez_dict.json"
|
20 |
+
flurstueck_shapefile = os.path.join(output_path, "flurstueck.shp")
|
21 |
+
nutzung_shapefile = os.path.join(output_path, "nutzung.shp")
|
22 |
+
nutzung_flurstueck_shapefile = os.path.join(output_path, "nutzungFlurstueck.shp")
|
23 |
+
gebauede_bauwerk_shapefile = os.path.join(output_path, "gebauedeBauwerk.shp")
|
24 |
+
verwaltungs_einheit_shapefile = os.path.join(output_path, "verwaltungsEinheit.shp")
|
25 |
+
kataster_bezirk_shapefile = os.path.join(output_path, "katasterBezirk.shp")
|
26 |
+
|
27 |
+
# Ensure the output directory exists
|
28 |
+
os.makedirs(output_path, exist_ok=True)
|
29 |
+
|
30 |
+
# Run scripts in the correct order
|
31 |
+
results = []
|
32 |
+
results.append(run_script('flurstueck.py', xml_file, flurstueck_shapefile))
|
33 |
+
results.append(f"Generated: {flurstueck_shapefile}")
|
34 |
+
results.append(run_script('nutzung.py', xml_file, bez_dict_file, nutzung_shapefile))
|
35 |
+
results.append(f"Generated: {nutzung_shapefile}")
|
36 |
+
results.append(run_script('nutflu.py', flurstueck_shapefile, nutzung_shapefile, nutzung_flurstueck_shapefile))
|
37 |
+
results.append(f"Generated: {nutzung_flurstueck_shapefile}")
|
38 |
+
results.append(run_script('guby.py', xml_file, gebauede_bauwerk_shapefile))
|
39 |
+
results.append(f"Generated: {gebauede_bauwerk_shapefile}")
|
40 |
+
results.append(run_script('ver.py', flurstueck_shapefile, xml_file, verwaltungs_einheit_shapefile))
|
41 |
+
results.append(f"Generated: {verwaltungs_einheit_shapefile}")
|
42 |
+
results.append(run_script('kat.py', flurstueck_shapefile, kataster_bezirk_shapefile))
|
43 |
+
results.append(f"Generated: {kataster_bezirk_shapefile}")
|
44 |
+
|
45 |
+
results.append("CONVERSION COMPLETED")
|
46 |
+
|
47 |
+
return "\n".join(results)
|
48 |
+
|
49 |
+
# Create Gradio interface
|
50 |
+
iface = gr.Interface(
|
51 |
+
fn=process_files,
|
52 |
+
inputs=[
|
53 |
+
gr.File(label="Input XML File"),
|
54 |
+
gr.Textbox(label="Output Path", placeholder="Enter the output directory path")
|
55 |
+
],
|
56 |
+
outputs="text",
|
57 |
+
title="NAS-ALKIS Conversion",
|
58 |
+
description="Upload an XML file and specify the output path to generate conversion."
|
59 |
+
)
|
60 |
+
|
61 |
+
# Launch the app
|
62 |
+
if __name__ == "__main__":
|
63 |
+
iface.launch(share=True)
|
bez_dict.json
ADDED
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"2620": "Abfallbehandlungsanlage",
|
3 |
+
"7000": "Abraum",
|
4 |
+
"1010": "Ackerland",
|
5 |
+
"8220": "Altarm",
|
6 |
+
"8210": "Altwasser",
|
7 |
+
"2014": "Andesit",
|
8 |
+
"5002": "Anhydritstein",
|
9 |
+
"5630": "Anlegestelle",
|
10 |
+
"3010": "Antimon",
|
11 |
+
"1450": "Ausstellung, Messe",
|
12 |
+
"4260": "Autokino, Freilichtkino",
|
13 |
+
"8500": "Bach",
|
14 |
+
"8640": "Baggersee",
|
15 |
+
"1420": "Bank, Kredit",
|
16 |
+
"2013": "Basalt, Diabas",
|
17 |
+
"1031": "Baumschule",
|
18 |
+
"1000": "Vegetationslose Fläche",
|
19 |
+
"1110": "Verwaltung",
|
20 |
+
"1460": "Beherbergung",
|
21 |
+
"1002": "Bentonit",
|
22 |
+
"2000": "Steine, Gestein, Festgestein",
|
23 |
+
"2710": "Betrieb",
|
24 |
+
"1780": "Betriebliche Sozialeinrichtung",
|
25 |
+
"2502": "Industrie und Gewerbefläche",
|
26 |
+
"2532": "Betriebsfläche Versorgungsanlage, Elektrizität",
|
27 |
+
"2582": "Betriebsfläche Versorgungsanlage, Funkund Fernmeldewesen",
|
28 |
+
"2562": "Betriebsfläche Versorgungsanlage, Gas",
|
29 |
+
"2522": "Betriebsfläche Versorgungsanlage, Wasser",
|
30 |
+
"2572": "Betriebsfläche Versorgungsanlage, Wärme",
|
31 |
+
"2552": "Betriebsfläche Versorgungsanlage, Öl",
|
32 |
+
"2602": "Betriebsfläche, Entsorgungsanlage",
|
33 |
+
"2622": "Betriebsfläche, Entsorgungsanlage Abfallbeseitigung",
|
34 |
+
"2612": "Betriebsfläche, Entsorgungsanlage Abwasserbeseitigung",
|
35 |
+
"2623": "Betriebsfläche, Entsorgungsanlage Schlamm",
|
36 |
+
"1120": "Unbebaute Gewässerbegleitfläche",
|
37 |
+
"3004": "Blei",
|
38 |
+
"4430": "Botanischer Garten",
|
39 |
+
"1200": "Stadtbahn",
|
40 |
+
"4021": "Braunkohle",
|
41 |
+
"3002": "Buntmetallerze",
|
42 |
+
"1310": "Laubwald mit Nadelholz",
|
43 |
+
"4330": "Campingplatz",
|
44 |
+
"2630": "Deponie (oberirdisch)",
|
45 |
+
"2640": "Deponie (untertägig)",
|
46 |
+
"2006": "Dolomitstein",
|
47 |
+
"3011": "Edelmetallerze",
|
48 |
+
"4160": "Eis-, Rollschuhbahn",
|
49 |
+
"3001": "Eisen",
|
50 |
+
"1100": "Öffentliche Zwecke",
|
51 |
+
"2600": "Entsorgung",
|
52 |
+
"4000": "Treib- und Brennstoffe",
|
53 |
+
"4300": "Erholungsfläche",
|
54 |
+
"3000": "Kohle",
|
55 |
+
"5210": "Fahrweg",
|
56 |
+
"1011": "Streuobstacker",
|
57 |
+
"5009": "Feldspat",
|
58 |
+
"5350": "Festplatz",
|
59 |
+
"8410": "Fleet",
|
60 |
+
"8230": "Flussmündungstrichter",
|
61 |
+
"5510": "Flughafen",
|
62 |
+
"8200": "Fluss",
|
63 |
+
"5006": "Flusspat",
|
64 |
+
"1760": "Forschung",
|
65 |
+
"7600": "Forstwirtschaftliche Betriebsfläche",
|
66 |
+
"4250": "Freilichtmuseum",
|
67 |
+
"4240": "Freilichttheater",
|
68 |
+
"4200": "Freizeitanlage",
|
69 |
+
"4230": "Freizeitpark",
|
70 |
+
"9403": "Friedhof (Park)",
|
71 |
+
"9402": "Friedhof (ohne Gebäude)",
|
72 |
+
"2580": "Funk- und Fernmeldeanlage",
|
73 |
+
"5130": "Fußgängerzone",
|
74 |
+
"5220": "Fußweg",
|
75 |
+
"5640": "Fähranlage",
|
76 |
+
"5230": "Gang",
|
77 |
+
"4460": "Garten",
|
78 |
+
"1030": "Geröll",
|
79 |
+
"2560": "Gaswerk",
|
80 |
+
"2341": "Gebäude- Freifläche zu Verkehrsanlagen, Schifffahrt",
|
81 |
+
"2601": "Gebäude- und Freifläche Entsorgungsanlage",
|
82 |
+
"2610": "Kläranlage, Klärwerk",
|
83 |
+
"2621": "Gebäude- und Freifläche Entsorgungsanlage, Abfallbeseitigung",
|
84 |
+
"2611": "Gebäude- und Freifläche Entsorgungsanlage, Abwasserbeseitigung",
|
85 |
+
"4301": "Gebäude- und Freifläche Erholung",
|
86 |
+
"4321": "Gebäude- und Freifläche, Bad",
|
87 |
+
"4431": "Gebäude- und Freifläche, Erholung, Botanik",
|
88 |
+
"4331": "Gebäude- und Freifläche, Camping",
|
89 |
+
"4101": "Gebäude- und Freifläche Erholung, Sport",
|
90 |
+
"9401": "Gebäude- und Freifläche Friedhof",
|
91 |
+
"1701": "Gebäude- und Freifläche Industrie und Gewerbe",
|
92 |
+
"2700": "Gebäude- und Freifläche Land- und Forstwirtschaft",
|
93 |
+
"2501": "Gebäude- und Freifläche Versorgungsanlage",
|
94 |
+
"2521": "Gebäude- und Freifläche Versorgungsanlage, Wasser",
|
95 |
+
"2531": "Gebäude- und Freifläche Versorgungsanlage, Elektrizität",
|
96 |
+
"2581": "Gebäude- und Freifläche Versorgungsanlage, Funk- und Fernmeldesystem",
|
97 |
+
"2561": "Gebäude- und Freifläche Versorgungsanlage, Gas",
|
98 |
+
"2571": "Gebäude- und Freifläche Versorgungsanlage, Wärme",
|
99 |
+
"2551": "Gebäude- und Freifläche Versorgungsanlage, Öl",
|
100 |
+
"5501": "Gebäude- und Freifläche zu Verkehrsanlagen, Luftfahrt",
|
101 |
+
"2321": "Gebäude- und Freifläche zu Verkehrsanlagen, Schiene",
|
102 |
+
"2311": "Gebäude- und Freifläche zu Verkehrsanlagen, Straße",
|
103 |
+
"4211": "Gebäude- und Freifläche, Zoologie",
|
104 |
+
"2100": "Gebäude- und Freifläche, Mischnutzung mit Wohnen",
|
105 |
+
"1150": "Gesundheit, Kur",
|
106 |
+
"2160": "Gewerbe und Industrie mit Wohnen",
|
107 |
+
"5001": "Gipsstein",
|
108 |
+
"2012": "Gneis",
|
109 |
+
"4110": "Golfplatz",
|
110 |
+
"8400": "Graben",
|
111 |
+
"2016": "Granit",
|
112 |
+
"2017": "Granodiorit",
|
113 |
+
"5011": "Graphit",
|
114 |
+
"2010": "Grauwacke",
|
115 |
+
"1770": "Grundstoff",
|
116 |
+
"4400": "Grünanlage",
|
117 |
+
"4410": "Grünfläche",
|
118 |
+
"1020": "Grünland",
|
119 |
+
"1490": "Gärtnerei",
|
120 |
+
"1102": "Güterverkehr",
|
121 |
+
"5610": "Hafenanlage (Landfläche)",
|
122 |
+
"9999": "Tagebau, Grube, Steinbruch",
|
123 |
+
"1440": "Handel",
|
124 |
+
"1400": "Handel und Dienstleistung",
|
125 |
+
"2150": "Handel und Dienstleistungen mit Wohnen",
|
126 |
+
"1720": "Handwerk",
|
127 |
+
"5211": "Hauptwirtschaftsweg",
|
128 |
+
"2570": "Heizwerk",
|
129 |
+
"1300": "Seilbahn, Bergbahn",
|
130 |
+
"9404": "Historischer Friedhof",
|
131 |
+
"1012": "Quarzsand",
|
132 |
+
"5530": "Hubschrauberflugplatz",
|
133 |
+
"4280": "Hundeübungsplatz",
|
134 |
+
"1700": "Industrie und Gewerbe",
|
135 |
+
"5000": "Schutt",
|
136 |
+
"5511": "Internationaler Flughafen",
|
137 |
+
"5004": "Kalisalz",
|
138 |
+
"1007": "Kalk, Kalktuff, Kreide",
|
139 |
+
"5005": "Kalkspat",
|
140 |
+
"2005": "Kalkstein",
|
141 |
+
"8300": "Kanal",
|
142 |
+
"1003": "Kaolin",
|
143 |
+
"8000": "Schrott, Altmaterial",
|
144 |
+
"1009": "Kies, Kiessand",
|
145 |
+
"1013": "Spargel",
|
146 |
+
"4440": "Kleingarten",
|
147 |
+
"4020": "Kohle",
|
148 |
+
"2530": "Kraftwerk",
|
149 |
+
"1130": "Kultur",
|
150 |
+
"3003": "Kupfer",
|
151 |
+
"8710": "Küstengewässer",
|
152 |
+
"1740": "Lagerplatz",
|
153 |
+
"5540": "Landeplatz, Sonderlandeplatz",
|
154 |
+
"6800": "Landwirtschaftliche Betriebsfläche",
|
155 |
+
"2020": "Lavaschlacke",
|
156 |
+
"1004": "Lehm",
|
157 |
+
"1005": "Löß, Lößlehm",
|
158 |
+
"1600": "Magnetschwebebahn",
|
159 |
+
"3009": "Mangan",
|
160 |
+
"5340": "Marktplatz",
|
161 |
+
"2008": "Marmor",
|
162 |
+
"1006": "Mergel",
|
163 |
+
"2004": "Mergelstein",
|
164 |
+
"2003": "Metamorphe Schiefer",
|
165 |
+
"4290": "Modellflugplatz",
|
166 |
+
"1320": "Schlossanlage",
|
167 |
+
"1051": "Obstbaumplantage",
|
168 |
+
"1050": "Obstplantage",
|
169 |
+
"1052": "Obststrauchplantage",
|
170 |
+
"4420": "Park",
|
171 |
+
"5310": "Parkplatz",
|
172 |
+
"5010": "Pegmatitsand",
|
173 |
+
"2015": "Porphyr, Quarzporphyr",
|
174 |
+
"1710": "Produktion",
|
175 |
+
"5008": "Quarz",
|
176 |
+
"2011": "Quarzit",
|
177 |
+
"5250": "Rad- und Fußweg",
|
178 |
+
"5240": "Radweg",
|
179 |
+
"2550": "Raffinerie",
|
180 |
+
"5320": "Rastplatz",
|
181 |
+
"5330": "Raststätte",
|
182 |
+
"5512": "Regionalflughafen",
|
183 |
+
"41400": "Reitplatz",
|
184 |
+
"5260": "Reitweg",
|
185 |
+
"1140": "Religiöse Einrichtung",
|
186 |
+
"4130": "Rennbahn",
|
187 |
+
"1470": "Restauration",
|
188 |
+
"1104": "S-Bahn",
|
189 |
+
"4220": "Safaripark, Wildpark",
|
190 |
+
"1008": "Sand",
|
191 |
+
"1040": "Weingarten",
|
192 |
+
"2009": "Sandstein",
|
193 |
+
"2002": "Schiefer, Dachschiefer",
|
194 |
+
"4150": "Schießanlage",
|
195 |
+
"6000": "Schlacke",
|
196 |
+
"5620": "Schleuse (Landfläche)",
|
197 |
+
"5007": "Schwerspat",
|
198 |
+
"4320": "Schwimmbad, Freibad",
|
199 |
+
"8610": "See",
|
200 |
+
"5550": "Segelfluggelände",
|
201 |
+
"1170": "Sicherheit und Ordnung",
|
202 |
+
"1160": "Soziales",
|
203 |
+
"8631": "Speicherbecken",
|
204 |
+
"4470": "Spielplatz, Bolzplatz",
|
205 |
+
"4100": "Sportanlage",
|
206 |
+
"8810": "Sporthafenbecken",
|
207 |
+
"4120": "Sportplatz",
|
208 |
+
"1302": "Standseilbahn",
|
209 |
+
"8630": "Stausee",
|
210 |
+
"4022": "Steinkohle",
|
211 |
+
"5003": "Steinsalz",
|
212 |
+
"1201": "Straßenbahn",
|
213 |
+
"2313": "Straßenentwässerungsanlage",
|
214 |
+
"1021": "Streuobstwiese",
|
215 |
+
"2021": "Talkschiefer, Speckstein",
|
216 |
+
"1730": "Tankstelle",
|
217 |
+
"8620": "Teich",
|
218 |
+
"4170": "Tennisplatz",
|
219 |
+
"1001": "Ton",
|
220 |
+
"2001": "Tonstein",
|
221 |
+
"4010": "Torf",
|
222 |
+
"1750": "Transport",
|
223 |
+
"2019": "Trass",
|
224 |
+
"2007": "Travertin",
|
225 |
+
"2018": "Tuff, Bimsstein",
|
226 |
+
"1202": "U-Bahn",
|
227 |
+
"2540": "Umspannstation",
|
228 |
+
"3008": "Uran",
|
229 |
+
"1480": "Vergnügung",
|
230 |
+
"2322": "Verkehrsbegleitfläche Bahnverkehr",
|
231 |
+
"2312": "Verkehrsbegleitfläche Straße",
|
232 |
+
"5520": "Verkehrslandeplatz",
|
233 |
+
"4270": "Verkehrsübungsplatz",
|
234 |
+
"1430": "Versicherung",
|
235 |
+
"2500": "Versorgungsanlage",
|
236 |
+
"1410": "Verwaltung, freie Berufe",
|
237 |
+
"2520": "Wasserwerk",
|
238 |
+
"22000": "Weg",
|
239 |
+
"1790": "Werft",
|
240 |
+
"5212": "Wirtschaftsweg",
|
241 |
+
"3007": "Wismut, Kobaldt, Nickel",
|
242 |
+
"4310": "Wochenend- und Ferienhausfläche",
|
243 |
+
"4450": "Wochenendplatz",
|
244 |
+
"2720": "Wohnen",
|
245 |
+
"2130": "Wohnen mit Gewerbe und Industrie",
|
246 |
+
"2120": "Wohnen mit Handel und Dienstleistungen",
|
247 |
+
"2110": "Wohnen mit Öffentlich",
|
248 |
+
"2730": "Wohnen und Betrieb",
|
249 |
+
"1301": "Zahnradbahn",
|
250 |
+
"3005": "Zink",
|
251 |
+
"3006": "Zinn",
|
252 |
+
"4210": "Zoo",
|
253 |
+
"2140": "Öffentlich mit Wohnen",
|
254 |
+
"4030": "Ölschiefer"
|
255 |
+
}
|
flurstueck.py
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
import geopandas as gpd
|
3 |
+
from shapely.geometry import Polygon
|
4 |
+
import xml.etree.ElementTree as ET
|
5 |
+
import multiprocessing as mp
|
6 |
+
import sys
|
7 |
+
|
8 |
+
# Parse the XML file correctly
|
9 |
+
def parse_xml(file_path):
|
10 |
+
tree = ET.parse(file_path) # Parse the entire XML file
|
11 |
+
root = tree.getroot() # Get the root element
|
12 |
+
return root
|
13 |
+
|
14 |
+
# Extract coordinates in bulk
|
15 |
+
def extract_coordinates(posList):
|
16 |
+
coordinates = [float(x) for x in posList.strip().split()]
|
17 |
+
return [(coordinates[i], coordinates[i + 1]) for i in range(0, len(coordinates), 2)]
|
18 |
+
|
19 |
+
# Create flurstnr based on zaehler and nenner
|
20 |
+
def create_flurstnr(zaehler, nenner=None):
|
21 |
+
return f"{zaehler}/{nenner}" if nenner else zaehler
|
22 |
+
|
23 |
+
# Create gmdschl by merging land, kreis, regierungsbezirk, and gemeinde
|
24 |
+
def create_gmdschl(land, regierungsbezirk, kreis, gemeinde):
|
25 |
+
return f"{land}{regierungsbezirk}{kreis}{gemeinde}"
|
26 |
+
|
27 |
+
# Find and extract kreis values
|
28 |
+
def find_kreis(root, namespaces):
|
29 |
+
kreis_dict = {}
|
30 |
+
for kreis_region in root.findall('.//adv:AX_KreisRegion', namespaces):
|
31 |
+
schluessel_gesamt = kreis_region.find('.//adv:schluesselGesamt', namespaces)
|
32 |
+
if schluessel_gesamt is not None:
|
33 |
+
kreis_dict[schluessel_gesamt.text] = kreis_region.find('.//adv:bezeichnung', namespaces).text
|
34 |
+
return kreis_dict
|
35 |
+
|
36 |
+
# Find and extract regbezirk values
|
37 |
+
def find_regbezirk(root, namespaces):
|
38 |
+
regbezirk_dict = {}
|
39 |
+
for regbezirk in root.findall('.//adv:AX_Regierungsbezirk', namespaces):
|
40 |
+
schluessel_gesamt = regbezirk.find('.//adv:schluesselGesamt', namespaces)
|
41 |
+
bezeichnung = regbezirk.find('.//adv:bezeichnung', namespaces)
|
42 |
+
if schluessel_gesamt is not None and bezeichnung is not None:
|
43 |
+
regbezirk_dict[schluessel_gesamt.text] = bezeichnung.text
|
44 |
+
return regbezirk_dict
|
45 |
+
|
46 |
+
# Create lookup dictionary
|
47 |
+
def create_lookup_dict(root, tag, key_path, value_path, namespaces):
|
48 |
+
lookup_dict = {}
|
49 |
+
for element in root.findall(f'.//adv:{tag}', namespaces):
|
50 |
+
key = element.find(key_path, namespaces)
|
51 |
+
value = element.find(value_path, namespaces)
|
52 |
+
if key is not None and value is not None:
|
53 |
+
lookup_dict[key.text] = value.text
|
54 |
+
return lookup_dict
|
55 |
+
|
56 |
+
# Create lagebeztxt lookup dictionary
|
57 |
+
def create_lagebeztxt_dict(root, namespaces):
|
58 |
+
lagebeztxt_dict = {}
|
59 |
+
for tag in ['AX_LagebezeichnungMitHausnummer', 'AX_LagebezeichnungOhneHausnummer']:
|
60 |
+
for element in root.findall(f'.//adv:{tag}', namespaces):
|
61 |
+
gml_id = element.get('{http://www.opengis.net/gml/3.2}id')
|
62 |
+
unverschluesselt = element.find('.//adv:unverschluesselt', namespaces)
|
63 |
+
hausnummer = element.find('.//adv:hausnummer', namespaces)
|
64 |
+
|
65 |
+
if unverschluesselt is not None:
|
66 |
+
if hausnummer is not None:
|
67 |
+
lagebeztxt_dict[gml_id] = f"{unverschluesselt.text} {hausnummer.text}"
|
68 |
+
else:
|
69 |
+
lagebeztxt_dict[gml_id] = unverschluesselt.text
|
70 |
+
else:
|
71 |
+
lagebeztxt_dict[gml_id] = "<null>"
|
72 |
+
|
73 |
+
return lagebeztxt_dict
|
74 |
+
|
75 |
+
# Process a single AX_Flurstueck element
|
76 |
+
def process_single_flurstueck(flurstueck, namespaces, lookup_dicts):
|
77 |
+
# Extracting coordinates in bulk
|
78 |
+
polygon_coords = [coord for posList in flurstueck.findall('.//gml:posList', namespaces)
|
79 |
+
for coord in extract_coordinates(posList.text)]
|
80 |
+
polygon = Polygon(polygon_coords)
|
81 |
+
|
82 |
+
# Extract attributes in a single pass
|
83 |
+
flaeche = flurstueck.find('.//adv:amtlicheFlaeche', namespaces)
|
84 |
+
flaeche = flaeche.text if flaeche is not None else None
|
85 |
+
|
86 |
+
flstkennz = flurstueck.find('.//adv:flurstueckskennzeichen', namespaces)
|
87 |
+
flstkennz = flstkennz.text if flstkennz is not None else None
|
88 |
+
|
89 |
+
zaehler = flurstueck.find('.//adv:zaehler', namespaces).text
|
90 |
+
nenner = flurstueck.find('.//adv:nenner', namespaces)
|
91 |
+
flurstnr = create_flurstnr(zaehler, nenner.text if nenner is not None else None)
|
92 |
+
|
93 |
+
# Gemeindeschlüssel extraction
|
94 |
+
gemeindekennzeichen = flurstueck.find('.//adv:AX_Gemeindekennzeichen', namespaces)
|
95 |
+
if gemeindekennzeichen is not None:
|
96 |
+
land = gemeindekennzeichen.find('.//adv:land', namespaces).text
|
97 |
+
kreis = gemeindekennzeichen.find('.//adv:kreis', namespaces).text
|
98 |
+
regierungsbezirk = gemeindekennzeichen.find('.//adv:regierungsbezirk', namespaces).text
|
99 |
+
gemeinde_code = gemeindekennzeichen.find('.//adv:gemeinde', namespaces).text
|
100 |
+
gmdschl = create_gmdschl(land, regierungsbezirk, kreis, gemeinde_code)
|
101 |
+
else:
|
102 |
+
gmdschl = None
|
103 |
+
land = None
|
104 |
+
regierungsbezirk = None
|
105 |
+
gemeinde_code = None
|
106 |
+
|
107 |
+
# Use the lookup dictionaries for faster data retrieval
|
108 |
+
merged_value = f"{land}{regierungsbezirk}{kreis}"
|
109 |
+
kreis_bezeichnung = lookup_dicts['kreis'].get(merged_value, "<null>")
|
110 |
+
|
111 |
+
regbezirk_key = f"{land}{regierungsbezirk}" if land and regierungsbezirk else None
|
112 |
+
regbezirk_bezeichnung = lookup_dicts['regbezirk'].get(regbezirk_key, "<null>")
|
113 |
+
|
114 |
+
gemeinde = lookup_dicts['gemeinde'].get(merged_value + gemeinde_code, "<null>") if gemeindekennzeichen is not None else "<null>"
|
115 |
+
land_name = lookup_dicts['land'].get(land, "<null>") if land else "<null>"
|
116 |
+
|
117 |
+
gemarkungsnummer = flurstueck.find('.//adv:AX_Gemarkung_Schluessel/adv:gemarkungsnummer', namespaces)
|
118 |
+
gemarkung = lookup_dicts['gemarkung'].get(f"{land}{gemarkungsnummer.text}", "<null>") if land is not None and gemarkungsnummer is not None else "<null>"
|
119 |
+
|
120 |
+
# Extract lagebeztxt using the lookup dictionary
|
121 |
+
weist_auf = flurstueck.find('.//adv:weistAuf[@xlink:href]', namespaces)
|
122 |
+
zeigt_auf = flurstueck.find('.//adv:zeigtAuf[@xlink:href]', namespaces)
|
123 |
+
|
124 |
+
if weist_auf is not None:
|
125 |
+
href = weist_auf.get('{http://www.w3.org/1999/xlink}href')
|
126 |
+
lagebeztxt = lookup_dicts['lagebeztxt'].get(href.split(":")[-1], "<null>")
|
127 |
+
elif zeigt_auf is not None:
|
128 |
+
href = zeigt_auf.get('{http://www.w3.org/1999/xlink}href')
|
129 |
+
lagebeztxt = lookup_dicts['lagebeztxt'].get(href.split(":")[-1], "<null>")
|
130 |
+
else:
|
131 |
+
lagebeztxt = "<null>"
|
132 |
+
|
133 |
+
# Return the extracted data as a dictionary
|
134 |
+
return {
|
135 |
+
'geometry': polygon,
|
136 |
+
'flaeche': flaeche,
|
137 |
+
'flstkennz': flstkennz,
|
138 |
+
'flur': 'Flur',
|
139 |
+
'flurstnr': flurstnr,
|
140 |
+
'gmdschl': gmdschl,
|
141 |
+
'regbezirk': regbezirk_bezeichnung,
|
142 |
+
'kreis': kreis_bezeichnung,
|
143 |
+
'gemeinde': gemeinde,
|
144 |
+
'land': land_name,
|
145 |
+
'gemarkung': gemarkung,
|
146 |
+
'lagebeztxt': lagebeztxt
|
147 |
+
}
|
148 |
+
|
149 |
+
# Process all AX_Flurstueck tags with optimizations
|
150 |
+
def process_flurstueck(root):
|
151 |
+
namespaces = {'gml': 'http://www.opengis.net/gml/3.2',
|
152 |
+
'adv': 'http://www.adv-online.de/namespaces/adv/gid/6.0',
|
153 |
+
'xlink': 'http://www.w3.org/1999/xlink'}
|
154 |
+
|
155 |
+
# Create lookup dictionaries
|
156 |
+
lookup_dicts = {
|
157 |
+
'kreis': find_kreis(root, namespaces),
|
158 |
+
'regbezirk': find_regbezirk(root, namespaces),
|
159 |
+
'gemeinde': create_lookup_dict(root, 'AX_Gemeinde', './/adv:schluesselGesamt', './/adv:bezeichnung', namespaces),
|
160 |
+
'land': create_lookup_dict(root, 'AX_Bundesland', './/adv:schluesselGesamt', './/adv:bezeichnung', namespaces),
|
161 |
+
'gemarkung': create_lookup_dict(root, 'AX_Gemarkung', './/adv:schluesselGesamt', './/adv:bezeichnung', namespaces),
|
162 |
+
'lagebeztxt': create_lagebeztxt_dict(root, namespaces)
|
163 |
+
}
|
164 |
+
|
165 |
+
# Use multiprocessing to process Flurstueck elements in parallel
|
166 |
+
with mp.Pool() as pool:
|
167 |
+
data = pool.starmap(
|
168 |
+
process_single_flurstueck,
|
169 |
+
[(flurstueck, namespaces, lookup_dicts) for flurstueck in root.findall('.//adv:AX_Flurstueck', namespaces)]
|
170 |
+
)
|
171 |
+
|
172 |
+
return data
|
173 |
+
|
174 |
+
# Main function
|
175 |
+
def main(xml_file, output_shapefile):
|
176 |
+
start_time = time.time()
|
177 |
+
root = parse_xml(xml_file)
|
178 |
+
data = process_flurstueck(root)
|
179 |
+
|
180 |
+
# Create a GeoDataFrame
|
181 |
+
gdf = gpd.GeoDataFrame(data)
|
182 |
+
gdf.set_crs(epsg=25832, inplace=True) # Set appropriate CRS
|
183 |
+
|
184 |
+
# Save to a shapefile
|
185 |
+
gdf.to_file(output_shapefile, driver='ESRI Shapefile')
|
186 |
+
|
187 |
+
# Save the .prj file with the specified projection
|
188 |
+
prj_content = ('PROJCS["ETRS89 / UTM zone 32N",'
|
189 |
+
'GEOGCS["ETRS89",'
|
190 |
+
'DATUM["European_Terrestrial_Reference_System_1989",'
|
191 |
+
'SPHEROID["GRS 1980",6378137,298.257222101]],'
|
192 |
+
'PRIMEM["Greenwich",0],'
|
193 |
+
'UNIT["degree",0.0174532925199433]],'
|
194 |
+
'PROJECTION["Transverse_Mercator"],'
|
195 |
+
'PARAMETER["latitude_of_origin",0],'
|
196 |
+
'PARAMETER["central_meridian",9],'
|
197 |
+
'PARAMETER["scale_factor",0.9996],'
|
198 |
+
'PARAMETER["false_easting",500000],'
|
199 |
+
'PARAMETER["false_northing",0],'
|
200 |
+
'UNIT["metre",1]]')
|
201 |
+
|
202 |
+
prj_file = output_shapefile.replace('.shp', '.prj')
|
203 |
+
with open(prj_file, 'w') as prj:
|
204 |
+
prj.write(prj_content)
|
205 |
+
|
206 |
+
end_time = time.time()
|
207 |
+
print(f"Processing complete. Shapefile saved as '{output_shapefile}'. Time taken: {end_time - start_time:.2f} seconds.")
|
208 |
+
|
209 |
+
# Example usage
|
210 |
+
if __name__ == "__main__":
|
211 |
+
xml_file = sys.argv[1]
|
212 |
+
output_shapefile = sys.argv[2]
|
213 |
+
main(xml_file, output_shapefile)
|
guby.py
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import xml.etree.ElementTree as ET
|
2 |
+
import shapefile
|
3 |
+
import sys
|
4 |
+
|
5 |
+
# Input XML file
|
6 |
+
input_xml = sys.argv[1]
|
7 |
+
|
8 |
+
# Output shapefile
|
9 |
+
output_shapefile = sys.argv[2]
|
10 |
+
|
11 |
+
# Create shapefile writer
|
12 |
+
w = shapefile.Writer(output_shapefile)
|
13 |
+
w.autoBalance = 1
|
14 |
+
|
15 |
+
# Define fields for shapefile
|
16 |
+
w.field('gebnutzbez', 'C') # Gebaeude Nutzung Bezeichnung
|
17 |
+
w.field('funktion', 'C') # Funktion value (mapped to text)
|
18 |
+
w.field('fktkurz', 'C') # Kurz Funktion (<null>)
|
19 |
+
w.field('name', 'C') # Name value
|
20 |
+
w.field('anzahlgs', 'C') # Anzahl der Oberirdischen Geschosse
|
21 |
+
w.field('lagebeztxt', 'C') # Lagebezeichnung text
|
22 |
+
|
23 |
+
# Parse the XML file
|
24 |
+
tree = ET.parse(input_xml)
|
25 |
+
root = tree.getroot()
|
26 |
+
|
27 |
+
# Namespace map
|
28 |
+
ns = {'gml': 'http://www.opengis.net/gml/3.2',
|
29 |
+
'adv': 'http://www.adv-online.de/namespaces/adv/gid/6.0'}
|
30 |
+
|
31 |
+
# Building function mapping
|
32 |
+
funktion_mapping = {
|
33 |
+
'1000': 'Wohngebäude',
|
34 |
+
'2000': 'Gebäude für Wirtschaft oder Gewerbe',
|
35 |
+
'3000': 'Gebäude für öffentliche Zwecke',
|
36 |
+
'3020': 'Gebäude für Bildung und Forschung',
|
37 |
+
'2463': 'Garage',
|
38 |
+
'1610': 'Überdachung',
|
39 |
+
'2523': 'Umformer',
|
40 |
+
'1620': 'Treppe',
|
41 |
+
'9999': 'Sonstiges',
|
42 |
+
'1700': 'Mauer',
|
43 |
+
'3041': 'Kirche',
|
44 |
+
'3065': 'Kinderkrippe, Kindergarten, Kindertagesstätte',
|
45 |
+
'3043': 'Kapelle',
|
46 |
+
'3072': 'Feuerwehr'
|
47 |
+
}
|
48 |
+
|
49 |
+
# Helper function to extract polygon coordinates
|
50 |
+
def extract_polygon(coords_text):
|
51 |
+
try:
|
52 |
+
coords = list(map(float, coords_text.split()))
|
53 |
+
return [(coords[i], coords[i+1]) for i in range(0, len(coords), 2)]
|
54 |
+
except Exception as e:
|
55 |
+
print(f"Error parsing coordinates: {e}")
|
56 |
+
return None
|
57 |
+
|
58 |
+
# Cache frequently accessed elements
|
59 |
+
lagebezeichnung_cache = {}
|
60 |
+
for lage_elem in root.findall('.//adv:AX_LagebezeichnungMitHausnummer', ns):
|
61 |
+
gml_id = lage_elem.attrib.get('{http://www.opengis.net/gml/3.2}id')
|
62 |
+
if gml_id:
|
63 |
+
unverschluesselt_elem = lage_elem.find('.//adv:lagebezeichnung/adv:AX_Lagebezeichnung/adv:unverschluesselt', ns)
|
64 |
+
hausnummer_elem = lage_elem.find('.//adv:hausnummer', ns)
|
65 |
+
unverschluesselt = unverschluesselt_elem.text if unverschluesselt_elem is not None else ''
|
66 |
+
hausnummer = hausnummer_elem.text if hausnummer_elem is not None else ''
|
67 |
+
lagebezeichnung_cache[gml_id] = f"{unverschluesselt} {hausnummer}".strip()
|
68 |
+
|
69 |
+
# Process AX_Gebaeude and AX_SonstigesBauwerkOderSonstigeEinrichtung
|
70 |
+
for gebaeude in root.findall('.//adv:AX_Gebaeude', ns) + root.findall('.//adv:AX_SonstigesBauwerkOderSonstigeEinrichtung', ns):
|
71 |
+
# Create 'gebnutzbez' value
|
72 |
+
gebnutzbez = 'Gebaeude' if gebaeude.tag == '{http://www.adv-online.de/namespaces/adv/gid/6.0}AX_Gebaeude' else 'Sonstiges Bauwerk Oder Sonstige Einrichtung'
|
73 |
+
|
74 |
+
# Extract and map 'funktion' value
|
75 |
+
funktion_elem = gebaeude.find('.//adv:gebaeudefunktion', ns)
|
76 |
+
if funktion_elem is None:
|
77 |
+
funktion_elem = gebaeude.find('.//adv:bauwerksfunktion', ns)
|
78 |
+
funktion = funktion_mapping.get(funktion_elem.text, 'Unbekannt') if funktion_elem is not None and funktion_elem.text else '<null>'
|
79 |
+
|
80 |
+
# Set 'fktkurz' to '<null>'
|
81 |
+
fktkurz = '<null>'
|
82 |
+
|
83 |
+
# Extract 'name' value
|
84 |
+
name_elem = gebaeude.find('.//adv:name', ns)
|
85 |
+
name = name_elem.text if name_elem is not None else '<null>'
|
86 |
+
|
87 |
+
# Extract 'anzahlDerOberirdischenGeschosse' value
|
88 |
+
anzahlgs_elem = gebaeude.find('.//adv:anzahlDerOberirdischenGeschosse', ns)
|
89 |
+
anzahlgs = anzahlgs_elem.text if anzahlgs_elem is not None else '<null>'
|
90 |
+
|
91 |
+
# Extract 'lagebeztxt' based on the 'zeigtAuf' reference
|
92 |
+
zeigtauf_elem = gebaeude.find('.//adv:zeigtAuf', ns)
|
93 |
+
lagebeztxt = '<null>'
|
94 |
+
if zeigtauf_elem is not None:
|
95 |
+
xlink_href = zeigtauf_elem.attrib.get('{http://www.w3.org/1999/xlink}href')
|
96 |
+
if xlink_href:
|
97 |
+
gml_id = xlink_href.split(':')[-1]
|
98 |
+
lagebeztxt = lagebezeichnung_cache.get(gml_id, '<null>')
|
99 |
+
|
100 |
+
# Extract coordinates for the polygon
|
101 |
+
pos_list = gebaeude.findall('.//gml:posList', ns)
|
102 |
+
polygon_coords = []
|
103 |
+
if pos_list:
|
104 |
+
for pos in pos_list:
|
105 |
+
coords = extract_polygon(pos.text)
|
106 |
+
if coords:
|
107 |
+
polygon_coords.extend(coords)
|
108 |
+
|
109 |
+
if polygon_coords:
|
110 |
+
# Add polygon and record to shapefile
|
111 |
+
w.poly([polygon_coords])
|
112 |
+
w.record(gebnutzbez, funktion, fktkurz, name, anzahlgs, lagebeztxt)
|
113 |
+
|
114 |
+
# Save shapefile
|
115 |
+
w.close()
|
116 |
+
|
117 |
+
# Define spatial reference (projection file)
|
118 |
+
with open(output_shapefile.replace('.shp', '.prj'), 'w') as prj_file:
|
119 |
+
prj_file.write('PROJCS["ETRS89 / UTM zone 32N",'
|
120 |
+
'GEOGCS["ETRS89",'
|
121 |
+
'DATUM["European_Terrestrial_Reference_System_1989",'
|
122 |
+
'SPHEROID["GRS 1980",6378137,298.257222101]],'
|
123 |
+
'PRIMEM["Greenwich",0],'
|
124 |
+
'UNIT["degree",0.0174532925199433]],'
|
125 |
+
'PROJECTION["Transverse_Mercator"],'
|
126 |
+
'PARAMETER["latitude_of_origin",0],'
|
127 |
+
'PARAMETER["central_meridian",9],'
|
128 |
+
'PARAMETER["scale_factor",0.9996],'
|
129 |
+
'PARAMETER["false_easting",500000],'
|
130 |
+
'PARAMETER["false_northing",0],'
|
131 |
+
'UNIT["metre",1]]')
|
kat.py
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import geopandas as gpd
|
2 |
+
from shapely.ops import unary_union
|
3 |
+
import shapefile
|
4 |
+
from shapely.geometry import Polygon, MultiPolygon
|
5 |
+
import sys
|
6 |
+
|
7 |
+
# Input shapefile
|
8 |
+
input_shapefile = sys.argv[1]
|
9 |
+
|
10 |
+
# Output shapefile
|
11 |
+
output_shapefile = sys.argv[2]
|
12 |
+
|
13 |
+
# Step 1: Load the shapefile
|
14 |
+
gdf = gpd.read_file(input_shapefile)
|
15 |
+
|
16 |
+
# Step 2: Fix invalid geometries
|
17 |
+
gdf['geometry'] = gdf['geometry'].buffer(0)
|
18 |
+
|
19 |
+
# Step 3: Group by gemarkung and create exterior boundaries
|
20 |
+
gemarkung_boundaries = {}
|
21 |
+
gemarkung_data = {}
|
22 |
+
for gemarkung, group in gdf.groupby('gemarkung'):
|
23 |
+
merged_polygon = unary_union(group.geometry)
|
24 |
+
if isinstance(merged_polygon, Polygon):
|
25 |
+
exterior_boundary = Polygon(merged_polygon.exterior)
|
26 |
+
elif isinstance(merged_polygon, MultiPolygon):
|
27 |
+
exterior_boundary = MultiPolygon([Polygon(poly.exterior) for poly in merged_polygon.geoms])
|
28 |
+
else:
|
29 |
+
print(f"Skipping invalid geometry for gemarkung: {gemarkung}")
|
30 |
+
continue
|
31 |
+
gemarkung_boundaries[gemarkung] = exterior_boundary
|
32 |
+
|
33 |
+
# Store additional data for each gemarkung
|
34 |
+
sample_row = group.iloc[0]
|
35 |
+
gemarkung_data[gemarkung] = {
|
36 |
+
'gemeinde': sample_row['gemeinde'],
|
37 |
+
'schluessel': sample_row['flstkennz'].split('___')[0]
|
38 |
+
}
|
39 |
+
|
40 |
+
# Step 4: Create a new shapefile with the exterior boundaries
|
41 |
+
w = shapefile.Writer(output_shapefile)
|
42 |
+
w.autoBalance = 1
|
43 |
+
|
44 |
+
# Define fields for shapefile
|
45 |
+
w.field('oid_1', 'C')
|
46 |
+
w.field('art', 'C')
|
47 |
+
w.field('name', 'C')
|
48 |
+
w.field('schluessel', 'C')
|
49 |
+
w.field('gemeinde', 'C')
|
50 |
+
|
51 |
+
# Add geometries and attribute values to the shapefile
|
52 |
+
for gemarkung, boundary in gemarkung_boundaries.items():
|
53 |
+
data = gemarkung_data[gemarkung]
|
54 |
+
|
55 |
+
# Original record
|
56 |
+
if isinstance(boundary, Polygon):
|
57 |
+
w.poly([list(boundary.exterior.coords)])
|
58 |
+
w.record(f"DE{data['schluessel']}", 'Gemarkung', gemarkung, data['schluessel'], data['gemeinde'])
|
59 |
+
elif isinstance(boundary, MultiPolygon):
|
60 |
+
for poly in boundary.geoms:
|
61 |
+
w.poly([list(poly.exterior.coords)])
|
62 |
+
w.record(f"DE{data['schluessel']}", 'Gemarkung', gemarkung, data['schluessel'], data['gemeinde'])
|
63 |
+
|
64 |
+
# Additional record
|
65 |
+
if isinstance(boundary, Polygon):
|
66 |
+
w.poly([list(boundary.exterior.coords)])
|
67 |
+
w.record(f"DE{data['schluessel']}000", 'Gemarkungsteil / Flur', 'Flur', f"{data['schluessel']}00", data['gemeinde'])
|
68 |
+
elif isinstance(boundary, MultiPolygon):
|
69 |
+
for poly in boundary.geoms:
|
70 |
+
w.poly([list(poly.exterior.coords)])
|
71 |
+
w.record(f"DE{data['schluessel']}000", 'Gemarkungsteil / Flur', 'Flur', f"{data['schluessel']}00", data['gemeinde'])
|
72 |
+
|
73 |
+
# Define spatial reference (projection file)
|
74 |
+
with open(output_shapefile.replace('.shp', '.prj'), 'w') as prj_file:
|
75 |
+
prj_file.write('PROJCS["ETRS89 / UTM zone 32N",'
|
76 |
+
'GEOGCS["ETRS89",'
|
77 |
+
'DATUM["European_Terrestrial_Reference_System_1989",'
|
78 |
+
'SPHEROID["GRS 1980",6378137,298.257222101]],'
|
79 |
+
'PRIMEM["Greenwich",0],'
|
80 |
+
'UNIT["degree",0.0174532925199433]],'
|
81 |
+
'PROJECTION["Transverse_Mercator"],'
|
82 |
+
'PARAMETER["latitude_of_origin",0],'
|
83 |
+
'PARAMETER["central_meridian",9],'
|
84 |
+
'PARAMETER["scale_factor",0.9996],'
|
85 |
+
'PARAMETER["false_easting",500000],'
|
86 |
+
'PARAMETER["false_northing",0],'
|
87 |
+
'UNIT["metre",1]]')
|
88 |
+
|
89 |
+
# Save the shapefile
|
90 |
+
w.close()
|
91 |
+
|
92 |
+
print(f"Shapefile '{output_shapefile}' created successfully with exterior boundaries and additional fields.")
|
main.py
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import subprocess
|
2 |
+
import time
|
3 |
+
|
4 |
+
def run_script(script_name, *args):
|
5 |
+
command = ['python', script_name] + list(args)
|
6 |
+
start_time = time.time()
|
7 |
+
result = subprocess.run(command, capture_output=True, text=True)
|
8 |
+
end_time = time.time()
|
9 |
+
elapsed_time = end_time - start_time
|
10 |
+
if result.returncode != 0:
|
11 |
+
print(f"Error running {script_name}: {result.stderr}")
|
12 |
+
else:
|
13 |
+
print(f"Successfully ran {script_name} in {elapsed_time:.2f} seconds")
|
14 |
+
return elapsed_time
|
15 |
+
|
16 |
+
if __name__ == "__main__":
|
17 |
+
# Define file paths
|
18 |
+
xml_file = "1546621_0.xml"
|
19 |
+
bez_dict_file = "bez_dict.json"
|
20 |
+
flurstueck_shapefile = "flurstueck.shp"
|
21 |
+
nutzung_shapefile = "nutzung.shp"
|
22 |
+
nutzung_flurstueck_shapefile = "nutzungFlurstueck.shp"
|
23 |
+
gebauede_bauwerk_shapefile = "gebauedeBauwerk.shp"
|
24 |
+
verwaltungs_einheit_shapefile = "verwaltungsEinheit.shp"
|
25 |
+
kataster_bezirk_shapefile = "katasterBezirk.shp"
|
26 |
+
|
27 |
+
# Track total execution time
|
28 |
+
total_start_time = time.time()
|
29 |
+
|
30 |
+
# Run scripts in the correct order
|
31 |
+
total_time = 0
|
32 |
+
total_time += run_script('flurstueck.py', xml_file, flurstueck_shapefile)
|
33 |
+
total_time += run_script('nutzung.py', xml_file, bez_dict_file, nutzung_shapefile)
|
34 |
+
total_time += run_script('nutflu.py', flurstueck_shapefile, nutzung_shapefile, nutzung_flurstueck_shapefile)
|
35 |
+
total_time += run_script('guby.py', xml_file, gebauede_bauwerk_shapefile)
|
36 |
+
total_time += run_script('ver.py', flurstueck_shapefile, xml_file, verwaltungs_einheit_shapefile)
|
37 |
+
total_time += run_script('kat.py', flurstueck_shapefile, kataster_bezirk_shapefile)
|
38 |
+
|
39 |
+
total_end_time = time.time()
|
40 |
+
total_elapsed_time = total_end_time - total_start_time
|
41 |
+
|
42 |
+
print(f"Total execution time: {total_elapsed_time:.2f} seconds")
|
nutflu.py
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import geopandas as gpd
|
2 |
+
import sys
|
3 |
+
|
4 |
+
def clean_geometries(gdf):
|
5 |
+
# Check and fix invalid geometries
|
6 |
+
gdf['geometry'] = gdf['geometry'].buffer(0) # This can help fix some topology issues
|
7 |
+
gdf = gdf[gdf.is_valid] # Remove invalid geometries
|
8 |
+
return gdf
|
9 |
+
|
10 |
+
def union_shapefiles(shapefile1, shapefile2, output_shapefile):
|
11 |
+
gdf1 = gpd.read_file(shapefile1)
|
12 |
+
gdf2 = gpd.read_file(shapefile2)
|
13 |
+
|
14 |
+
# Clean geometries
|
15 |
+
gdf1 = clean_geometries(gdf1)
|
16 |
+
gdf2 = clean_geometries(gdf2)
|
17 |
+
|
18 |
+
# Perform the union
|
19 |
+
union_gdf = gpd.overlay(gdf1, gdf2, how='union')
|
20 |
+
|
21 |
+
# Save the result to a new shapefile
|
22 |
+
union_gdf.to_file(output_shapefile)
|
23 |
+
|
24 |
+
# Example usage
|
25 |
+
if __name__ == "__main__":
|
26 |
+
shapefile1 = sys.argv[1]
|
27 |
+
shapefile2 = sys.argv[2]
|
28 |
+
output_shapefile = sys.argv[3]
|
29 |
+
union_shapefiles(shapefile1, shapefile2, output_shapefile)
|
nutzung.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import xml.etree.ElementTree as ET
|
2 |
+
import shapefile
|
3 |
+
import json
|
4 |
+
import re
|
5 |
+
import codecs
|
6 |
+
from shapely.geometry import Polygon, MultiPolygon
|
7 |
+
import sys
|
8 |
+
|
9 |
+
# Input XML file
|
10 |
+
input_xml = sys.argv[1]
|
11 |
+
|
12 |
+
# Input JSON file for bez_dict
|
13 |
+
bez_dict_file = sys.argv[2]
|
14 |
+
|
15 |
+
# Output shapefile
|
16 |
+
output_shapefile = sys.argv[3]
|
17 |
+
|
18 |
+
# Load bez_dict from JSON file
|
19 |
+
with codecs.open(bez_dict_file, 'r', encoding='utf-8') as f:
|
20 |
+
bez_dict = json.load(f)
|
21 |
+
|
22 |
+
# Create shapefile writer
|
23 |
+
w = shapefile.Writer(output_shapefile)
|
24 |
+
w.autoBalance = 1
|
25 |
+
|
26 |
+
# Define fields for shapefile
|
27 |
+
w.field('nutzart', 'C') # Nutzart as string
|
28 |
+
w.field('bez', 'C') # BEZ as string
|
29 |
+
w.field('name', 'C') # NAME as string
|
30 |
+
|
31 |
+
# Parse the XML file
|
32 |
+
tree = ET.parse(input_xml)
|
33 |
+
root = tree.getroot()
|
34 |
+
|
35 |
+
# List of tag names to process
|
36 |
+
tags_to_process = [
|
37 |
+
"AX_Gehoelz", "AX_Wohnbauflaeche", "AX_UnlandVegetationsloseFlaeche",
|
38 |
+
"AX_Strassenverkehr", "AX_StehendesGewaesser", "AX_SportFreizeitUndErholungsflaeche",
|
39 |
+
"AX_Platz", "AX_Landwirtschaft", "AX_IndustrieUndGewerbeflaeche",
|
40 |
+
"AX_Fliessgewaesser", "AX_FlaecheGemischterNutzung", "AX_Wald", "AX_Weg", "AX_Friedhof",
|
41 |
+
"AX_FlaecheBesondererFunktionalerPraegung", "AX_Bahnverkehr"
|
42 |
+
]
|
43 |
+
|
44 |
+
# Helper function to extract coordinates and create Polygon
|
45 |
+
def extract_polygon(coords_text):
|
46 |
+
coords = list(map(float, coords_text.split()))
|
47 |
+
return [(coords[i], coords[i+1]) for i in range(0, len(coords), 2)]
|
48 |
+
|
49 |
+
# Helper function to format nutzart
|
50 |
+
def format_nutzart(tag):
|
51 |
+
# Remove 'AX_' prefix and add spaces before capital letters
|
52 |
+
return ' '.join(re.findall('[A-Z][^A-Z]*', tag[3:]))
|
53 |
+
|
54 |
+
# Helper function to extract bez value
|
55 |
+
def extract_bez(elem):
|
56 |
+
funktion_elem = elem.find('.//{http://www.adv-online.de/namespaces/adv/gid/6.0}funktion')
|
57 |
+
vegetationsmerkmal_elem = elem.find('.//{http://www.adv-online.de/namespaces/adv/gid/6.0}vegetationsmerkmal')
|
58 |
+
|
59 |
+
if funktion_elem is not None:
|
60 |
+
return bez_dict.get(funktion_elem.text, "<null>")
|
61 |
+
elif vegetationsmerkmal_elem is not None:
|
62 |
+
return bez_dict.get(vegetationsmerkmal_elem.text, "<null>")
|
63 |
+
else:
|
64 |
+
return "<null>"
|
65 |
+
|
66 |
+
# Process the elements
|
67 |
+
for tag_name in tags_to_process:
|
68 |
+
for elem in root.findall(f".//{{http://www.adv-online.de/namespaces/adv/gid/6.0}}{tag_name}"):
|
69 |
+
|
70 |
+
# Format nutzart
|
71 |
+
nutzart = format_nutzart(tag_name)
|
72 |
+
|
73 |
+
# Extract bez
|
74 |
+
bez = extract_bez(elem)
|
75 |
+
|
76 |
+
# Extract name
|
77 |
+
name_elem = elem.find('.//{http://www.adv-online.de/namespaces/adv/gid/6.0}name')
|
78 |
+
name = name_elem.text if name_elem is not None else "<null>"
|
79 |
+
|
80 |
+
# Extract coordinates for polygon
|
81 |
+
coordinates = elem.findall('.//{http://www.opengis.net/gml/3.2}posList')
|
82 |
+
if coordinates:
|
83 |
+
polygon_coords = []
|
84 |
+
for coord_elem in coordinates:
|
85 |
+
polygon_coords.extend(extract_polygon(coord_elem.text))
|
86 |
+
|
87 |
+
# Create a Shapely polygon and fix invalid geometries
|
88 |
+
try:
|
89 |
+
poly = Polygon(polygon_coords)
|
90 |
+
if not poly.is_valid:
|
91 |
+
poly = poly.buffer(0)
|
92 |
+
|
93 |
+
if isinstance(poly, Polygon):
|
94 |
+
w.poly([list(poly.exterior.coords)])
|
95 |
+
elif isinstance(poly, MultiPolygon):
|
96 |
+
for p in poly.geoms:
|
97 |
+
w.poly([list(p.exterior.coords)])
|
98 |
+
|
99 |
+
# Add record to shapefile
|
100 |
+
w.record(nutzart, bez, name)
|
101 |
+
except Exception as e:
|
102 |
+
print(f"Error processing polygon: {e}")
|
103 |
+
|
104 |
+
# Save shapefile
|
105 |
+
w.close()
|
106 |
+
|
107 |
+
print("Shapefile created successfully.")
|
108 |
+
|
109 |
+
# Create .prj file
|
110 |
+
prj = open(output_shapefile.replace('.shp', '.prj'), "w")
|
111 |
+
epsg = 'PROJCS["ETRS89 / UTM zone 32N",GEOGCS["ETRS89",DATUM["European_Terrestrial_Reference_System_1989",SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6258"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4258"]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",9],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Easting",EAST],AXIS["Northing",NORTH],AUTHORITY["EPSG","25832"]]'
|
112 |
+
prj.write(epsg)
|
113 |
+
prj.close()
|
114 |
+
|
115 |
+
print("Shapefile and .prj file created successfully.")
|
requirements.txt
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiofiles==23.2.1
|
2 |
+
altgraph==0.17.4
|
3 |
+
annotated-types==0.7.0
|
4 |
+
anyio==4.6.0
|
5 |
+
attrs==24.2.0
|
6 |
+
certifi==2024.8.30
|
7 |
+
charset-normalizer==3.3.2
|
8 |
+
click==8.1.7
|
9 |
+
click-plugins==1.1.1
|
10 |
+
cligj==0.7.2
|
11 |
+
colorama==0.4.6
|
12 |
+
contourpy==1.3.0
|
13 |
+
cycler==0.12.1
|
14 |
+
fastapi==0.115.0
|
15 |
+
ffmpy==0.4.0
|
16 |
+
filelock==3.16.1
|
17 |
+
fiona==1.10.0
|
18 |
+
fonttools==4.53.1
|
19 |
+
fsspec==2024.9.0
|
20 |
+
GDAL==3.9.2
|
21 |
+
geopandas==1.0.1
|
22 |
+
gradio==4.44.0
|
23 |
+
gradio_client==1.3.0
|
24 |
+
h11==0.14.0
|
25 |
+
httpcore==1.0.5
|
26 |
+
httpx==0.27.2
|
27 |
+
huggingface-hub==0.25.0
|
28 |
+
idna==3.10
|
29 |
+
importlib_resources==6.4.5
|
30 |
+
Jinja2==3.1.4
|
31 |
+
kiwisolver==1.4.7
|
32 |
+
lxml==5.3.0
|
33 |
+
markdown-it-py==3.0.0
|
34 |
+
MarkupSafe==2.1.5
|
35 |
+
matplotlib==3.9.2
|
36 |
+
mdurl==0.1.2
|
37 |
+
numpy @ file:///D:/bld/numpy_1725411960638/work/dist/numpy-2.1.1-cp312-cp312-win_amd64.whl#sha256=e94e5df6c5700d841731b7e98583918f6096aee8c7f282eddef410fb039b3879
|
38 |
+
orjson==3.10.7
|
39 |
+
packaging==24.1
|
40 |
+
pandas==2.2.2
|
41 |
+
pefile==2024.8.26
|
42 |
+
pillow==10.4.0
|
43 |
+
psutil==6.0.0
|
44 |
+
pydantic==2.9.2
|
45 |
+
pydantic_core==2.23.4
|
46 |
+
pydub==0.25.1
|
47 |
+
Pygments==2.18.0
|
48 |
+
pyinstaller==6.10.0
|
49 |
+
pyinstaller-hooks-contrib==2024.8
|
50 |
+
pyogrio==0.9.0
|
51 |
+
pyparsing==3.1.4
|
52 |
+
pyproj==3.6.1
|
53 |
+
pyshp==2.3.1
|
54 |
+
python-dateutil==2.9.0.post0
|
55 |
+
python-multipart==0.0.10
|
56 |
+
pytz==2024.2
|
57 |
+
pywin32-ctypes==0.2.3
|
58 |
+
PyYAML==6.0.2
|
59 |
+
requests==2.32.3
|
60 |
+
rich==13.8.1
|
61 |
+
ruff==0.6.7
|
62 |
+
semantic-version==2.10.0
|
63 |
+
setuptools==72.1.0
|
64 |
+
shapely==2.0.6
|
65 |
+
shellingham==1.5.4
|
66 |
+
six==1.16.0
|
67 |
+
sniffio==1.3.1
|
68 |
+
starlette==0.38.6
|
69 |
+
tomlkit==0.12.0
|
70 |
+
tqdm==4.66.5
|
71 |
+
typer==0.12.5
|
72 |
+
typing_extensions==4.12.2
|
73 |
+
tzdata==2024.1
|
74 |
+
urllib3==2.2.3
|
75 |
+
uvicorn==0.30.6
|
76 |
+
websockets==12.0
|
77 |
+
wheel==0.43.0
|
ver.py
ADDED
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import geopandas as gpd
|
2 |
+
from shapely.ops import unary_union
|
3 |
+
import xml.etree.ElementTree as ET
|
4 |
+
import shapefile
|
5 |
+
from shapely.geometry import Polygon, MultiPolygon
|
6 |
+
import sys
|
7 |
+
|
8 |
+
# Input shapefile
|
9 |
+
input_shapefile = sys.argv[1]
|
10 |
+
|
11 |
+
# Input XML file
|
12 |
+
input_xml = sys.argv[2]
|
13 |
+
|
14 |
+
# Output shapefile
|
15 |
+
output_shapefile = sys.argv[3]
|
16 |
+
|
17 |
+
# Create shapefile writer for the output
|
18 |
+
w = shapefile.Writer(output_shapefile)
|
19 |
+
w.autoBalance = 1
|
20 |
+
|
21 |
+
# Step 1: Load the shapefile and get the merged polygon with exterior boundary
|
22 |
+
gdf = gpd.read_file(input_shapefile)
|
23 |
+
|
24 |
+
# Step 1.1: Identify and fix invalid geometries
|
25 |
+
gdf['valid'] = gdf.is_valid
|
26 |
+
|
27 |
+
# Print invalid geometries
|
28 |
+
invalid_geometries = gdf[~gdf['valid']]
|
29 |
+
if not invalid_geometries.empty:
|
30 |
+
print(f"Invalid geometries found: {len(invalid_geometries)}. Attempting to fix them.")
|
31 |
+
|
32 |
+
# Fix invalid geometries using buffer(0)
|
33 |
+
gdf['geometry'] = gdf['geometry'].buffer(0)
|
34 |
+
|
35 |
+
# Combine all polygons into one using unary_union
|
36 |
+
merged_polygon = unary_union(gdf.geometry)
|
37 |
+
|
38 |
+
# Step 1.2: Check if merged_polygon is a MultiPolygon or a single Polygon
|
39 |
+
if isinstance(merged_polygon, Polygon):
|
40 |
+
exterior_boundaries = [merged_polygon.exterior]
|
41 |
+
elif isinstance(merged_polygon, MultiPolygon):
|
42 |
+
exterior_boundaries = [poly.exterior for poly in merged_polygon.geoms]
|
43 |
+
else:
|
44 |
+
raise TypeError("Resulting geometry is neither a Polygon nor a MultiPolygon.")
|
45 |
+
|
46 |
+
# Define fields for shapefile
|
47 |
+
w.field('art', 'C') # Type of tag (Gemeinde, Bundesland, etc.)
|
48 |
+
w.field('name', 'C') # Name value
|
49 |
+
w.field('schluessel', 'C') # Schlüssel value
|
50 |
+
w.field('uebaname', 'C') # Uebaname value
|
51 |
+
w.field('ueobjekt', 'C') # Ueobjekt value
|
52 |
+
|
53 |
+
# Parse the XML file
|
54 |
+
tree = ET.parse(input_xml)
|
55 |
+
root = tree.getroot()
|
56 |
+
|
57 |
+
# Namespace map
|
58 |
+
ns = {'gml': 'http://www.opengis.net/gml/3.2',
|
59 |
+
'adv': 'http://www.adv-online.de/namespaces/adv/gid/6.0'}
|
60 |
+
|
61 |
+
# Store the tag names
|
62 |
+
tag_names = {
|
63 |
+
'AX_Gemeinde': 'Gemeinde',
|
64 |
+
'AX_Bundesland': 'Bundesland',
|
65 |
+
'AX_Regierungsbezirk': 'Regierungsbezirk',
|
66 |
+
'AX_KreisRegion': 'Kreis / kreisfreie Stadt'
|
67 |
+
}
|
68 |
+
|
69 |
+
# Track if "Gemeinde" and "Kreis / kreisfreie Stadt" have already been added
|
70 |
+
first_gemeinde_added = False
|
71 |
+
first_kreis_added = False
|
72 |
+
|
73 |
+
# Helper function to extract polygon coordinates
|
74 |
+
def extract_polygon(coords_text):
|
75 |
+
try:
|
76 |
+
coords = list(map(float, coords_text.split()))
|
77 |
+
return [(coords[i], coords[i+1]) for i in range(0, len(coords), 2)]
|
78 |
+
except Exception as e:
|
79 |
+
print(f"Error parsing coordinates: {e}")
|
80 |
+
return None
|
81 |
+
|
82 |
+
# Step 3: Add the extracted XML data to the shapefile along with the exterior boundary from the polygon
|
83 |
+
tags = ['AX_Gemeinde', 'AX_Bundesland', 'AX_Regierungsbezirk', 'AX_KreisRegion']
|
84 |
+
data = {}
|
85 |
+
|
86 |
+
for tag in tags:
|
87 |
+
for element in root.findall(f'.//adv:{tag}', ns):
|
88 |
+
tag_name = tag_names.get(tag, '<null>')
|
89 |
+
name_elem = element.find('.//adv:bezeichnung', ns)
|
90 |
+
name = name_elem.text if name_elem is not None else '<null>'
|
91 |
+
|
92 |
+
schluessel_elem = element.find('.//adv:schluesselGesamt', ns)
|
93 |
+
schluessel = schluessel_elem.text if schluessel_elem is not None else '<null>'
|
94 |
+
|
95 |
+
# Define uebaname based on tag
|
96 |
+
if tag == 'AX_Gemeinde':
|
97 |
+
uebaname = data.get('AX_KreisRegion', {}).get('name', '<null>')
|
98 |
+
elif tag == 'AX_Bundesland':
|
99 |
+
uebaname = '<null>'
|
100 |
+
elif tag == 'AX_Regierungsbezirk':
|
101 |
+
uebaname = data.get('AX_Bundesland', {}).get('name', '<null>')
|
102 |
+
elif tag == 'AX_KreisRegion':
|
103 |
+
uebaname = data.get('AX_Regierungsbezirk', {}).get('name', '<null>')
|
104 |
+
|
105 |
+
# Define ueobjekt based on schluessel
|
106 |
+
if tag == 'AX_Gemeinde':
|
107 |
+
ueobjekt = f"DE{schluessel[:5]}"
|
108 |
+
elif tag == 'AX_Bundesland':
|
109 |
+
ueobjekt = '<null>'
|
110 |
+
elif tag == 'AX_Regierungsbezirk':
|
111 |
+
ueobjekt = '<null>'
|
112 |
+
elif tag == 'AX_KreisRegion':
|
113 |
+
ueobjekt = f"DE{schluessel[:3]}"
|
114 |
+
|
115 |
+
data[tag] = {'name': name, 'schluessel': schluessel}
|
116 |
+
|
117 |
+
# Skip if first Gemeinde or Kreis / kreisfreie Stadt has already been added
|
118 |
+
if (tag == 'AX_Gemeinde' and first_gemeinde_added) or (tag == 'AX_KreisRegion' and first_kreis_added):
|
119 |
+
continue
|
120 |
+
|
121 |
+
# Set flags after first Gemeinde or Kreis / kreisfreie Stadt is added
|
122 |
+
if tag == 'AX_Gemeinde':
|
123 |
+
first_gemeinde_added = True
|
124 |
+
elif tag == 'AX_KreisRegion':
|
125 |
+
first_kreis_added = True
|
126 |
+
|
127 |
+
print("="*50)
|
128 |
+
print(tag_name)
|
129 |
+
print(name)
|
130 |
+
print(schluessel)
|
131 |
+
print(uebaname)
|
132 |
+
print(ueobjekt)
|
133 |
+
print("="*50)
|
134 |
+
|
135 |
+
# Add record to shapefile (with the tag data)
|
136 |
+
w.record(tag_name, name, schluessel, uebaname, ueobjekt)
|
137 |
+
|
138 |
+
# Step 4: Add the exterior boundaries to the shapefile
|
139 |
+
for boundary in exterior_boundaries:
|
140 |
+
w.poly([list(boundary.coords)])
|
141 |
+
|
142 |
+
# Define spatial reference (projection file)
|
143 |
+
with open(output_shapefile.replace('.shp', '.prj'), 'w') as prj_file:
|
144 |
+
prj_file.write('PROJCS["ETRS89 / UTM zone 32N",'
|
145 |
+
'GEOGCS["ETRS89",'
|
146 |
+
'DATUM["European_Terrestrial_Reference_System_1989",'
|
147 |
+
'SPHEROID["GRS 1980",6378137,298.257222101]],'
|
148 |
+
'PRIMEM["Greenwich",0],'
|
149 |
+
'UNIT["degree",0.0174532925199433]],'
|
150 |
+
'PROJECTION["Transverse_Mercator"],'
|
151 |
+
'PARAMETER["latitude_of_origin",0],'
|
152 |
+
'PARAMETER["central_meridian",9],'
|
153 |
+
'PARAMETER["scale_factor",0.9996],'
|
154 |
+
'PARAMETER["false_easting",500000],'
|
155 |
+
'PARAMETER["false_northing",0],'
|
156 |
+
'UNIT["metre",1]]')
|
157 |
+
|
158 |
+
# Save the shapefile
|
159 |
+
w.close()
|