aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgdamms <damguillotin@gmail.com>2026-07-24 12:44:20 +0200
committergdamms <damguillotin@gmail.com>2026-07-24 12:44:20 +0200
commit77aed43a99a295f3202063edefcb7ce01e011958 (patch)
treee34a042a75cdb52e2d30afc11dc82660c22f36d5 /src
downloaddog-friendly-data-77aed43a99a295f3202063edefcb7ce01e011958.tar.gz
dog-friendly-data-77aed43a99a295f3202063edefcb7ce01e011958.zip
first commit
Diffstat (limited to 'src')
-rw-r--r--src/geojson2kml.py61
-rw-r--r--src/n2000.py96
-rw-r--r--src/rn.py81
-rw-r--r--src/split_kml.py73
-rw-r--r--src/test_export.py100
5 files changed, 411 insertions, 0 deletions
diff --git a/src/geojson2kml.py b/src/geojson2kml.py
new file mode 100644
index 0000000..b923d98
--- /dev/null
+++ b/src/geojson2kml.py
@@ -0,0 +1,61 @@
+import json
+import xml.etree.ElementTree as ET
+
+
+KML_NS = "http://www.opengis.net/kml/2.2"
+ET.register_namespace("", KML_NS)
+
+
+def geojson_to_kml(geojson: dict) -> ET.ElementTree[ET.Element]:
+ root = ET.Element(f"kml", xmlns=KML_NS)
+ document = ET.SubElement(root, f"Document")
+
+ ET.SubElement(document, f"name").text = geojson.get("name", "Unnamed")
+
+ for feature in geojson.get("features", []):
+ placemark = ET.SubElement(document, f"Placemark")
+
+ ET.SubElement(placemark, f"name").text = ""
+ ET.SubElement(placemark, f"description").text = ""
+
+ # TODO: Handle other geometry types if needed
+ if feature["geometry"]["type"] != "MultiPolygon":
+ print(f"Skipping feature with unsupported geometry type: {feature['geometry']['type']}")
+ continue
+
+ for polygon in feature["geometry"]["coordinates"][0]:
+ polygon_el = ET.SubElement(placemark, f"Polygon")
+
+ outer = ET.SubElement(polygon_el, f"outerBoundaryIs")
+ ring = ET.SubElement(outer, f"LinearRing")
+
+ coords = ET.SubElement(ring, f"coordinates")
+ coords.text = "\n".join(
+ f"{lon},{lat}"
+ for lon, lat in polygon
+ )
+
+ ET.indent(root)
+
+ return ET.ElementTree(root)
+
+
+def main():
+ import argparse
+
+ parser = argparse.ArgumentParser(description="Convert GeoJSON to KML")
+ parser.add_argument("input_file", help="Path to the input GeoJSON file")
+ parser.add_argument("-o", "--output_file", help="Path to the output KML file")
+ args = parser.parse_args()
+
+ with open(args.input_file, "r") as geojson_file:
+ geojson = json.load(geojson_file)
+
+ output_file = args.output_file or args.input_file.rsplit(".", 1)[0] + ".kml"
+
+ kml = geojson_to_kml(geojson)
+ kml.write(output_file, encoding="utf-8", xml_declaration=True)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/n2000.py b/src/n2000.py
new file mode 100644
index 0000000..357f744
--- /dev/null
+++ b/src/n2000.py
@@ -0,0 +1,96 @@
+import re
+import requests
+
+
+reponse = requests.get("https://www.natura2000.fr/carte-natura2000")
+regex = r"\"entity_id\":\"(\d+)\""
+matches = re.findall(regex, reponse.text)
+
+
+kml_file = open("natura_2000.kml", "w", encoding="utf-8")
+kml_file.write("""<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+ <name>Natura 2000</name>
+""")
+
+
+for i, match in enumerate(matches):
+ url = f"https://www.natura2000.fr/leaflet-ajax-popup/node/{match}/pop_up_n2000_field/und?_wrapper_format=drupal_ajax"
+ response = requests.get(url)
+
+ regex = r"href=\\u0022\\/site-natura\\/(.*?)\\u0022"
+ match = re.search(regex, response.text)
+
+ if not match:
+ print(f"No match found for entity_id {match}.")
+ continue
+
+ site_url = f"https://www.natura2000.fr/site-natura/{match.group(1)}"
+ response = requests.get(site_url)
+
+ regex = r"<title>(.*?) \| Natura 2000</title>"
+ match = re.search(regex, response.text)
+ if not match:
+ print(f"No name found for {site_url}.")
+ continue
+ name = match.group(1)
+
+ regex = r"\"type\":\"(multipolygon|polygon)\""
+ match = re.search(regex, response.text)
+ if not match:
+ print(f"No geometry type found for {site_url}.")
+ continue
+ geometry_type = match.group(1)
+
+ if geometry_type == "polygon":
+ regex = r"\"points\":(\[\[.*?\]\])"
+ elif geometry_type == "multipolygon":
+ regex = r"\"points\":(\[\[\[.*?\]\]\])"
+ else:
+ print(f"Unsupported geometry type {geometry_type} for {site_url}.")
+ continue
+
+ match = re.search(regex, response.text)
+ if not match:
+ print(f"No coordinates found for {site_url}.")
+ continue
+ polygons = eval(match.group(1))
+
+ if geometry_type == "polygon":
+ # Wrap single polygon in a list to treat it as multipolygon
+ polygons = [polygons]
+
+ kml_file.write(f"""
+ <Placemark>
+ <name>{name}</name>
+ <description>
+ {name}<br/>{site_url}
+ </description>
+ <MultiGeometry>
+ """)
+
+ for polygon in polygons:
+ kml_file.write("""
+ <Polygon>
+ <outerBoundaryIs>
+ <LinearRing>
+ <coordinates>
+ """)
+ for point in polygon[0]:
+ kml_file.write(f"{point['lon']},{point['lat']},0\n")
+ kml_file.write("""
+ </coordinates>
+ </LinearRing>
+ </outerBoundaryIs>
+ </Polygon>
+ """)
+ kml_file.write(f"""
+ </MultiGeometry>
+ </Placemark>
+ """)
+
+ print(f"Processed: {name} ({i+1: 5d}/{len(matches)})")
+
+kml_file.write(""" </Document>
+</kml>""")
diff --git a/src/rn.py b/src/rn.py
new file mode 100644
index 0000000..0c6589c
--- /dev/null
+++ b/src/rn.py
@@ -0,0 +1,81 @@
+import requests
+import re
+
+
+def get_coordinates_from_url(url):
+ response = requests.get(url)
+ regex = r"\[\[-?\d+\.\d+,-?\d+\.\d+\](?:,\[-?\d+\.\d+,-?\d+\.\d+\])+\]"
+ matche = re.search(regex, response.text)
+ if not matche:
+ return None
+ coordinates = eval(matche.group(0))
+ return coordinates
+
+
+response = requests.get("https://reserves-naturelles.org/reserves-naturelles/")
+regex = r"<li class=\"reserve\"[\s\S]*?</li>"
+matches = re.findall(regex, response.text)
+
+kml_file = open("reserves_naturelles.kml", "w", encoding="utf-8")
+kml_file.write("""<?xml version="1.0" encoding="UTF-8"?>
+<kml xmlns="http://www.opengis.net/kml/2.2">
+ <Document>
+ <name>RN</name>
+""")
+
+
+for i, match in enumerate(matches):
+
+ regex = r"title=\"([^\"]+)\""
+ full_name = re.search(regex, match)
+ if not full_name:
+ print(f"Full name not found in the match: {match}")
+ continue
+ full_name = full_name.group(1)
+
+ regex = r"class=\"rsv_nom\">([^<]+)<"
+ name = re.search(regex, match)
+ if not name:
+ print(f"Name not found in the match: {match}")
+ continue
+ name = name.group(1)
+
+ regex = r"href=\"([^\"]+)\""
+ url = re.search(regex, match)
+ if not url:
+ print(f"URL not found in the match: {match}")
+ continue
+ url = url.group(1)
+
+ coordinates = get_coordinates_from_url(url)
+ if not coordinates:
+ print(f"Coordinates not found for URL: {url}")
+ continue
+
+ kml_file.write(f"""
+ <Placemark>
+ <name>{name}</name>
+ <description>
+ {full_name}<br/>{url}
+ </description>
+ <Polygon>
+ <outerBoundaryIs>
+ <LinearRing>
+ <coordinates>
+""")
+ for coordinate in coordinates:
+ kml_file.write(f" {coordinate[0]},{coordinate[1]},0\n")
+ kml_file.write("""
+ </coordinates>
+ </LinearRing>
+ </outerBoundaryIs>
+ </Polygon>
+ </Placemark>
+""")
+
+ print(f"Processed: {name} ({i+1: 3d}/{len(matches)})")
+
+
+kml_file.write("""
+ </Document>
+</kml>""")
diff --git a/src/split_kml.py b/src/split_kml.py
new file mode 100644
index 0000000..931704b
--- /dev/null
+++ b/src/split_kml.py
@@ -0,0 +1,73 @@
+import copy
+import os
+import shutil
+import xml.etree.ElementTree as ET
+import pathlib
+import tempfile
+
+
+KML_NS = "http://www.opengis.net/kml/2.2"
+ET.register_namespace("", KML_NS)
+
+
+def split_kml(input_file: str, split_size: int = 5) -> list[str]:
+ input_path = pathlib.Path(input_file)
+ input_name = input_path.stem
+
+ kml = ET.parse(input_path)
+ root = kml.getroot()
+ prefix = f"{{{root.tag.split('}')[0][1:]}}}"
+ document = root.find(f"{prefix}Document")
+ if document is None:
+ raise ValueError("No Document element found in the KML file.")
+ placemarks = document.findall(f"{prefix}Placemark")
+
+ current_placemark = placemarks.pop(0)
+
+ split_files = []
+
+ new_root = copy.deepcopy(root)
+ new_document = new_root.find(f"{prefix}Document")
+ if new_document is None:
+ raise ValueError("No Document element found in the new KML file.")
+ for placemark in new_document.findall(f"{prefix}Placemark"):
+ new_document.remove(placemark)
+ new_document.append(copy.deepcopy(current_placemark))
+ current_tmp_file = tempfile.NamedTemporaryFile(suffix=".kml")
+ ET.ElementTree(new_root).write(current_tmp_file.name, encoding="utf-8", xml_declaration=True)
+
+ while placemarks:
+ current_placemark = placemarks.pop(0)
+
+ new_document.append(copy.deepcopy(current_placemark))
+
+ new_tmp_file = tempfile.NamedTemporaryFile(suffix=".kml")
+ ET.ElementTree(new_root).write(new_tmp_file.name, encoding="utf-8", xml_declaration=True)
+
+ if os.path.getsize(new_tmp_file.name) < split_size * 1024 * 1024:
+ current_tmp_file = new_tmp_file
+ else:
+ n = len(split_files) + 1
+ output_path = input_path.parent / f"{input_name}_{n}.kml"
+ shutil.copy(current_tmp_file.name, output_path)
+ split_files.append(str(output_path))
+
+ new_root = copy.deepcopy(root)
+ new_document = new_root.find(f"{prefix}Document")
+ if new_document is None:
+ raise ValueError("No Document element found in the new KML file.")
+ for placemark in new_document.findall(f"{prefix}Placemark"):
+ new_document.remove(placemark)
+ new_document.append(copy.deepcopy(current_placemark))
+
+ current_tmp_file = tempfile.NamedTemporaryFile(suffix=".kml")
+ ET.ElementTree(new_root).write(current_tmp_file.name, encoding="utf-8", xml_declaration=True)
+
+ n = len(split_files) + 1
+ output_path = input_path.parent / f"{input_name}_{n}.kml"
+ shutil.copy(current_tmp_file.name, output_path)
+ split_files.append(str(output_path))
+
+ return split_files
+
+
diff --git a/src/test_export.py b/src/test_export.py
new file mode 100644
index 0000000..8f5ff38
--- /dev/null
+++ b/src/test_export.py
@@ -0,0 +1,100 @@
+import re
+import pathlib
+
+
+def check_duplicate_names(kml_file: str, quiet: bool = False) -> dict:
+ """Check for duplicate names in the exported KML file.
+
+ Args:
+ kml_file (str): Path to the KML file exported from Google My Maps.
+ quiet (bool, optional): If True, suppress output. Defaults to False.
+ Returns:
+ dict: A dictionary with names as keys and their counts as values.
+ """
+ if not quiet:
+ print("\nChecking for duplicate names in the exported KML file...")
+
+ with open(kml_file, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ regex = r"<name>(<!\[CDATA\[)?(.*?)(\]\]>)?</name>"
+ matches = re.findall(regex, content)
+
+ names = {}
+ for match in matches:
+ name = match[1]
+ if name in names:
+ names[name] += 1
+ else:
+ names[name] = 1
+
+ if not quiet:
+ if any(count > 1 for count in names.values()):
+ print("Duplicate names found in the exported KML file:")
+ for name, count in names.items():
+ if count > 1:
+ print(f" - {name}: {count} times")
+ else:
+ print("No duplicate names found in the exported KML file.")
+
+ return names
+
+
+def check_missing_names(kml_file: str, kml_glob: str, quiet: bool = False) -> dict:
+ """Check for missing names in the exported KML file compared to other KML files.
+
+ Args:
+ kml_file (str): Path to the KML file exported from Google My Maps.
+ kml_glob (str): Glob pattern to match KML files to check against the exported KML file.
+ quiet (bool, optional): If True, suppress output. Defaults to False.
+ Returns:
+ { 'file_name': { 'missing': [list of missing names], 'total': total number of names in the file } }
+ """
+ if not quiet:
+ print("\nChecking for missing names in the exported KML file compared to other KML files...")
+
+ names = check_duplicate_names(kml_file, quiet=True)
+
+ missing_files = {}
+
+ for file in pathlib.Path().glob(kml_glob):
+ with open(file, "r", encoding="utf-8") as f:
+ content = f.read()
+
+ regex = r"<name>(<!\[CDATA\[)?(.*?)(\]\]>)?</name>"
+ matches = re.findall(regex, content)
+
+ current_names = set(match[1] for match in matches)
+
+ for name in current_names:
+ if not name in names:
+ if file.name not in missing_files:
+ missing_files[file.name] = {'missing': [], 'total': len(
+ current_names)-1} # because of the name of the document
+ missing_files[file.name]['missing'].append(name)
+
+ if not quiet:
+ if not missing_files:
+ print("All names in the KML files matching the glob pattern were found in the exported KML file.")
+ else:
+ print("Files with names not found in the exported KML file:")
+ for file, count in missing_files.items():
+ print(f" - {file}: missing {len(count['missing'])}/{count['total']}")
+ for name in count['missing']:
+ print(f" - {name}")
+
+ return missing_files
+
+
+if __name__ == "__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ description="Check for duplicate or missing names in the KML file exported from Google My Maps.")
+ parser.add_argument("kml_file", type=str, help="Path to the KML file exported from Google My Maps.")
+ parser.add_argument("kml_glob", type=str,
+ help="Glob pattern to match KML files to check against the exported KML file.")
+ args = parser.parse_args()
+
+ check_duplicate_names(args.kml_file)
+ check_missing_names(args.kml_file, args.kml_glob)