"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _typeof = require("@babel/runtime/helpers/typeof");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = exports.FeatureGeometry = exports.FeatureCollection = exports.FeatureBuildingOptions = exports.FEATURE_TYPES = void 0;

var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));

var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get"));

var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));

var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));

var _classPrivateFieldGet2 = _interopRequireDefault(require("@babel/runtime/helpers/classPrivateFieldGet"));

var _classPrivateFieldSet2 = _interopRequireDefault(require("@babel/runtime/helpers/classPrivateFieldSet"));

var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));

var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));

var THREE = _interopRequireWildcard(require("three"));

var _Extent = _interopRequireDefault(require("./Geographic/Extent"));

var _Coordinates = _interopRequireDefault(require("./Geographic/Coordinates"));

var _Crs = _interopRequireDefault(require("./Geographic/Crs"));

var _Style = _interopRequireDefault(require("./Style"));

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }

function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }

function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }

function defaultExtent(crs) {
  return new _Extent["default"](crs, Infinity, -Infinity, Infinity, -Infinity);
}

function _extendBuffer(feature, size) {
  feature.vertices.length += size * feature.size;

  if (feature.normals) {
    feature.normals.length = feature.vertices.length;
  }
}

function _setGeometryValues(geom, feature, _long, lat, alt, normal) {
  if (feature.normals) {
    normal.toArray(feature.normals, feature._pos);
  }

  feature._pushValues(_long, lat, alt);

  if (geom.size == 3) {
    geom.altitude.min = Math.min(geom.altitude.min, alt);
    geom.altitude.max = Math.max(geom.altitude.max, alt);
  }
}

var coordOut = new _Coordinates["default"]('EPSG:4326', 0, 0, 0);
var FEATURE_TYPES = {
  POINT: 0,
  LINE: 1,
  POLYGON: 2
};
exports.FEATURE_TYPES = FEATURE_TYPES;
var typeToStyleProperty = ['point', 'stroke', 'fill'];
/**
 * @property {string} crs - The CRS to convert the input coordinates to.
 * @property {string} [structure='2d'] - data structure type : 2d or 3d.
 * If the structure is 3d, the feature have 3 dimensions by vertices positions and
 * a normal for each vertices.
 * @property {Extent|boolean} [filteringExtent=undefined] - Optional filter to reject
 * features outside of extent. Extent filtering is file extent if filteringExtent is true.
 * @property {boolean} [buildExtent] - If true the geometry will
 * have an extent property containing the area covered by the geometry.
 * Default value is false if `structure` parameter is set to '3d', and true otherwise.
 * True if the layer does not inherit from {@link GeometryLayer}.
 * @property {string} forcedExtentCrs - force feature extent crs if buildExtent is true.
 * @property {function} [filter] - Filter function to remove features
 * @property {boolean} [mergeFeatures=true] - If true all geometries are merged by type and multi-type.
 * @property {Style} style - The style to inherit when creating
 * style for all new features.
 *
*/

var FeatureBuildingOptions = /*#__PURE__*/(0, _createClass2["default"])(function FeatureBuildingOptions() {
  (0, _classCallCheck2["default"])(this, FeatureBuildingOptions);
});
/**
 * @property {Extent} extent - The 2D extent containing all the points
 * composing the geometry.
 * @property {Object[]} indices - Contains the indices that define the geometry.
 * Objects stored in this array have two properties, an `offset` and a `count`.
 * The offset is related to the overall number of vertices in the Feature.
 *
 * @property {Object} properties - Properties of the geometry. It can be
 * anything specified in the GeoJSON under the `properties` property.
 */

exports.FeatureBuildingOptions = FeatureBuildingOptions;

var _currentExtent = /*#__PURE__*/new WeakMap();

