File size: 3,444 Bytes
0c88eb4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import ibis
from ibis import _

conn = ibis.duckdb.connect("tmp3", extensions=["spatial"])
# ca_parquet = "https://data.source.coop/cboettig/ca30x30/ca_areas.parquet"
# or use local copy:
ca_parquet = "ca_areas.parquet"


# negative buffer to account for overlapping boundaries. 
buffer = -30 #30m buffer 

tbl = (
    conn.read_parquet(ca_parquet)
    .cast({"SHAPE": "geometry"})
    .rename(geom = "SHAPE")
    .filter(_.reGAP < 3) # only gap 1 and 2 count towards 30x30
)

# polygons with release_year 2024 are a superset of release_year 2023. 
# use anti_join to isolate the objects that are in release_year 2024 but not release_year 2023 (aka newly established). 
tbl_2023 = tbl.filter(_.Release_Year == 2023).mutate(geom=_.geom.buffer(buffer)) 
tbl_2024 = tbl.filter(_.Release_Year == 2024)
intersects = tbl_2024.anti_join(tbl_2023, _.geom.intersects(tbl_2023.geom))

new2024 = intersects.select("OBJECTID").mutate(established = 2024) # saving IDs to join on

ca = (conn
      .read_parquet(ca_parquet)
      .cast({"SHAPE": "geometry"})
      .mutate(area = _.SHAPE.area())
      .filter(_.Release_Year == 2024) # having both 2023 and 2024 is redudant since 2024 is the superset.
      .left_join(new2024, "OBJECTID") # newly established 2024 polygons 
      .mutate(established=_.established.fill_null(2023)) 
      .mutate(geom = _.SHAPE.convert("epsg:3310","epsg:4326"))
      .rename(name = "cpad_PARK_NAME", access_type = "cpad_ACCESS_TYP", manager = "cpad_MNG_AGENCY",
              manager_type = "cpad_MNG_AG_LEV", id = "OBJECTID", type = "TYPE")
      .mutate(manager = _.manager.substitute({"": "Unknown"})) 
      .mutate(manager_type = _.manager_type.substitute({"": "Unknown"}))
      .mutate(access_type = _.access_type.substitute({"": "Unknown Access"}))
      .mutate(name = _.name.substitute({"": "Unknown"}))
      .select(_.established, _.reGAP, _.name, _.access_type, _.manager, _.manager_type,
              _.Easement, _.Acres, _.id, _.type, _.geom)
     )

ca2024 = ca.execute()

ca2024.to_parquet("ca2024-30m.parquet")

ca2024.to_file("ca2024-30m.geojson") # tippecanoe can't parse geoparquet :-(


## Upload to Huggingface
# https://huggingface.co/datasets/boettiger-lab/ca-30x30/

from huggingface_hub import HfApi, login
import streamlit as st
login(st.secrets["HF_TOKEN"])
api = HfApi()

def hf_upload(file):
    info = api.upload_file(
            path_or_fileobj=file,
            path_in_repo=file,
            repo_id="boettiger-lab/ca-30x30",
            repo_type="dataset",
        )
hf_upload("ca2024-30m.parquet")



import subprocess
import os

def generate_pmtiles(input_file, output_file, max_zoom=12):
    # Ensure Tippecanoe is installed
    if subprocess.call(["which", "tippecanoe"], stdout=subprocess.DEVNULL) != 0:
        raise RuntimeError("Tippecanoe is not installed or not in PATH")

    # Construct the Tippecanoe command
    command = [
        "tippecanoe",
        "-o", output_file,
        "-z", str(max_zoom),
        "--drop-densest-as-needed",
        "--extend-zooms-if-still-dropping",
        "--force",
        input_file
    ]

    # Run Tippecanoe
    try:
        subprocess.run(command, check=True)
        print(f"Successfully generated PMTiles file: {output_file}")
    except subprocess.CalledProcessError as e:
        print(f"Error running Tippecanoe: {e}")

generate_pmtiles("ca2024-30m.geojson", "ca2024-30m-tippe.pmtiles")
hf_upload("ca2024-30m-tippe.pmtiles")