This Python analysis uses whale sightings and vessel data to suggest a speed reduction zone for the coast of Dominica.
In this analysis, a suggested speed reduction zone is outlined based on the preferred habitat of local sperm whales off the coast of Dominica. This speed reduction zone is approximated using whale sightings recorded between 2008 and 2015. Based on vessel data from 2015, the extra travel time that vessels would take to complete their journey through the speed reduction zone is calculated. In conclusion, about 27 days would be the increased travel time due to a 10-knot reduced-speed zone in the identified whale habitat.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import geopandas as gpd
import shapely.geometry
# read in Dominica file and project to correct crs
= gpd.read_file("data/dominica")
dominica
dominica.crs
= dominica.to_crs("EPSG:2002")
dominica dominica.crs
<Projected CRS: EPSG:2002>
Name: Dominica 1945 / British West Indies Grid
Axis Info [cartesian]:
- E[east]: Easting (metre)
- N[north]: Northing (metre)
Area of Use:
- name: Dominica - onshore.
- bounds: (-61.55, 15.14, -61.2, 15.69)
Coordinate Operation:
- name: British West Indies Grid
- method: Transverse Mercator
Datum: Dominica 1945
- Ellipsoid: Clarke 1880 (RGS)
- Prime Meridian: Greenwich
# plot Dominica outline
= plt.subplots(figsize=(5, 5), dpi=200)
fig, ax True)
ax.grid('lightblue')
ax.set_facecolor(
= dominica.plot(ax = ax)
dominica_plot "Dominica") ax.set_title(
Text(0.5, 1.0, 'Dominica')
= gpd.read_file("data/sightings2005_2018.csv") whale_sightings
# set geometry and correct crs
= gpd.points_from_xy(whale_sightings['Long'], whale_sightings['Lat'],
whale_geoms = "EPSG:4326")
crs
= whale_sightings.set_geometry(whale_geoms)
whale_with_geoms
= whale_with_geoms.to_crs("EPSG:2002") whale_with_geoms
whale_with_geoms.total_bounds
array([ 408480.65208369, 1532792.74594092, 498500.30495702,
1796964.39970292])
# making the grid
= whale_with_geoms.total_bounds
xmin, ymin, xmax, ymax
# Grid cell size
= 2000
length = 2000
width
= list(np.arange(xmin, xmax + width, width))
col = list(np.arange(ymin, ymax + length, length))
row
# making a polygon from the corner points
def make_cell(x, y, cell_size):
= [
ring
(x, y),+ cell_size, y),
(x + cell_size, y + cell_size),
(x + cell_size)
(x, y
]= shapely.geometry.Polygon(ring)
cell return cell
# making the cells
= []
cells = 2000
cell_size for x in col:
for y in row:
= make_cell(x, y, cell_size)
cell
cells.append(cell)
= gpd.GeoDataFrame({'geometry': cells}, crs=2002) grid
# plotting the grid
= plt.subplots(figsize = (10,10), dpi = 300)
fig, ax True)
ax.grid(
= ax, facecolor = "lightblue", edgecolor = "black", lw = 0.1)
grid.plot(ax = ax, color = "red", markersize = 0.05)
whale_with_geoms.plot(ax = ax)
dominica.plot(ax
set(xlabel = "",
ax.= "")
ylabel "Whale Sightings in Dominica 2008-2015", fontsize = 7)
ax.set_title(
# plt.setp(ax.set_xticks([]), ax.set_yticks([]))
plt.show()
# spatially join the data
= grid.sjoin(whale_with_geoms, how = "inner")
join
# count the rows, which would be the points within the cells
'count'] = join.groupby(join.index).count()['index_right']
grid[
# include only counts greater than 20
= grid[grid['count'] > 20]
over_20
# unary union
= over_20.geometry.unary_union
over_20_union
= over_20_union.convex_hull
over_20_convex_hull
= gpd.GeoDataFrame(geometry =
speed_reduction_zone gpd.GeoSeries(over_20_convex_hull))
# plotting the speed reduction zone
= plt.subplots(figsize = (10,10), dpi = 300)
fig, ax True)
ax.grid(
= ax, facecolor = "lightblue", edgecolor = "black", lw = 0.1)
grid.plot(ax = ax, color = "red", markersize = 0.05)
speed_reduction_zone.plot(ax = ax)
dominica.plot(ax
set(xlabel = "",
ax.= "",
ylabel = "")
title "Dominica Speed Reduction Zone", fontsize = 7)
ax.set_title(#plt.setp(ax.set_xticks([]), ax.set_yticks([]))
plt.show()
= gpd.read_file("data/station1249.csv") vessel_data
# set geometries and use correct crs
= gpd.points_from_xy(vessel_data['LON'], vessel_data['LAT'],
points_vessels = "EPSG:4326")
crs
= gpd.GeoDataFrame(vessel_data, geometry = points_vessels)
vessel_data_geom
= vessel_data_geom.to_crs("EPSG:2002") vessel_data_geom
# getting TIMESTAMP in datetime format
"TIMESTAMP"] = pd.to_datetime(vessel_data_geom["TIMESTAMP"])
vessel_data_geom[ vessel_data_geom.dtypes
field_1 object
MMSI object
LON object
LAT object
TIMESTAMP datetime64[ns]
geometry geometry
dtype: object
# spatially intersect the vessel data with the speed reduction zone
= vessel_data_geom.within(speed_reduction_zone.loc[0,
reduction_zone_mask 'geometry'])
= vessel_data_geom.loc[reduction_zone_mask] vessels_within_zone
# sort vessels df by MMSI and Timestamp
= vessels_within_zone.sort_values(by=['MMSI', 'TIMESTAMP'])
vessels_within_zone
# shift data frame
= vessels_within_zone.shift(1)
vessels_within_zone_shift
# join original with shift to have both locations in one row
= vessels_within_zone.join(vessels_within_zone_shift,
joined_vessels_within_zone = "left",
how = "start",
lsuffix = "end")
rsuffix
# drop rows with mismatching MMSI
= joined_vessels_within_zone[
joined_vessels_within_zone "MMSIstart"] == joined_vessels_within_zone["MMSIend"]] joined_vessels_within_zone[
# add geometry
= joined_vessels_within_zone.set_geometry("geometrystart")
joined_vessels_within_zone = joined_vessels_within_zone.set_geometry("geometryend") joined_vessels_within_zone
# CALCULATIONS
# distance
'distance_meters'] =
joined_vessels_within_zone['geometryend'].distance(joined_vessels_within_zone['geometrystart'])
joined_vessels_within_zone[
# time
'time'] = joined_vessels_within_zone['TIMESTAMPstart'] -
joined_vessels_within_zone['TIMESTAMPend']
joined_vessels_within_zone[
# time minutes
'time_minutes'] = joined_vessels_within_zone['time']/
joined_vessels_within_zone[1,'m')
np.timedelta64(
# average speed
'speed_meters_min'] =
joined_vessels_within_zone['distance_meters'] /
joined_vessels_within_zone['time_minutes']
joined_vessels_within_zone[
# time at 10 knots
'time_10knots'] =
joined_vessels_within_zone['distance_meters'] / 308.67
joined_vessels_within_zone[
# difference betweeen time it actually took and how long it would have taken
# at 10 knots
'time_difference'] =
joined_vessels_within_zone['time_10knots'] -
joined_vessels_within_zone['time_minutes']
joined_vessels_within_zone[
'over_0'] =
joined_vessels_within_zone['time_difference'] >= 0 joined_vessels_within_zone[
# Find all values of time_difference that are above 0 and add them up
= joined_vessels_within_zone[joined_vessels_within_zone['over_0'] == True]
fast_vessels 'time_difference'].sum() fast_vessels[
40140.306780090905
40,140.31 minutes = 669 hours = 27.88 days
27.88 days is the extra time it would take for shipping due to the speed reduction zone in our identified whale habitat.
For attribution, please cite this work as
Cruz (2022, Jan. 23). Felicia Cruz: Protecting Whales from Ships. Retrieved from https://fmcruz23.github.io/posts/2022-01-23-whales/
BibTeX citation
@misc{cruz2022protecting, author = {Cruz, Felicia}, title = {Felicia Cruz: Protecting Whales from Ships}, url = {https://fmcruz23.github.io/posts/2022-01-23-whales/}, year = {2022} }