Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion public/common/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2440,6 +2440,64 @@ def from_path(path: str):

return obj


def parse_date(date_str: str, is_end: bool = False):
"""
Parse a partial or full date string and return ISO8601 with UTC 'Z'.
Handles year-only, year-month, and full dates.
"""
from datetime import timedelta
import pandas as pd

# Handle partial dates
if len(date_str) == 4: # Year only
date_str = f"{date_str}-12-31" if is_end else f"{date_str}-01-01"
elif len(date_str) == 7: # Year-month
y, m = date_str.split('-')
if is_end:
last_day = pd.Timestamp(f"{y}-{m}-01").days_in_month
date_str = f"{y}-{m}-{last_day:02d}"
else:
date_str = f"{y}-{m}-01"

# Parse with pandas (handles most formats automatically)
dt = pd.to_datetime(date_str, errors='coerce', utc=True)
if pd.isna(dt):
raise ValueError(f"Could not parse date: {date_str}")

# Set end_date-of-day if needed
if is_end and dt.hour == 0 and dt.minute == 0 and dt.second == 0:
dt = dt.replace(hour=23, minute=59, second=59)

return dt


def split_datetime(start_date: str, end_date: str, n: int = 10, gap_milliseconds:int = 1000):
"""
Split start_date/end_date dates into n chunks, returning list of (start_iso, end_iso) strings.
"""
from datetime import timedelta
start_dt = parse_date(start_date, is_end=False)
end_dt = parse_date(end_date, is_end=True)

total_seconds = (end_dt - start_dt).total_seconds()
chunk_seconds = total_seconds / n

chunks = []
for i in range(n):
chunk_start = start_dt + timedelta(seconds=i * chunk_seconds)
chunk_end = start_dt + timedelta(seconds=(i + 1) * chunk_seconds)
if i < n - 1:
chunk_end = chunk_end - timedelta(milliseconds=gap_milliseconds)

chunks.append((
chunk_start.strftime("%Y-%m-%dT%H:%M:%SZ"),
chunk_end.strftime("%Y-%m-%dT%H:%M:%SZ")
))

return chunks


def split_gdf(gdf, n: int = None, zoom: int = None, clip: bool = False, return_type: str = "gdf"):
import geopandas as gpd
import pickle
Expand Down Expand Up @@ -3669,7 +3727,7 @@ def estimate_zoom(bounds, target_num_tiles=1):


def get_tiles(
bounds=None, target_num_tiles=1, zoom=None, max_tile_recursion=6, as_gdf=True, clip=False, verbose=False
bounds=None, target_num_tiles=1, zoom=None, max_tile_recursion=6, as_gdf=True, clip=False, add_bounds=False, verbose=False
):
bounds = to_gdf(bounds)
import mercantile
Expand Down Expand Up @@ -3724,6 +3782,8 @@ def get_tiles(
if verbose:
print(f"Generated {len(gdf)} tiles at zoom level {zoom_level}")

if add_bounds:
gdf["bounds"] = gdf["geometry"].map(lambda x: list(x.bounds))
if clip:
return gdf.clip(bounds) if as_gdf else gdf[["x", "y", "z"]].values
else:
Expand Down