# The ID of the data source is the name of this file. Should be short, consists of one or two words. No space and special characters. Hyphen ("_") is allowed. The ID is for the program identify the data source.

# Data source name. Should be clear, meaningful, and human readable.
data_source_name = 'OpenStreetMap'

# provide a brief description (1 line) of the data source to inform AI whether need to use this data source.
# Need to contain critical information such as extent and period.
brief_description = '''
You can download the administrative boundaries, street networks, points of interest (POIs) from OpenStreetMap.
'''

# Put the technical requirements or details for the data source. One line for a requirement. No need to number them.
# Line by line.
# If the data source need s API key, put the key in a separate text file (e.g., "OpenWeather.keys") in the "Keys" directory. Note that the file name is the data source ID! The content in the file is like: OpenWeather_key = "XXX". The double quotation mark is needed. Then, in the first line of handbook use the key as "The OpenWeather API key is: {OpenWeather_key}". NEVER share the key in the handbook!
# Note that the placeholder of "{OpenWeather_key}" will be replaced by the real key in the .keys file.
handbook = '''
You may use the Overpass API, which is faster than OSMnx library.
If you need to download the administrative boundary of only one place from OpenStreetMap, please use a Python package named 'OSMnx' by this code line: `gdf = ox.geocode_to_gdf(place_name)`. This method is fast and recommended. You do not need to use Overpass and then parse the reply.
The overall workflow for fetching data within a place's boundary: 1) get the requested place's osm_id by `osm_id = ox.geocode_to_gdf(place_name)['osm_id'][0]`; 2) form the Overpass query. E.g., `f'relation({osm_id}); map_to_area->.rel; way(area.rel)[railway];'` 3) fetch and save the data.
The reason of using `ox.geocode_to_gdf()' to get the osm_id is that this function uses Nominatim API to convert the fuzzy place name into a polygon that most match the given place name, rather than exact matching, which may not return the requested polygon.
If you need to use a boundary to filter features in GeoPandas, this is the code: `gpd.sjoin(gdf, boundary, how='inner', op='within')`.
If you need to download multiple administrative boundaries at the same level, e.g., states or provinces, DO NOT use OSMnx because it is slow. You can use Overpass API. Example code: `area['ISO3166-1'='US'][admin_level=2]->.us;(relation(area.us)['admin_level'='4'];);out geom;`. Overpass API is  quicker and simpler; you only need to carefully set up the administrative level.
Only use OSMnx to obtain the place boundaries; do no use it to download networks or POIs as it is very slow! Instead, use Overpass Query (endpoint: https://overpass-api.de/api/interpreter).
If using Overpass API, you need to output the geometry, i.e., using `out geom;` in the query. The geometry can be accessed by `returned_json['elements']['geometry']`; the geomtry is a list of points as `{'lat': 30.5, 'lon': 114.2}`.
If you need to determine the requested area of some small cities or places, try not to use the 'admin_level' tag, because you may not know its admin_level correctly. E.g., `area[name=small_place]` is better than `relation[name=small_place][admin_level=6]`, level=6 may be wrong. DO NOT assume the admin_level which lower than 4.
Use GeoPandas, rather than `osgeo` Python package, to create vectors.
You need to create Python code to download and save the data. Another program will execute your code directly.
Put your reply into a Python code block, explanation or conversation can be Python comments at the beginning of the code block(enclosed by ```python and ```).
The download code is only in a function named 'download_data()'. The last line is to execute this function.
When downloading OSM data, no need to use 'building' tags if it is not asked for.
You need to keep most attributes of the downloaded data, such as place name, street name, road type, and level.
Throw an error if the program fails to download the data; no need to handle the exceptions.
If you need to convert the OpenStreetMap returned JSON to GeoJSON, you can add this line to the OverPass query: `item ::=::,::geom=geom(),_osm_type=type(), ::id=id();`. Note the converted GeoJSON may only contains polygons, no polygons.
This is a program for your reference; you can improve the data parsing but please keep the pipeline to form the Overpass queries (i.e., `f'relation(osm_id); map_to_area->.rel;`, do not use ``area(osm_id)), which has been manually verified: {code_example}
'''

