Styling Oceans with Bathymetry in MapLibre

Aidan H
4 min readJun 11, 2023

--

MapLibre GL JS is the standard for open source mapping on the web. Forked in response to Mapbox GL JS’ 2020 change to a proprietary license, MapLibre supports most features of Mapbox without the cost and terms. However, some features require a specific data source.

Bathymetry — ocean depth — data visualization improves aesthetics at low zoom levels by adding detail to what would otherwise be plain ocean. MapTiler makes it easy to add a bathymetry source to your style.

Let’s get started!

Adding the source in MapTiler

You’ll need a free MapTiler account and a custom map.

Open the map in the advanced style editor, click on Data Sources in the toolbar and then scroll down to select Ocean.

You should now see #ocean listed among your Active Sources.

Don’t forget to save and publish your style!

Adding the layer in MapLibre

With the data source in place, we’re ready to display the bathymetry data on our map. Open the source for your MapLibre map or create a new one, and update the style URL to match the MapTiler style you just created.

Add a fill layer with source "ocean”, and source-layer "contour".

map.on("style.load", () => {
map.addLayer({
id: "bathymetry",
type: "fill",
source: "ocean",
"source-layer": "contour",
paint: {
"fill-color": "#111"
},
});
});
This map is interactive — give it a spin!

Our bathymetry layer is now visible — but it’s all black.

To visualize depth data with different colors, let’s write a step expression to change the fill-color property according to the value of the source’s "depth" field.

paint: {
"fill-color":
[
"step",
["get", "depth"],
"#000",
-6000, "#111",
-5000, "#222",
-4000, "#333",
-3000, "#444",
-2000, "#555",
-1000, "#666",
-500, "#777",
-200, "#888",
],
},

Congrats, you’ve made a bathymetry visualization!

But there’s still two issues. Can you guess what they are before scrolling down? Perhaps if we zoom in around Florida?

That doesn’t look quite right…

Did you notice that the edges of our oceans are missing? That’s because shallow oceans (<50m deep) aren’t included in MapTiler’s "ocean"source. Less surprisingly, neither are lakes.

We can display both by changing the color of the map’s water layer, which is visible beneath our added bathymetry layer. We could use the same color as our most shallow bathymetry layer, but I like to add one more slightly lighter shade:

map.setPaintProperty("water", "fill-color", "#999");
Much better! Note the color of lakes matches shallow oceans.

Have you noticed the remaining issue? This is an insidious one — do you see anything different at low zoom levels?

If you noticed that ocean labels have disappeared, you’re right! This is because our new bathymetry layer has been added above all other layers, including label layers.

To be able to see our bathymetry layer without obscuring labels, we need to place it directly above the water layer.map.addLayer accepts a second optional argument that is the layer that will be above our new layer, so to place our layer directly above the water layer we pass in the layer currently directly above water. You can see the layer stack in the sidebar of the MapTiler style editor or with map.getStyle().layers.

As of June 2023, in MapTiler’s Streets style, this is theaerowaylayer. In the Light style, it is waterway. Let’s put our layer in its place:

map.on("style.load", () => {
map.addLayer({
id: "bathymetry",
type: "fill",
source: "ocean",
"source-layer": "contour",
paint: {
"fill-color":
[
"step",
["get", "depth"],
"#000",
-6000, "#111",
-5000, "#222",
-4000, "#333",
-3000, "#444",
-2000, "#555",
-1000, "#666",
-500, "#777",
-200, "#888",
],
},
}, "waterway");
});

Labels are back, and there you have it!

Now you’re ready to style your bathymetry to suite your map.

Editing the fill-color expression can be tedious. But never fear: here’s an Observable page to play around with different color ramps:

Next up: bathymetry in halftone!

--

--