GeoServer and Cesium for Terrain Rendering

Hello!

I'm trying to build a terrain visualization stack on my local machine using Cesius and GeoServer (using other proprietary servers is not an option). Unfortunately, I didn't have much luck.

1 - Why was WebMapServiceTerrainProvider removed from the master branch? In an older discussion here in cesium-dev there was a patch to make it work on latest Cesium. Is there any other problem with it?

2 - What's the point of using BIL format on GeoServer instead of plain PNG or other format? I seem to understand it has something to do with terrain data resolution, but I'm not quite sure.

I'm sorry for the naive questions, but I would like to understand :slight_smile:

Alessio

Hello I'm implementing a solution a terrain provider which uses Geoserver.
This solution works with bil,gif,png ang jpeg formats. The bil format is an array directly directly machine readable. The others formats need a little transformation (picture to array).
I'm using SRTM 250 meters in my tests.
Best regards

Thank you very much for your reply. I just found your repository and compiled Cesium with GeoServerProvider support.

I know this is a bit OT, but can you point me to a guide or something to understand how to import SRTM data to GeoServer?

Also would you please post a snippet on how to use your provider in Cesium?

Thank you very much!

Hello,
1- you can download SRTM data at http://srtm.csi.cgiar.org/
2- you need to install GDAL tools and python to work with SRTM http://trac.osgeo.org/gdal/wiki/DownloadingGdalBinaries
3- you need to install geoserver image pyramid plugin
4- a guide is available at http://docs.geoserver.org/latest/en/user/tutorials/imagepyramid/imagepyramid.html

I didn't make a documentation to my implementation of geoserver terrain provider. I'll write it soon. I saw that you find my github.
Once you defined a layer of SRTM in geoserver, you could use this snippet:

var terrainProvider = new Cesium.GeoserverTerrainProvider({
        url : "http://localhost:8080/geoserver/elevation/wms",
        layerName: "SRTM250",
        maxLevel:4,
        heightmapWidth:65
    });
    centralBody.terrainProvider = terrainProvider;

Thank you for your reply and support. Unfortunately, even though I've succeeded in visualizing something, the resulting rendering is not correct. I think I'm missing something here. That's what I did so far:

1. Installed both the DDS/BIL and tyhe Image Pyramid plugin
2. Downloaded GDAL and SRTM250
3. Used this command (taken from your previous post):
gdal_retile.py -v -r lanczos -levels 6 -ps 2048 2048 -co "TILED=YES" -co "COMPRESS=DEFLATE" -co "ZLEVEL=4" -ot Int16 -targetDir pyramid srtm250.tif
4. Added a new layer in GeoServer
5. USed the code you provided