# Note for add code example: using the placeholder as "{code_example}" in the handbook. LLM-Find program will replace the placeholder using the variables in this .toml file.

# The following code example will be used to replace the placeholder "{code_example}" in the above handbook string.
code_example = '''


# Below is a program to download the province boundaries of Cuba.
import geopandas as gpd
import pandas as pd
import osmnx as ox
import requests
import json
from shapely.ops import linemerge, unary_union, polygonize
from shapely.geometry import MultiLineString, Polygon, MultiPolygon, LineString
from shapely.ops import polygonize

def download_data():
    # Define Overpass API query to download province boundaries of Cuba
    # This Overpass query is good for multiple polygons.
    # `boundary=ox.place_to_gdf(place)` is to get the first polygon returned by the Nominatim.org API.
    osm_id = ox.geocode_to_gdf(place_name)['osm_id'][0] # get the osm_id then get the relation by osm_id.


    # IMPORTANT: keep the pipeline to form the Overpass query; it has been manually verified!
    # Note that area(osm_id) is not correct because it is not the native OpenStreetMap data structure.
    overpass_url = "https://overpass-api.de/api/interpreter"
    overpass_query = f"""
    [out:json];
    relation({osm_id});   // Note that it is 'relation()'!
    map_to_area->.rel;   // DO not forget this line!
    relation(area.rel)["admin_level"="4"];
    out geom;  // keep geom!
    """

    # if only request the boundary only, use `gdf = ox.geocode_to_gdf()` is the fastest way! You do not need to use Overpass and then parse the reply.

    # Send request to Overpass API
    response = requests.get(overpass_url, params={'data': overpass_query})
    response.raise_for_status()  # Automatically raises an error for bad status codes
    data = response.json()

    # Parse the JSON response
    property_list = []
    geometry_list = []

    for element in data['elements']:  # each province
        way_list = []
        outer_lines = []
        inner_lines = []

        for member in element.get('members', []):  # each way/polyline

            if 'geometry' in member:
                if member['type'] == 'way':
                    way_points = [(point['lon'], point['lat']) for point in member['geometry']]
                    line_string = LineString(way_points)
                    if member['role'] == 'outer':
                        outer_lines.append(line_string)

                    if member['role'] == 'inner':
                        inner_lines.append(line_string)

        # Create polygon. We use Multi-polygon to represent all polygons
        merged = linemerge([*outer_lines]) # merge LineStrings
        borders = unary_union(merged) # linestrings to a MultiLineString
        outer_polygons = list(polygonize(borders))
        outer_polyon = MultiPolygon(outer_polygons)

        if len(inner_lines) > 0:
            merged = linemerge([*inner_lines]) # merge LineStrings
            borders = unary_union(merged) # linestrings to a MultiLineString
            inner_polyons = list(polygonize(borders))
            inner_polyon = MultiPolygon(inner_polyons)
            final_polygon = outer_polyon.difference(inner_polyon)

        else:
            final_polygon = outer_polyon

        geometry_list.append(final_polygon)

        # extract the properties
        properties = {
            key: ', '.join(map(str, value)) if isinstance(value, list) else str(value)
            for key, value in element.items() if key not in {'geometry', 'members'}
        }
        property_list.append(properties)

    df = pd.DataFrame.from_dict(property_list)

    gdf = gpd.GeoDataFrame(df, geometry=geometry_list)
    gdf.crs = 'EPSG:4326'

    # # Save to GeoPackage
    output_file = r"E:\Cuba_Province_boundary.gpkg"
    gdf.to_file(output_file, layer='province_boundaries', driver='GPKG')

download_data()
'''