Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Polish subject and patrol workflow #229

Open
wants to merge 12 commits into
base: legacy
Choose a base branch
from
Open
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
12 changes: 6 additions & 6 deletions ecoscope_workflows/tasks/analysis/_aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,31 @@ def dataframe_column_mean(
df: AnyDataFrame,
column_name: ColumnName,
) -> Annotated[float, Field(description="The mean of the column")]:
return df[column_name].mean()
return round(df[column_name].mean(), 2)


@task
def dataframe_column_sum(
df: AnyDataFrame,
column_name: ColumnName,
) -> Annotated[float, Field(description="The sum of the column")]:
return df[column_name].sum()
return round(df[column_name].sum(), 2)


@task
def dataframe_column_max(
df: AnyDataFrame,
column_name: ColumnName,
) -> Annotated[float, Field(description="The max of the column")]:
return df[column_name].max()
return round(df[column_name].max(), 2)


@task
def dataframe_column_min(
df: AnyDataFrame,
column_name: ColumnName,
) -> Annotated[float, Field(description="The min of the column")]:
return df[column_name].min()
return round(df[column_name].min(), 2)


@task
Expand Down Expand Up @@ -81,7 +81,7 @@ def apply_arithmetic_operation(
) -> Annotated[
float | int, Field(description="The result of the arithmetic operation")
]:
return operations[operation](a, b)
return round(operations[operation](a, b), 2)


@task
Expand All @@ -90,4 +90,4 @@ def get_day_night_ratio(
) -> Annotated[float, Field(description="Daynight ratio")]:
from ecoscope.analysis import astronomy

return astronomy.get_daynight_ratio(df)
return round(astronomy.get_daynight_ratio(df), 2)
Binary file not shown.
Binary file not shown.
16 changes: 10 additions & 6 deletions ecoscope_workflows/tasks/results/_ecomap.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class LayerStyleBase(BaseModel):
class PolylineLayerStyle(LayerStyleBase):
layer_type: Literal["polyline"] = Field("polyline", exclude=True)
get_color: str | list[int] | list[list[int]] | None = None
get_width: float = 1
get_width: float = 3
color_column: str | None = None
width_units: UnitType = "pixels"
cap_rounded: bool = True
Expand All @@ -36,7 +36,7 @@ class ShapeLayerStyle(LayerStyleBase):

class PointLayerStyle(ShapeLayerStyle):
layer_type: Literal["point"] = Field("point", exclude=True)
get_radius: float = 1
get_radius: float = 5
radius_units: UnitType = "pixels"


