https://colab.research.google.com/assets/colab-badge.svg

EcoMap#

Setup#

Ecoscope#

[ ]:
ECOSCOPE_RAW = "https://raw.githubusercontent.com/wildlife-dynamics/ecoscope/master"

%pip install ecoscope &> /dev/null
[ ]:
import os
import sys

import geopandas as gpd
import numpy as np
import pandas as pd
import shapely

import ecoscope

ecoscope.init(selenium=True, silent=True)

Google Drive Setup#

[ ]:
output_dir = "Ecoscope-Outputs"

if "google.colab" in sys.modules:
    from google.colab import drive

    drive.mount("/content/drive/", force_remount=True)
    output_dir = os.path.join("/content/drive/MyDrive/", output_dir)

os.makedirs(output_dir, exist_ok=True)

Earth Engine#

[ ]:
import ee

try:
    EE_ACCOUNT = os.getenv("EE_ACCOUNT")
    EE_PRIVATE_KEY_DATA = os.getenv("EE_PRIVATE_KEY_DATA")
    if EE_ACCOUNT and EE_PRIVATE_KEY_DATA:
        ee.Initialize(credentials=ee.ServiceAccountCredentials(EE_ACCOUNT, key_data=EE_PRIVATE_KEY_DATA))
    else:
        ee.Initialize()

except ee.EEException:
    ee.Authenticate()
    ee.Initialize()

Load sample data#

Vehicle tracks#

[ ]:
ecoscope.io.download_file(
    f"{ECOSCOPE_RAW}/tests/sample_data/vector/KDB025Z.csv",
    os.path.join(output_dir, "KDB025Z.csv"),
)

vehicle_gdf = pd.read_csv(os.path.join(output_dir, "KDB025Z.csv"), index_col="id")
vehicle_gdf["geometry"] = vehicle_gdf["geometry"].apply(lambda x: shapely.wkt.loads(x))
vehicle_gdf = ecoscope.base.Relocations.from_gdf(gpd.GeoDataFrame(vehicle_gdf, crs=4326))
vehicle_gdf = ecoscope.base.Trajectory.from_relocations(vehicle_gdf)

Elephant Sightings#

[ ]:
ecoscope.io.download_file(
    f"{ECOSCOPE_RAW}/tests/sample_data/vector/elephant_sighting.csv",
    os.path.join(output_dir, "elephant_sighting.csv"),
)

events_df = pd.read_csv(os.path.join(output_dir, "elephant_sighting.csv"), index_col="id")
events_df["geometry"] = events_df["geometry"].apply(lambda x: shapely.wkt.loads(x))
events_gdf = gpd.GeoDataFrame(events_df, crs=4326)

Regions#

[ ]:
ecoscope.io.download_file(
    f"{ECOSCOPE_RAW}/tests/sample_data/vector/maec_4zones_UTM36S.gpkg",
    os.path.join(output_dir, "maec_4zones_UTM36S.gpkg"),
)

region_gdf = gpd.read_file(os.path.join(output_dir, "maec_4zones_UTM36S.gpkg")).to_crs(4326)

MovBank Relocations#

[ ]:
ecoscope.io.download_file(
    f"{ECOSCOPE_RAW}/tests/sample_data/vector/movbank_data.csv",
    os.path.join(output_dir, "movbank_data.csv"),
)

df = pd.read_csv(os.path.join(output_dir, "movbank_data.csv"), index_col=0)
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(x=df["location-long"], y=df["location-lat"]), crs=4326)
movbank_relocations_gdf = ecoscope.base.Relocations.from_gdf(
    gdf, groupby_col="individual-local-identifier", time_col="timestamp"
)

pnts_filter = ecoscope.base.RelocsCoordinateFilter(
    min_x=-5,
    max_x=1,
    min_y=12,
    max_y=18,
    filter_point_coords=[[180, 90], [0, 0]],
)
movbank_relocations_gdf.apply_reloc_filter(pnts_filter, inplace=True)
movbank_relocations_gdf.remove_filtered(inplace=True)

EcoMap#

Basic Two-Layer EcoMap#

[ ]:
# Initialize EcoMap by setting the zoom level and center
m = ecoscope.mapping.EcoMap(center=(0.0236, 37.9062), zoom=6, height=800, width=1000, static=False)

# Add two tiled basemaps (OSM and Google satellite)
m.add_basemap("OpenStreetMap")
m.add_tile_layer(
    url="https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}",
    name="Google Satellite",
    attribution="Google",
    opacity=0.5,
)

# Display
m

Elephant Sighting Map#

[ ]:
m = ecoscope.mapping.EcoMap(width=800, height=600)

# Add two tiled basemaps (OSM and Google Satellite Hybrid)
m.add_basemap("OpenStreetMap")
m.add_basemap("HYBRID")

# Set DEM visualization parameters
vis_params = {"min": 0, "max": 4000, "opacity": 0.5, "palette": ["006633", "E5FFCC", "662A00", "D8D8D8", "F5F5F5"]}

# Add Google Earth Engine elevation layer
dem = ee.Image("USGS/SRTMGL1_003")
m.add_ee_layer(dem.updateMask(dem.gt(0)), vis_params, "DEM")

# Zoom in and add regions outlines
m.zoom_to_gdf(region_gdf)
m.add_gdf(
    region_gdf,
    style_kwds={"fillOpacity": 0.1, "opacity": 0.5, "color": "black"},
    color=["#7fc97f", "#beaed4", "#fdc086", "#ffff99"],
)