var FeatureGeometry = /*#__PURE__*/function () {
  /**
   * @param {Feature} feature geometry
   */
  function FeatureGeometry(feature) {
    (0, _classCallCheck2["default"])(this, FeatureGeometry);

    _classPrivateFieldInitSpec(this, _currentExtent, {
      writable: true,
      value: void 0
    });

    this.indices = [];
    this.properties = {};
    this.size = feature.size;

    if (feature.extent) {
      this.extent = defaultExtent(feature.extent.crs);
      (0, _classPrivateFieldSet2["default"])(this, _currentExtent, defaultExtent(feature.extent.crs));
    }

    this.altitude = {
      min: Infinity,
      max: -Infinity
    };
  }
  /**
   * Add a new marker to indicate the starting of sub geometry and extends the vertices buffer.
   * Then you have to push new the coordinates of sub geometry.
   * The sub geometry stored in indices, see constructor for more information.
   * @param {number} count - count of vertices
   * @param {Feature} feature - the feature containing the geometry
   */


  (0, _createClass2["default"])(FeatureGeometry, [{
    key: "startSubGeometry",
    value: function startSubGeometry(count, feature) {
      var last = this.indices.length - 1;
      var extent = this.extent ? defaultExtent(this.extent.crs) : undefined;
      var offset = last > -1 ? this.indices[last].offset + this.indices[last].count : feature.vertices.length / this.size;
      this.indices.push({
        offset: offset,
        count: count,
        extent: extent
      });
      (0, _classPrivateFieldSet2["default"])(this, _currentExtent, extent);

      _extendBuffer(feature, count);
    }
    /**
     * After you have pushed new the coordinates of sub geometry without
     * `startSubGeometry`, this function close sub geometry. The sub geometry
     * stored in indices, see constructor for more information.
     * @param {number} count count of vertices
     * @param {Feature} feature - the feature containing the geometry
     */

  }, {
    key: "closeSubGeometry",
    value: function closeSubGeometry(count, feature) {
      var last = this.indices.length - 1;
      var offset = last > -1 ? this.indices[last].offset + this.indices[last].count : feature.vertices.length / this.size - count;
      this.indices.push({
        offset: offset,
        count: count,
        extent: (0, _classPrivateFieldGet2["default"])(this, _currentExtent)
      });

      if (this.extent) {
        this.extent.union((0, _classPrivateFieldGet2["default"])(this, _currentExtent));
        (0, _classPrivateFieldSet2["default"])(this, _currentExtent, defaultExtent(this.extent.crs));
      }
    }
  }, {
    key: "getLastSubGeometry",
    value: function getLastSubGeometry() {
      var last = this.indices.length - 1;
      return this.indices[last];
    }
  }, {
    key: "baseAltitude",
    value: function baseAltitude(feature, coordinates) {
      var base_altitude = feature.style[typeToStyleProperty[feature.type]].base_altitude || 0;
      return isNaN(base_altitude) ? base_altitude(this.properties, coordinates) : base_altitude;
    }
    /**
     * Push new coordinates in vertices buffer.
     * @param {Coordinates} coordIn The coordinates to push.
     * @param {Feature} feature - the feature containing the geometry
     */

  }, {
    key: "pushCoordinates",
    value: function pushCoordinates(coordIn, feature) {
      coordIn.z = this.baseAltitude(feature, coordIn);
      coordIn.as(feature.crs, coordOut);
      feature.transformToLocalSystem(coordOut);

      _setGeometryValues(this, feature, coordOut.x, coordOut.y, coordOut.z, coordOut.geodesicNormal); // expand extent if present


      if ((0, _classPrivateFieldGet2["default"])(this, _currentExtent)) {
        (0, _classPrivateFieldGet2["default"])(this, _currentExtent).expandByCoordinates(feature.useCrsOut ? coordOut : coordIn);
      }
    }
    /**
     * Push new values coordinates in vertices buffer.
     * No geographical conversion is made or the normal doesn't stored.
     * No local transformation is made on coordinates.
     *
     * @param {Feature} feature - the feature containing the geometry
     * @param {number} long The longitude coordinate.
     * @param {number} lat The latitude coordinate.
     * @param {THREE.Vector3} [normal] the normal on coordinates (only for `EPSG:4978` projection).
     */

  }, {
    key: "pushCoordinatesValues",
    value: function pushCoordinatesValues(feature, _long2, lat, normal) {
      var altitude = this.baseAltitude(feature);

      _setGeometryValues(this, feature, _long2, lat, altitude, normal); // expand extent if present


      if ((0, _classPrivateFieldGet2["default"])(this, _currentExtent)) {
        (0, _classPrivateFieldGet2["default"])(this, _currentExtent).expandByValuesCoordinates(_long2, lat);
      }
    }
    /**
     * update geometry extent with the last sub geometry extent.
     */

  }, {
    key: "updateExtent",
    value: function updateExtent() {
      if (this.extent) {
        var last = this.indices[this.indices.length - 1];

        if (last) {
          this.extent.union(last.extent);
        }
      }
    }
  }]);
  return FeatureGeometry;
}();

