| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777 | import Cartesian3 from "../Core/Cartesian3.js";import defaultValue from "../Core/defaultValue.js";import defer from "../Core/defer.js";import defined from "../Core/defined.js";import destroyObject from "../Core/destroyObject.js";import DeveloperError from "../Core/DeveloperError.js";import Ellipsoid from "../Core/Ellipsoid.js";import getJsonFromTypedArray from "../Core/getJsonFromTypedArray.js";import ComponentDatatype from "../Core/ComponentDatatype.js";import CesiumMath from "../Core/Math.js";import Matrix4 from "../Core/Matrix4.js";import Rectangle from "../Core/Rectangle.js";import RuntimeError from "../Core/RuntimeError.js";import Cesium3DTileBatchTable from "./Cesium3DTileBatchTable.js";import Cesium3DTileFeatureTable from "./Cesium3DTileFeatureTable.js";import Vector3DTilePoints from "./Vector3DTilePoints.js";import Vector3DTilePolygons from "./Vector3DTilePolygons.js";import Vector3DTilePolylines from "./Vector3DTilePolylines.js";import Vector3DTileClampedPolylines from "./Vector3DTileClampedPolylines.js";import decodeVectorPolylinePositions from "../Core/decodeVectorPolylinePositions.js";/** * Represents the contents of a * {@link https://github.com/CesiumGS/3d-tiles/tree/vctr/TileFormats/VectorData|Vector} * tile in a {@link https://github.com/CesiumGS/3d-tiles/tree/main/specification|3D Tiles} tileset. * <p> * Implements the {@link Cesium3DTileContent} interface. * </p> * * @alias Vector3DTileContent * @constructor * * @private */function Vector3DTileContent(tileset, tile, resource, arrayBuffer, byteOffset) {  this._tileset = tileset;  this._tile = tile;  this._resource = resource;  this._polygons = undefined;  this._polylines = undefined;  this._points = undefined;  this._contentReadyPromise = undefined;  this._readyPromise = defer();  this._metadata = undefined;  this._batchTable = undefined;  this._features = undefined;  /**   * Part of the {@link Cesium3DTileContent} interface.   */  this.featurePropertiesDirty = false;  this._group = undefined;  initialize(this, arrayBuffer, byteOffset);}Object.defineProperties(Vector3DTileContent.prototype, {  featuresLength: {    get: function () {      return defined(this._batchTable) ? this._batchTable.featuresLength : 0;    },  },  pointsLength: {    get: function () {      if (defined(this._points)) {        return this._points.pointsLength;      }      return 0;    },  },  trianglesLength: {    get: function () {      let trianglesLength = 0;      if (defined(this._polygons)) {        trianglesLength += this._polygons.trianglesLength;      }      if (defined(this._polylines)) {        trianglesLength += this._polylines.trianglesLength;      }      return trianglesLength;    },  },  geometryByteLength: {    get: function () {      let geometryByteLength = 0;      if (defined(this._polygons)) {        geometryByteLength += this._polygons.geometryByteLength;      }      if (defined(this._polylines)) {        geometryByteLength += this._polylines.geometryByteLength;      }      return geometryByteLength;    },  },  texturesByteLength: {    get: function () {      if (defined(this._points)) {        return this._points.texturesByteLength;      }      return 0;    },  },  batchTableByteLength: {    get: function () {      return defined(this._batchTable) ? this._batchTable.memorySizeInBytes : 0;    },  },  innerContents: {    get: function () {      return undefined;    },  },  readyPromise: {    get: function () {      return this._readyPromise.promise;    },  },  tileset: {    get: function () {      return this._tileset;    },  },  tile: {    get: function () {      return this._tile;    },  },  url: {    get: function () {      return this._resource.getUrlComponent(true);    },  },  metadata: {    get: function () {      return this._metadata;    },    set: function (value) {      this._metadata = value;    },  },  batchTable: {    get: function () {      return this._batchTable;    },  },  group: {    get: function () {      return this._group;    },    set: function (value) {      this._group = value;    },  },});function createColorChangedCallback(content) {  return function (batchId, color) {    if (defined(content._polygons)) {      content._polygons.updateCommands(batchId, color);    }  };}function getBatchIds(featureTableJson, featureTableBinary) {  let polygonBatchIds;  let polylineBatchIds;  let pointBatchIds;  let i;  const numberOfPolygons = defaultValue(featureTableJson.POLYGONS_LENGTH, 0);  const numberOfPolylines = defaultValue(featureTableJson.POLYLINES_LENGTH, 0);  const numberOfPoints = defaultValue(featureTableJson.POINTS_LENGTH, 0);  if (numberOfPolygons > 0 && defined(featureTableJson.POLYGON_BATCH_IDS)) {    const polygonBatchIdsByteOffset =      featureTableBinary.byteOffset +      featureTableJson.POLYGON_BATCH_IDS.byteOffset;    polygonBatchIds = new Uint16Array(      featureTableBinary.buffer,      polygonBatchIdsByteOffset,      numberOfPolygons    );  }  if (numberOfPolylines > 0 && defined(featureTableJson.POLYLINE_BATCH_IDS)) {    const polylineBatchIdsByteOffset =      featureTableBinary.byteOffset +      featureTableJson.POLYLINE_BATCH_IDS.byteOffset;    polylineBatchIds = new Uint16Array(      featureTableBinary.buffer,      polylineBatchIdsByteOffset,      numberOfPolylines    );  }  if (numberOfPoints > 0 && defined(featureTableJson.POINT_BATCH_IDS)) {    const pointBatchIdsByteOffset =      featureTableBinary.byteOffset +      featureTableJson.POINT_BATCH_IDS.byteOffset;    pointBatchIds = new Uint16Array(      featureTableBinary.buffer,      pointBatchIdsByteOffset,      numberOfPoints    );  }  const atLeastOneDefined =    defined(polygonBatchIds) ||    defined(polylineBatchIds) ||    defined(pointBatchIds);  const atLeastOneUndefined =    (numberOfPolygons > 0 && !defined(polygonBatchIds)) ||    (numberOfPolylines > 0 && !defined(polylineBatchIds)) ||    (numberOfPoints > 0 && !defined(pointBatchIds));  if (atLeastOneDefined && atLeastOneUndefined) {    throw new RuntimeError(      "If one group of batch ids is defined, then all batch ids must be defined."    );  }  const allUndefinedBatchIds =    !defined(polygonBatchIds) &&    !defined(polylineBatchIds) &&    !defined(pointBatchIds);  if (allUndefinedBatchIds) {    let id = 0;    if (!defined(polygonBatchIds) && numberOfPolygons > 0) {      polygonBatchIds = new Uint16Array(numberOfPolygons);      for (i = 0; i < numberOfPolygons; ++i) {        polygonBatchIds[i] = id++;      }    }    if (!defined(polylineBatchIds) && numberOfPolylines > 0) {      polylineBatchIds = new Uint16Array(numberOfPolylines);      for (i = 0; i < numberOfPolylines; ++i) {        polylineBatchIds[i] = id++;      }    }    if (!defined(pointBatchIds) && numberOfPoints > 0) {      pointBatchIds = new Uint16Array(numberOfPoints);      for (i = 0; i < numberOfPoints; ++i) {        pointBatchIds[i] = id++;      }    }  }  return {    polygons: polygonBatchIds,    polylines: polylineBatchIds,    points: pointBatchIds,  };}const sizeOfUint32 = Uint32Array.BYTES_PER_ELEMENT;function createFloatingPolylines(options) {  return new Vector3DTilePolylines(options);}function createClampedPolylines(options) {  return new Vector3DTileClampedPolylines(options);}function initialize(content, arrayBuffer, byteOffset) {  byteOffset = defaultValue(byteOffset, 0);  const uint8Array = new Uint8Array(arrayBuffer);  const view = new DataView(arrayBuffer);  byteOffset += sizeOfUint32; // Skip magic number  const version = view.getUint32(byteOffset, true);  if (version !== 1) {    throw new RuntimeError(      `Only Vector tile version 1 is supported.  Version ${version} is not.`    );  }  byteOffset += sizeOfUint32;  const byteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  if (byteLength === 0) {    content._readyPromise.resolve(content);    return;  }  const featureTableJSONByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  if (featureTableJSONByteLength === 0) {    throw new RuntimeError(      "Feature table must have a byte length greater than zero"    );  }  const featureTableBinaryByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const batchTableJSONByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const batchTableBinaryByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const indicesByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const positionByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const polylinePositionByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const pointsPositionByteLength = view.getUint32(byteOffset, true);  byteOffset += sizeOfUint32;  const featureTableJson = getJsonFromTypedArray(    uint8Array,    byteOffset,    featureTableJSONByteLength  );  byteOffset += featureTableJSONByteLength;  const featureTableBinary = new Uint8Array(    arrayBuffer,    byteOffset,    featureTableBinaryByteLength  );  byteOffset += featureTableBinaryByteLength;  let batchTableJson;  let batchTableBinary;  if (batchTableJSONByteLength > 0) {    // PERFORMANCE_IDEA: is it possible to allocate this on-demand?  Perhaps keep the    // arraybuffer/string compressed in memory and then decompress it when it is first accessed.    //    // We could also make another request for it, but that would make the property set/get    // API async, and would double the number of numbers in some cases.    batchTableJson = getJsonFromTypedArray(      uint8Array,      byteOffset,      batchTableJSONByteLength    );    byteOffset += batchTableJSONByteLength;    if (batchTableBinaryByteLength > 0) {      // Has a batch table binary      batchTableBinary = new Uint8Array(        arrayBuffer,        byteOffset,        batchTableBinaryByteLength      );      // Copy the batchTableBinary section and let the underlying ArrayBuffer be freed      batchTableBinary = new Uint8Array(batchTableBinary);      byteOffset += batchTableBinaryByteLength;    }  }  const numberOfPolygons = defaultValue(featureTableJson.POLYGONS_LENGTH, 0);  const numberOfPolylines = defaultValue(featureTableJson.POLYLINES_LENGTH, 0);  const numberOfPoints = defaultValue(featureTableJson.POINTS_LENGTH, 0);  const totalPrimitives = numberOfPolygons + numberOfPolylines + numberOfPoints;  const batchTable = new Cesium3DTileBatchTable(    content,    totalPrimitives,    batchTableJson,    batchTableBinary,    createColorChangedCallback(content)  );  content._batchTable = batchTable;  if (totalPrimitives === 0) {    return;  }  const featureTable = new Cesium3DTileFeatureTable(    featureTableJson,    featureTableBinary  );  const region = featureTable.getGlobalProperty("REGION");  if (!defined(region)) {    throw new RuntimeError(      "Feature table global property: REGION must be defined"    );  }  const rectangle = Rectangle.unpack(region);  const minHeight = region[4];  const maxHeight = region[5];  const modelMatrix = content._tile.computedTransform;  let center = featureTable.getGlobalProperty(    "RTC_CENTER",    ComponentDatatype.FLOAT,    3  );  if (defined(center)) {    center = Cartesian3.unpack(center);    Matrix4.multiplyByPoint(modelMatrix, center, center);  } else {    center = Rectangle.center(rectangle);    center.height = CesiumMath.lerp(minHeight, maxHeight, 0.5);    center = Ellipsoid.WGS84.cartographicToCartesian(center);  }  const batchIds = getBatchIds(featureTableJson, featureTableBinary);  byteOffset += (4 - (byteOffset % 4)) % 4;  if (numberOfPolygons > 0) {    featureTable.featuresLength = numberOfPolygons;    const polygonCounts = defaultValue(      featureTable.getPropertyArray(        "POLYGON_COUNTS",        ComponentDatatype.UNSIGNED_INT,        1      ),      featureTable.getPropertyArray(        "POLYGON_COUNT",        ComponentDatatype.UNSIGNED_INT,        1      ) // Workaround for old vector tilesets using the non-plural name    );    if (!defined(polygonCounts)) {      throw new RuntimeError(        "Feature table property: POLYGON_COUNTS must be defined when POLYGONS_LENGTH is greater than 0"      );    }    const polygonIndexCounts = defaultValue(      featureTable.getPropertyArray(        "POLYGON_INDEX_COUNTS",        ComponentDatatype.UNSIGNED_INT,        1      ),      featureTable.getPropertyArray(        "POLYGON_INDEX_COUNT",        ComponentDatatype.UNSIGNED_INT,        1      ) // Workaround for old vector tilesets using the non-plural name    );    if (!defined(polygonIndexCounts)) {      throw new RuntimeError(        "Feature table property: POLYGON_INDEX_COUNTS must be defined when POLYGONS_LENGTH is greater than 0"      );    }    // Use the counts array to determine how many position values we want. If we used the byte length then    // zero padding values would be included and cause the delta zig-zag decoding to fail    const numPolygonPositions = polygonCounts.reduce(function (total, count) {      return total + count * 2;    }, 0);    const numPolygonIndices = polygonIndexCounts.reduce(function (      total,      count    ) {      return total + count;    },    0);    const indices = new Uint32Array(arrayBuffer, byteOffset, numPolygonIndices);    byteOffset += indicesByteLength;    const polygonPositions = new Uint16Array(      arrayBuffer,      byteOffset,      numPolygonPositions    );    byteOffset += positionByteLength;    let polygonMinimumHeights;    let polygonMaximumHeights;    if (      defined(featureTableJson.POLYGON_MINIMUM_HEIGHTS) &&      defined(featureTableJson.POLYGON_MAXIMUM_HEIGHTS)    ) {      polygonMinimumHeights = featureTable.getPropertyArray(        "POLYGON_MINIMUM_HEIGHTS",        ComponentDatatype.FLOAT,        1      );      polygonMaximumHeights = featureTable.getPropertyArray(        "POLYGON_MAXIMUM_HEIGHTS",        ComponentDatatype.FLOAT,        1      );    }    content._polygons = new Vector3DTilePolygons({      positions: polygonPositions,      counts: polygonCounts,      indexCounts: polygonIndexCounts,      indices: indices,      minimumHeight: minHeight,      maximumHeight: maxHeight,      polygonMinimumHeights: polygonMinimumHeights,      polygonMaximumHeights: polygonMaximumHeights,      center: center,      rectangle: rectangle,      boundingVolume: content.tile.boundingVolume.boundingVolume,      batchTable: batchTable,      batchIds: batchIds.polygons,      modelMatrix: modelMatrix,    });  }  if (numberOfPolylines > 0) {    featureTable.featuresLength = numberOfPolylines;    const polylineCounts = defaultValue(      featureTable.getPropertyArray(        "POLYLINE_COUNTS",        ComponentDatatype.UNSIGNED_INT,        1      ),      featureTable.getPropertyArray(        "POLYLINE_COUNT",        ComponentDatatype.UNSIGNED_INT,        1      ) // Workaround for old vector tilesets using the non-plural name    );    if (!defined(polylineCounts)) {      throw new RuntimeError(        "Feature table property: POLYLINE_COUNTS must be defined when POLYLINES_LENGTH is greater than 0"      );    }    let widths = featureTable.getPropertyArray(      "POLYLINE_WIDTHS",      ComponentDatatype.UNSIGNED_SHORT,      1    );    if (!defined(widths)) {      widths = new Uint16Array(numberOfPolylines);      for (let i = 0; i < numberOfPolylines; ++i) {        widths[i] = 2.0;      }    }    // Use the counts array to determine how many position values we want. If we used the byte length then    // zero padding values would be included and cause the delta zig-zag decoding to fail    const numPolylinePositions = polylineCounts.reduce(function (total, count) {      return total + count * 3;    }, 0);    const polylinePositions = new Uint16Array(      arrayBuffer,      byteOffset,      numPolylinePositions    );    byteOffset += polylinePositionByteLength;    const tileset = content._tileset;    const examineVectorLinesFunction = tileset.examineVectorLinesFunction;    if (defined(examineVectorLinesFunction)) {      const decodedPositions = decodeVectorPolylinePositions(        new Uint16Array(polylinePositions),        rectangle,        minHeight,        maxHeight,        Ellipsoid.WGS84      );      examineVectorLines(        decodedPositions,        polylineCounts,        batchIds.polylines,        batchTable,        content.url,        examineVectorLinesFunction      );    }    let createPolylines = createFloatingPolylines;    if (defined(tileset.classificationType)) {      createPolylines = createClampedPolylines;    }    content._polylines = createPolylines({      positions: polylinePositions,      widths: widths,      counts: polylineCounts,      batchIds: batchIds.polylines,      minimumHeight: minHeight,      maximumHeight: maxHeight,      center: center,      rectangle: rectangle,      boundingVolume: content.tile.boundingVolume.boundingVolume,      batchTable: batchTable,      classificationType: tileset.classificationType,      keepDecodedPositions: tileset.vectorKeepDecodedPositions,    });  }  if (numberOfPoints > 0) {    const pointPositions = new Uint16Array(      arrayBuffer,      byteOffset,      numberOfPoints * 3    );    byteOffset += pointsPositionByteLength;    content._points = new Vector3DTilePoints({      positions: pointPositions,      batchIds: batchIds.points,      minimumHeight: minHeight,      maximumHeight: maxHeight,      rectangle: rectangle,      batchTable: batchTable,    });  }}function createFeatures(content) {  const featuresLength = content.featuresLength;  if (!defined(content._features) && featuresLength > 0) {    const features = new Array(featuresLength);    if (defined(content._polygons)) {      content._polygons.createFeatures(content, features);    }    if (defined(content._polylines)) {      content._polylines.createFeatures(content, features);    }    if (defined(content._points)) {      content._points.createFeatures(content, features);    }    content._features = features;  }}Vector3DTileContent.prototype.hasProperty = function (batchId, name) {  return this._batchTable.hasProperty(batchId, name);};Vector3DTileContent.prototype.getFeature = function (batchId) {  //>>includeStart('debug', pragmas.debug);  const featuresLength = this.featuresLength;  if (!defined(batchId) || batchId < 0 || batchId >= featuresLength) {    throw new DeveloperError(      `batchId is required and between zero and featuresLength - 1 (${        featuresLength - 1      }).`    );  }  //>>includeEnd('debug');  createFeatures(this);  return this._features[batchId];};Vector3DTileContent.prototype.applyDebugSettings = function (enabled, color) {  if (defined(this._polygons)) {    this._polygons.applyDebugSettings(enabled, color);  }  if (defined(this._polylines)) {    this._polylines.applyDebugSettings(enabled, color);  }  if (defined(this._points)) {    this._points.applyDebugSettings(enabled, color);  }};Vector3DTileContent.prototype.applyStyle = function (style) {  createFeatures(this);  if (defined(this._polygons)) {    this._polygons.applyStyle(style, this._features);  }  if (defined(this._polylines)) {    this._polylines.applyStyle(style, this._features);  }  if (defined(this._points)) {    this._points.applyStyle(style, this._features);  }};Vector3DTileContent.prototype.update = function (tileset, frameState) {  let ready = true;  if (defined(this._polygons)) {    this._polygons.classificationType = this._tileset.classificationType;    this._polygons.debugWireframe = this._tileset.debugWireframe;    this._polygons.update(frameState);    ready = ready && this._polygons._ready;  }  if (defined(this._polylines)) {    this._polylines.update(frameState);    ready = ready && this._polylines._ready;  }  if (defined(this._points)) {    this._points.update(frameState);    ready = ready && this._points._ready;  }  if (defined(this._batchTable) && ready) {    this._batchTable.update(tileset, frameState);  }  if (!defined(this._contentReadyPromise)) {    const pointsPromise = defined(this._points)      ? this._points.readyPromise      : undefined;    const polygonPromise = defined(this._polygons)      ? this._polygons.readyPromise      : undefined;    const polylinePromise = defined(this._polylines)      ? this._polylines.readyPromise      : undefined;    const that = this;    this._contentReadyPromise = Promise.all([      pointsPromise,      polygonPromise,      polylinePromise,    ])      .then(function () {        that._readyPromise.resolve(that);      })      .catch(function (error) {        that._readyPromise.reject(error);      });  }};Vector3DTileContent.prototype.getPolylinePositions = function (batchId) {  const polylines = this._polylines;  if (!defined(polylines)) {    return undefined;  }  return polylines.getPositions(batchId);};Vector3DTileContent.prototype.isDestroyed = function () {  return false;};Vector3DTileContent.prototype.destroy = function () {  this._polygons = this._polygons && this._polygons.destroy();  this._polylines = this._polylines && this._polylines.destroy();  this._points = this._points && this._points.destroy();  this._batchTable = this._batchTable && this._batchTable.destroy();  return destroyObject(this);};function examineVectorLines(  positions,  counts,  batchIds,  batchTable,  url,  callback) {  const countsLength = counts.length;  let polylineStart = 0;  for (let i = 0; i < countsLength; i++) {    const count = counts[i] * 3;    const linePositions = positions.slice(polylineStart, polylineStart + count);    polylineStart += count;    callback(linePositions, batchIds[i], url, batchTable);  }}export default Vector3DTileContent;
 |