# Add trajectory
vehicle_gdf.geometry.explore(m=m, color="#468af7")

# Add elephant sighting events
m.add_gdf(
    events_gdf[["geometry", "serial_number", "location", "title", "event_type"]],
    color="#f746ad",
    marker_type="circle_marker",
    marker_kwds={"radius": 7, "fill": True, "draggable": False},
)

# Add title
m.add_title(title="Elephant Sighting Map", align="center", font_size="18px")

# Add north-arrow
m.add_north_arrow(
    position="top-left",
)

# Add legend
m.add_legend(legend_dict={"KDB025Z_Tracks": "468af7", "Elephant_Sighting_Events": "f746ad"})

# Display
m

Add local geotiff to a map#

[ ]:
ecoscope.io.download_file(
    f"{ECOSCOPE_RAW}/tests/sample_data/raster/mara_dem.tif",
    os.path.join(output_dir, "mara_dem.tif"),
)

m = ecoscope.mapping.EcoMap(width=800, height=600)
m.add_local_geotiff(path=os.path.join(output_dir, "mara_dem.tif"), zoom=True, cmap="jet", colorbar=True)
m

Day-Night Relocations Map#

[ ]:
movbank_relocations_gdf["is_night"] = ecoscope.analysis.astronomy.is_night(
    movbank_relocations_gdf.geometry, movbank_relocations_gdf.fixtime
)

movbank_relocations_gdf[["groupby_col", "fixtime", "geometry", "is_night"]]
[ ]:
colors = ["#292965" if is_night else "#e7a553" for is_night in movbank_relocations_gdf.is_night]

m = movbank_relocations_gdf[["groupby_col", "fixtime", "geometry", "is_night"]].explore(
    m=ecoscope.mapping.EcoMap(width=800, height=600), color=colors
)
m.zoom_to_gdf(movbank_relocations_gdf)

m.add_legend(title="Is Night", legend_dict={True: "292965", False: "e7a553"})
m.add_north_arrow(position="topright")
m.add_title(title="Day-Night Relocation Map", align="center", font_size="18px")

m

Day-Night Trajectory Map#

[ ]:
movbank_trajectory_gdf = ecoscope.base.Trajectory.from_relocations(movbank_relocations_gdf)
movbank_traj_seg_filter = ecoscope.base.TrajSegFilter(
    min_length_meters=0.0,
    max_length_meters=float("inf"),
    min_time_secs=0.0,
    max_time_secs=4 * 60 * 60,
    min_speed_kmhr=0.0,
    max_speed_kmhr=10.0,
)
movbank_trajectory_gdf.apply_traj_filter(movbank_traj_seg_filter, inplace=True)
[ ]:
colors = ["#292965" if is_night else "#e7a553" for is_night in movbank_trajectory_gdf.extra__is_night]

m = movbank_trajectory_gdf[["groupby_col", "segment_start", "segment_end", "geometry", "extra__is_night"]].explore(
    color=colors, m=ecoscope.mapping.EcoMap(width=800, height=600)
)
m.zoom_to_gdf(movbank_trajectory_gdf)

m.add_legend(title="Is Night", legend_dict={True: "292965", False: "e7a553"})
m.add_north_arrow(position="topright")
m.add_title(title="Day-Night Trajectory Map", align="center", font_size="18px")

m

Speed Map#

[ ]:
m = ecoscope.mapping.EcoMap()
m.add_speedmap(trajectory=movbank_trajectory_gdf, classification_method="equal_interval", num_classes=6, bins=None)
m.add_north_arrow(position="topright")
m.add_title(title="Elephant Speed Map", align="center", font_size="18px")
m.zoom_to_gdf(movbank_trajectory_gdf)

m

ETD Range#

[ ]:
raster_profile = ecoscope.io.raster.RasterProfile(
    pixel_size=250.0,  # You may need to reduce grid size if you receive a warning during the calculation
    crs="ESRI:102022",  # Albers Africa Equal Area Conic
    nodata_value=np.nan,
    band_count=1,
)
[ ]:
def f(trajectory_gdf):
    output_path = os.path.join(output_dir, f"{trajectory_gdf.name}.tif")

    ecoscope.analysis.UD.calculate_etd_range(
        trajectory_gdf=trajectory_gdf,
        output_path=output_path,
        max_speed_kmhr=1.05 * trajectory_gdf.speed_kmhr.max(),  # Choose a value above the max recorded segment speed
        raster_profile=raster_profile,
        expansion_factor=1.3,
    )
    return output_path


etd = movbank_trajectory_gdf.groupby("groupby_col").apply(f)
[ ]:
percentile_areas = ecoscope.analysis.get_percentile_area(
    percentile_levels=[50, 60, 70, 80, 90, 99.9], raster_path=etd.at["Salif Keita"], subject_id="Salif Keita"
).to_crs(4326)

m = ecoscope.mapping.EcoMap(width=800, height=600, static=True)
m.add_gdf(percentile_areas, column="percentile", cmap="RdYlGn")
m.zoom_to_gdf(percentile_areas)

m.add_title(title="Salif ETD Range", align="center", font_size="18px")
m.add_north_arrow(position="topleft")

m

Export#

As HTML#

[ ]:
m.to_html(os.path.join(output_dir, "ecomap.html"))

As PNG#

[ ]:
m.to_png(os.path.join(output_dir, "ecomap.png"))