Quote

Of course it’s easy when you get everything you want. You just check the boxes and conclude that the theory is correct. However, it’s exactly when things go wrong that you end up learning something.
—L.S.

Background Information

From 24–25 October 2025, some students from the National University of Singapore's Special Programme in Science (SPS) conducted a fieldwork research project at Jetty Lagoon, St. John's Island, Singapore. This fieldwork research project has two main aims: to investigate the distribution patterns of the seagrass species Halophila ovalis at the lagoon, and to expose undergraduates to real-world data collection in the field. The data collected provides basic information about seagrass meadow characteristics across the intertidal zone.

The fieldwork was conducted during evening low tide periods, which provided safe access to the intertidal zone for data collection. Apart from the time of the fieldwork and equipment provided to them, the students were tasked to figure out the rest of the study details on their own. This included devising their own methodology and data analysis across several groups to be carried out across the two days. After data collection, seagrass samples were washed, analyzed and the data visualized in an interactive map available here.

Motivations

Taking inspiration from Dan Luu’s blog entries, this essay serves as a collection of the thinking processes, execution and personal reflections I have made whilst working on a personal project to create an interactive map of the Halophila ovalis (henceforth lovingly referred to as “seagrass”) spatial distribution at Jetty Lagoon, St. John’s Island, Singapore. The interactive map is

I’m partially inspired to document this process because of one of my students. He is a freshman who is currently learning to program in Python. Quickly realizing both the difficulty and benefits of incorporating it into his everyday, he sought to know how one may hone this skill, and where to apply it. To both appease his curiousity and inspire him, I thought it would be helpful to document how I implemented this website as an example of a real-world application. Beyond my students, I hope that this essay may serve as a reference for future readers what encompasses computational thinking, the value of planning, and the realities of visualizing messy and unformatted data from the field across different platforms. To more skilled professionals, if you’ve encountered mistakes in my processes, or know more efficient ways to go about doing something, feel free to send me suggestions for improvement.

Lastly, another reason behind embarking on this project was because I thought it would be a good test of what I’ve learned about front-end development over the past couple of months. Programming as a hobby is fun, and I want to work on things that I enjoy.

General Outline

The process by which I planned to build this site involved three main steps:

  1. Build a map of the fieldwork site
  2. Store, display and interact with data
  3. Format and incorporate the relevant data

Further below, you will find how I went about breaking these 3 steps down further, documented in a time-sequential manner, highlighting the main problems faced and my workarounds.

Building A Map

Gathering Resources

At this point, all I had was two sheets of data about the seagrass samples across all 33 sample sites, and the GPS data for the seagrass meadow delimitations. I was not going to work or display my data using spreadsheets. This was my first problem: #1 The data in the sheets needed to be downloaded and reformatted for easier manipulation.

First, I downloaded all the data I required in csv files for later use in Python. Pandas is a package that lets one work with data quickly and easily, and since I was already familiar with it the answer here is obvious: import the relevant data into pandas dataframes and clean up any useless columns. Likewise, I downloaded the gps data in csv files which I messily plotted to get an idea of the shape of the seagrass meadows, not to scale.

My Rationalization

Since I planned to build a map to display the seagrass meadows, I reasoned that the easiest way to display these 33 samples was via a scatter plot. Using an image overlay, I could then display the image over the sample sites. This meant I needed to get an image to overlay.

I had some options: Google maps, OpenStreetMap etc.. What I needed, however, was clear satellite view images which Google provides. Annoyingly, the pesky icons on Google maps cannot be removed so easily. Not wanting to spend too much time trying to fix this minor issue, I scaled the image as large as my desktop screen allowed and saved it; better to capture as much detail as possible with a larger image than scaling up something with lower resolution later. In hindsight, an easy fix would be to input the image to an AI image editor and have it remove the icons. But again, it’s work with rewards that are disproportionate to the effort required.

TL; DR

  • download and clean all data obtained
  • resolved to using a scatter plot with image overlay
  • used a screenshot from google maps, appropriately scaled for later use

