| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460 |
- import Cartesian4 from "../Core/Cartesian4.js";
- import clone from "../Core/clone.js";
- import Color from "../Core/Color.js";
- import defined from "../Core/defined.js";
- import Rectangle from "../Core/Rectangle.js";
- import Resource from "../Core/Resource.js";
- import RuntimeError from "../Core/RuntimeError.js";
- import Cesium3DTileset from "./Cesium3DTileset.js";
- import I3SNode from "./I3SNode.js";
- import I3SSymbology from "./I3SSymbology.js";
-
- /**
- * This class implements an I3S layer. In CesiumJS each I3SLayer creates a Cesium3DTileset.
- * <p>
- * Do not construct this directly, instead access layers through {@link I3SDataProvider}.
- * </p>
- * @alias I3SLayer
- * @internalConstructor
- * @privateParam {I3SDataProvider} dataProvider The i3s data provider
- * @privateParam {object} layerData The layer data that is loaded from the scene layer
- * @privateParam {I3SDataProvider|I3SSublayer} parent The parent of that layer
- */
- function I3SLayer(dataProvider, layerData, parent) {
- this._dataProvider = dataProvider;
- this._parent = parent;
-
- if (!defined(layerData.href) && defined(layerData.id)) {
- // assign a default layer
- layerData.href = `layers/${layerData.id}`;
- }
-
- const parentUrl = this._parent.resource.getUrlComponent();
-
- let tilesetUrl = "";
- if (parentUrl.match(/layers\/\d/)) {
- tilesetUrl = `${parentUrl}`.replace(/\/+$/, "");
- } else {
- // Add '/' to url if needed + `${layerData.href}` if tilesetUrl not already in ../layers/[id] format
- tilesetUrl = `${parentUrl}`
- .replace(/\/?$/, "/")
- .concat(`${layerData.href}`);
- }
-
- this._version = layerData.store.version;
- const splitVersion = this._version.split(".");
- this._majorVersion = parseInt(splitVersion[0]);
- this._minorVersion = splitVersion.length > 1 ? parseInt(splitVersion[1]) : 0;
-
- this._resource = new Resource({ url: tilesetUrl });
- this._resource.setQueryParameters(
- this._dataProvider.resource.queryParameters,
- );
- this._resource.appendForwardSlash();
- this._data = layerData;
- this._rootNode = undefined;
- this._nodePages = {};
- this._nodePageFetches = {};
- this._extent = undefined;
- this._tileset = undefined;
- this._geometryDefinitions = undefined;
- this._filters = [];
- this._symbology = undefined;
-
- this._computeGeometryDefinitions(true);
- this._computeExtent();
- }
-
- Object.defineProperties(I3SLayer.prototype, {
- /**
- * Gets the resource for the layer.
- * @memberof I3SLayer.prototype
- * @type {Resource}
- * @readonly
- */
- resource: {
- get: function () {
- return this._resource;
- },
- },
-
- /**
- * Gets the root node of this layer.
- * @memberof I3SLayer.prototype
- * @type {I3SNode}
- * @readonly
- */
- rootNode: {
- get: function () {
- return this._rootNode;
- },
- },
- /**
- * Gets the Cesium3DTileset for this layer.
- * @memberof I3SLayer.prototype
- * @type {Cesium3DTileset|undefined}
- * @readonly
- */
- tileset: {
- get: function () {
- return this._tileset;
- },
- },
- /**
- * Gets the I3S data for this object.
- * @memberof I3SLayer.prototype
- * @type {object}
- * @readonly
- */
- data: {
- get: function () {
- return this._data;
- },
- },
-
- /**
- * The version string of the loaded I3S dataset
- * @memberof I3SLayer.prototype
- * @type {string}
- * @readonly
- */
- version: {
- get: function () {
- return this._version;
- },
- },
-
- /**
- * The major version number of the loaded I3S dataset
- * @memberof I3SLayer.prototype
- * @type {number}
- * @readonly
- */
- majorVersion: {
- get: function () {
- return this._majorVersion;
- },
- },
-
- /**
- * The minor version number of the loaded I3S dataset
- * @memberof I3SLayer.prototype
- * @type {number}
- * @readonly
- */
- minorVersion: {
- get: function () {
- return this._minorVersion;
- },
- },
-
- /**
- * When <code>true</code>, when the loaded I3S version is 1.6 or older
- * @memberof I3SLayer.prototype
- * @type {boolean}
- * @readonly
- */
- legacyVersion16: {
- get: function () {
- if (!defined(this.version)) {
- return undefined;
- }
- if (
- this.majorVersion < 1 ||
- (this.majorVersion === 1 && this.minorVersion <= 6)
- ) {
- return true;
- }
- return false;
- },
- },
- });
-
- /**
- * Loads the content, including the root node definition and its children
- * @param {Cesium3DTileset.ConstructorOptions} [cesium3dTilesetOptions] options for Cesium3dTileset constructor
- * @returns {Promise<void>} A promise that is resolved when the layer data is loaded
- * @private
- */
- I3SLayer.prototype.load = async function (cesium3dTilesetOptions) {
- if (this._data.spatialReference.wkid !== 4326) {
- throw new RuntimeError(
- `Unsupported spatial reference: ${this._data.spatialReference.wkid}`,
- );
- }
-
- if (this._dataProvider.applySymbology) {
- this._symbology = new I3SSymbology(this);
- }
- await this._dataProvider.loadGeoidData();
- await this._loadRootNode(cesium3dTilesetOptions);
- await this._create3DTileset(cesium3dTilesetOptions);
-
- this._rootNode._tile = this._tileset._root;
- this._tileset._root._i3sNode = this._rootNode;
- if (this.legacyVersion16) {
- return this._rootNode._loadChildren();
- }
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._computeGeometryDefinitions = function (useCompression) {
- // create a table of all geometry buffers based on
- // the number of attributes and whether they are
- // compressed or not, sort them by priority
-
- this._geometryDefinitions = [];
-
- if (defined(this._data.geometryDefinitions)) {
- for (
- let defIndex = 0;
- defIndex < this._data.geometryDefinitions.length;
- defIndex++
- ) {
- const geometryBuffersInfo = [];
- const geometryBuffers =
- this._data.geometryDefinitions[defIndex].geometryBuffers;
-
- for (let bufIndex = 0; bufIndex < geometryBuffers.length; bufIndex++) {
- const geometryBuffer = geometryBuffers[bufIndex];
- const collectedAttributes = [];
- let compressed = false;
-
- if (defined(geometryBuffer.compressedAttributes) && useCompression) {
- // check if compressed
- compressed = true;
- const attributes = geometryBuffer.compressedAttributes.attributes;
- for (let i = 0; i < attributes.length; i++) {
- collectedAttributes.push(attributes[i]);
- }
- } else {
- // uncompressed attributes
- for (const attribute in geometryBuffer) {
- if (attribute !== "offset") {
- collectedAttributes.push(attribute);
- }
- }
- }
-
- geometryBuffersInfo.push({
- compressed: compressed,
- attributes: collectedAttributes,
- index: geometryBuffers.indexOf(geometryBuffer),
- });
- }
-
- // rank the buffer info
- geometryBuffersInfo.sort(function (a, b) {
- if (a.compressed && !b.compressed) {
- return -1;
- } else if (!a.compressed && b.compressed) {
- return 1;
- }
- return a.attributes.length - b.attributes.length;
- });
- this._geometryDefinitions.push(geometryBuffersInfo);
- }
- }
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._findBestGeometryBuffers = function (
- definition,
- attributes,
- ) {
- // find the most appropriate geometry definition
- // based on the required attributes, and by favouring
- // compression to improve bandwidth requirements
-
- const geometryDefinition = this._geometryDefinitions[definition];
-
- if (defined(geometryDefinition)) {
- for (let index = 0; index < geometryDefinition.length; ++index) {
- const geometryBufferInfo = geometryDefinition[index];
- let missed = false;
- const geometryAttributes = geometryBufferInfo.attributes;
- for (let attrIndex = 0; attrIndex < attributes.length; attrIndex++) {
- if (!geometryAttributes.includes(attributes[attrIndex])) {
- missed = true;
- break;
- }
- }
- if (!missed) {
- return {
- bufferIndex: geometryBufferInfo.index,
- definition: geometryDefinition,
- geometryBufferInfo: geometryBufferInfo,
- };
- }
- }
- // If no match found return first geometryBuffer
- if (defined(geometryDefinition[0])) {
- return {
- bufferIndex: 0,
- definition: geometryDefinition,
- geometryBufferInfo: geometryDefinition[0],
- };
- }
- }
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._loadRootNode = function (cesium3dTilesetOptions) {
- if (defined(this._data.nodePages)) {
- let rootIndex = 0;
- if (defined(this._data.nodePages.rootIndex)) {
- rootIndex = this._data.nodePages.rootIndex;
- }
- this._rootNode = new I3SNode(this, rootIndex, true);
- } else {
- this._rootNode = new I3SNode(this, this._data.store.rootNode, true);
- }
-
- return this._rootNode.load(cesium3dTilesetOptions);
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._getNodeInNodePages = function (nodeIndex) {
- const index = Math.floor(nodeIndex / this._data.nodePages.nodesPerPage);
- const offsetInPage = nodeIndex % this._data.nodePages.nodesPerPage;
- return this._loadNodePage(index).then(function (data) {
- return data.nodes[offsetInPage];
- });
- };
-
- /**
- * @private
- */
- I3SLayer._fetchJson = function (resource) {
- return resource.fetchJson();
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._loadNodePage = function (page) {
- const that = this;
-
- // If node page was already requested return the same promise
- if (!defined(this._nodePageFetches[page])) {
- const nodePageResource = this.resource.getDerivedResource({
- url: `nodepages/${page}/`,
- });
- const fetchPromise = I3SLayer._fetchJson(nodePageResource).then(
- function (data) {
- if (defined(data.error) && data.error.code !== 200) {
- return Promise.reject(data.error);
- }
-
- that._nodePages[page] = data.nodes;
- return data;
- },
- );
-
- this._nodePageFetches[page] = fetchPromise;
- }
-
- return this._nodePageFetches[page];
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._computeExtent = function () {
- if (defined(this._data.fullExtent)) {
- this._extent = Rectangle.fromDegrees(
- this._data.fullExtent.xmin,
- this._data.fullExtent.ymin,
- this._data.fullExtent.xmax,
- this._data.fullExtent.ymax,
- );
- } else if (defined(this._data.store.extent)) {
- this._extent = Rectangle.fromDegrees(
- this._data.store.extent[0],
- this._data.store.extent[1],
- this._data.store.extent[2],
- this._data.store.extent[3],
- );
- }
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._create3DTileset = async function (cesium3dTilesetOptions) {
- const inPlaceTileset = {
- asset: {
- version: "1.0",
- },
- geometricError: Number.MAX_VALUE,
- root: this._rootNode._create3DTileDefinition(),
- };
-
- const tilesetBlob = new Blob([JSON.stringify(inPlaceTileset)], {
- type: "application/json",
- });
-
- const tilesetUrl = URL.createObjectURL(tilesetBlob);
- const outlineColor = this._symbology?.defaultSymbology?.edges?.color;
- if (defined(outlineColor) && !defined(cesium3dTilesetOptions?.outlineColor)) {
- cesium3dTilesetOptions = defined(cesium3dTilesetOptions)
- ? clone(cesium3dTilesetOptions)
- : {};
- cesium3dTilesetOptions.outlineColor = Color.fromCartesian4(
- Cartesian4.fromArray(outlineColor),
- );
- }
- this._tileset = await Cesium3DTileset.fromUrl(
- tilesetUrl,
- cesium3dTilesetOptions,
- );
- this._tileset.show = this._parent.show;
- this._tileset._isI3STileSet = true;
- this._tileset.tileUnload.addEventListener(function (tile) {
- tile._i3sNode._clearGeometryData();
- URL.revokeObjectURL(tile._contentResource._url);
- tile._contentResource = tile._i3sNode.resource;
- });
-
- this._tileset.tileVisible.addEventListener(function (tile) {
- if (defined(tile._i3sNode)) {
- tile._i3sNode._loadChildren();
- }
- });
- };
-
- /**
- * @private
- */
- I3SLayer.prototype._updateVisibility = function () {
- if (defined(this._tileset)) {
- this._tileset.show = this._parent.show;
- }
- };
-
- /**
- * Filters the drawn elements of a layer to specific attribute names and values
- * @param {I3SNode.AttributeFilter[]} [filters=[]] The collection of attribute filters
- * @returns {Promise<void>} A promise that is resolved when the filter is applied
- */
- I3SLayer.prototype.filterByAttributes = function (filters) {
- // Filters are applied for each node in the layer when the node model is loaded
- this._filters = defined(filters) ? clone(filters, true) : [];
-
- // Forced filtering is required for loaded nodes only
- const rootNode = this._rootNode;
- if (defined(rootNode)) {
- return rootNode._filterFeatures();
- }
- return Promise.resolve();
- };
-
- export default I3SLayer;
|