visgl / deck.gl

WebGL2 powered visualization framework
https://deck.gl
MIT License
12.14k stars 2.08k forks source link

[Bug] The Cesium Google 3D Tiles endpoint provides an _incorrect_ url for the Tile3dLayer to use with the CesiumIonLoader #8437

Open vsigno opened 8 months ago

vsigno commented 8 months ago

Description

I’m trying to load Google 3DTiles as a MapBoxLayer using Tile3DLayer and CesiumIonLoader.

using:

const CesiumLayer= new MapboxLayer({
    id: 'tiles',
    type: Tile3DLayer,
    data: 'https://assets.ion.cesium.com/2275207/tileset.json', //Google 3DTiles asset
    loader: CesiumIonLoader,
    loadOptions: {
  'cesium-ion': {accessToken: 'AccessToken'}
},

output this error:

Uncaught (in promise) Error: loader assertion failed.
    at assert (assert.ts:7:11)
    at getIonTilesetMetadata (ion.ts:23:3)
    at async Tile3DLayer._loadTileset (tile-3d-layer.ts:188:30)

Looking at the Cesium endpoint API, I can see that the result is of type 3DTiles, however is not formatted as the usual Cesium asset, but the URL is nested in a options object

{
    "type": "3DTILES",
    "externalType": "3DTILES",
    "options": {
        "url": "https://tile.googleapis.com/v1/3dtiles/root.json?key=<KEY>"
    },
    "attributions": [
        {
            "html": "<span><a href=\"https://cesium.com\" target=\"_blank\"><img alt=\"Cesium ion\" src=\"https://assets.ion.cesium.com/ion-credit.png\"></a></span>",
            "collapsible": false
        },
        {
            "html": "<a href=\"https://cesium.com/pricing/\" target=\"_blank\">Upgrade for commercial use.</a>",
            "collapsible": false
        },
        {
            "html": "<span><img alt=\"Google\" src=\"https://assets.ion.cesium.com/google-credit.png\"></span>",
            "collapsible": false
        }
    ]
}

the usual Cesium asset response is this:

{
    "type": "3DTILES",
    "url": "https://assets.ion.cesium.com/us-east-1/asset_depot/96188/OpenStreetMap/2023-01-02/tileset.json?v=16",
    "accessToken": "<ACCESS Token>",
    "attributions": [
        {
            "html": "<span><a href=\"https://cesium.com\" target=\"_blank\"><img alt=\"Cesium ion\" src=\"https://assets.ion.cesium.com/ion-credit.png\"></a></span>",
            "collapsible": false
        },
        {
            "html": "<a href=\"https://cesium.com/pricing/\" target=\"_blank\">Upgrade for commercial use.</a>",
            "collapsible": false
        },
        {
            "html": "<span>© <a href=\"https://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors</span>",
            "collapsible": true
        }
    ]
}

Changing the data to that URL, and removing the Loader and load options makes everything work as expected.

const CesiumLayer= new MapboxLayer({
    id: 'tiles',
    type: Tile3DLayer,
    data: 'https://tile.googleapis.com/v1/3dtiles/root.json?key=<KEY>'
)}

I suspect the issue lies with Deck.gl’s Tile3DLayer, and not with the Loader.gl’s CesiumIonLoader (well, the issue is actually the response from Cesium). A solution would be for the Tile3DLayer to check if url exists in the response, if not look at options.url and ignore CesiumIonLoader.

Flavors

Expected Behavior

Tile3DLayer should handle the Cesium response and visualise the Google 3D tiles.

Steps to Reproduce

JS

import {MapboxLayer} from '@deck.gl/mapbox';
import { CesiumIonLoader } from '@loaders.gl/3d-tiles';
import mapboxgl from 'mapbox-gl';
import {Tile3DLayer} from '@deck.gl/geo-layers';
import {Deck, log} from '@deck.gl/core';

const INITIAL_VIEW_STATE = {
    longitude: -0.12599998138976254, //0.12,
    latitude: 51.521592969384706, //51.5, 
    zoom: 15,
    bearing: 0,
    pitch: 0
};

// Set your API key here
const API_TOKEN_Mapbox = 'TOKEN';
mapboxgl.accessToken = API_TOKEN_Mapbox;

// MapBox Vector Tile
const map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/dark-v10',
    interactive: true,
    center: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude],
    zoom: INITIAL_VIEW_STATE.zoom,
    bearing: INITIAL_VIEW_STATE.bearing,
    pitch: INITIAL_VIEW_STATE.pitch
});

map.on('load', () => {

 const CesiumLayer= new MapboxLayer({
     id: 'tiles',
     type: Tile3DLayer,
     data: 'https://assets.ion.cesium.com/2275207/tileset.json',
     loader: CesiumIonLoader,
     loadOptions: {
     'cesium-ion': {accessToken: '<TOKEN>'}
 },
},
});
map.addLayer(CesiumLayer);
});

HTML

<body>
  <div id="app">
    <div id="container">
      <div id="map"></div>
    </div>
  </div>
</body>

CSS

 body {
      margin: 0;
      padding: 0;
    }

    #container {
      position: fixed;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
    }

    #map {
      position: absolute;
      top: 0;
      bottom: 0;
      width: 100%;
    }

Environment

Logs

No response

felixpalmer commented 8 months ago

Have you checked with the Cesium team if that is the expected response? This seems more like an issue with their API, than with loaders/deck. Also, is there a reason why you don't just use the Google endpoint directly?

If you just want to normalize the response, you could try using dataTransform

vsigno commented 8 months ago

Thanks, I agree with you that this is an API issue rather than a loaderGL issue. I have just posed a question on the Cesium community regarding the endpoint response. I’m using Cesium instead of Google to avoid registering for an additional service, as the visualization I’m creating utilizes other assets from Cesium. Of course, you are right, the easiest workaround would be to use Google directly.

I’ll have a look at the dataTransform to see if I can access the URL. However, I’m not sure it will work, as I need the Cesium loader to perform the authentication (and get the Google URL with the appended Token), but the same loader needs to be disabled in order to correctly load the Google tiles.

vsigno commented 8 months ago

Update from the Cesium Team, the response from the endpoint is as expected. Cesium uses both internal and external assets, and the two different responses reflect that difference. So the issue still really lies with the CesiumIonLoader that, at the moment, cannot handle the external assets. I'll have a look to the ion.ts.

felixpalmer commented 8 months ago

@vsigno could you try and open an PR in the loaders.gl repo? There is a function for parsing the metadata which I think could be modified here