maplibre / maplibre-gl-js

MapLibre GL JS - Interactive vector tile maps in the browser
https://maplibre.org/maplibre-gl-js/docs/
Other
6.69k stars 717 forks source link

Blocky terrain when using high resolution data #4900

Closed rbrundritt closed 3 weeks ago

rbrundritt commented 3 weeks ago

I've been testing several platforms to generate 3D terrain using high resolution DEM and Lidar data (0.5M to 1M resolution). I'm finding that when I load this into MapLibre and zoom in such that the high-resolution data is loaded, the map terrain becomes very blocky. I believe this is due to how the terrain mesh is generated from the height map image of the tile (I encountered similar issues with height maps in Babylon and three.js). I'm generating tiles that 512x512 in size down to zoom level 17 which should give me a resolution of just over 1 meter. Visually inspecting the tiles the data looks pretty smooth/gradient and not blocky. I created the tiles using rio-rbgify and pretty sure that's not the issue.

I also created quantized mesh tiles from the same DEM data and the difference in quality is very noticeable (it looks beautiful in comparison).

Actual behavior: MapLibre

HighResolutionTerrain_MapLibre

Desired behavior: What I achieved using quantized mesh and Cesium

HighResolutionTerrain_QuantizedMesh

Steps to Trigger Behavior

maplibre-gl-js version: 4.7.1

Here is a simple app and the generated tiles that reproduces this issue. MapLibre_HighResolutionTerrain.zip

Here is the DEM file I used to create the tiles: club_dem_54453779.zip

Link to Demonstration

I've also uploaded a version of this here: https://rbrundritt.azurewebsites.net/Demos/MapLibre/Terrain/tile_test.html#20.14/47.5329806/-122.1391759/-60/85

Thoughts on resolution

larsmaxfield commented 3 weeks ago

TL;DR: Issue with rio-rgbify encoding interval, not MapLibre.

The source DEM file appears to be a 32-bit floating TIFF when I read it with OpenCV in Python:

import cv2

image = cv2.imread(r"club_dem_54453779.tif", -1)  # Read unchanged
print(image.dtype)  # Show type
print(image[100, 200])  # Show value at row 100, column 200
float32
188.16412

From what I understand, rio-rgbify defaults to an interval of 1 for encoding. If you keep this default interval, it will essentially round those elevation floats to the nearest whole number. This quantization then ultimately causes the blocky terrain you see.

To smoothen the terrain, you could try encoding at a finer interval with rio-rgbify by specifying interval=0.1. This will round those elevation floats to the nearest tenth. This is actually MapLibre's default for decoding terrain.

If you go finer in the interval (0.01, 0.005, etc.) to get an even smoother terrain, you will need to set the terrain encoding to "custom" and specify the encoding values in terrainSource like this:


    const interval = 0.01;
    const baseShift = 0.0;  // rio-rgbify default
    const redFactor = 256.0*256.0*interval;
    const greenFactor = 256.0*interval;
    const blueFactor = interval;

...

                terrainSource: {
                    type: 'raster-dem',
                    tiles: ['tiles/{z}/{x}/{y}.png'],
                    tileSize: 1024,
                    bounds: [-122.156627, 47.527467, -122.127463, 47.540503],
                    minzoom: 13,
                    maxzoom: 18,
                    encoding: "custom",
                    redFactor: redFactor,
                    greenFactor: greenFactor,
                    blueFactor: blueFactor,
                    baseShift: baseShift,
                },
acalcutt commented 3 weeks ago

I've been messing a bit with swissALTI 0.5 dems here

In my first attempt I had made this terarium terrain down to zoom 17 and experience the blocky terrain you mentioned. https://stackblitz.com/edit/web-platform-qq1it1?file=index.html

I just did a new attempt in terrainRGB down to zoom 16 and it seems like it doesn't seem like it experienced that issue https://stackblitz.com/edit/web-platform-cwvqs7?file=index.html

In my first attempt, the terrariun mbtiles, the interval isn't really adjustable so I assume that is like using the default 1 interval. In the second attempt I switched to terrainrgb with the maplibre 0.1 interval defaults. I also tried lowering the zoom 1 level based on some discussions with another user on slack (which I can't seem to find now)

There is also an option now to make a custom interval now below 0.1 if you wanted, but in my testing it seems like it increases file size.

Also, this discussion on slack https://osmus.slack.com/archives/C03TFH5NE83/p1725381818028079