Skip to content
Merged
Show file tree
Hide file tree
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
120 changes: 95 additions & 25 deletions src/rtgs_lab_tools/agricultural_modeling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,101 @@
Migrated from rtgsET library.
"""

from .crop_parameters import get_crop_names, get_crop_parameters, get_crop_status
from .distance_speed import (
degrees_to_radians,
feet_to_meters,
meters_per_second_to_miles_per_hour,
miles_per_hour_to_meters_per_second,
)
from .evapotranspiration import (
calculate_reference_et,
get_required_columns,
validate_input_data,
)
from .growing_degree_days import (
calculate_corn_heat_units,
calculate_gdd_modified,
calculate_gdd_original,
)
from .temperature import celsius_to_fahrenheit, fahrenheit_to_celsius
from .weather_api import (
check_missing_dates,
date_chunks,
fetch_weather_data,
validate_coordinates,
validate_date_range,
)
# Heavy dependencies are imported lazily when needed
# This prevents long load times for simple commands like 'rtgs --help'


def __getattr__(name):
"""Lazy loading of heavy dependencies"""
# Crop parameters
if name == "get_crop_names":
from .crop_parameters import get_crop_names

return get_crop_names
elif name == "get_crop_parameters":
from .crop_parameters import get_crop_parameters

return get_crop_parameters
elif name == "get_crop_status":
from .crop_parameters import get_crop_status

return get_crop_status
# Distance and speed conversions
elif name == "degrees_to_radians":
from .distance_speed import degrees_to_radians

return degrees_to_radians
elif name == "feet_to_meters":
from .distance_speed import feet_to_meters

return feet_to_meters
elif name == "meters_per_second_to_miles_per_hour":
from .distance_speed import meters_per_second_to_miles_per_hour

return meters_per_second_to_miles_per_hour
elif name == "miles_per_hour_to_meters_per_second":
from .distance_speed import miles_per_hour_to_meters_per_second

return miles_per_hour_to_meters_per_second
# Evapotranspiration (heavy numpy/pandas dependencies)
elif name == "calculate_reference_et":
from .evapotranspiration import calculate_reference_et

return calculate_reference_et
elif name == "get_required_columns":
from .evapotranspiration import get_required_columns

return get_required_columns
elif name == "validate_input_data":
from .evapotranspiration import validate_input_data

return validate_input_data
# Growing degree days
elif name == "calculate_corn_heat_units":
from .growing_degree_days import calculate_corn_heat_units

return calculate_corn_heat_units
elif name == "calculate_gdd_modified":
from .growing_degree_days import calculate_gdd_modified

return calculate_gdd_modified
elif name == "calculate_gdd_original":
from .growing_degree_days import calculate_gdd_original

return calculate_gdd_original
# Temperature conversions (lightweight)
elif name == "celsius_to_fahrenheit":
from .temperature import celsius_to_fahrenheit

return celsius_to_fahrenheit
elif name == "fahrenheit_to_celsius":
from .temperature import fahrenheit_to_celsius

return fahrenheit_to_celsius
# Weather API utilities (requests dependency)
elif name == "check_missing_dates":
from .weather_api import check_missing_dates

return check_missing_dates
elif name == "date_chunks":
from .weather_api import date_chunks

return date_chunks
elif name == "fetch_weather_data":
from .weather_api import fetch_weather_data

return fetch_weather_data
elif name == "validate_coordinates":
from .weather_api import validate_coordinates

return validate_coordinates
elif name == "validate_date_range":
from .weather_api import validate_date_range

return validate_date_range
else:
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


__all__ = [
# Temperature conversions
Expand Down
14 changes: 13 additions & 1 deletion src/rtgs_lab_tools/data_parser/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
"""Universal JSON packet parser for historical packets based on device JSON schema tool for RTGS Lab Tools."""

from .core import parse_gems_data
# Heavy dependencies are imported lazily when needed
# This prevents long load times for simple commands like 'rtgs --help'


def __getattr__(name):
"""Lazy loading of heavy dependencies"""
if name == "parse_gems_data":
from .core import parse_gems_data

return parse_gems_data
else:
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


__all__ = ["parse_gems_data"]
24 changes: 21 additions & 3 deletions src/rtgs_lab_tools/device_configuration/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
"""Device configuration management tools for RTGS Lab Tools."""

from .cli import device_configuration_cli
from .particle_client import ParticleClient
from .update_configuration import ParticleConfigUpdater
# Heavy dependencies are imported lazily when needed
# This prevents long load times for simple commands like 'rtgs --help'


def __getattr__(name):
"""Lazy loading of heavy dependencies"""
if name == "device_configuration_cli":
from .cli import device_configuration_cli

return device_configuration_cli
elif name == "ParticleClient":
from .particle_client import ParticleClient

return ParticleClient
elif name == "ParticleConfigUpdater":
from .update_configuration import ParticleConfigUpdater

return ParticleConfigUpdater
else:
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


__all__ = ["device_configuration_cli", "ParticleConfigUpdater", "ParticleClient"]
55 changes: 51 additions & 4 deletions src/rtgs_lab_tools/gridded_data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,57 @@
"""Gridded climate data access tools for RTGS Lab Tools."""

# Import sources for immediate availability
from .utils import sources
# Only import lightweight utilities immediately
from .utils import load_roi_json, sources

# Heavy dependencies are imported lazily when needed
# This prevents long load times for simple commands like 'rtgs --help'


def __getattr__(name):
"""Lazy loading of heavy dependencies"""
if name == "init_ee":
from .gee import init_ee

return init_ee
elif name == "list_GEE_vars":
from .gee import list_GEE_vars

return list_GEE_vars
elif name == "load_roi":
from .gee import load_roi

return load_roi
elif name == "search_images":
from .gee import search_images

return search_images
elif name == "download_GEE_point":
from .gee import download_GEE_point

return download_GEE_point
elif name == "download_GEE_raster":
from .gee import download_GEE_raster

return download_GEE_raster
elif name == "quick_search":
from .planet import quick_search

return quick_search
elif name == "download_scenes":
from .planet import download_scenes

return download_scenes
elif name == "download_clipped_scenes":
from .planet import download_clipped_scenes

return download_clipped_scenes
elif name == "extract_time_series":
from .processors import extract_time_series

return extract_time_series
else:
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

# Empty __init__.py to avoid loading heavy dependencies at import time
# Functions are imported directly from submodules when needed

__all__ = [
"download_GEE_raster",
Expand Down
24 changes: 12 additions & 12 deletions src/rtgs_lab_tools/gridded_data/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,19 @@ def download_clipped_scenes(
cli_ctx.setup("download-scenes", verbose, log_file, no_postgres_log)

try:
from ..gridded_data import download_clipped_scenes, load_roi
from ..gridded_data import download_clipped_scenes, load_roi_json

# Load ROI from file
# Load ROI from file (Planet Labs doesn't use Earth Engine)
if roi:
roi = load_roi(roi).getInfo()
roi = load_roi_json(roi)

download_clipped_scenes(
source=source,
meta_file=meta_file,
roi=roi,
start_date=start_date,
end_date=end_date,
clouds=clouds,
clouds=int(clouds) if clouds else None,
out_dir=out_dir,
)

Expand Down Expand Up @@ -141,19 +141,19 @@ def download_scenes(
cli_ctx.setup("download-scenes", verbose, log_file, no_postgres_log)

try:
from ..gridded_data import download_scenes, load_roi
from ..gridded_data import download_scenes, load_roi_json

# Load ROI from file
# Load ROI from file (Planet Labs doesn't use Earth Engine)
if roi:
roi = load_roi(roi).getInfo()
roi = load_roi_json(roi)

download_scenes(
source=source,
meta_file=meta_file,
roi=roi,
start_date=start_date,
end_date=end_date,
clouds=clouds,
clouds=int(clouds) if clouds else None,
out_dir=out_dir,
)

Expand Down Expand Up @@ -211,19 +211,19 @@ def planet_search(
cli_ctx.setup("planet-search", verbose, log_file, no_postgres_log)

try:
from ..gridded_data import load_roi, quick_search
from ..gridded_data import load_roi_json, quick_search

# Load ROI from file
# Load ROI from file (Planet Labs doesn't use Earth Engine)
if roi:
roi = load_roi(roi).getInfo()
roi = load_roi_json(roi)
# print(roi_bounds)

quick_search(
source=source,
roi=roi,
start_date=start_date,
end_date=end_date,
clouds=clouds,
clouds=int(clouds) if clouds else None,
out_dir=out_dir,
)

Expand Down
5 changes: 5 additions & 0 deletions src/rtgs_lab_tools/gridded_data/gee.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,10 @@ def download_GEE_point(name, source, bands, roi, start_date, end_date, out_dir):
size = collection.size().getInfo()
print(f"Found {size} images to process")

if size == 0:
print("No images found for the specified date range and location")
return None

scale = collection.first().select(bands[0]).projection().nominalScale().getInfo()

os.makedirs(out_dir, exist_ok=True)
Expand Down Expand Up @@ -245,6 +249,7 @@ def clip_img(img):

roi = roi.geometry()

qa_band = None # Initialize qa_band for climate datasets
if name in qa_bands.keys():
qa_band = qa_bands[name]

Expand Down
11 changes: 11 additions & 0 deletions src/rtgs_lab_tools/gridded_data/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,14 @@
"MYD": "state_1km", # Aqua
"VIIRS": "QF1",
}


def load_roi_json(path):
"""Load ROI as plain JSON for Planet Labs (no Earth Engine)"""
import json

with open(path) as f:
roi_geom = json.load(f)

# Convert to format Planet Labs functions expect
return {"features": [{"geometry": roi_geom}]}
45 changes: 37 additions & 8 deletions src/rtgs_lab_tools/sensing_data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,42 @@
"""Sensing data tools for RTGS Lab Tools."""

from .data_extractor import (
extract_data,
get_nodes_for_project,
get_raw_data,
list_available_projects,
list_projects,
)
from .file_operations import create_zip_archive, save_data
# Heavy dependencies are imported lazily when needed
# This prevents long load times for simple commands like 'rtgs --help'


def __getattr__(name):
"""Lazy loading of heavy dependencies"""
if name == "extract_data":
from .data_extractor import extract_data

return extract_data
elif name == "get_nodes_for_project":
from .data_extractor import get_nodes_for_project

return get_nodes_for_project
elif name == "get_raw_data":
from .data_extractor import get_raw_data

return get_raw_data
elif name == "list_available_projects":
from .data_extractor import list_available_projects

return list_available_projects
elif name == "list_projects":
from .data_extractor import list_projects

return list_projects
elif name == "create_zip_archive":
from .file_operations import create_zip_archive

return create_zip_archive
elif name == "save_data":
from .file_operations import save_data

return save_data
else:
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")


__all__ = [
"extract_data",
Expand Down
Loading
Loading