Expand Down Expand Up @@ -129,16 +129,20 @@ def draw_ecomap(

m = EcoMap(static=static, default_widgets=False)

if title:
m.add_title(title)
# if title:
# m.add_title(title)

m.add_scale_bar()
m.add_north_arrow(
**(north_arrow_style.model_dump(exclude_none=True) if north_arrow_style else {})
)

if tile_layer:
m.add_layer(EcoMap.get_named_tile_layer(tile_layer))
# if tile_layer:
# m.add_layer(EcoMap.get_named_tile_layer(tile_layer))
m.add_layer(EcoMap.get_named_tile_layer("SATELLITE"))
terrain_layer = EcoMap.get_named_tile_layer("TERRAIN")
terrain_layer.opacity = 0.5
m.add_layer(terrain_layer)

geo_layers = [geo_layers] if not isinstance(geo_layers, list) else geo_layers
for layer_def in geo_layers:
Expand Down
4 changes: 2 additions & 2 deletions ecoscope_workflows/tasks/results/_widget_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def create_plot_widget_single_view(
The widget.
"""
return WidgetSingleView(
widget_type="plot",
widget_type="graph",
title=title,
view=view,
data=data,
Expand Down Expand Up @@ -119,7 +119,7 @@ def create_single_value_widget_single_view(
The widget.
"""
return WidgetSingleView(
widget_type="single_value",
widget_type="stat",
title=title,
view=view,
data=data,
Expand Down
5 changes: 2 additions & 3 deletions ecoscope_workflows/tasks/results/_widget_types.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from typing import TypeAlias, Literal
from dataclasses import dataclass
from pathlib import Path
from typing import Literal, TypeAlias

from pydantic_core import Url

from ecoscope_workflows.indexes import CompositeFilter


WidgetTypes = Literal["plot", "map", "text", "single_value"]
WidgetTypes = Literal["graph", "map", "text", "stat"]

PrecomputedHTMLWidgetData: TypeAlias = Path | Url
TextWidgetData: TypeAlias = str
Expand Down
24 changes: 20 additions & 4 deletions examples/compilation-specs/patrol_workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ workflow:
partial:
df: ${{ workflow.traj_add_temporal_index.return }}
groupers: ${{ workflow.groupers.return }}

- name: Create map layer for each Patrol Trajectories group
id: patrol_traj_map_layers
task: create_map_layer
Expand All @@ -54,12 +55,19 @@ workflow:
partial:
df: ${{ workflow.filter_patrol_events.return }}

# Colormap patrol events
- name: Patrol Events Colormap
id: pe_colormap
task: apply_color_map
partial:
df: ${{ workflow.pe_add_temporal_index.return }}

# patrol events map layers
- name: Split Patrol Events by Group
id: split_pe_groups
task: split_groups
partial:
df: ${{ workflow.pe_add_temporal_index.return }}
df: ${{ workflow.pe_colormap.return }}
groupers: ${{ workflow.groupers.return }}
- name: Create map layers for each Patrols Events group
id: patrol_events_map_layers
Expand Down Expand Up @@ -192,8 +200,7 @@ workflow:
partial:
widgets: ${{ workflow.avg_speed_sv_widgets.return }}


# Grouped single value widget (4) - max speed per group
# Grouped single value widget (5) - max speed per group
- name: Calculate Max Speed Per Group
id: max_speed
task: dataframe_column_max
Expand Down Expand Up @@ -263,11 +270,20 @@ workflow:
task: calculate_time_density
partial:
trajectory_gdf: ${{ workflow.patrol_traj.return }}

# Colormap time density
- name: Time Density Colormap
id: td_colormap
task: apply_color_map
partial:
df: ${{ workflow.td.return }}

- name: Create map layer from Time Density
id: td_map_layer
task: create_map_layer
partial:
geodataframe: ${{ workflow.td.return }}
geodataframe: ${{ workflow.td_colormap.return }}

- name: Draw Ecomap from Time Density
id: td_ecomap
task: draw_ecomap
Expand Down
2 changes: 1 addition & 1 deletion examples/dags/subject_tracking_dag.script_sequential.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,4 @@
.call()
)

print(subject_tracking_dashboard)
print(subject_tracking_dashboard.model_dump_json())
35 changes: 23 additions & 12 deletions examples/params/patrol_workflow_params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ groupers:
# Parameters for 'Get Patrol Observations from EarthRanger' using task `get_patrol_observations`.
patrol_obs:
client: "mep_dev" # (<class 'ecoscope_workflows.connections.EarthRangerClientProtocol'>, BeforeValidator(func=<bound method DataConnection.client_from_named_connection of <class 'ecoscope_workflows.connections.EarthRangerConnection'>>), WithJsonSchema(json_schema={'type': 'string', 'description': 'A named EarthRanger connection.'}, mode=None))
since: "2011-01-01" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Start date'))
until: "2023-01-01" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='End date'))
patrol_type: ["0ef3bf48-b44c-4a4e-a145-7ab2e38c9a57"] # (<class 'str'>, FieldInfo(annotation=NoneType, required=False, default=None, description='Comma-separated list of type of patrol UUID'))
since: "2015-01-01" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Start date'))
until: "2015-12-31" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='End date'))
patrol_type: ["05ad114e-1aff-4602-bc83-efd333cdd8a2"] # (<class 'str'>, FieldInfo(annotation=NoneType, required=False, default=None, description='Comma-separated list of type of patrol UUID'))
include_patrol_details: True # (<class 'bool'>, FieldInfo(annotation=NoneType, required=False, default=False, description='Include patrol details'))
status: ["done"]

Expand Down Expand Up @@ -66,13 +66,9 @@ patrol_traj_map_widget:
# Parameters for 'Get Patrol Events from EarthRanger' using task `get_patrol_events`.
patrol_events:
client: "mep_dev" # (<class 'ecoscope_workflows.connections.EarthRangerClientProtocol'>, BeforeValidator(func=<bound method DataConnection.client_from_named_connection of <class 'ecoscope_workflows.connections.EarthRangerConnection'>>), WithJsonSchema(json_schema={'type': 'string', 'description': 'A named EarthRanger connection.'}, mode=None))
since: "2011-01-01" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Start date'))
until: "2023-01-01" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='End date'))
patrol_type: [
"804c75f0-ed55-4bf5-b6fc-1aaf166bcf84",
"fde19413-cb55-443e-9be6-d10e4a48c668",
"0ef3bf48-b44c-4a4e-a145-7ab2e38c9a57",
] # (<class 'str'>, FieldInfo(annotation=NoneType, required=False, default=None, description='Comma-separated list of type of patrol UUID'))
since: "2015-01-01T00:00:00+03:00" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Start date'))
until: "2015-12-31T00:00:00+03:00" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='End date'))
patrol_type: ["05ad114e-1aff-4602-bc83-efd333cdd8a2"] # (<class 'str'>, FieldInfo(annotation=NoneType, required=False, default=None, description='Comma-separated list of type of patrol UUID'))
status: ["active", "done"]

