If you’re having issues with draping your data on a 3D Tiles mesh, check the following:

  • Does your 3D Tiles layer (TileSet3DLayer) have isDrapeTarget set to true?

  • Does the data you want to drape have the correct DrapeTarget?

See Draping on 3D tiles for details.

If you fulfilled both conditions, and you still don’t see your data being draped, check if it’s a RasterLayer and if it’s the bottom-most layer in the layer tree. In that case, you must apply a workaround to drape your raster layer.

Limitation of draping the bottom-most raster layer on 3D tiles

Even though you set layer.rasterStyle.drapeTarget to DrapeTarget.MESH or DrapeTarget.ALL, and you set tileset3DLayer.isDrapeTarget to true, you still don’t see the raster data draped on the mesh.

Cause

Due to a technical limitation, you can’t drape the bottom-most RasterLayer in the layer tree on a 3D Tiles mesh.

Workaround

As a workaround, you can add another "dummy" RasterLayer beneath the layer you want to drape. As a result, the layer you want to drape isn’t the bottom-most raster layer in the layer tree anymore. It then gets draped correctly on 3D Tiles.

Program: Workaround for draping the bottom-most raster layer on 3D Tiles mesh data.
import {Map} from "@luciad/ria/view/Map.js";
import {createHereMapsTileSetModel} from "@luciad/ria/model/tileset/HereMapsTileSetModel.js";
import {RasterTileSetLayer} from "@luciad/ria/view/tileset/RasterTileSetLayer.js";
import {DrapeTarget} from "@luciad/ria/view/style/DrapeTarget.js";
import {TileSet3DLayer} from "@luciad/ria/view/tileset/TileSet3DLayer.js";
import {OGC3DTilesModel} from "@luciad/ria/model/tileset/OGC3DTilesModel.js";
import {RasterTileSetModel} from "@luciad/ria/model/tileset/RasterTileSetModel.js";
import {TileCoordinate} from "@luciad/ria/model/tileset/TileCoordinate.js";
import {getReference} from "@luciad/ria/reference/ReferenceProvider.js";
import {createBounds} from "@luciad/ria/shape/ShapeFactory.js";
import {RasterDataType} from "@luciad/ria/model/tileset/RasterDataType.js";

async function addMapLayers(map: Map) {
  const dummyRasterLayer = createDummyRasterLayer();
  map.layerTree.addChild(dummyRasterLayer, "bottom")
  const drapedHereMapsLayer = await createHereMapsLayer();
  map.layerTree.addChild(drapedHereMapsLayer, "above", dummyRasterLayer);

  const meshLayer = await create3DTilesMeshLayer();
  map.layerTree.addChild(meshLayer, "above", drapedHereMapsLayer);
}

function createDummyRasterLayer(): RasterTileSetLayer {
  const model = new DummyRasterTileSetModel();
  return new RasterTileSetLayer(model, {
    label: "Dummy raster layer"
  });
}

class DummyRasterTileSetModel extends RasterTileSetModel {

  private _image: HTMLImageElement;

  constructor(color: string = "rgb(255, 255, 255)") {
    const epsg4326 = getReference("EPSG:4326");
    super({
      reference: epsg4326,
      bounds: createBounds(epsg4326, [-180, 360, -90, 180]),
      levelCount: 19,
      tileWidth: 256,
      tileHeight: 256,
      level0Rows: 1,
      level0Columns: 1,
      dataType: RasterDataType.IMAGE
    });

    const canvas = document.createElement("canvas");
    canvas.width = 256;
    canvas.height = 256;
    const ctx = canvas.getContext("2d")!;
    ctx.fillStyle = color;
    ctx.fillRect(0, 0, canvas.height, canvas.width);
    this._image = document.createElement('img');
    this._image.src = canvas.toDataURL("image/png");
  }

  getImage(tile: TileCoordinate, onSuccess: (tile: TileCoordinate, image: HTMLImageElement) => void,
           onError: (tile: TileCoordinate, error?: any) => void): void {
    onSuccess(tile, this._image);
  }
}

async function createHereMapsLayer(): Promise<RasterTileSetLayer> {
  const model = await createHereMapsTileSetModel({
    apiKey: "CENSORED",
    base: "aerial",
    type: "maptile",
    scheme: "satellite.day"
  });
  const layer = new RasterTileSetLayer(model, {
    label: "HERE Maps",
  });
  layer.rasterStyle.drapeTarget = DrapeTarget.MESH;
  return layer;
}

async function create3DTilesMeshLayer(): Promise<TileSet3DLayer> {
  const model = await OGC3DTilesModel.create(
      "https://sampleservices.luciad.com/ogc/3dtiles/marseille-mesh/tileset.json");
  return new TileSet3DLayer(model, {
    qualityFactor: 0.8,
    label: "OGC 3D Tiles Mesh",
    isDrapeTarget: true,
    isPartOfTerrain: false,
    drapeSlopeAngle: 60
  });
}