exports.FeatureGeometry = FeatureGeometry;

function push2DValues(value0, value1) {
  this.vertices[this._pos++] = value0;
  this.vertices[this._pos++] = value1;
}

function push3DValues(value0, value1) {
  var value2 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
  this.vertices[this._pos++] = value0;
  this.vertices[this._pos++] = value1;
  this.vertices[this._pos++] = value2;
}
/**
 *
 * This class improves and simplifies the construction and conversion of geographic data structures.
 * It's an intermediary structure between geomatic formats and THREE objects.
 *
 * **Warning**, the data (`extent` or `Coordinates`) can be stored in a local system.
 * To use vertices or extent in `Feature.crs` projection,
 * it's necessary to transform `Coordinates` or `Extent` by `FeatureCollection.matrixWorld`.
 *
 * ```js
 * // To have feature extent in featureCollection.crs projection:
 * feature.extent.applyMatrix4(featureCollection.matrixWorld);
 *
 * // To have feature vertex in feature.crs projection:
 * coord.crs = feature.crs;
 * coord.setFromArray(feature.vertices)
 * coord.applyMatrix4(featureCollection.matrixWorld);
 *```
 *
 * @property {string} type - Geometry type, can be `point`, `line`, or
 * `polygon`.
 * @property {number[]} vertices - All the vertices of the Feature.
 * @property {number[]} normals - All the normals of the Feature.
 * @property {number} size - the number of values of the array that should be associated with a coordinates.
 * The size is 3 with altitude and 2 without altitude.
 * @property {boolean} hasRawElevationData - indicates if the geographic coordinates, from original source, has an elevation,
 * the coordinates has a third coordinate.
 * @property {string} crs - Geographic or Geocentric coordinates system.
 * @property {FeatureGeometry[]} geometries - An array containing all {@link
 * FeatureGeometry}.
 * @property {Extent?} extent - The extent containing all the geometries
 * composing the feature.
 */


var Feature = /*#__PURE__*/function () {
  /**
   *
   * @param {string} type type of Feature. It can be 'point', 'line' or 'polygon'.
   * @param {FeatureCollection} collection Parent feature collection.
   */
  function Feature(type, collection) {
    (0, _classCallCheck2["default"])(this, Feature);

    if (Object.keys(FEATURE_TYPES).find(function (t) {
      return FEATURE_TYPES[t] === type;
    })) {
      this.type = type;
    } else {
      throw new Error("Unsupported Feature type: ".concat(type));
    }

    this.geometries = [];
    this.vertices = [];
    this.crs = collection.crs;
    this.size = collection.size;
    this.normals = collection.crs == 'EPSG:4978' ? [] : undefined;
    this.hasRawElevationData = false;
    this.transformToLocalSystem = collection.transformToLocalSystem.bind(collection);

    if (collection.extent) {
      // this.crs is final crs projection, is out projection.
      // If the extent crs is the same then we use output coordinate (coordOut) to expand it.
      this.extent = defaultExtent(collection.extent.crs);
      this.useCrsOut = this.extent.crs == this.crs;
    }

    this._pos = 0;
    this._pushValues = (this.size === 3 ? push3DValues : push2DValues).bind(this);
    this.style = new _Style["default"]({}, collection.style);
    this.altitude = {
      min: Infinity,
      max: -Infinity
    };
  }
  /**
   * Instance a new {@link FeatureGeometry}  and push in {@link Feature}.
   * @returns {FeatureGeometry} the instancied geometry.
   */


  (0, _createClass2["default"])(Feature, [{
    key: "bindNewGeometry",
    value: function bindNewGeometry() {
      var geometry = new FeatureGeometry(this);
      this.geometries.push(geometry);
      return geometry;
    }
    /**
     * Update {@link Extent} feature with {@link Extent} geometry
     * @param {FeatureGeometry} geometry used to update Feature {@link Extent}
     */

  }, {
    key: "updateExtent",
    value: function updateExtent(geometry) {
      if (this.extent) {
        this.extent.union(geometry.extent);
      }

      if (this.size == 3) {
        this.altitude.min = Math.min(this.altitude.min, geometry.altitude.min);
        this.altitude.max = Math.max(this.altitude.max, geometry.altitude.max);
      }
    }
    /**
     * @returns {number} the count of geometry.
     */

  }, {
    key: "geometryCount",
    get: function get() {
      return this.geometries.length;
    }
  }]);
  return Feature;
}();

