"use strict";

var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");

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

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

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

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

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

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

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;
  }
}

var defaultNormal = new THREE.Vector3(0, 0, 1);
/**
 * @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.
 */

var FeatureGeometry =
/*#__PURE__*/
function () {
  /**
   * @param {Feature} feature geometry
   */
  function FeatureGeometry(feature) {
    (0, _classCallCheck2["default"])(this, FeatureGeometry);
    this.extent = feature.extent ? defaultExtent(feature.crs) : undefined;
    this.indices = [];
    this._feature = feature;
    this.properties = {};
    this._currentExtent = feature.extent ? defaultExtent(feature.crs) : undefined;
  }
  /**
   * 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
   */


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

      _extendBuffer(this._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
     */

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

      if (this.extent) {
        this.extent.union(this._currentExtent);
        this._currentExtent = defaultExtent(this._feature.crs);
      }
    }
  }, {
    key: "getLastSubGeometry",
    value: function getLastSubGeometry() {
      var last = this.indices.length - 1;
      return this.indices[last];
    }
    /**
     * Push new coordinates in vertices buffer.
     * @param {Coordinates} coord The coordinates to push.
     */

  }, {
    key: "pushCoordinates",
    value: function pushCoordinates(coord) {
      if (coord.crs !== this._feature.crs) {
        coord.as(this._feature.crs, coord);
      }

      if (this._feature.normals) {
        coord.geodesicNormal.toArray(this._feature.normals, this._feature._pos);
      }

      this._feature._pushValues(this._feature, coord.x, coord.y, coord.z); // expand extent if present


      if (this._currentExtent) {
        this._currentExtent.expandByCoordinates(coord);
      }
    }
    /**
    * Push new values coordinates in vertices buffer.
    * No geographical conversion is made or the normal doesn't stored.
    *
    * @param {number} long The longitude coordinate.
    * @param {number} lat The latitude coordinate.
    * @param {number} [alt=0] The altitude coordinate.
    * @param {THREE.Vector3} [normal=THREE.Vector3(0,0,1)] the normal on coordinates.
    */

  }, {
    key: "pushCoordinatesValues",
    value: function pushCoordinatesValues(_long, lat) {
      var alt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      var normal = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : defaultNormal;

      if (this._feature.normals) {
        normal.toArray(this._feature.normals, this._feature._pos);
      }

      this._feature._pushValues(this._feature, _long, lat, alt); // expand extent if present


      if (this._currentExtent) {
        this._currentExtent.expandByValuesCoordinates(_long, lat, alt);
      }
    }
    /**
     * @returns {number} the number of values of the array that should be associated with a coordinates.
     * The size is 3 with altitude and 2 without altitude.
     */

  }, {
    key: "updateExtent",

    /**
     * update geometry extent with the last sub geometry extent.
     */
    value: function updateExtent() {
      if (this.extent) {
        var last = this.indices[this.indices.length - 1];

        if (last) {
          this.extent.union(last.extent);
        }
      }
    }
  }, {
    key: "size",
    get: function get() {
      return this._feature.size;
    }
  }]);
  return FeatureGeometry;
}();

exports.FeatureGeometry = FeatureGeometry;

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

function push3DValues(feature, value0, value1) {
  var value2 = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
  feature.vertices[feature._pos++] = value0;
  feature.vertices[feature._pos++] = value1;
  feature.vertices[feature._pos++] = value2;
}

var FEATURE_TYPES = {
  POINT: 0,
  LINE: 1,
  POLYGON: 2
};
/**
 *
 * This class improves and simplifies the construction and conversion of geographic data structures.
 * It's an intermediary structure between geomatic formats and THREE objects.
 *
 * @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 {string} crs - Geographic or Geocentric coordinates system.
 * @property {Array.<FeatureGeometry>} geometry - The feature's geometry.
 * @property {Extent?} extent - The extent containing all the geometries
 * composing the feature.
 */

exports.FEATURE_TYPES = FEATURE_TYPES;