Establishing A Scale

My second problem: #2 A scale bar or some form of calibration is required to map distance. This was easily done using the Measure Distance function available in Google Maps. Using the long pipe at the lagoon as reference, I could map the physical distance of the pipe to its distance in the plot. Using Pythagoras’ Theorem,

TL; DR

  • identify scale using the pipe as points of reference

Store, Display and Interact With Data

Visualizing In HTML

Working in Python is straightforward if you only intend to produce a plot for yourself that can be later uploaded to Github. In contrast, I planned to condense everything required for my interactive map into one html file and upload it to the Internet. This meant I was now faced with my third problem: #3 Find a way to visualize changes to this website in real-time.

A quick Google search taught me how to host a html server on my device using Python from the terminal:

python3 -m http.server 8080

With some help from Gemini, I was able to modify boilerplate code to display the image using javascript and enable interactive elements on it (points that reveal information on hover). What and how things are displayed was easily adjusted by editing the relevant styles of the HTML elements.

TL; DR

  • host a server via python to visualize changes
  • overlay the map from earlier onto a plot
  • incorporate interactive elements to the plot

Create A Grid

The lagoon was split into 12 areas along 11 transects. Data was collected along these transects, thus I wanted to display the transect lines using a grid. My fourth problem was this: #4 Create a grid to display the transect lines used for sampling.

Of course, there are different ways to make a grid. One could render lines on the map or use axis grids rotated about an angle, for example. Although I chose the latter, I will not recommend this option for reasons outlined below:

Working with lines has the benefit of not changing the coordinates system of the underlying plot. Alternatively, using a grid requires a rotation but guarantees that lines will stretch across the image without having to specify lengths for each line. However, it was only after displaying the grid that I realized having grid lines stretch across my image looked horrible.

Succumbing to sunk cost, I ended up making many adjustments to the grid by rendering the grids for select y-values, displaying parts at a time only. To make matters worse, the transect lines were not evenly spaced apart along the x-direction so I had to manually adjust the code to display a grid for certain ranges of x-values at a time too. In hindsight, my approach was rather unintelligent and a demonstration of my failure to plan appropriately.

TL; DR

  • visualize the 11 transects and adjust the gaps between them
  • manually adjust the length of grid lines so they only render on the lagoon, and only for certain ranges of x-values

Vector Mapping

The next step was to find a way to render the seagrass meadows and restricted areas on this plot. Abstracting problem five give us this: #5 Find a way to represent areas on the map.

I had GPS data which is basically rows of latitude and logitude data. This can be abstracted as points with and coordinates which meant I could draw shapes on the map. The answer came to me quickly: use the GPS data points as vertices of polygons. Thereafter, I could fill the polygons and use those shapes as representations for the seagrass meadows on the lagoon.

The trouble with the GPS data, however, was that it is messy and does not form nicely closed loops. Some vertices cross one another forming figure-eight patterns that looked terrible. This meant having to extract, trace and reorder the points so that the general area of the seagrass meadows are conserved while allowing for a successive string of points to trace the perimeter of the polygons.

I started by mapping out the restricted area on the plot, using any features on the map overlay to help pinpoint the rectangular area of the restricted area. Given that a rectangle only has 4 points, this was done manually without need for any coding in Python. As I moved on to try mapping the seagrass meadows, I quickly realized it would be silly and impractical to try to convert the GPS data into points on the plot manually.

TL; DR

  • create polygons; fill and trace to demarcate relevant areas
  • map out the restricted area manually
  • find a better way to map out the seagrass meadows

Python To The Rescue

#6 Find a way to convert dozens of latitude and longitude coordinates into and coordinates on my map.

I paused to think about what I already had available: I had plots of the GPS data in Python, albeit represented in latitude and longitude and in a myriad of colours. My overlay plot had a rather poorly scaled coordinate system. Nevertheless, in both plots what stayed constant was the relative distances between points. This meant I could scale the coordinates to fit the integer values of my plot by giving it a minimum and maximum range of values. While this still required some tinkering with polygons to figure out what values were suitable minimum and maximums for each collection of GPS coordinates, it was fairly reliable in displaying the shapes of the meadows.

