GeoJsonLoader.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. import Cartesian3 from "../../Core/Cartesian3.js";
  2. import Check from "../../Core/Check.js";
  3. import ComponentDatatype from "../../Core/ComponentDatatype.js";
  4. import defaultValue from "../../Core/defaultValue.js";
  5. import defined from "../../Core/defined.js";
  6. import Ellipsoid from "../../Core/Ellipsoid.js";
  7. import IndexDatatype from "../../Core/IndexDatatype.js";
  8. import Matrix4 from "../../Core/Matrix4.js";
  9. import PrimitiveType from "../../Core/PrimitiveType.js";
  10. import RuntimeError from "../../Core/RuntimeError.js";
  11. import Transforms from "../../Core/Transforms.js";
  12. import AttributeType from "../AttributeType.js";
  13. import JsonMetadataTable from "../JsonMetadataTable.js";
  14. import MetadataSchema from "../MetadataSchema.js";
  15. import ModelComponents from "../ModelComponents.js";
  16. import PropertyTable from "../PropertyTable.js";
  17. import ResourceLoader from "../ResourceLoader.js";
  18. import StructuralMetadata from "../StructuralMetadata.js";
  19. import VertexAttributeSemantic from "../VertexAttributeSemantic.js";
  20. import Buffer from "../../Renderer/Buffer.js";
  21. import BufferUsage from "../../Renderer/BufferUsage.js";
  22. /**
  23. * Loads a GeoJson model as part of the <code>MAXAR_content_geojson</code> extension with the following constraints:
  24. * <ul>
  25. * <li>The top level GeoJSON type must be FeatureCollection or Feature.</li>
  26. * <li>The geometry types must be LineString, MultiLineString, MultiPolygon, Polygon, MultiPoint, or Point.</li>
  27. * <li>Polygon and polyline geometries are converted to geodesic lines.</li>
  28. * <li>Only WGS84 geographic coordinates are supported.</li>
  29. * </ul>
  30. * <p>
  31. * Implements the {@link ResourceLoader} interface.
  32. * </p>
  33. *
  34. * @alias GeoJsonLoader
  35. * @constructor
  36. * @augments ResourceLoader
  37. * @private
  38. *
  39. * @param {object} options Object with the following properties:
  40. * @param {object} options.geoJson The GeoJson object.
  41. */
  42. function GeoJsonLoader(options) {
  43. options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  44. //>>includeStart('debug', pragmas.debug);
  45. Check.typeOf.object("options.geoJson", options.geoJson);
  46. //>>includeEnd('debug');
  47. this._geoJson = options.geoJson;
  48. this._components = undefined;
  49. }
  50. if (defined(Object.create)) {
  51. GeoJsonLoader.prototype = Object.create(ResourceLoader.prototype);
  52. GeoJsonLoader.prototype.constructor = GeoJsonLoader;
  53. }
  54. Object.defineProperties(GeoJsonLoader.prototype, {
  55. /**
  56. * The cache key of the resource.
  57. *
  58. * @memberof GeoJsonLoader.prototype
  59. *
  60. * @type {string}
  61. * @readonly
  62. * @private
  63. */
  64. cacheKey: {
  65. get: function () {
  66. return undefined;
  67. },
  68. },
  69. /**
  70. * The loaded components.
  71. *
  72. * @memberof GeoJsonLoader.prototype
  73. *
  74. * @type {ModelComponents.Components}
  75. * @readonly
  76. * @private
  77. */
  78. components: {
  79. get: function () {
  80. return this._components;
  81. },
  82. },
  83. });
  84. /**
  85. * Loads the resource.
  86. * @returns {Promise<GeoJsonLoader>} A promise which resolves to the loader when the resource loading is completed.
  87. * @private
  88. */
  89. GeoJsonLoader.prototype.load = function () {
  90. return Promise.resolve(this);
  91. };
  92. /**
  93. * Processes the resource until it becomes ready.
  94. *
  95. * @param {FrameState} frameState The frame state.
  96. * @private
  97. */
  98. GeoJsonLoader.prototype.process = function (frameState) {
  99. //>>includeStart('debug', pragmas.debug);
  100. Check.typeOf.object("frameState", frameState);
  101. //>>includeEnd('debug');
  102. if (defined(this._components)) {
  103. return true;
  104. }
  105. this._components = parse(this._geoJson, frameState);
  106. this._geoJson = undefined;
  107. return true;
  108. };
  109. function ParsedFeature() {
  110. this.lines = undefined;
  111. this.points = undefined;
  112. this.properties = undefined;
  113. }
  114. function ParseResult() {
  115. this.features = [];
  116. }
  117. function parsePosition(position) {
  118. const x = position[0];
  119. const y = position[1];
  120. const z = defaultValue(position[2], 0.0);
  121. return new Cartesian3(x, y, z);
  122. }
  123. function parseLineString(coordinates) {
  124. const positionsLength = coordinates.length;
  125. const line = new Array(positionsLength);
  126. for (let i = 0; i < positionsLength; i++) {
  127. line[i] = parsePosition(coordinates[i]);
  128. }
  129. const lines = [line];
  130. return lines;
  131. }
  132. function parseMultiLineString(coordinates) {
  133. const linesLength = coordinates.length;
  134. const lines = new Array(linesLength);
  135. for (let i = 0; i < linesLength; i++) {
  136. lines[i] = parseLineString(coordinates[i])[0];
  137. }
  138. return lines;
  139. }
  140. function parsePolygon(coordinates) {
  141. // Treat exterior polygon and interior polygons as lines
  142. const linesLength = coordinates.length;
  143. const lines = new Array(linesLength);
  144. for (let i = 0; i < linesLength; i++) {
  145. lines[i] = parseLineString(coordinates[i])[0];
  146. }
  147. return lines;
  148. }
  149. function parseMultiPolygon(coordinates) {
  150. const polygonsLength = coordinates.length;
  151. const lines = [];
  152. for (let i = 0; i < polygonsLength; i++) {
  153. Array.prototype.push.apply(lines, parsePolygon(coordinates[i]));
  154. }
  155. return lines;
  156. }
  157. function parsePoint(coordinates) {
  158. return [parsePosition(coordinates)];
  159. }
  160. function parseMultiPoint(coordinates) {
  161. const pointsLength = coordinates.length;
  162. const points = new Array(pointsLength);
  163. for (let i = 0; i < pointsLength; i++) {
  164. points[i] = parsePosition(coordinates[i]);
  165. }
  166. return points;
  167. }
  168. const geometryTypes = {
  169. LineString: parseLineString,
  170. MultiLineString: parseMultiLineString,
  171. MultiPolygon: parseMultiPolygon,
  172. Polygon: parsePolygon,
  173. MultiPoint: parseMultiPoint,
  174. Point: parsePoint,
  175. };
  176. const primitiveTypes = {
  177. LineString: PrimitiveType.LINES,
  178. MultiLineString: PrimitiveType.LINES,
  179. MultiPolygon: PrimitiveType.LINES,
  180. Polygon: PrimitiveType.LINES,
  181. MultiPoint: PrimitiveType.POINTS,
  182. Point: PrimitiveType.POINTS,
  183. };
  184. function parseFeature(feature, result) {
  185. if (!defined(feature.geometry)) {
  186. return;
  187. }
  188. const geometryType = feature.geometry.type;
  189. const geometryFunction = geometryTypes[geometryType];
  190. const primitiveType = primitiveTypes[geometryType];
  191. const coordinates = feature.geometry.coordinates;
  192. if (!defined(geometryFunction)) {
  193. return;
  194. }
  195. if (!defined(coordinates)) {
  196. return;
  197. }
  198. const parsedFeature = new ParsedFeature();
  199. if (primitiveType === PrimitiveType.LINES) {
  200. parsedFeature.lines = geometryFunction(coordinates);
  201. } else if (primitiveType === PrimitiveType.POINTS) {
  202. parsedFeature.points = geometryFunction(coordinates);
  203. }
  204. parsedFeature.properties = feature.properties;
  205. result.features.push(parsedFeature);
  206. }
  207. function parseFeatureCollection(featureCollection, result) {
  208. const features = featureCollection.features;
  209. const featuresLength = features.length;
  210. for (let i = 0; i < featuresLength; i++) {
  211. parseFeature(features[i], result);
  212. }
  213. }
  214. const geoJsonObjectTypes = {
  215. FeatureCollection: parseFeatureCollection,
  216. Feature: parseFeature,
  217. };
  218. const scratchCartesian = new Cartesian3();
  219. function createLinesPrimitive(features, toLocal, frameState) {
  220. // Count the number of vertices and indices
  221. let vertexCount = 0;
  222. let indexCount = 0;
  223. const featureCount = features.length;
  224. for (let i = 0; i < featureCount; i++) {
  225. const feature = features[i];
  226. if (defined(feature.lines)) {
  227. const linesLength = feature.lines.length;
  228. for (let j = 0; j < linesLength; j++) {
  229. const line = feature.lines[j];
  230. vertexCount += line.length;
  231. indexCount += (line.length - 1) * 2;
  232. }
  233. }
  234. }
  235. // Allocate typed arrays
  236. const positionsTypedArray = new Float32Array(vertexCount * 3);
  237. const featureIdsTypedArray = new Float32Array(vertexCount);
  238. const indicesTypedArray = IndexDatatype.createTypedArray(
  239. vertexCount,
  240. indexCount
  241. );
  242. const indexDatatype = IndexDatatype.fromTypedArray(indicesTypedArray);
  243. // Process the data. Convert positions to local ENU. Generate indices.
  244. const localMin = new Cartesian3(
  245. Number.POSITIVE_INFINITY,
  246. Number.POSITIVE_INFINITY,
  247. Number.POSITIVE_INFINITY
  248. );
  249. const localMax = new Cartesian3(
  250. Number.NEGATIVE_INFINITY,
  251. Number.NEGATIVE_INFINITY,
  252. Number.NEGATIVE_INFINITY
  253. );
  254. let vertexCounter = 0;
  255. let segmentCounter = 0;
  256. for (let i = 0; i < featureCount; i++) {
  257. const feature = features[i];
  258. if (!defined(feature.lines)) {
  259. continue;
  260. }
  261. const linesLength = feature.lines.length;
  262. for (let j = 0; j < linesLength; j++) {
  263. const line = feature.lines[j];
  264. const positionsLength = line.length;
  265. for (let k = 0; k < positionsLength; k++) {
  266. const cartographic = line[k];
  267. const globalCartesian = Cartesian3.fromDegrees(
  268. cartographic.x,
  269. cartographic.y,
  270. cartographic.z,
  271. Ellipsoid.WGS84,
  272. scratchCartesian
  273. );
  274. const localCartesian = Matrix4.multiplyByPoint(
  275. toLocal,
  276. globalCartesian,
  277. scratchCartesian
  278. );
  279. Cartesian3.minimumByComponent(localMin, localCartesian, localMin);
  280. Cartesian3.maximumByComponent(localMax, localCartesian, localMax);
  281. Cartesian3.pack(localCartesian, positionsTypedArray, vertexCounter * 3);
  282. featureIdsTypedArray[vertexCounter] = i;
  283. if (k < positionsLength - 1) {
  284. indicesTypedArray[segmentCounter * 2] = vertexCounter;
  285. indicesTypedArray[segmentCounter * 2 + 1] = vertexCounter + 1;
  286. segmentCounter++;
  287. }
  288. vertexCounter++;
  289. }
  290. }
  291. }
  292. // Create GPU buffers
  293. const positionBuffer = Buffer.createVertexBuffer({
  294. typedArray: positionsTypedArray,
  295. context: frameState.context,
  296. usage: BufferUsage.STATIC_DRAW,
  297. });
  298. positionBuffer.vertexArrayDestroyable = false;
  299. const featureIdBuffer = Buffer.createVertexBuffer({
  300. typedArray: featureIdsTypedArray,
  301. context: frameState.context,
  302. usage: BufferUsage.STATIC_DRAW,
  303. });
  304. featureIdBuffer.vertexArrayDestroyable = false;
  305. const indexBuffer = Buffer.createIndexBuffer({
  306. typedArray: indicesTypedArray,
  307. context: frameState.context,
  308. usage: BufferUsage.STATIC_DRAW,
  309. indexDatatype: indexDatatype,
  310. });
  311. indexBuffer.vertexArrayDestroyable = false;
  312. // Create ModelComponents
  313. const positionAttribute = new ModelComponents.Attribute();
  314. positionAttribute.semantic = VertexAttributeSemantic.POSITION;
  315. positionAttribute.componentDatatype = ComponentDatatype.FLOAT;
  316. positionAttribute.type = AttributeType.VEC3;
  317. positionAttribute.count = vertexCount;
  318. positionAttribute.min = localMin;
  319. positionAttribute.max = localMax;
  320. positionAttribute.buffer = positionBuffer;
  321. const featureIdAttribute = new ModelComponents.Attribute();
  322. featureIdAttribute.semantic = VertexAttributeSemantic.FEATURE_ID;
  323. featureIdAttribute.setIndex = 0;
  324. featureIdAttribute.componentDatatype = ComponentDatatype.FLOAT;
  325. featureIdAttribute.type = AttributeType.SCALAR;
  326. featureIdAttribute.count = vertexCount;
  327. featureIdAttribute.buffer = featureIdBuffer;
  328. const attributes = [positionAttribute, featureIdAttribute];
  329. const material = new ModelComponents.Material();
  330. material.unlit = true;
  331. const indices = new ModelComponents.Indices();
  332. indices.indexDatatype = indexDatatype;
  333. indices.count = indicesTypedArray.length;
  334. indices.buffer = indexBuffer;
  335. const featureId = new ModelComponents.FeatureIdAttribute();
  336. featureId.featureCount = featureCount;
  337. featureId.propertyTableId = 0;
  338. featureId.setIndex = 0;
  339. featureId.positionalLabel = "featureId_0";
  340. const featureIds = [featureId];
  341. const primitive = new ModelComponents.Primitive();
  342. primitive.attributes = attributes;
  343. primitive.indices = indices;
  344. primitive.featureIds = featureIds;
  345. primitive.primitiveType = PrimitiveType.LINES;
  346. primitive.material = material;
  347. return primitive;
  348. }
  349. function createPointsPrimitive(features, toLocal, frameState) {
  350. // Count the number of vertices
  351. let vertexCount = 0;
  352. const featureCount = features.length;
  353. for (let i = 0; i < featureCount; i++) {
  354. const feature = features[i];
  355. if (defined(feature.points)) {
  356. vertexCount += feature.points.length;
  357. }
  358. }
  359. // Allocate typed arrays
  360. const positionsTypedArray = new Float32Array(vertexCount * 3);
  361. const featureIdsTypedArray = new Float32Array(vertexCount);
  362. // Process the data. Convert positions to local ENU.
  363. const localMin = new Cartesian3(
  364. Number.POSITIVE_INFINITY,
  365. Number.POSITIVE_INFINITY,
  366. Number.POSITIVE_INFINITY
  367. );
  368. const localMax = new Cartesian3(
  369. Number.NEGATIVE_INFINITY,
  370. Number.NEGATIVE_INFINITY,
  371. Number.NEGATIVE_INFINITY
  372. );
  373. let vertexCounter = 0;
  374. for (let i = 0; i < featureCount; i++) {
  375. const feature = features[i];
  376. if (!defined(feature.points)) {
  377. continue;
  378. }
  379. const pointsLength = feature.points.length;
  380. for (let j = 0; j < pointsLength; j++) {
  381. const cartographic = feature.points[j];
  382. const globalCartesian = Cartesian3.fromDegrees(
  383. cartographic.x,
  384. cartographic.y,
  385. cartographic.z,
  386. Ellipsoid.WGS84,
  387. scratchCartesian
  388. );
  389. const localCartesian = Matrix4.multiplyByPoint(
  390. toLocal,
  391. globalCartesian,
  392. scratchCartesian
  393. );
  394. Cartesian3.minimumByComponent(localMin, localCartesian, localMin);
  395. Cartesian3.maximumByComponent(localMax, localCartesian, localMax);
  396. Cartesian3.pack(localCartesian, positionsTypedArray, vertexCounter * 3);
  397. featureIdsTypedArray[vertexCounter] = i;
  398. vertexCounter++;
  399. }
  400. }
  401. // Create GPU buffers
  402. const positionBuffer = Buffer.createVertexBuffer({
  403. typedArray: positionsTypedArray,
  404. context: frameState.context,
  405. usage: BufferUsage.STATIC_DRAW,
  406. });
  407. positionBuffer.vertexArrayDestroyable = false;
  408. const featureIdBuffer = Buffer.createVertexBuffer({
  409. typedArray: featureIdsTypedArray,
  410. context: frameState.context,
  411. usage: BufferUsage.STATIC_DRAW,
  412. });
  413. featureIdBuffer.vertexArrayDestroyable = false;
  414. // Create ModelComponents
  415. const positionAttribute = new ModelComponents.Attribute();
  416. positionAttribute.semantic = VertexAttributeSemantic.POSITION;
  417. positionAttribute.componentDatatype = ComponentDatatype.FLOAT;
  418. positionAttribute.type = AttributeType.VEC3;
  419. positionAttribute.count = vertexCount;
  420. positionAttribute.min = localMin;
  421. positionAttribute.max = localMax;
  422. positionAttribute.buffer = positionBuffer;
  423. const featureIdAttribute = new ModelComponents.Attribute();
  424. featureIdAttribute.semantic = VertexAttributeSemantic.FEATURE_ID;
  425. featureIdAttribute.setIndex = 0;
  426. featureIdAttribute.componentDatatype = ComponentDatatype.FLOAT;
  427. featureIdAttribute.type = AttributeType.SCALAR;
  428. featureIdAttribute.count = vertexCount;
  429. featureIdAttribute.buffer = featureIdBuffer;
  430. const attributes = [positionAttribute, featureIdAttribute];
  431. const material = new ModelComponents.Material();
  432. material.unlit = true;
  433. const featureId = new ModelComponents.FeatureIdAttribute();
  434. featureId.featureCount = featureCount;
  435. featureId.propertyTableId = 0;
  436. featureId.setIndex = 0;
  437. featureId.positionalLabel = "featureId_0";
  438. const featureIds = [featureId];
  439. const primitive = new ModelComponents.Primitive();
  440. primitive.attributes = attributes;
  441. primitive.featureIds = featureIds;
  442. primitive.primitiveType = PrimitiveType.POINTS;
  443. primitive.material = material;
  444. return primitive;
  445. }
  446. function parse(geoJson, frameState) {
  447. const result = new ParseResult();
  448. // Parse the GeoJSON
  449. const parseFunction = geoJsonObjectTypes[geoJson.type];
  450. if (defined(parseFunction)) {
  451. parseFunction(geoJson, result);
  452. }
  453. const features = result.features;
  454. const featureCount = features.length;
  455. if (featureCount === 0) {
  456. throw new RuntimeError("GeoJSON must have at least one feature");
  457. }
  458. // Allocate space for property values
  459. const properties = {};
  460. for (let i = 0; i < featureCount; i++) {
  461. const feature = features[i];
  462. const featureProperties = defaultValue(
  463. feature.properties,
  464. defaultValue.EMPTY_OBJECT
  465. );
  466. for (const propertyId in featureProperties) {
  467. if (featureProperties.hasOwnProperty(propertyId)) {
  468. if (!defined(properties[propertyId])) {
  469. properties[propertyId] = new Array(featureCount);
  470. }
  471. }
  472. }
  473. }
  474. // Fill in the property values. Default to empty string for undefined values.
  475. for (let i = 0; i < featureCount; i++) {
  476. const feature = features[i];
  477. for (const propertyId in properties) {
  478. if (properties.hasOwnProperty(propertyId)) {
  479. const value = defaultValue(feature.properties[propertyId], "");
  480. properties[propertyId][i] = value;
  481. }
  482. }
  483. }
  484. const jsonMetadataTable = new JsonMetadataTable({
  485. count: featureCount,
  486. properties: properties,
  487. });
  488. const propertyTable = new PropertyTable({
  489. id: 0,
  490. count: featureCount,
  491. jsonMetadataTable: jsonMetadataTable,
  492. });
  493. const propertyTables = [propertyTable];
  494. const schema = MetadataSchema.fromJson({});
  495. const structuralMetadata = new StructuralMetadata({
  496. schema: schema,
  497. propertyTables: propertyTables,
  498. });
  499. // Find the cartographic bounding box
  500. const cartographicMin = new Cartesian3(
  501. Number.POSITIVE_INFINITY,
  502. Number.POSITIVE_INFINITY,
  503. Number.POSITIVE_INFINITY
  504. );
  505. const cartographicMax = new Cartesian3(
  506. Number.NEGATIVE_INFINITY,
  507. Number.NEGATIVE_INFINITY,
  508. Number.NEGATIVE_INFINITY
  509. );
  510. let hasLines = false;
  511. let hasPoints = false;
  512. for (let i = 0; i < featureCount; i++) {
  513. const feature = features[i];
  514. if (defined(feature.lines)) {
  515. hasLines = true;
  516. const linesLength = feature.lines.length;
  517. for (let j = 0; j < linesLength; j++) {
  518. const line = feature.lines[j];
  519. const positionsLength = line.length;
  520. for (let k = 0; k < positionsLength; k++) {
  521. Cartesian3.minimumByComponent(
  522. cartographicMin,
  523. line[k],
  524. cartographicMin
  525. );
  526. Cartesian3.maximumByComponent(
  527. cartographicMax,
  528. line[k],
  529. cartographicMax
  530. );
  531. }
  532. }
  533. }
  534. if (defined(feature.points)) {
  535. hasPoints = true;
  536. const pointsLength = feature.points.length;
  537. for (let j = 0; j < pointsLength; j++) {
  538. const point = feature.points[j];
  539. Cartesian3.minimumByComponent(cartographicMin, point, cartographicMin);
  540. Cartesian3.maximumByComponent(cartographicMax, point, cartographicMax);
  541. }
  542. }
  543. }
  544. // Compute the ENU matrix
  545. const cartographicCenter = Cartesian3.midpoint(
  546. cartographicMin,
  547. cartographicMax,
  548. new Cartesian3()
  549. );
  550. const ecefCenter = Cartesian3.fromDegrees(
  551. cartographicCenter.x,
  552. cartographicCenter.y,
  553. cartographicCenter.z,
  554. Ellipsoid.WGS84,
  555. new Cartesian3()
  556. );
  557. const toGlobal = Transforms.eastNorthUpToFixedFrame(
  558. ecefCenter,
  559. Ellipsoid.WGS84,
  560. new Matrix4()
  561. );
  562. const toLocal = Matrix4.inverseTransformation(toGlobal, new Matrix4());
  563. const primitives = [];
  564. if (hasLines) {
  565. primitives.push(createLinesPrimitive(features, toLocal, frameState));
  566. }
  567. if (hasPoints) {
  568. primitives.push(createPointsPrimitive(features, toLocal, frameState));
  569. }
  570. const node = new ModelComponents.Node();
  571. node.index = 0;
  572. node.primitives = primitives;
  573. const nodes = [node];
  574. const scene = new ModelComponents.Scene();
  575. scene.nodes = nodes;
  576. const components = new ModelComponents.Components();
  577. components.scene = scene;
  578. components.nodes = nodes;
  579. components.transform = toGlobal;
  580. components.structuralMetadata = structuralMetadata;
  581. return components;
  582. }
  583. /**
  584. * Unloads the resource.
  585. * @private
  586. */
  587. GeoJsonLoader.prototype.unload = function () {
  588. this._components = undefined;
  589. };
  590. export default GeoJsonLoader;