var Feature =
/*#__PURE__*/
function () {
  /**
   *
   * @param {string} type type of Feature. It can be 'point', 'line' or 'polygon'.
   * @param {string} crs Geographic or Geocentric coordinates system.
   * @param {Object} [options={}] options to build feature.
   * @param {boolean} [options.buildExtent] Build extent and update when adding new vertice.
   * @param {boolean} [options.withAltitude] Set vertice altitude when adding new vertice.
   * @param {boolean} [options.withNormal] Set vertice normal when adding new vertice.
   */
  function Feature(type, crs) {
    var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    (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.geometry = [];
    /**
     * @property {number[]} vertices the vertices
     */

    this.vertices = [];
    this.normals = options.withNormal ? [] : undefined;
    this.crs = crs;
    this.size = options.withAltitude ? 3 : 2;
    this.extent = options.buildExtent ? defaultExtent(crs) : undefined;
    this._pos = 0;
    this._pushValues = this.size === 3 ? push3DValues : push2DValues;
  }
  /**
   * 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.geometry.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);
      }
    }
    /**
     * @returns {number} the count of geometry.
     */

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

var _default = Feature;
/**
 * @property {Feature[]} features - The array of features composing the
 * collection.
 * @property {Extent?} extent - The 2D extent containing all the features
 * composing the collection.
 * @property {string} crs - Geographic or Geocentric coordinates system.
 * @property {boolean} isFeatureCollection - Used to check whether this is FeatureCollection.
 * @property {THREE.Vector3} translation - Apply translation on vertices and extent to transform on coordinates system.
 * @property {THREE.Vector3} scale - Apply scale on vertices and extent to transform on coordinates system.
 *
 * An object regrouping a list of [features]{@link Feature} and the extent of this collection.
 */

exports["default"] = _default;

var FeatureCollection =
/*#__PURE__*/
function () {
  function FeatureCollection(crs, options) {
    (0, _classCallCheck2["default"])(this, FeatureCollection);
    this.isFeatureCollection = true;
    this.crs = crs;
    this.features = [];
    this.optionsFeature = options || {};
    this.extent = this.optionsFeature.buildExtent ? defaultExtent(crs) : undefined;
    this.translation = new THREE.Vector3();
    this.scale = new THREE.Vector3(1, 1, 1);
  }
  /**
   * Update FeatureCollection extent with `extent` or all features extent if
   * `extent` is `undefined`.
   * @param {Extent} extent
   */


  (0, _createClass2["default"])(FeatureCollection, [{
    key: "updateExtent",
    value: function updateExtent(extent) {
      if (this.extent) {
        var extents = extent ? [extent] : this.features.map(function (feature) {
          return feature.extent;
        });
        var _iteratorNormalCompletion = true;
        var _didIteratorError = false;
        var _iteratorError = undefined;

        try {
          for (var _iterator = extents[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
            var ext = _step.value;
            this.extent.union(ext);
          }
        } catch (err) {
          _didIteratorError = true;
          _iteratorError = err;
        } finally {
          try {
            if (!_iteratorNormalCompletion && _iterator["return"] != null) {
              _iterator["return"]();
            }
          } finally {
            if (_didIteratorError) {
              throw _iteratorError;
            }
          }
        }
      }
    }
    /**
     * Remove features that don't have [FeatureGeometry]{@link FeatureGeometry}.
     */

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

  }, {
    key: "pushFeature",
    value: function pushFeature(feature) {
      this.features.push(feature);
      this.updateExtent(feature.extent);
    }
    /**
     * 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: "getFeatureByType",
    value: function getFeatureByType(type) {
      var feature = this.features.find(function (feature) {
        return feature.type === type;
      });

      if (feature && this.optionsFeature.mergeFeatures) {
        return feature;
      } else {
        var newFeature = new Feature(type, this.crs, this.optionsFeature);
        this.features.push(newFeature);
        return newFeature;
      }
    }
  }]);
  return FeatureCollection;
}();

exports.FeatureCollection = FeatureCollection;