# Parameters for 'Apply Relocation Coordinate Filter' using task `apply_reloc_coord_filter`.
Expand All @@ -87,16 +83,22 @@ filter_patrol_events:
# Parameters for 'Add temporal index to Patrol Events' using task `add_temporal_index`.
pe_add_temporal_index:
index_name: "month" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='A name for the new index which will be added.'))
time_col: "updated_at" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Name of existing column containing time data.'))
time_col: "time" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Name of existing column containing time data.'))
directive: "%B" # (typing.Literal['%a', '%A', '%b', '%B', '%c', '%d', '%f', '%H', '%I', '%j', '%m', '%M', '%p', '%S', '%U', '%w', '%W', '%x', '%X', '%y', '%Y', '%z', '%%'], FieldInfo(annotation=NoneType, required=True, description='A directive for formatting the time data.'))

pe_colormap:
input_column_name: "event_type" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='The name of the column with categorical values.'))
colormap: "tab20b" # (str | list[str], FieldInfo(annotation=NoneType, required=True, description='Either a named mpl.colormap or a list of string hex values.'))
output_column_name: "event_type_colormap"

# Parameters for 'Split Patrol Events by Group' using task `split_groups`.
split_pe_groups: {}

# Parameters for 'Create map layers for each Patrols Events group' using task `create_map_layer`.
patrol_events_map_layers:
layer_style:
layer_type: "point"
fill_color_column: "event_type_colormap"

# Parameters for 'Combine Trajectories and Patrol Events layers' using task `groupbykey`.
combined_traj_and_pe_map_layers: {}
Expand Down Expand Up @@ -184,7 +186,7 @@ max_speed_grouped_widget: {}

# Parameters for 'Draw Time Series Bar Chart for Patrols Events' using task `draw_time_series_bar_chart`.
patrol_events_bar_chart:
x_axis: "updated_at" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='The dataframe column to plot in the x axis.'))
x_axis: "time" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='The dataframe column to plot in the x axis.'))
y_axis: "event_type" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='The dataframe column to plot in the y axis.'))
category: "event_type" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='The dataframe column to stack in the y axis.'))
agg_function: "count" # (typing.Literal['count', 'mean', 'sum', 'min', 'max'], FieldInfo(annotation=NoneType, required=True, description='The aggregate function to apply to the group.'))
Expand Down Expand Up @@ -244,10 +246,19 @@ td:
expansion_factor: 1.3 # (<class 'float'>, FieldInfo(annotation=NoneType, required=False, default=1.3))
percentiles: [50.0, 60.0, 70.0, 80.0, 90.0, 95.0] # (list[float], FieldInfo(annotation=NoneType, required=False, default=[50.0, 60.0, 70.0, 80.0, 90.0, 95.0]))

# Parameters for 'Patrol Events Colormap' using task `apply_color_map`.
td_colormap:
input_column_name: "percentile" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='The name of the column with categorical values.'))
colormap: "RdYlGn_r" # (str | list[str], FieldInfo(annotation=NoneType, required=True, description='Either a named mpl.colormap or a list of string hex values.'))
output_column_name: "percentile_colormap"

# Parameters for 'Create map layer from Time Density' using task `create_map_layer`.
td_map_layer:
layer_style:
layer_type: "polygon"
fill_color_column: "percentile_colormap"
opacity: 0.7
get_line_width: 0

# Parameters for 'Draw Ecomap from Time Density' using task `draw_ecomap`.
td_ecomap:
Expand Down
24 changes: 12 additions & 12 deletions examples/params/subject_tracking_params.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Parameters for 'Set Groupers' using task `set_groupers`.
groupers:
groupers: # (list[ecoscope_workflows.tasks.groupby._groupby.Grouper], FieldInfo(annotation=NoneType, required=True, description=' Index(es) and/or column(s) to group by, along with\n optional display names and help text.\n '))
- index_name: "month"
display_name: "Month"
help_text: "The month in which the patrol began or the event was updated."
- index_name: "year"
display_name: "Year"
help_text: "The year."

