Last Monday I took my oceanography students to Itaguaré beach near Bertioga. It is a very interesting region with a very peculiar (and dynamic) geomorphology. It can change drastically from one day to another, ranging from a small lagoon to a dry beach passing through a small stream of water reaching the ocean.
The image below shows a scenario similar to the one we saw last Monday. In reality, the water level was higher and the stream was completely hidden. (The image is from Google Earth's "time-series" capture.)
from IPython.display import Image
Image('./data/itaguare_cropped.jpg', format='jpg')
Our plan was to test a "home-made" drifter and test it in the field. We used an old feature-phone as a GPS, a PET plastic bottle, and tape. Just turn on the phone GPS, place it inside the bottle and seal it with the tape. That's it!
We had a little contest for drifter designs, but unfortunately we did not have GPS for them. However, it was interesting to deploy them at the same time and observe them drifting away from each other due to drogue/sail effects and/or the current non-linearity.
I borrowed a function from another post to geo-reference the aerial image to plot our drifter tracks.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
def make_map(llcrnrlon=None, urcrnrlon=None, llcrnrlat=None, urcrnrlat=None, img='./data/itaguare_cropped.jpg'):
m = Basemap(projection='merc', llcrnrlon=llcrnrlon, urcrnrlon=urcrnrlon,
llcrnrlat=llcrnrlat, urcrnrlat=urcrnrlat, resolution='c',
lat_ts=(llcrnrlat + urcrnrlat) / 2.)
fig, ax = plt.subplots(figsize=(10, 10), facecolor='none')
m.ax = ax
image = plt.imread(img)
m.imshow(image, origin='upper', alpha=0.75)
meridians = np.linspace(llcrnrlon, urcrnrlon, 4)
parallels = np.linspace(llcrnrlat, urcrnrlat, 4)
kw = dict(linewidth=0)
m.drawparallels(parallels, labels=[1, 0, 0, 0], **kw)
m.drawmeridians(meridians, labels=[0, 0, 0, 1], **kw)
return fig, m
After a few deployments we chose just two to plot, to avoid polluting the figure. In addition we converted from longitude and latitude to UTM coordinates and computed the drifter velocity.
from pandas import DataFrame
df1 = DataFrame.from_csv('./data/drifter_01.txt')
df2 = DataFrame.from_csv('./data/drifter_02.txt')
We used Pyproj to convert from lon, lat to UTM coordinates. Such a small area is easier to wrap your mind around if you use meters.
from pyproj import Proj
myProj = Proj("+proj=utm +zone=23K, +south +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
UTMx, UTMy = myProj(df1['lon'].values, df1['lat'].values)
df1['UTMx'], df1['UTMy'] = UTMx, UTMy
UTMx, UTMy = myProj(df2['lon'].values, df2['lat'].values)
df2['UTMx'], df2['UTMy'] = UTMx, UTMy
For the velocity we performed just a simple differentiation scheme, nothing fancy so the students can follow the computations easily:
\begin{align*} \dfrac{dx}{dt} &= u \\ \dfrac{dy}{dt} &= v \end{align*}
def vel(x, t):
dx = np.diff(x)
dt = np.diff(t) / np.timedelta64(1, 's')
return np.r_[0, dx / dt]
df1['u'] = vel(df1['UTMx'], df1.index.values)
df1['v'] = vel(df1['UTMy'], df1.index.values)
df2['u'] = vel(df2['UTMx'], df2.index.values)
df2['v'] = vel(df2['UTMy'], df2.index.values)
These are how our final data tables look like with the collected and calculated data.
df1[['lon', 'lat']].head(5)
df1[['UTMx', 'UTMy', 'u', 'v']].head(5)
The figure below shows the positions overlaid on the aerial image. The lower left and upper right corner positions were obtained by marking them on Google Earth and than cropping at the mark with an image editor. (That is probably the easiest to obtain and geo-reference an image.)
llcrnrlon = -(45 + (58 + 32.27/60) / 60) # 45°58'32.27"W
urcrnrlon = -(45 + (57 + 54.54/60) / 60) # 45°57'54.54"W
llcrnrlat = -(23 + (47 + 7.65/60) / 60) # 23°47' 7.65"S
urcrnrlat = -(23 + (46 + 42.25/60) / 60) # 23°46'42.25"S
fig, m = make_map(llcrnrlon=llcrnrlon, urcrnrlon=urcrnrlon, llcrnrlat=llcrnrlat, urcrnrlat=urcrnrlat)
kw = dict(marker='o', markeredgecolor='w', markersize=6, linestyle='none', latlon=True)
m.plot(df1['lon'].values, df1['lat'].values, markerfacecolor='b', **kw)
m.plot(df2['lon'].values, df2['lat'].values, markerfacecolor='cornflowerblue', **kw)
# Start.
m.plot(df1['lon'].values[0], df1['lat'].values[0], markerfacecolor='g', **kw)
m.plot(df2['lon'].values[0], df2['lat'].values[0], markerfacecolor='g', **kw)
# End.
m.plot(df1['lon'].values[-1], df1['lat'].values[-1], markerfacecolor='r', **kw)
m.plot(df2['lon'].values[-1], df2['lat'].values[-1], markerfacecolor='r', **kw)
m.ax.grid(False)
The first deployment (blue) was not that interesting, the drifter kept going back and forth due to the waves. The second deployment (red) was more interesting, the drifter went against the stream course ending up inside the cove. The next cell shows a $x, y$ plot centered at the deployment point with speed and direction to help understand what was going on.
x = df2['UTMx'] - df2['UTMx'][0]
y = df2['UTMy'] - df2['UTMy'][0]
fig, ax = plt.subplots()
kw = dict(cmap=plt.cm.rainbow, zorder=1)
speed = np.sqrt(df2['u']**2 + df2['v']**2)
ax.scatter(x, y, c=speed, **kw)
kw = dict(scale=20, alpha=0.6, color='black', zorder=3)
Q = ax.quiver(x, y, df2['u'], df2['v'], **kw)
fig.tight_layout()
ax.grid(True)
One theory we raised was that the wind started to pick up around $y$ = 200 meters. In fact we did noticed stronger winds at some point, but the lack of wind data render us unable to confirm that. There was a lot of back-and-forth movement as well because the waves were entering the lagoon. But I filtered them by averaging the data every 8 seconds. Hopefully, next time we will have our homemade anemometer.
I'll leave you with some pics of the field trip taken by Carol Gabani:
def html_fig_table(images, ncols=2, nrows=5):
if ncols*nrows != len(images):
raise ValueError("ncols x nrows must be equal the number of images!")
unit = ["<th><img src='%s'/></th>"]
row = ['<tr>\n%s\n</tr>' % '\n'.join(unit*ncols)]
table = """<table>%s</table>""" % '\n'.join(row*nrows)
return table % tuple(images)
import base64
from glob import glob
from IPython.display import display
images = []
for img in glob("../../images/itaguare*.jpg"):
with open(img, "rb") as f:
base64_data = base64.b64encode(f.read()).decode("ascii")
images.append('data:image/png;base64,{0}'.format(base64_data))
table = html_fig_table(images, ncols=2, nrows=5)
display(HTML(table))
HTML(html)