The behavioral studies of Adélie penguins are addressed to central place foraging theory which explains that breeding seabirds should energetically optimize prey acquisition and, therefore, foraging is expected to be located where prey is most available, within limits defined by the energetics of the species [1]. Despite the conditions of the Antarctic such as sea-surface temperature, chlorophyll concentration, or sea ice cover, the penguins must find foraging success in different locations based on oceanographic characteristics.
In this map review, I am going to work with a subset that contains the movement tracks of 16 penguins recorded during the year 2018 in Antarctica. The entire dataset contains the tracking information of 129 penguins recorded during the period 2010–12–21 to 2018–01–11 and can be found in Movebank directly here.
Final map animation: [HERE!](https://github.com/bryanvallejo16/penguin-tracking-antarctica) Repository: HERE!
Objective
The main objective of this practice is to visualize the penguin tracks in Orthographic projection with pyproj. Additionally, to create a map animation with Keplergl that shows the behavioral movement of the penguins during foraging activity on the coastline. So, we are going to obtain two products: 1) Map with Orthographic view, and 2) Map-animation of penguin tracks.
1) Creating a map with Orthographic Projection
The idea of Orthographic Projection is to visualize the objects in an azimuthal perspective (perpendicular) in a three-dimensional globe. For this, we are going to use the python library pyproj and geopandas. The first main step is to visualize the dataset.
import geopandas as gpd
import pandas as pd
from pyproj import CRS
from keplergl import KeplerGl
import matplotlib.pyplot as plt
# reading data
fp = r'data/penguin-tracks.gjson'
geodata = gpd.read_file(fp, driver='GeoJSON')
# defining timestamp columns
geodata['timestamp'] = pd.to_datetime(geodata['timestamp'])
geodata['year'] = pd.DatetimeIndex(geodata['timestamp']).year
geodata['t'] = geodata['timestamp'].astype(str)
The code above simply reads the data and creates two extra columns. year
that contains the year of the tracks for this example all records have 2018 and t
that contains the timestamp as a string type. The last column is needed in a string type for the map animation. Then, as a quick visualization in WGS84, we run the next line to visualize the data.
geodata.plot(column='ind_ident', figsize=(12,6), markersize=0.2)

Now, I am going to load the Natural Earth layer of global countries directly from geopandas and project it in Orthographic Projection. So, in the way we are observing a rounded globe, I am going to center the Orthographic projection in the South Pole (Antarctic) which means latitude -90°
and longitude 0°
. The CRS of the Orthographic projection is obtained by a transformation: from proj4 (parameters) to CRS. The code goes like this:
# create world layer
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
# define center of Orthographic Projection
lat = -90
lon = 0
ortho = CRS.from_proj4("+proj=ortho +lat_0={} +lon_0={} +x_0=0 +y_0=0 +a=6370997 +b=6370997 +units=m +no_defs".format(lat, lon))
# Re-project and plot
geodataor = geodata.to_crs(ortho)
world = world.to_crs(ortho)
world.plot(figsize=(12,6))

Now, we create a unique figure, ax
where world layer and penguin tracks are going to be added. In this unique, figure, ax
we set x and y limits the bounding box of the Antarctica continent. First, we get the bounding box of Antarctica then we plot it. Code goes like this:
antarctic = list(world.loc[world['continent']=='Antarctica'].geometry)[0]
antarctic

Now we set the x min, x max, y min, y max of the bounding box:
bounds = antarctic.bounds
xmin = bounds[0]
xmax = bounds[2]
ymin = bounds[1]
ymax = bounds[3]
Now, the unique figure, ax
:
# create unique fig, ax
fig, ax = plt.subplots(figsize=(12, 8))
# adding layers
world.plot(ax=ax)
geodataor.plot(ax=ax, markersize=10, color='red')
# limits
ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax))
plt.axis('off')

Now that we know the location of the penguin tracks we sharp the final map.
plt.style.use('seaborn')
# create unique fig, ax
fig, ax = plt.subplots(figsize=(10, 7))
# adding layers
world.plot(ax=ax, color='#CED7E0', edgecolor='black')
geodataor.plot(ax=ax, column = 'ind_ident', markersize=7, alpha=0.8, cmap='tab10', legend=True)
# limits
ax.set(xlim=(1500000, 1800000), ylim=(-2050000, -1800000))
# plt.axis('off')
plt.grid(False)
ax.set_facecolor('#0F4983')
plt.title('Adélie Penguins tracks in Antarctic')
ax.get_legend().set_bbox_to_anchor((1.2,1))
ax.get_legend().set_title("Penguin ID")
plt.savefig(r'png/penguin-tracks.png')

2) Creating a map animation with KeplerGL
Now, we want to visualize the movement pattern of the penguin tracks. We select only the columns we need for the map animation in the geodata
layer. Then, a great tool to visualize the movement pattern is the KeplerGL library. The steps to create a KeplerGL map are to start with an empty instance, then add the data, then save the map. As next:
# selecting the needed columns
geodata = geodata[['t', 'ind_ident', 'geometry']]
# Create KeplerGl instance
m = KeplerGl(height=600)
# Add stop durations
m.add_data(geodata, 'Penguins')
m
Here we configure the map as desired with KeplerGL.

Finally, just save the map after configuration.
# Save map as html
m.save_to_html(file_name='index.html')
Conclusion
You can use the Orthographic Projection as you want, not only with this data but you may notice you always need to include in the same map, everything with the same projection. This case Orthometric. For KeplerGL always the data must be in Geographic CRS (EPSG 4326).
The penguin tracking records contain spatial gaps. As usual, this happens with movement data and it is related to the signal of the GPS. There are options to reconstruct the track (path). If you are interested in knowing more about animal tracking and movement data ping me on my LinkedIn profile.
References
[1] Ballard, G., Schmidt, A., Toniolo, V., Veloz, S., Jongsomjit, D., Arrigo, K., Ainley, D. (2019). "Fine-scale oceanographic features characterizing successful Adélie penguin foraging in the SW Ross Sea". Vol. 608: 263–277. DOI: https://doi.org/10.3354/meps12801