Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a hillshade layer #1157

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open

Add a hillshade layer #1157

wants to merge 13 commits into from

Conversation

jleedev
Copy link
Member

@jleedev jleedev commented Sep 18, 2024

What's going on here:

  • Add a hillshade layer using elevation from https://registry.opendata.aws/terrain-tiles/. It goes up to z15 but limit to z13 for aesthetics and data quality, and perhaps performance.
  • Upgrade maplibre-gl to get anti-anti-fingerprinting and performance improvements.
  • Add a control to toggle the hillshade. Maplibre conveniently has a terrain icon.

TODO here:

  • Remove inline colors
  • Off by default

Future work:

  • Use the prebaked raster hillshade if we can make it blend.
  • Ponder the balance between physical and cultural map features — to me, solid green parks on top of hillshade starts to blur the line.
  • Ponder the balance between global landcover and hand-mapped natural features.
  • Develop the terrain toggle button some more — de-emphasize landuse fills to mainly outlines; toggle hillshade and global landcover (and contour lines); desaturate road classes; emphasize cycle and hiking routes; introduce peaks and ridges and valleys and islands; etc.
  • Store the selected map style in the URL hash.

Fixes #780

Copy link
Member

@1ec5 1ec5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a beautiful starting point. I shared some screenshots with the AARoads Wiki folks and the reception has been quite positive.