var _default = Feature;
exports["default"] = _default;

var doNothing = function () {};

var transformToLocalSystem3D = function (coord, collection) {
  coord.geodesicNormal.applyNormalMatrix(collection.normalMatrixInverse);
  return coord.applyMatrix4(collection.matrixWorldInverse);
};

var transformToLocalSystem2D = function (coord, collection) {
  return coord.applyMatrix4(collection.matrixWorldInverse);
};

var axisZ = new THREE.Vector3(0, 0, 1);
var alignYtoEast = new THREE.Quaternion();
/**
 * An object regrouping a list of [features]{@link Feature} and the extent of this collection.
 * **Warning**, the data (`extent` or `Coordinates`) can be stored in a local system.
 * The local system center is the `center` property.
 * To use `Feature` vertices or `FeatureCollection/Feature` extent in FeatureCollection.crs projection,
 * it's necessary to transform `Coordinates` or `Extent` by `FeatureCollection.matrixWorld`.
 *
 * ```js
 * // To have featureCollection extent in featureCollection.crs projection:
 * featureCollection.extent.applyMatrix4(featureCollection.matrixWorld);
 *
 * // To have feature vertex in featureCollection.crs projection:
 * const vertices = featureCollection.features[0].vertices;
 * coord.crs = featureCollection.crs;
 * coord.setFromArray(vertices)
 * coord.applyMatrix4(featureCollection.matrixWorld);
 *```
 *
 * @extends THREE.Object3D
 *
 * @property {Feature[]} features - The array of features composing the
 * collection.
 * @property {Extent?} extent - The 2D extent containing all the features
 * composing the collection. The extent projection is the same local projection `FeatureCollection`.
 * To transform `FeatureCollection.extent` to `FeatureCollection.crs` projection, the transformation matrix must be applied.
 *
 * **WARNING** if crs is `EPSG:4978` because the 3d geocentric system doesn't work with 2D `Extent`,
 * The FeatureCollection.extent projection is the original projection.
 * In this case, there isn't need to transform the extent.
 *
 * @property {string} crs - Geographic or Geocentric coordinates system.
 * @property {boolean} isFeatureCollection - Used to check whether this is FeatureCollection.
 * @property {number} size - The size structure, it's 3 for 3d and 2 for 2d.
 * @property {Style} style - The collection style used to display the feature collection.
 * @property {boolean} isInverted - This option is to be set to the
 * correct value, true or false (default being false), if the computation of
 * the coordinates needs to be inverted to same scheme as OSM, Google Maps
 * or other system. See [this link]{@link
 * https://alastaira.wordpress.com/2011/07/06/converting-tms-tile-coordinates-to-googlebingosm-tile-coordinates}
 * for more informations.
 * @property {THREE.Matrix4} matrixWorldInverse - The matrix world inverse.
 * @property {Coordinates} center - The local center coordinates in `EPSG:4326`.
 * The local system is centred in this center.
 *
 */

var _transformToLocalSystem = /*#__PURE__*/new WeakMap();

var _setLocalSystem = /*#__PURE__*/new WeakMap();