By now, I also realized that the hover capabilities of the polygons were blocking my cursor from hoverin over some samples, blocking me from viewing the sample data. Since the polygons would not to provide any additional data from hovering over them, I turned off this capability for the polygons representing seagrass meadows.

TL; DR

  • use python to convert coordinate data into pixel by setting customized x and y ranges
  • scale the areas to match original GPS data

Format and Insert Data

Data Entry

With the GPS data completed, the bulk of my work was over. The last major step was to convert all data about the sampling sites (currently in csv format) to a format that can be used in HTML. Now, one way to go about doing this was to manually enter pieces of data into the 30 lines that represent the 30 points (Only 30 because 3 of the samples have 0 seagrass at all). For your reference, in HTML one sample is represented by a line that looks like this:

{ x: 339, y: 148, hover: '\<b\>01-3 (Mean of 5 replicates) \<\/b\> \<\br\>\<\b\>Coverage (%):\<\/b\>\&nbsp; 0 \<\br\>\<b\>Shoot Count:\<\/b\>\&nbsp;	0 \<\br\>\<b\>Shoot Density (/m^2):\<\/b\>\&nbsp; 0 \<\br\>\<b\>Shoot Biomass (g):\<\/b\>\&nbsp; 0 \<\br\>\<b\>Root Biomass (g):\<\/b\>\&nbsp; 0 \<\br\>\<b\>Total Biomass (g):\<\/b\>\&nbsp; 0	' },

As you may have realized, I couldn’t have just copy-pasted the coordinates into the HTML because the line of code for each sample contained both coordinate data and hover data. Each sample was unique; manual entry was out of the question. Hence, we’ve arrived at my penultimate problem: #7 Find a way to transfer the strings of coordinates for my polygons into HTML.

I took a break to reflect on a better approach. This problem basically requires me to export some numbers and insert it into 30 different strings. My useful data is contained in a pandas dataframe, which can easily be iterated through anyway. I resolved to use f-strings to fix my issue, printing out the 30 lines using a for loop.

The next step requires a way to store the strings of the data about the 30 sample points and the 30 sample points themselves. Lists were tedious if I wanted to copy and paste things into the HTML file after because it may not render the way I want it to in the console. I needed a way to store strings… and came to a realization: I was already using notepad to store csv files, so why not use txt files to store my strings instead. It was that simple.

After tweaking the df to store any numbers as strings—rather than numeric data, because I needed group number to be displayed with leading zeros and wouldn’t do any further mathematical operations anyway—I instructed Python to write the data the into a txt file.

The final problem was an easy one I’ve solved countless times prior: #8 Find a way to insert the relevant data after a specific word in a string. Here’s the solution: Parse through the string, look for the keyword (hover:) and then insert the relevant data and remove any old values. A for loop iterated through each row and wrote the output in a new txt file which allowed me to conveniently paste the 30 lines of code into my HTML file. Another advantage of txt files is that they can be opened with notepad.exe, and so any artifacts that needed to be replaced could be removed using the Replace operation anyway. And with that, we’re done!

TL; DR

  • store the strings for each point and for the data to be inserted into separate txt files
  • iterate through the rows in my txt files to insert data to where they need to be
  • paste the result into the original html file

Add A Write-up

I added a header with logos and page title so the website wasn’t just a map with funny shapes on it. The page colours were chosen to compliment the image but also stick to the brand colours of SPS. A simple write-up conveying background information and methodlogy were included for completion.

Upload to Cloudflare

Finally, I wanted to host this page somewhere the internet. Since I was already familiar with Cloudflare, this was another easy answer. I created a repository on Github to track changes, and then linked it with my Cloudflare account to host the page automatically. Within minutes, my weekend’s worth of hard work was live, and this project considered complete.