@@ -83,6 +84,8 @@ export function build(locales) {

lyrFerry.ferry,

lyrHillshade.hillshading,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Terrarium tiles are a bit sensitive to artificial structures like dams that look a bit strange over water, like the Lock and Dam Number 12 on the Mississippi River at Bellevue, Iowa:

A big embankment formed by the hillshading layer in the middle of the river.

If we move this layer lower in the stack, such as right below water_line, then the waterbody layer would tend to obscure this and other anomalies over water:

Most of the embankment gone, save for a small secondary protrusion on the landward side.

However, the tradeoff is that the “hill” shading would no longer double as a bathymetry layer at sea:

Before After
Seamounts and ridges on the sea floor around the Bahamas. Solid water fill.

While bathymetry is pretty cool, I think it’s far enough out of scope of a (land) transportation map as to be a distraction. It also looks a bit discolored without hypsometric tinting, whereas on land the absence of that tinting is quite reasonable on a non-topographic map. (General-purpose overview maps would prioritize showing the continental shelf using hypsometric tinting before bothering with elevation shading.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this means farewell to "tunnels under terrain", which was pretty cute.

It's a little odd to see the ocean floor load and then get covered with water but it makes sense when you think about it.

Now there are no longer enormous glitches all over the entire ocean, just a few.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally, #995 gives tunnels a different treatment that wouldn’t really exhibit the cute underlapping effect anyways.

The flash of bathymetry feels like an unsubtle Easter egg, of which we have too few in this project. 🙃 In seriousness, maybe we could load the DEM source and hillshade layer only after the main sources and layers load, to avoid jank and unnecessary bandwidth and power usage. To avoid distracting the user on land, we could fade in the hillshade layer with a hillshade-exaggeration-transition property, akin to deferred 3D terrain in F4Map, though I don’t know how well that would perform.

Comment on lines 15 to 17
"hillshade-shadow-color": "rgba(102,85,51,1)",
"hillshade-highlight-color": "rgba(255,255,204,1)",
"hillshade-accent-color": "rgba(0,0,0,1)",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With these colors, some road lines don’t have a lot of contrast against the background, while the knockouts around labels stand out enough to look like halos again. Maybe we could try moving all three colors closer to the preexisting background color. This could also help the layer recede into the background while still conveying information about rugged terrain to those who look for it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tamed it a lot. Since the hillshade is now restricted to the luminance space, I increase the exaggeration at low zooms since it no longer fights with other colors. It fades away at high zooms to avoid ruining the foreground by being a murky blob, and to avoid getting confused by things like stadiums.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the flat lands, the old version made it actually look like a crumpled paper map :-D

@jleedev jleedev added enhancement New feature or request labels Sep 19, 2024
}

_onClick = () => {
if (this._map.getLayoutProperty(this._layerId, "visibility") == "none") {
Copy link
Member

@1ec5 1ec5 Sep 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Astonishingly, my iPhone 8 is able to keep up with the map with the hillshading enabled, but it turns the battery meter into a sad trombone. Should we hide the layer by default on mobile devices, or somehow detect less powerful devices?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I upgraded maplibre-gl, the changelog mentions some performance improvements, want to try again?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first render is reasonably quick, but there’s a lot more stuttering while panning than on main. The battery goes down by a few percent after about 20 seconds of panning around the Appalachians at zoom level 8. That’s still much better than the majority of ad-laden news websites these days, but the stuttering is a bummer.

It still avoids strong artifacts and maplibre-gl 4 is using much less
memory.

Reduce maximum hillshade-exaggeration from 1 to 0.5.
The default is 0.5; going higher throws away contrast.
@quincylvania
Copy link
Contributor

The OSM US Tileservice now has pre-rendered hillside and contour lines available (thanks @jake-low!) Using these would significantly improve client-side performance. If the hillshade parameter aren't to your liking then we can consider rendering another set.

    "hillshade": {
      "type": "raster",
      "url": "https://dwuxtsziek7cf.cloudfront.net/hillshade.json"
    },
    "contours": {
      "type": "vector",
      "url": "https://dwuxtsziek7cf.cloudfront.net/contours-feet.json"
    }

@1ec5
Copy link
Member

1ec5 commented Sep 20, 2024

There’s some discussion about both approaches in #780. Dynamic hillshading with a hillshade layer is already a compromise for performance compared to the 3D terrain prototyped in #341. A visual comparison would be helpful. Dynamic hillshading retains the benefits of easier customization (especially if anyone forks the style for a slightly different appearance) and dynamic lighting when rotating the map, but the effect is undermined when tilting the map. That said, I don’t know how frequently people rotate or tilt this map, since the paper map aesthetic doesn’t necessarily promote that kind of usage. Who knows, if drawing performance turns out to be the main issue, maybe we could use the prerendered hillshading on mobile devices and the dynamic hillshading otherwise.

@jleedev
Copy link
Member Author

jleedev commented Sep 20, 2024

The prerendered tiles are a lot harder to wrangle the desired look out of. Ideally we want to blend it with the beige background color, solid fills like aerodrome, park, and the fat purple boundary casing. (Or at least, that's what happened immediately when I slotted the dynamic hillshade on top of those layers, and I like it.)

As a workaround, I tried to make the background color very dark and saturated and put the hillshade jpg with 96% alpha -- this doesn't look great, and the aggressive background shows through when the hillshade tiles aren't loaded.

Another way to make it work is to change all those background layers to be alpha blended on top of the hillshade. Might work but the colors would all end up a bit murky; they're currently fully opaque and paint on top of each other.

Just spitballing, what if the hillshade tiles were instead solid black with alpha channel, so that they can blend with a background color more easily. Or perhaps some color instead of black, so that the raster-* layer properties can be used. (Either webp or png?)

(It would be very nice if maplibre supported a color matrix or blend modes to be able to do this, but that's far away.)

@1ec5
Copy link
Member

1ec5 commented Sep 20, 2024

If performance is the only concern, I’d favor merging this feature as is, but disabled by default (again, maybe only for certain user agents). Then we could figure out how to make it more performant or provide alternatives before enabling it by default.

@zekefarwell zekefarwell marked this pull request as ready for review September 20, 2024 15:33
@zekefarwell
Copy link
Collaborator

zekefarwell commented Sep 20, 2024

Oops I didn't mean to click the ready for review button. Maybe that's ok though? Anyway feel free to change it back if not...

@zekefarwell
Copy link
Collaborator

zekefarwell commented Sep 20, 2024

Overall, I'm really liking the look of Americana with hill shading. It does diverge from the paper highway map aesthetic, but I've always felt that some level of hill shading would help fill in the empty spaces. It's just a matter of balancing the shading so it's not too overpowering, but still visible enough. This style isn't an outdoors focused topo map after all.

A visual comparison would be helpful. Dynamic hillshading retains the benefits of easier customization (especially if anyone forks the style for a slightly different appearance) and dynamic lighting when rotating the map, but the effect is undermined when tilting the map.

Here's a comparison of the same spot with this PR and in OpenTrailMap which uses the raster hillshade tiles @quincylvania mentioned. Location: PR preview, OpenTrailMap.

This PR Open Trail Map
americana-hillshade-pr-1157 ⬆️ north up opentrailmap-hillshade ⬆️ north up
americana-hillshade-pr-1157-180-deg ⬇️ north down opentrailmap-hillshade-180-deg ⬇️ north down

Subjectively, the OpenTrailMap hillshading is a bit more pleasing to my eye. I'm not entirely sure why this is. Maybe uni-directional vs multi-directional lighting? On the other hand, the OpenTrailMap shading does look a bit strange when the map is rotated 180 degrees. It is quite a cool how the dynamic lighting implemented in this PR causes the hillshading to change as the map is rotated.

That said, I don’t know how frequently people rotate or tilt this map, since the paper map aesthetic doesn’t necessarily promote that kind of usage.

Unless a map has 3D terrain and buildings1 I find map rotation and tilting to be more of a fun gimmick than a useful feature. I mostly trigger it by accident when using a two finger pinch to zoom on a touch screen and the gesture gets interpreted as a slight rotation or tilt as well. I'd be fine with rotate and tilt being disabled.

Footnotes

  1. Yes Americana technically does have 3D buildings set to a very short height. I'm referring to full height 3D buildings.

@1ec5
Copy link
Member

1ec5 commented Sep 21, 2024

It does diverge from the paper highway map aesthetic, but I've always felt that some level of hill shading would help fill in the empty spaces.

Actually, there’s quite a bit of precedent for road maps out west to include subtle hillshading. Aside from filling in blank space, it communicates to the motorist whether they’ve got a climb or potentially some challenging winter road conditions ahead of them. What’s new is the modicum of hillshading in flatter regions, where paper maps tend not to bother. But for us, it’s all one map.

Subjectively, the OpenTrailMap hillshading is a bit more pleasing to my eye. I'm not entirely sure why this is. Maybe uni-directional vs multi-directional lighting?

I assume it has more to do with us fidgeting with the exaggeration, color, and maximum zoom level (which affects the resolution).

@jleedev
Copy link
Member Author

jleedev commented Sep 23, 2024

I darkened the shadow color somewhat, and it's a closer match to the OpenTrailMap screenshot. I think I was trying to avoid broadly darkening the existing background color, but it's probably fine. It's no darker than buildings right now. (Some labels are already difficult to read and this change doesn't make it worse.)

@jleedev
Copy link
Member Author

jleedev commented Sep 23, 2024

As an example, here are the artifacts seen at higher zoom levels (with colors turned up for emphasis):

15 12
Screenshot 2024-09-23 12 37 10 Screenshot 2024-09-23 12 38 18

Limiting the resolution smooths out these weird street-shaped ridges. Could maybe go to 13 but by that point it's starting to fade out, so it would just be unnecessary tiles.

@zekefarwell
Copy link
Collaborator

zekefarwell commented Sep 23, 2024

There are definitely some bad artifacts in this DEM. In the US it seems mostly pretty okay, but in other parts of the world it can be a bit janky. Area of the Alps for example:

Screenshot_23-9-2024_13946_preview ourmap us

location: https://preview.ourmap.us/pr/1157/#map=12.14/46.78211/8.62852

@jake-low
Copy link

@jleedev: seems like you have a good grasp already of the limitations of prerendered hillshade tiles, but I just wanted to reply with some details about what I did for OpenTrailMap in case it's useful to you.

As you mentioned, there are basically two ways MapLibre can be used to blend a prerendered hillshade tileset with another style layer like a fill layer: either you put the hillshade on top, or you put the fill layer on top. Both have drawbacks. If the hillshade is on top, it washes out the colors (reduces the saturation) of the fill layer. If the fill layer is on top, it reduces the contrast of the hillshading.

You can try to compensate against either effect, by increasing the base saturation of the fill layer style, or the base contrast of the hillshade rasters, respectively. But if the fill layer doesn't cover the whole planet (i.e. there are areas of the map that aren't covered by a polygon in the layer source), then you'll see boundaries in the rendered map where either the saturation of the fill layer or the contrast of the hillshade changes undesirably, depending on which choice you make.

Another factor that might influence this choice is how MapLibre handles opacity blending. When you set a fill layer to 50% opacity, it blends each feature in the layer at that opacity, so areas where features overlap will have a higher total opacity. This may or may not be desired.

What I've personally found to work pretty well, and what I did for the hillshading I worked on for OpenTrailMap, is to set the hillshade layer above the background layer and above fill layers for things like landcover and landuse, and then to increase the saturation of colors used in those layers to compensate. I usually set the hillshade layer at between 25% and 50% opacity. For OpenTrailMap I chose 50%. For a map where the terrain wasn't a primary focus I'd probably choose something closer to 25%. I also usually make it so that the hillshade layer reduces opacity further as you zoom in beyond the hillshade tileset's maximum zoom, in order to hide pixelation that appears.

I find this the nicest approach from a cartography perspective because you still have direct control over the colors of all the layers placed underneath the hillshade (and don't have to worry about how they'll blend with each other, which can lead to a "muddy" effect as you mentioned). And the hillshade raster that's overlaid on top has a predictable effect on the colors below: it desaturates them by a factor that's roughly proportional to the opacity of the hillshade. That predictability makes the effect easier to correct for when picking colors.

Just spitballing, what if the hillshade tiles were instead solid black with alpha channel, so that they can blend with a background color more easily. Or perhaps some color instead of black, so that the raster-* layer properties can be used. (Either webp or png?)

That's an interesting idea. PNG would probably not be a good choice, since it would dramatically increase the size of the tileset (hillshading has lots of high frequency detail that compresses really well as JPEG). WebP might work, though I haven't tested whether MapLibre supports alpha channels in WebP images. You'd still have to deal with the same tradeoff described above, but only in areas with lots of relief - flat areas would be mostly transparent. I don't have an intuitive idea of whether this would look pleasant or not, but it could be interesting to explore that approach.

Really the purpose is to avoid filling the screen with dark gray when overzooming on a hill (and instead to fill the screen with a nice beige).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add hill-shading to expose terrain
7 participants