IO Tasks¶
ecoscope.platform.tasks.io ¶
Attributes¶
SpatialFeaturesConfig
module-attribute
¶
SpatialFeaturesConfig = LocalFileSpatialFeatures | RemoteFileSpatialFeatures | EarthRangerSpatialFeatures
Classes¶
EarthRangerSpatialFeatures ¶
Bases: BaseModel
Attributes¶
data_source
instance-attribute
¶
data_source: Annotated[str, Field(title='Data Source', description='Select one of your configured EarthRanger data sources.')]
model_config
class-attribute
instance-attribute
¶
source
class-attribute
instance-attribute
¶
spatial_features_group_name
instance-attribute
¶
spatial_features_group_name: Annotated[str, Field(description='Name of the spatial features group in EarthRanger')]
LocalFileSpatialFeatures ¶
Bases: BaseModel
Attributes¶
file_path
instance-attribute
¶
file_path: Annotated[str, Field(description='Path to geoparquet (.parquet) or geopackage (.gpkg) file')]
layer
class-attribute
instance-attribute
¶
layer: Annotated[str | SkipJsonSchema[None], AdvancedField(default=None, description='Layer name (only applicable to geopackage files)')] = None
model_config
class-attribute
instance-attribute
¶
name_column
instance-attribute
¶
source
class-attribute
instance-attribute
¶
Methods:¶
validate_file_extension ¶
validate_file_extension() -> LocalFileSpatialFeatures
Source code in ecoscope/platform/tasks/io/_spatial_features.py
RemoteFileSpatialFeatures ¶
Bases: BaseModel
Attributes¶
layer
class-attribute
instance-attribute
¶
layer: Annotated[str | SkipJsonSchema[None], AdvancedField(default=None, description='Layer name (only applicable for geopackage files)')] = None
model_config
class-attribute
instance-attribute
¶
name_column
instance-attribute
¶
source
class-attribute
instance-attribute
¶
url
instance-attribute
¶
Methods:¶
validate_url_extension ¶
validate_url_extension() -> RemoteFileSpatialFeatures
Source code in ecoscope/platform/tasks/io/_spatial_features.py
Functions:¶
calculate_ndvi_range ¶
calculate_ndvi_range(client: EarthEngineClient, roi: AnyGeoDataFrame, time_range: Annotated[TimeRange, Field(description='Current time range for trend analysis')], image_size: Annotated[int, Field(description='Number of historical satellite images to fetch. Set to a large value to retrieve all available historical data. Only used when baseline_time_range is not provided.')] = 1000000000, baseline_time_range: Annotated[Optional[BaselineTimeRange], Field(description='Explicit historical baseline and current time range. When provided, overrides time_range and image_size.')] = None, ndvi_method: Annotated[NDVIMethod, Field(description="Method to obtain NDVI values. 'MODIS MYD13A1 16-Day Composite': Uses pre-calculated NDVI from 16-day composites. Provides quality-filtered 'best pixel' values at 500m resolution with ~0.025 accuracy. Better for phenology studies but may saturate in dense canopies. 'MODIS MCD43A4 Daily NBAR': Uses daily nadir BRDF-adjusted reflectance. Computes NDVI from NIR/Red bands with view-angle correction for consistent measurements. Higher temporal resolution but more susceptible to cloud gaps.")] = 'MODIS MYD13A1 16-Day Composite', grouping_unit: Annotated[GroupingUnit, Field(description="Temporal unit for grouping historical data when calculating statistics. 'month': Compare against same calendar month (1-12). 'week': Compare against same ISO week number (1-53). 'day_of_year': Compare against same day of year (1-366). 'modis_16_day': Compare against same MODIS 16-day composite period (0-22).")] = 'month') -> AnyDataFrame
Calculate NDVI values over an ROI using Earth Engine Image Collections.
Compares current NDVI values against historical statistics (min, max, mean) grouped by a configurable temporal unit to identify trends and anomalies.
Supports two modes
- Mode A (default): Uses time_range + image_size to fetch N historical images before the current period. Deprecated; will be removed in a future release.
- Mode B: Uses baseline_time_range with explicit historical_start, current_start, current_end for precise control over both periods.
Functions:
| Name | Description |
|---|---|
- MODIS MYD13A1 16-Day Composite |
Uses pre-calculated NDVI band |
- MODIS MCD43A4 Daily NBAR |
Computes NDVI from (NIR - Red) / (NIR + Red) |
Grouping
- month: Compare against same calendar month across historical years
- week: Compare against same ISO week number across historical years
- day_of_year: Compare against same day of year across historical years
- modis_16_day: Compare against same MODIS 16-day composite period (0-22) across historical years
Source code in ecoscope/platform/tasks/io/_earthengine.py
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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
determine_season_windows ¶
determine_season_windows(client: EarthEngineClient, roi: AnyGeoDataFrame, time_range: Annotated[TimeRange, Field(description='Time range filter')]) -> AnyDataFrame
Determine wet/dry season windows from NDVI values over an ROI.
Computes standardized NDVI values using MODIS MCD43A4 daily NBAR data, then identifies seasonal transition points to classify time windows as "dry" or "wet" seasons.
Source code in ecoscope/platform/tasks/io/_earthengine.py
download_roi ¶
download_roi(url: Annotated[str, Field(description='The path to ROI gpkg file')], roi_column: Annotated[str | None, Field(description='The column name of the ROI name')] = 'name', roi_name: Annotated[str | None, Field(description='The ROI name')] = None, layer_name: Annotated[str | None, Field(description='The layer name')] = None) -> AnyGeoDataFrame
Download ROI from a URL.
Source code in ecoscope/platform/tasks/io/_downloader.py
get_analysis_field_from_event_details ¶
get_analysis_field_label_from_event_details ¶
get_analysis_field_unit_from_event_details ¶
get_category_field_from_event_details ¶
get_category_field_label_from_event_details ¶
get_choices_from_v2_event_type ¶
get_choices_from_v2_event_type(client: EarthRangerClient, event_type: SingleEventTypeAnnotation, choice_field: Annotated[str | SkipJsonSchema[None], Field(description='The choice field to lookup values from')]) -> dict[str, str]
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_event_type_display_names_from_events ¶
get_event_type_display_names_from_events(client: EarthRangerClient, events_gdf: EventGDF, append_category_names: AppendCategorySelectionAnnotation = 'duplicates') -> EventsWithDisplayNamesGDF
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_event_type_from_event_details ¶
get_events ¶
get_events(client: EarthRangerClient, time_range: TimeRangeAnnotation, event_types: EventTypesAnnotation, event_columns: EventColumnsAnnotation = None, include_null_geometry: IncludeNullGeometryAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True, include_details: IncludeDetailsAnnotation = False, include_updates: IncludeUpdatesAnnotation = False, include_related_events: IncludeRelatedEventsAnnotation = False, include_display_values: IncludeDisplayValuesAnnotation = False, force_point_geometry: ForcePointGeometryAnnotation = True) -> EventGDF | EventsWithDisplayNamesGDF | EmptyDataFrame
Get events.
Source code in ecoscope/platform/tasks/io/_earthranger.py
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 | |
get_events_from_combined_params ¶
get_events_from_combined_params(combined_params: CombinedEventsAndDetailsParams) -> EventGDF | EventsWithDisplayNamesGDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_events_from_smart ¶
get_events_from_smart(client: SmartClient, time_range: Annotated[TimeRange, Field(description='Time range filter')], ca_uuid: Annotated[str, Field(description='Conservation Area UUID', title='Conservation Area UUID')], language_uuid: Annotated[str, Field(description='Language UUID', title='Language UUID')]) -> EventGDF | EmptyDataFrame
Get events.
Source code in ecoscope/platform/tasks/io/_smart.py
get_fields_from_event_type_schema ¶
get_fields_from_event_type_schema(client: EarthRangerClient, event_type: SingleEventTypeAnnotation) -> dict[str, str]
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrol_events ¶
get_patrol_events(client: EarthRangerClient, time_range: TimeRangeAnnotation, patrol_types: PatrolTypesAnnotation, event_types: EventTypesAnnotation, status: PatrolStatusAnnotation = None, include_null_geometry: IncludeNullGeometryAnnotation = True, truncate_to_time_range: TruncateToTimeRangeAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True, sub_page_size: SubPageSizeAnnotation = 100, include_display_values: IncludeDisplayValuesAnnotation = False, patrols_overlap_daterange: PatrolsOverlapDateRangeAnnotation = True) -> EventGDF | EventsWithDisplayNamesGDF | EmptyDataFrame
Get events from patrols.
Source code in ecoscope/platform/tasks/io/_earthranger.py
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | |
get_patrol_events_from_combined_params ¶
get_patrol_events_from_combined_params(combined_params: CombinedPatrolAndEventsParams) -> EventGDF | EventsWithDisplayNamesGDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrol_observations ¶
get_patrol_observations(client: EarthRangerClient, time_range: TimeRangeAnnotation, patrol_types: PatrolTypesAnnotation, status: PatrolStatusAnnotation = None, include_patrol_details: IncludePatrolDetailsAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True, sub_page_size: SubPageSizeAnnotation = 100, patrols_overlap_daterange: PatrolsOverlapDateRangeAnnotation = True) -> PatrolObservationsGDF | EmptyDataFrame
Get observations for a patrol type from EarthRanger.
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrol_observations_from_combined_params ¶
get_patrol_observations_from_combined_params(combined_params: CombinedPatrolAndEventsParams) -> PatrolObservationsGDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrol_observations_from_patrols_df ¶
get_patrol_observations_from_patrols_df(client: EarthRangerClient, patrols_df: PatrolsDF, include_patrol_details: IncludePatrolDetailsAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True, sub_page_size: SubPageSizeAnnotation = 100) -> PatrolObservationsGDF | EmptyDataFrame
Get observations for a patrol type from EarthRanger.
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrol_observations_from_patrols_df_and_combined_params ¶
get_patrol_observations_from_patrols_df_and_combined_params(patrols_df: PatrolsDF, combined_params: CombinedPatrolAndEventsParams) -> PatrolObservationsGDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrol_observations_from_smart ¶
get_patrol_observations_from_smart(client: SmartClient, time_range: Annotated[TimeRange, Field(description='Time range filter')], ca_uuid: Annotated[str, Field(description='Conservation Area UUID', title='Conservation Area UUID')], language_uuid: Annotated[str, Field(description='Language UUID', title='Language UUID')], patrol_mandate: Annotated[str | SkipJsonSchema[None], AdvancedField(default=None, description='Patrol Mandate', title='Patrol Mandate')] = None, patrol_transport: Annotated[str | SkipJsonSchema[None], AdvancedField(default=None, description='Patrol Transport', title='Patrol Transport')] = None) -> PatrolObservationsGDF | EmptyDataFrame
Get observations for a patrol type from Smart.
Source code in ecoscope/platform/tasks/io/_smart.py
get_patrols ¶
get_patrols(client: EarthRangerClient, time_range: TimeRangeAnnotation, patrol_types: PatrolTypesAnnotation, status: PatrolStatusAnnotation = None, raise_on_empty: RaiseOnEmptyAnnotation = True, sub_page_size: SubPageSizeAnnotation = 100, patrols_overlap_daterange: PatrolsOverlapDateRangeAnnotation = True) -> PatrolsDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_patrols_from_combined_params ¶
get_patrols_from_combined_params(combined_params: CombinedPatrolAndEventsParams) -> PatrolsDF | EmptyDataFrame
get_spatial_features_group ¶
get_spatial_features_group(client: EarthRangerClient, spatial_features_group_name: Annotated[str, Field(description='The name of the group to fetch')]) -> RegionsGDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
get_subjectgroup_observations ¶
get_subjectgroup_observations(client: EarthRangerClient, subject_group_name: Annotated[str, Field(description='⚠️ The use of a group with mixed subtypes could lead to unexpected results')], time_range: Annotated[TimeRange, Field(description='Time range filter')], raise_on_empty: Annotated[bool, AdvancedField(default=True, description='Whether or not to abort the workflow if no data is returned from EarthRanger')] = True, include_details: Annotated[bool, AdvancedField(default=False, title='Include Observation Details', description='Whether or not to include observation details')] = False, include_subjectsource_details: Annotated[bool, AdvancedField(default=False, title='Include Subject Source Details', description='Whether or not to include subject source details')] = False, filter: Annotated[ExclusionFilter, AdvancedField(default=clean, description='Filter observations based on exclusion flags.')] = 'clean') -> SubjectGroupObservationsGDF | EmptyDataFrame
Get observations for a subject group from EarthRanger.
Source code in ecoscope/platform/tasks/io/_earthranger.py
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 | |
load_spatial_features_group ¶
load_spatial_features_group(config: Annotated[SpatialFeaturesConfig, Field(title='Spatial Feature Data Source')]) -> RegionsGDF | EmptyDataFrame
Load spatial feature group from local file, remote URL, or EarthRanger.
Supports geoparquet and geopackage files. All geometries are converted to CRS 4326 and filtered to polygons only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
Annotated[SpatialFeaturesConfig, Field(title='Spatial Feature Data Source')]
|
Configuration specifying the source type and parameters. |
required |
Returns:
| Type | Description |
|---|---|
RegionsGDF | EmptyDataFrame
|
RegionsGDF with columns: pk, name, short_name, feature_type, geometry, metadata. |
RegionsGDF | EmptyDataFrame
|
Returns EmptyDataFrame if no polygons found. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If file format is invalid or required columns are missing. |
Source code in ecoscope/platform/tasks/io/_spatial_features.py
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 | |
persist_df ¶
persist_df(df: Annotated[AnyDataFrame, Field(description='Dataframe to persist')], root_path: Annotated[str, Field(description='Root path to persist text to')], filename: Annotated[str | None, Field(description=' Optional filename to persist text to within the `root_path`.\n If not provided, a filename will be generated based on a hash of the df content.\n ')] = None, filetype: Annotated[FileType, Field(description='The output format')] = 'csv') -> Annotated[str, Field(description='Path to persisted data')]
Persist dataframe to a file or cloud storage object.
Source code in ecoscope/platform/tasks/io/_persist.py
persist_json ¶
persist_json(data: Annotated[dict[str, Any] | BaseModel, Field(description='JSON-serializable dict or pydantic model to persist')], root_path: Annotated[str, Field(description='Root path to persist data to')], filename: Annotated[str | None, Field(description=" Optional filename (within `root_path`). If not provided, a filename will\n be generated from a hash of the serialized JSON content. The `.json`\n extension is appended automatically when one isn't already present.\n ", exclude=True)] = None, filename_suffix: Annotated[str | None, Field(description='If present, appended to the filename stem before the extension.', exclude=True)] = None) -> Annotated[str, Field(description='Path to persisted JSON')]
Serialize JSON-shaped data and persist to a file or cloud storage object.
Source code in ecoscope/platform/tasks/io/_persist.py
persist_text ¶
persist_text(text: Annotated[str, Field(description='Text to persist')], root_path: Annotated[str, Field(description='Root path to persist text to')], filename: Annotated[str | None, Field(description=' Optional filename to persist text to within the `root_path`.\n If not provided, a filename will be generated based on a hash of the text content.\n ', exclude=True)] = None, filename_suffix: Annotated[str | None, Field(description=' If present, will be appended to the filename as filename_suffix.html\n ', exclude=True)] = None) -> Annotated[str, Field(description='Path to persisted text')]
Persist text to a file or cloud storage object.
Source code in ecoscope/platform/tasks/io/_persist.py
set_er_connection ¶
set_event_details_params ¶
set_event_details_params(client: str, time_range: TimeRangeAnnotation, event_type: SingleEventTypeAnnotation, analysis_field: AnalysisFieldAnnotation, analysis_field_label: AnalysisFieldLabelAnnotation, analysis_field_unit: AnalysisFieldUnitAnnotation, event_columns: EventColumnsAnnotation = DefaultEventColumns, category_field: CategoryFieldAnnotation = '', category_field_label: CategoryFieldLabelAnnotation = '', include_null_geometry: IncludeNullGeometryAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True, include_details: IncludeDetailsAnnotation = True, include_updates: IncludeUpdatesAnnotation = False, include_related_events: IncludeRelatedEventsAnnotation = False, include_display_values: IncludeDisplayValuesAnnotation = False) -> Annotated[CombinedEventsAndDetailsParams, Field(description='Passthrough selected events settings for use in downstream tasks')]
Source code in ecoscope/platform/tasks/io/_earthranger.py
set_gee_connection ¶
set_patrol_status ¶
set_patrol_types ¶
set_patrol_types(patrol_types: Annotated[list[str], Field(description='Specify the patrol type(s) to analyze (optional). Leave empty to analyze all patrol types.')]) -> Annotated[list[str], Field(description='Passthrough selected patrol types for use in downstream EarthRanger queries')]
Source code in ecoscope/platform/tasks/io/_earthranger.py
set_patrols_and_patrol_events_params ¶
set_patrols_and_patrol_events_params(client: str, time_range: TimeRangeAnnotation, patrol_types: PatrolTypesAnnotation, event_types: EventTypesAnnotation, status: PatrolStatusAnnotation = None, include_patrol_details: IncludePatrolDetailsAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True, include_null_geometry: IncludeNullGeometryAnnotation = True, truncate_to_time_range: TruncateToTimeRangeAnnotation = True, sub_page_size: SubPageSizeAnnotation = 100, patrols_overlap_daterange: PatrolsOverlapDateRangeAnnotation = True) -> Annotated[CombinedPatrolAndEventsParams, Field(description='Passthrough selected patrol and event types for use in downstream EarthRanger queries')]
Source code in ecoscope/platform/tasks/io/_earthranger.py
set_smart_connection ¶
unpack_events_from_patrols_df ¶
unpack_events_from_patrols_df(patrols_df: PatrolsDF, event_types: EventTypesAnnotation, time_range: TimeRangeAnnotation, include_null_geometry: IncludeNullGeometryAnnotation = True, truncate_to_time_range: TruncateToTimeRangeAnnotation = True, raise_on_empty: RaiseOnEmptyAnnotation = True) -> EventGDF | EmptyDataFrame
Source code in ecoscope/platform/tasks/io/_earthranger.py
unpack_events_from_patrols_df_and_combined_params ¶
unpack_events_from_patrols_df_and_combined_params(patrols_df: PatrolsDF, combined_params: CombinedPatrolAndEventsParams) -> EventGDF | EmptyDataFrame