# Parameters for 'Get Subject Group Observations from EarthRanger' using task `get_subjectgroup_observations`.
subject_obs:
client: "mep_dev" # (<class 'ecoscope_workflows.connections.EarthRangerClientProtocol'>, BeforeValidator(func=<bound method DataConnection.client_from_named_connection of <class 'ecoscope_workflows.connections.EarthRangerConnection'>>), WithJsonSchema(json_schema={'type': 'string', 'description': 'A named EarthRanger connection.'}, mode=None))
subject_group_name: "Elephants" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Name of EarthRanger Subject'))
since: "2011-01-01" # (<class 'datetime.datetime'>, FieldInfo(annotation=NoneType, required=True, description='Start date'))
until: "2023-01-01" # (<class 'datetime.datetime'>, FieldInfo(annotation=NoneType, required=True, description='End date'))
subject_group_name: "Salif" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Name of EarthRanger Subject'))
since: "2008-01-01" # (<class 'datetime.datetime'>, FieldInfo(annotation=NoneType, required=True, description='Start date'))
until: "2011-10-01" # (<class 'datetime.datetime'>, FieldInfo(annotation=NoneType, required=True, description='End date'))
include_inactive: False # (<class 'bool'>, FieldInfo(annotation=NoneType, required=True, description='Whether or not to include inactive subjects'))

# Parameters for 'Transform Observations to Relocations' using task `process_relocations`.
Expand All @@ -26,16 +26,16 @@ subject_reloc:
subject_traj:
min_length_meters: 0.001 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
max_length_meters: 10000 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
max_time_secs: 3600 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
max_time_secs: 21600 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
min_time_secs: 1 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
max_speed_kmhr: 120 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
max_speed_kmhr: 10 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))
min_speed_kmhr: 0.0 # (<class 'float'>, FieldInfo(annotation=NoneType, required=True))

# Parameters for 'Add temporal index to Subject Trajectories' using task `add_temporal_index`.
traj_add_temporal_index:
index_name: "month" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='A name for the new index which will be added.'))
index_name: "year" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='A name for the new index which will be added.'))
time_col: "segment_start" # (<class 'str'>, FieldInfo(annotation=NoneType, required=True, description='Name of existing column containing time data.'))
directive: "%B" # (typing.Literal['%a', '%A', '%b', '%B', '%c', '%d', '%f', '%H', '%I', '%j', '%m', '%M', '%p', '%S', '%U', '%w', '%W', '%x', '%X', '%y', '%Y', '%z', '%%'], FieldInfo(annotation=NoneType, required=True, description='A directive for formatting the time data.'))
directive: "%Y" # (typing.Literal['%a', '%A', '%b', '%B', '%c', '%d', '%f', '%H', '%I', '%j', '%m', '%M', '%p', '%S', '%U', '%w', '%W', '%x', '%X', '%y', '%Y', '%z', '%%'], FieldInfo(annotation=NoneType, required=True, description='A directive for formatting the time data.'))

# Parameters for 'Split Subject Trajectories by Group' using task `split_groups`.
split_subject_traj_groups: {}
Expand All @@ -49,7 +49,7 @@ traj_map_layers:
traj_ecomap:
tile_layer: "OpenStreetMap" # (str, FieldInfo(annotation=NoneType, required=False))
static: False # (<class 'bool'>, FieldInfo(annotation=NoneType, required=False))
title: "Subject Group Trajectory Map" # (<class 'str'>, FieldInfo(annotation=NoneType, required=False))
# title: "Subject Group Trajectory Map" # (<class 'str'>, FieldInfo(annotation=NoneType, required=False))

# Parameters for 'Persist ecomap as Text' using task `persist_text`.
ecomap_html_urls: {}
Expand Down Expand Up @@ -122,7 +122,7 @@ td_map_layer:
td_ecomap:
tile_layer: OpenStreetMap # (str, FieldInfo(annotation=NoneType, required=False))
static: False # (<class 'bool'>, FieldInfo(annotation=NoneType, required=False))
title: "Great Map" # (<class 'str'>, FieldInfo(annotation=NoneType, required=False))
# title: "Great Map" # (<class 'str'>, FieldInfo(annotation=NoneType, required=False))

# Parameters for 'Persist Ecomap as Text' using task `persist_text`.
td_ecomap_html_url: {}
Expand Down
Loading