Unable to view high-resolution map in offline mode

Hi,

I tried the instructions in the Offline Guide for approx a 20 sq km area in Lhasa, China. However, the results that I got aren’t looking good at all.

Case 1: This results in a very poor/low resolution image.

let viewer = new Cesium.Viewer('cesiumContainer', {
    baseLayer: Cesium.ImageryLayer.fromProviderAsync(Cesium.TileMapServiceImageryProvider.fromUrl(Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII'))),
    baseLayerPicker: false,
    geocoder: true
})

let tileset
(async () => {
    tileset = await Cesium.Cesium3DTileset.fromUrl('http://localhost:3003/lhasa-19sqkm/3d-tiles-only/tileset.json')
})()

try {
    viewer.scene.primitives.add(tileset)
} catch (ex) {
    // ...
}

Case 2: Assuming that the Natural Earth II data used above was the reason for the observed low resolution, I next replaced it with my own clipped version that I created via Cesium Ion:

    ...
    baseLayer: Cesium.ImageryLayer.fromProviderAsync(Cesium.TileMapServiceImageryProvider.fromUrl(Cesium.buildModuleUrl('http://localhost:3003/lhasa-19sqkm/imagery-and-terrain/tileset.json'))),

in the above constructor. All that I saw then was a solid blue color, no matter how much I zoomed out!

My question is twofold:

  1. What am I doing wrong in Case 2?
  2. What do I need to do both in Cesium Ion and CesiumJS to be able to view the highest possible resolution for an arbitrary region in offline mode, given that Bing and Google map data isn’t available for creating clips for offline use?

The clip btw was created in Cesium Ion with:

  • Sentinel-2 as the source for Imagery and Terrain, and
  • Cesium OSM Buildings for 3D Tiles.

Hi there,

From a quick glance, it looks like you may be intermingling 3D Tiles and Imagery Providers, which are not compatible.

My guess would be that all of the exports are packages in the 3D Tiles format, and you should instead try loading the tileset.json file with Cesium.Cesium3DTileset instead.

I’ve moved this over to the Cesium ion category so these Cesium ion team can confirm.

My guess would be that all of the exports are packages in the 3D Tiles format, and you should instead try loading the tileset.json file with Cesium.Cesium3DTileset instead.

Yes I can confirm that this would be the function you would have to use.

Since clips are a combination of terrain + imagery, they are exported in 3D Tiles format which would need to be loaded from the function stated above by Gabby. The TileMapServiceImageryProvider is for a different format that Cesium ion uses internally.

Hi @Gabby_Getz, @Ankit_Trehan ,

Appreciate your replies.

I get the following errors when I replace my original code

baseLayer: Cesium.ImageryLayer.fromProviderAsync(Cesium.TileMapServiceImageryProvider.fromUrl(Cesium.buildModuleUrl('http://localhost:3003/lhasa-19sqkm/imagery-and-terrain/tileset.json'))),

with

baseLayer: await Cesium.ImageryLayer.fromProviderAsync(Cesium.Cesium3DTileset.fromUrl('http://localhost:3003/lhasa-19sqkm/imagery-and-terrain/tileset.json'))
Cesium.js:197205 TypeError: Cannot read properties of undefined (reading 'projection')
    at ImageryLayer._createTileImagerySkeletons (Cesium.js:196565:58)
    at Cesium.js:199986:19
    at QuadtreePrimitive.forEachLoadedTile (Cesium.js:202131:9)
    at GlobeSurfaceTileProvider._onLayerAdded (Cesium.js:199985:22)
    at GlobeSurfaceTileProvider._onLayerShownOrHidden (Cesium.js:200033:12)
    at Event.raiseEvent (Cesium.js:16278:22)
    at Cesium.js:201228:31
    at Event.raiseEvent (Cesium.js:16278:22)
    at handlePromise (Cesium.js:197216:28)
handleError10 @ Cesium.js:197205
handlePromise @ Cesium.js:197218
await in handlePromise
ImageryLayer.fromProviderAsync @ Cesium.js:196518

Cesium.js:230794 An error occurred while rendering.  Rendering has stopped.
TypeError: Cannot read properties of undefined (reading 'projection')
TypeError: Cannot read properties of undefined (reading 'projection')
    at ImageryLayer._createTileImagerySkeletons (http://localhost:3000/cesium/Cesium.js:196565:58)
    at GlobeSurfaceTile.processImagery (http://localhost:3000/cesium/Cesium.js:190931:24)
    at GlobeSurfaceTile.processStateMachine (http://localhost:3000/cesium/Cesium.js:190891:46)
    at GlobeSurfaceTileProvider.loadTile (http://localhost:3000/cesium/Cesium.js:199388:34)
    at processSinglePriorityLoadQueue (http://localhost:3000/cesium/Cesium.js:202826:20)
    at processTileLoadQueue (http://localhost:3000/cesium/Cesium.js:202791:26)
    at QuadtreePrimitive.endFrame (http://localhost:3000/cesium/Cesium.js:202250:5)
    at Globe.endFrame (http://localhost:3000/cesium/Cesium.js:203574:21)
    at render (http://localhost:3000/cesium/Cesium.js:229375:19)
    at tryAndCatchError (http://localhost:3000/cesium/Cesium.js:229384:7)
CesiumWidget.showErrorPanel @ Cesium.js:230794
CesiumWidget._onRenderError @ Cesium.js:230493

The only additional changes I made this time (apart from using the API call you suggested above) are:

  1. Using await before the call.
  2. Not using Cesium.buildModuleUrl when passing the url argument to fromUrl().

Could you help me understand where I am going wrong

  1. in my clipping of the Imagery/Terrain and 3D Tiles in Cesium Ion; and
  2. in my subsequent attempt at rendering the clipped data?

Additionally, may I ask what resources I could use to learn more about the various geospatial data formats, their capabilities vis-a-vis CesiumJS and Cesium Ion, which of these data formats or data sources can or cannot safely intermingle etc in the context of making high-resolution offline maps accessible to the end-users of my application?

Regards,

Hi,

Since the clipped tileset is 3D Tiles and not imagery, the baseLayer property does not apply to the clipped tileset. Instead, you would have to do something similar to what you did in Case 1 of your original post. You would have to load the clipped data using Cesium.Cesium3DTileset.fromUrl

You can learn more about Cesium Js’s API through this link, our supported data formats are linked here.

Hi @Ankit_Trehan , @Gabby_Getz :

I went through the two links you shared above – Tiler Data Types and Formats, and the general Cesium API docs – and I still don’t understand how the combined imagery and terrain data, downloaded in 3D tileset format and served from a local tile server (as described in the Offline Guide), can be specified in the Cesium Viewer.

Because neither the Offline Guide nor the API docs nor the SandCastle examples include a fully functional example of how to get the clipped and downloaded data (imagery, terrrain, and 3D Tiles) served in offline mode, I could only read and re-read the API docs and try a few variations. All unsuccessfully, so far.

Attempt 3: This code results in the globe not displaying. The imagery doesn’t displaying either, except for a few, very small monochrome gray patches here and there probably corresponding to the building tops (whereas the online map of the same region is multicolored and very detailed). And that is very likely because the imageryAndTerrainTileset URL never shows as getting accessed in the tile-server log. Only the 3D Tiles url (3d-tiles-only/tileset.json) is seen getting accessed in the tile-server log.

viewer = new Cesium.Viewer(cesiumContainer, {
    imageryProvider: false,
    terrainProvider: false,
    baseLayerPicker: false,
    geocoder: true
})

const imageryAndTerrainTileset = await new Cesium.Cesium3DTileset({
    url: 'http://localhost:3003/lhasa-a/imagery-and-terrain/tileset.json'
})
viewer.scene.primitives.add(imageryAndTerrainTileset)

let tileset = await Cesium.Cesium3DTileset.fromUrl('http://localhost:3003/lhasa-a/3d-tiles-only/tileset.json')
viewer.scene.primitives.add(tileset)

Attempt 4: The following code results in the exact same behavior as Attempt 3 above.

let terrainImageryUrl = 'http://localhost:3003/lhasa-a/imagery-and-terrain/tileset.json'
let terrainProvider = new Cesium.CesiumTerrainProvider({ url: terrainImageryUrl })
let imageryProvider = new Cesium.UrlTemplateImageryProvider({ url: terrainImageryUrl})

viewer = new Cesium.Viewer(cesiumContainer, {
    terrainProvider: terrainProvider,
    imageryProvider: imageryProvider,
    baseLayerPicker: false,
    geocoder: true
})

let tilesetUrl = 'http://localhost:3003/lhasa-a/3d-tiles-only/tileset.json'
let tileset = await Cesium.Cesium3DTileset.fromUrl(tilesetUrl)
viewer.scene.primitives.add(tileset)

You can learn more about Cesium Js’s API through this link, our supported data formats are linked here.

Inferring things from the API docs is proving incredibly difficult. I really wish at least the Offline Guide had provided a fully functional sample program.

A related question: Where is knowledge such as the following currently documented?

  • TileMapServiceImageryProvider is not to be used for loading imagery+terrain data if in 3D Tiles format, and the correct API is Cesium.Cesium3DTileset.
  • The baseLayer property applies to 3D Tiles but not to the imagery.

Regards,
/HS

Hi @HSimons,

Sorry to hear you are still struggling with this. To see a low-res offline globe in combination with your exported 3D Tileset, you’ll need to do the following:

const viewer = new Cesium.Viewer("cesiumContainer", {
  // Set the base imagery layer to the Natural Earth II imagery
  // that is shipped with CesiumJS
  baseLayer: Cesium.ImageryLayer.fromProviderAsync(
    Cesium.TileMapServiceImageryProvider.fromUrl(
      Cesium.buildModuleUrl("Assets/Textures/NaturalEarthII"),
    ),
  ),
  baseLayerPicker: false,
  // The geocoder reaches out to Cesium ion for search queries 
  // and will need to be disabled in a completely disconnected environment
  geocoder: false, 
});

const tileset = await Cesium.Cesium3DTileset.fromUrl('http://localhost:3003/lhasa-a/3d-tiles-only/tileset.json');
viewer.scene.primitives.add(tileset);
viewer.zoomTo(tileset);
  • I’m not sure what the content of imagery-and-terrain is in your example. Could you explain more? Does it contain a global imagery data set?
  • You also mentioned that only the tops of buildings are showing. I’m not totally sure why without looking at the data, but I suspect this is due to the height of the buildings being below the WGS84 ellipsoid. You can have the buildings show up overtop of the base globe by adding the following line:
viewer.scene.globe.depthTestAgainstTerrain = false;
  • You certainly bring up a fair point about documentation. While we have docs on the various concepts such as terrain and imagery, 3D Tiles, and using offline data, it’s clear that we need targeted docs for the clip and ship use case which synthesizes these broader concepts into a ready-to-go solution. Based on your feedback, I’ve opened a feature request to create this documentation.

Thank you!

Hi @Gabby_Getz

The code you shared is nearly the same as the one in Case 1 of my very first email in this thread, except that:

  1. I had used geoCoder: true (just to be able to move around while debugging); and
  2. I wasn’t calling viewer.zoomTo(tileset)

Barring the above two, my Case 1 code is identical to yours. I reconfirm that this code works without any gross issues such as exceptions, blue-colored background, a missing globe, or imagery reduced merely to a few faint gray patches (possibly corresponding to full-color building tops in the live, hires map).

However, the problem with my Case 1 code was/is that it displays the map in lowres, enough to make it unusable in our application. We need at a Zoom Level 18 or better.

My (quite possibly incorrect) thinking so far has been: Because the assets that come packaged with the NPM cesium module (version 1.120.0) could no way be hires (owing to the bulkiness of any hires imagery data, in general), the “clip and ship” feature of Ion would possibly afford me data that is at least a tad higher res than that, if not as hires as Bing Maps or Google Photorealistic Tiles data (which I am well aware isn’t allowed to be downloaded as part of the clip). Further: If it’s not a tad higher res, it shouldn’t at least so low res as to make it unusable (in our specific application).

So, in line with the above thinking, I’ve been trying to use .../imagery-and-terrain/tileset.json from my local tile-server, and that is where I’ve been running into issues.

I’m not sure what the content of imagery-and-terrain is in your example. Could you explain more? Does it contain a global imagery data set?

The clip is for a small region over Lhasa, China.

I tried both Sentinel-2 and Natural Earth II sources for imagery and terrain, in case it made any difference. It didn’t, except that one of these two sources for imagery displayed far worse than the other.

Here are the details of the clip.
(Just to reiterate: I tried the 3D Tiles data with both versions of the imagery-and-terrain data with details below.)

  Select clip type: Clip 3D Tiles 
  Select 3D Tiles: Cesium OSM Buildings
  Select Region: 
    North: 29.681881004
    East: 91.184111274
    South: 29.646959451
    West: 91.139132862
  Clip options: 
    Name: lhasa-a-3d-tiles-only
    Format: 3D Tiles (not glTF)

  Select clip type: Clip imagery and terrain
  Select imagery and terrain:
    Imagery: Sentinel-2      <------------
    Terrain: Cesium World Terrain
  Select Region: 
    North: 29.681881004
    East: 91.184111274
    South: 29.646959451
    West: 91.139132862
  Clip options: 
    Name: lhasa-a-imagery-and-terrrain
    Format: 3D Tiles (not glTF)
    
  Select clip type: Clip imagery and terrain
  Select imagery and terrain:
    Imagery: Natural Earth II with Shaded Relief, Water, and Drainages   <------------
    Terrain: Cesium World Terrain
  Select Region: 
    North: 29.681881004
    East: 91.184111274
    South: 29.646959451
    West: 91.139132862
  Clip options: 
    Name: lhasa-a-imagery-and-terrrain-natEarthII
    Format: 3D Tiles (not glTF)

You also mentioned that only the tops of buildings are showing. I’m not totally sure why without looking at the data, but I suspect this is due to the height of the buildings being below the WGS84 ellipsoid. You can have the buildings show up overtop of the base globe by adding the following line: viewer.scene.globe.depthTestAgainstTerrain = false;

My building-tops related observation could possibly be a wrong interpretation – all I can (re)confirm seeing is a few, very faint, gray patches over a black background. But the more concrete part of the observation was, the imagery-and-terrain URL wasn’t even getting hit on my local tile-server; only the .../3d-tiles-only/tileset.json was getting hit(!), telling me that my API usage for loading the .../imagery-and-terrain/tileset.json from the local tile-server was wrong.

I’d tried viewer.scene.globe.depthTestAgainstTerrain = true earlier because I did want to be able to depth-test (in our application) but it made no visual difference to the display of the imagery and the globe. Also, I tried setting this property to false (per your suggestion) and, again, it made no visual difference.

I could share the clipped data if needed. I’m including a screenshot of what I saw in Attempts 3 and 4 earlier. (Both attempts produced the same results, as I noted earlier. The entities in red in the screenshot are my own. The faint gray patches are what I’ve been positing as “building-tops” but they could just as well be some other pixel artifacts.)

Thanks.