aboutsummaryrefslogtreecommitdiff
path: root/src/rn.py
blob: c78f1c2226bacf56586965e84edd86c892c3bc13 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import argparse
import json
import re
import xml.etree.ElementTree as ET

import requests

from kml_utils import add_placemark, add_polygon, new_document, write_kml

LIST_URL = "https://reserves-naturelles.org/reserves-naturelles/"


def get_coordinates_from_url(url: str) -> list[tuple[float, float]] | None:
    """Fetch a reserve page and extract its outer polygon ring.

    Args:
        url (str): URL of the reserve page.
    Returns:
        list[tuple[float, float]] | None: (longitude, latitude) points, or None if no
        coordinates could be found on the page.
    """
    response = requests.get(url)
    match = re.search(
        r"\[\[-?\d+\.\d+,-?\d+\.\d+\](?:,\[-?\d+\.\d+,-?\d+\.\d+\])+\]", response.text
    )
    if not match:
        return None
    return [tuple(point) for point in json.loads(match.group(0))]


def get_reserves(list_url: str = LIST_URL) -> list[dict]:
    """Fetch the reserves list page and extract each reserve's name and URL.

    Args:
        list_url (str, optional): URL of the reserves list page. Defaults to LIST_URL.
    Returns:
        list[dict]: One {"full_name", "name", "url"} entry per reserve found on the page.
    """
    response = requests.get(list_url)
    items = re.findall(r"<li class=\"reserve\"[\s\S]*?</li>", response.text)

    reserves = []
    for item in items:
        full_name_match = re.search(r"title=\"([^\"]+)\"", item)
        name_match = re.search(r"class=\"rsv_nom\">([^<]+)<", item)
        url_match = re.search(r"href=\"([^\"]+)\"", item)

        if not full_name_match or not name_match or not url_match:
            print(f"Skipping incomplete list entry: {item}")
            continue

        reserves.append(
            {
                "full_name": full_name_match.group(1),
                "name": name_match.group(1),
                "url": url_match.group(1),
            }
        )

    return reserves


def scrape_reserves(list_url: str = LIST_URL) -> ET.Element:
    """Scrape reserves-naturelles.org and build a KML tree with one Placemark per reserve.

    Args:
        list_url (str, optional): URL of the reserves list page. Defaults to LIST_URL.
    Returns:
        ET.Element: The root <kml> element.
    """
    root, document = new_document("RN")

    reserves = get_reserves(list_url)
    for i, reserve in enumerate(reserves):
        coordinates = get_coordinates_from_url(reserve["url"])
        if not coordinates:
            print(f"No coordinates found for URL: {reserve['url']}")
            continue

        placemark = add_placemark(
            document,
            name=reserve["name"],
            description=f"{reserve['full_name']}<br/>{reserve['url']}",
        )
        add_polygon(placemark, coordinates)

        print(f"Processed: {reserve['name']} ({i + 1:3d}/{len(reserves)})")

    return root


def main():
    parser = argparse.ArgumentParser(description="Scrape reserves-naturelles.org and export reserves to KML")
    parser.add_argument("-o", "--output", default="reserves_naturelles.kml", help="Path to the output KML file")
    args = parser.parse_args()

    root = scrape_reserves()
    write_kml(root, args.output)


if __name__ == "__main__":
    main()