var FeatureCollection = /*#__PURE__*/function (_THREE$Object3D) {
  (0, _inherits2["default"])(FeatureCollection, _THREE$Object3D);

  var _super = _createSuper(FeatureCollection);

  /**
   * @param      {FeatureBuildingOptions|Layer}  options  The building options .
   */
  function FeatureCollection(options) {
    var _options$source;

    var _this;

    (0, _classCallCheck2["default"])(this, FeatureCollection);
    _this = _super.call(this);

    _classPrivateFieldInitSpec((0, _assertThisInitialized2["default"])(_this), _transformToLocalSystem, {
      writable: true,
      value: transformToLocalSystem2D
    });

    _classPrivateFieldInitSpec((0, _assertThisInitialized2["default"])(_this), _setLocalSystem, {
      writable: true,
      value: doNothing
    });

    _this.isFeatureCollection = true;
    _this.crs = _Crs["default"].formatToEPSG(options.accurate || !((_options$source = options.source) !== null && _options$source !== void 0 && _options$source.crs) ? options.crs : options.source.crs);
    _this.features = [];
    _this.mergeFeatures = options.mergeFeatures === undefined ? true : options.mergeFeatures;
    _this.size = options.structure == '3d' ? 3 : 2;
    _this.filterExtent = options.filterExtent;
    _this.style = options.style;
    _this.isInverted = false;
    _this.matrixWorldInverse = new THREE.Matrix4();
    _this.center = new _Coordinates["default"]('EPSG:4326', 0, 0);

    if (_this.size == 2) {
      _this.extent = options.buildExtent === false ? undefined : defaultExtent(options.forcedExtentCrs || _this.crs);
      (0, _classPrivateFieldSet2["default"])((0, _assertThisInitialized2["default"])(_this), _setLocalSystem, function (center) {
        // set local system center
        center.as(_this.crs, _this.center); // set position to local system center

        _this.position.copy(center);

        _this.updateMatrixWorld();

        (0, _classPrivateFieldSet2["default"])((0, _assertThisInitialized2["default"])(_this), _setLocalSystem, doNothing);
      });
    } else {
      _this.extent = options.buildExtent ? defaultExtent(options.forcedExtentCrs || _this.crs) : undefined;
      (0, _classPrivateFieldSet2["default"])((0, _assertThisInitialized2["default"])(_this), _setLocalSystem, function (center) {
        // set local system center
        center.as('EPSG:4326', _this.center);

        if (_this.crs == 'EPSG:4978') {
          // align Z axe to geodesic normal.
          _this.quaternion.setFromUnitVectors(axisZ, center.geodesicNormal); // align Y axe to East


          alignYtoEast.setFromAxisAngle(axisZ, THREE.MathUtils.degToRad(90 + _this.center.longitude));

          _this.quaternion.multiply(alignYtoEast);
        } // set position to local system center


        _this.position.copy(center);

        _this.updateMatrixWorld();

        _this.normalMatrix.getNormalMatrix(_this.matrix);

        _this.normalMatrixInverse = new THREE.Matrix3().copy(_this.normalMatrix).invert();
        (0, _classPrivateFieldSet2["default"])((0, _assertThisInitialized2["default"])(_this), _setLocalSystem, doNothing);
      });
      (0, _classPrivateFieldSet2["default"])((0, _assertThisInitialized2["default"])(_this), _transformToLocalSystem, transformToLocalSystem3D);
    }

    _this.altitude = {
      min: Infinity,
      max: -Infinity
    };
    return _this;
  }
  /**
   * Apply the matrix World inverse on the coordinates.
   * This method is used when the coordinates is pushed
   * to transform it in local system.
   *
   * @param   {Coordinates}  coordinates  The coordinates
   * @returns {Coordinates} The coordinates in local system
   */


  (0, _createClass2["default"])(FeatureCollection, [{
    key: "transformToLocalSystem",
    value: function transformToLocalSystem(coordinates) {
      (0, _classPrivateFieldGet2["default"])(this, _setLocalSystem).call(this, coordinates);
      return (0, _classPrivateFieldGet2["default"])(this, _transformToLocalSystem).call(this, coordinates, this);
    }
    /**
     * Update FeatureCollection extent with `extent` or all features extent if
     * `extent` is `undefined`.
     * @param {Extent} extent
     */

  }, {
    key: "updateExtent",
    value: function updateExtent(extent) {
      if (this.extent) {
        var extents = extent ? [extent] : this.features.map(function (feature) {
          return feature.extent;
        });

        var _iterator = _createForOfIteratorHelper(extents),
            _step;

        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var ext = _step.value;
            this.extent.union(ext);
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }
      }

      if (this.size == 3) {
        var _iterator2 = _createForOfIteratorHelper(this.features),
            _step2;

        try {
          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
            var feature = _step2.value;
            this.altitude.min = Math.min(this.altitude.min, feature.altitude.min);
            this.altitude.max = Math.max(this.altitude.max, feature.altitude.max);
          }
        } catch (err) {
          _iterator2.e(err);
        } finally {
          _iterator2.f();
        }
      }

      this.altitude.min = this.altitude.min == Infinity ? 0 : this.altitude.min;
      this.altitude.max = this.altitude.max == -Infinity ? 0 : this.altitude.max;
    }
    /**
     * Updates the global transform of the object and its descendants.
     *
     * @param {booolean}  force   The force
     */

  }, {
    key: "updateMatrixWorld",
    value: function updateMatrixWorld(force) {
      (0, _get2["default"])((0, _getPrototypeOf2["default"])(FeatureCollection.prototype), "updateMatrixWorld", this).call(this, force);
      this.matrixWorldInverse.copy(this.matrixWorld).invert();
    }
    /**
     * Remove features that don't have [FeatureGeometry]{@link FeatureGeometry}.
     */

  }, {
    key: "removeEmptyFeature",
    value: function removeEmptyFeature() {
      this.features = this.features.filter(function (feature) {
        return feature.geometries.length;
      });
    }
    /**
     * Push the `feature` in FeatureCollection.
     * @param {Feature} feature
     */

  }, {
    key: "pushFeature",
    value: function pushFeature(feature) {
      this.features.push(feature);
      this.updateExtent(feature.extent);
    }
  }, {
    key: "requestFeature",
    value: function requestFeature(type, callback) {
      var feature = this.features.find(callback);

      if (feature && this.mergeFeatures) {
        return feature;
      } else {
        var newFeature = new Feature(type, this);
        this.features.push(newFeature);
        return newFeature;
      }
    }
    /**
     * Returns the Feature by type if `mergeFeatures` is `true` or returns the
     * new instance of typed Feature.
     *
     * @param {string} type the type requested
     * @returns {Feature}
     */

  }, {
    key: "requestFeatureByType",
    value: function requestFeatureByType(type) {
      return this.requestFeature(type, function (feature) {
        return feature.type === type;
      });
    }
    /**
     * Returns the Feature by type if `mergeFeatures` is `true` or returns the
     * new instance of typed Feature.
     *
     * @param {string} id the id requested
     * @param {string} type the type requested
     * @returns {Feature}
     */

  }, {
    key: "requestFeatureById",
    value: function requestFeatureById(id, type) {
      return this.requestFeature(type, function (feature) {
        return feature.id === id;
      });
    }
    /**
     * Add a new feature with references to all properties.
     * It allows to have features with different styles
     * without having to duplicate the geometry.
     * @param      {Feature}   feature  The feature to reference.
     * @return     {Feature}  The new referenced feature
     */

  }, {
    key: "newFeatureByReference",
    value: function newFeatureByReference(feature) {
      var ref = new Feature(feature.type, this);
      ref.extent = feature.extent;
      ref.geometries = feature.geometries;
      ref.normals = feature.normals;
      ref.size = feature.size;
      ref.vertices = feature.vertices;
      ref.altitude.min = feature.altitude.min;
      ref.altitude.max = feature.altitude.max;
      ref._pos = feature._pos;
      this.features.push(ref);
      return ref;
    }
  }, {
    key: "setParentStyle",
    value: function setParentStyle(style) {
      if (style) {
        this.features.forEach(function (f) {
          f.style.parent = style;
        });
      }
    }
  }]);
  return FeatureCollection;
}(THREE.Object3D);

exports.FeatureCollection = FeatureCollection;