Unfortunately I get glitchy visualization (see this picture: http://postimg.org/image/bj7c5mrcv/ ) and many errors in the JS Console (see the picture).

I think the visualization is wrong also because I see lakes textures over montains geometries..!

I have sames glitches:
- Mount Everest in sandCastle with cesium provider: http://postimg.org/image/e33fiqlg5/
- Same spot with geoserver terrain provider, 250 m SRTM and maxlevel=4: http://postimg.org/image/j9ekts07p/ (maybe sampling of terrain change geometry of terrain??)
- Same spot with geoserver terrain provider, 250 m SRTM and maxlevel=5: http://postimg.org/image/b56gp1dsl/
- Same spot with geoserver terrain provider, 250 m SRTM and maxlevel=7: http://postimg.org/image/jpzsg7nyt/
- Same spot with geoserver terrain provider, 90 m SRTM and maxlevel=8: http://postimg.org/image/em8is0cut/

I suppose that quality of tiles influence sampling of terrains and so for the maxlevel that you can define in geoserver terrain provider (it's experimental).

Also I regenerate my tiles with lower dimension (1024X1024) from ASCII SRTM 250m. This operation takes lots of time on my laptop (10hours and the first level is not coompleted). After I generate tiles from SRTM 90m.
I'll give you my conclusion or my update on my github repository.

I'm not quite sure it's a matter of data resolution: I've slightly modified the old WebMapServiceTerrainProvider and made it work with the current cesium build (b-24). It's not perfect, but doesn't seem to show the same glitch. Can you confirm?

The working WebMapServiceTerrainProvider is available here: https://dl.dropboxusercontent.com/u/10671464/WebMapServiceTerrainProvider.js

I should try with snippets of code where it change Endianness of array. I can't evaluate while my computer works on generation of tiles...
But by the way, your geoserver can product data in application/bil16?

Yes, seems like a reasonable place to look for a bug to me. Yes, my server can generate application/bil16 using this plugin (http://docs.geoserver.org/stable/en/user/community/dds/index.html)

But you can also try in my implementation before me.In Source / Scene /WmsParserHelper.js at line 322 you've got a static array in which is defined how to manage data array format from web map service protocole. This array is not complete but you can define a new type on same model to give to constructor of geoserverTerrainprovider:
var terrainProvider = new Cesium.GeoserverTerrainProvider({
        url : "http://localhost:8080/geoserver/elevation/wms",
        layerName: "SRTM250",
        maxLevel:4,
        heightmapWidth:65,
        formatArray: {
        format : "application/bil16",
        postProcessArray : function(bufferIn) {
// bil is 16 bits big endian cell
                var viewerIn = new DataView(bufferIn);
                var littleEndianBuffer = new ArrayBuffer(bufferIn.byteLength);
                var viewerOut = new DataView(littleEndianBuffer);
                //time to switch bytes!!
                for (var i = 0; i < bufferIn.byteLength; i += 2) {
                    viewerOut.setInt16(i, viewerIn.getInt16(i, false), true);
                }
             
            return new Uint16Array(viewerOut.buffer);
        },
        terrainDataStructure : {
            heightScale : 1.0,
            heightOffset : 0.0,
            elementsPerHeight : 1,
            stride : 1,
            elementMultiplier : 256.0,
            isBigEndian : false
        }
    }
    });
Moreover, you'll need to change:
-line 30 from this.getMetaDatafromURL(description.url, description.proxy);
to
this.getMetaDatafromURL(description.url, description.proxy,description);
- line 32 from this.getMetaDatafromXML(description.xml);
to
this.getMetaDatafromXML(description.xml,description);
- line 36 from WmsParserHelper.prototype.getMetaDatafromURL = function(urlofServer, proxy) {
to
WmsParserHelper.prototype.getMetaDatafromURL = function(urlofServer, proxy,description) {
- line 47 from that.getMetaDatafromXML(xml);
to
that.getMetaDatafromXML(xml,description);

Sorry for these requests but I can't manage to test now. I will have more time during week end.

Can you send pictures with your modifications?

Does not seem to work any better: Italy is flat except on the coast (see http://postimg.org/image/vw1y4jg27/ )

Hello,
I study problems with my implementation thanks to your snippet. I found that array of data doesn't have the expected size. In this case, the array must not be used (problem comes from geoserver).
for example, if the size of tile is 64 pixels X 64 pixels, the size of the array should be 64*64*2=8192 bits. BUT sometimes the returned array from server is 4 times greater.
So changes were made to cope this inconsistency. Nevertheless, with this segregation of arrays, the terrain cannot have all his tiles defined and the result is so so...

I'll clean and send my code tomorrow. Thanks for your help, I'll try to found an other solution.

Thank you for your support and for figuring this out. I'd be willing to test the new code whenever you are ready.

Just out of curiosity: how are you planning to handle "nodata" values coming from GeoServer? (i.e. points for which a measurement is not available, for example the sea)

Hello,
I pushed an update on my git. My next problem is to force a promise from when.js to be synchronous. With this, instead of transfering a false array where each cell have an altitude of 0 meter, I will indicate to Cesium that the request must be remade. In this way, the altitude of each cell of the map will be consistent, even if camera moves.
So if any one know how to wait until a promise is fulfilled (make it synchronous), I'm interested

The array providen by geoserver follow bil format where each 2 bytes match a (short) int which is "equal" to the high of the cell (more or less). Each pixel have a color on 16 bits so I presume that there is no "nodata". Maybe nodata is translate by a cell of 0 meter?

That's odd, but for some particular reason, no geometry is generated on my machine with your latest version. It looks like heightBuffer contains only null data, even though bil tiles are correct.

As for the nodata, my GeoTiff file has a NODATA Value of "2147483647" (which is 2^31 - 1). By serving the tiles through the BIL plugin, since this value does not fit into a 16 bit location, NODATAs are representing using the 32768 value (2^15).

Which results in water areas literarly in orbit (see http://postimg.org/image/8ddu369vn/ ). I'm trying to figure out how to fix this. Has anybody any idea?

My plan is to build some sort of "water mask" out of the array data, in order to take advantage of Cesium cool water effects.

Hello, what you wrote is very interessing because in my implementation if a cell is higher than 20000 m, I said that the bil was wrong (see cesium / Source / Scene / WmsParserHelper.js line 303). Also, what is the best pratice? The cell should be at 0 or ...?

That's very interesting: that's probably why I'm not seeing anything on screen. What I'm doing is processing the array I receive from GeoServer and setting every location which has a nodata value (in my case 32767) to 0. I'm setting up a water mask as well, and it seems to work very well with my dataset (see http://postimg.org/image/kir6d2tvz/ ).

Unfortunately, I'm not sure what I'm doing could be considered a best practice. I'm not sure setting the "no data" locations to 0 is the right thing to do, as there could be legit places with the 0 value. So I'm not quite sure that's right nor where to look to be sure. Maybe someone else from the CesiumJS community might know a little bit more about this stuff.

Hi folks - when you are satisfied with your implementation, we’re happy to list it as a Cesium plugin for the benefit of the community. Just open a pull request to cesium-plugins-list to add it to the list.

Thanks,

Patrick

Hello, with a better resolution of SRTM map, (90m instead of 250m) I have good result and relief is maintained against camera movement!!
So soon I'll follow Patrick Cozzi advice and I'll pull a new Cesium plugin for geoserver.