"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _Feature = require("../Core/Feature");
var _Coordinates = _interopRequireDefault(require("../Core/Geographic/Coordinates"));
function pointIsOverLine(point, linePoints, epsilon, offset, count, size) {
  const x0 = point.x;
  const y0 = point.y;
  // for each segment of the line (j is i -1)
  for (let i = offset + size, j = offset; i < offset + count; j = i, i += size) {
    /* **********************************************************
        norm     : norm of vector P1P2
        distance : distance point P0 to line P1P2
        scalar   : dot product of P1P0 and P1P2 divide by norm, it represents the projection of P0 on the line
         Point is over segment P1P2 if :
            * if the distance, , is inferior to epsilon
            * and if :  -epsilon ≤ scalar ≤ (||P1P2|| +  epsilon)
                         + (P0) _
                        |      |
                        |      |
         <---scalar---->|    distance
                        |      |
                        |      v
         +-------------------------------+
        (P1)                            (P2)
    *********************************************************** */

    const x1 = linePoints[i];
    const y1 = linePoints[i + 1];
    const x2 = linePoints[j];
    const y2 = linePoints[j + 1];
    const x21 = x2 - x1;
    const y21 = y2 - y1;
    const norm = Math.sqrt(x21 * x21 + y21 * y21);
    const scalar = ((x0 - x1) * x21 + (y0 - y1) * y21) / norm;
    if (scalar >= -epsilon && scalar <= norm + epsilon) {
      const distance = Math.abs(y21 * x0 - x21 * y0 + x2 * y1 - y2 * x1) / norm;
      if (distance <= epsilon) {
        return true;
      }
    }
  }
  return false;
}
function getClosestPoint(point, points, epsilon, offset, count, size) {
  const x0 = point.x;
  const y0 = point.y;
  let squaredEpsilon = epsilon * epsilon;
  let closestPoint;
  for (let i = offset; i < offset + count; i += size) {
    const x1 = points[i];
    const y1 = points[i + 1];
    const xP = x0 - x1;
    const yP = y0 - y1;
    const n = xP * xP + yP * yP;
    if (n < squaredEpsilon) {
      closestPoint = [points[i], points[i + 1]];
      squaredEpsilon = n;
    }
  }
  return closestPoint;
}
function pointIsInsidePolygon(point, polygonPoints, epsilon, offset, count, size) {
  // ray-casting algorithm based on
  // http://wrf.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

  const x = point.x;
  const y = point.y;
  let inside = false;
  // in first j is last point of polygon
  // for each segment of the polygon (j is i -1)
  for (let i = offset, j = offset + count - size; i < offset + count; j = i, i += size) {
    const xi = polygonPoints[i];
    const yi = polygonPoints[i + 1];
    const xj = polygonPoints[j];
    const yj = polygonPoints[j + 1];
    if (pointIsOverLine(point, [xi, yi, xj, yj], epsilon, 0, 4, 2)) {
      return true;
    }

    // isIntersect semi-infinite ray horizontally with polygon's edge

    if (yi > y != yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
      inside = !inside;
    }
  }
  return inside;
}
function isFeatureSingleGeometryUnderCoordinate(coordinate, type, coordinates, epsilon, offset, count, size) {
  if (type == _Feature.FEATURE_TYPES.LINE && pointIsOverLine(coordinate, coordinates, epsilon, offset, count, size)) {
    return true;
  } else if (type == _Feature.FEATURE_TYPES.POLYGON && pointIsInsidePolygon(coordinate, coordinates, epsilon, offset, count, size)) {
    return true;
  } else if (type == _Feature.FEATURE_TYPES.POINT) {
    const closestPoint = getClosestPoint(coordinate, coordinates, epsilon, offset, count, size);
    if (closestPoint) {
      return {
        coordinates: closestPoint
      };
    }
  }
}
function isFeatureUnderCoordinate(coordinate, feature, epsilon, result) {
  const featCoord = coordinate.as(feature.crs);
  for (const geometry of feature.geometries) {
    if (geometry.extent == undefined || geometry.extent.isPointInside(featCoord, epsilon)) {
      const offset = geometry.indices[0].offset * feature.size;
      const count = geometry.indices[0].count * feature.size;
      const under = isFeatureSingleGeometryUnderCoordinate(featCoord, feature.type, feature.vertices, epsilon, offset, count, feature.size);
      if (under) {
        result.push({
          type: feature.type,
          geometry,
          coordinates: under.coordinates /* || coordinates */,
          style: feature.style
        });
      }
    }
  }
}
const coord = new _Coordinates.default('EPSG:4326', 0, 0, 0);
var _default = {
  /**
   * Filter from a list of features, features that are under a coordinate.
   *
   * @param {Coordinates} coordinate - The coordinate for the filter
   * condition.
   * @param {Feature|FeatureCollection} collection - A single feature or a
   * collection of them, to filter given the previous coordinate.
   * @param {number} [epsilon=0.1] Tolerance around the coordinate (in
   * coordinate's unit).
   *
   * @return {Feature[]} Array of filtered features.
   */
  filterFeaturesUnderCoordinate(coordinate, collection) {
    let epsilon = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.1;
    const result = [];

    // transform coordinates to collection system projection
    coordinate.as(collection.crs, coord);

    // transform coordinates to local system
    coord.applyMatrix4(collection.matrixWorldInverse);
    if (collection.extent.isPointInside(coord, epsilon)) {
      if (collection.isFeatureCollection) {
        epsilon *= Math.sqrt(collection.scale.x ** 2 + collection.scale.y ** 2);
        for (const feature of collection.features) {
          if (feature.extent && !feature.extent.isPointInside(coord, epsilon)) {
            continue;
          }
          isFeatureUnderCoordinate(coord, feature, epsilon, result);
        }
      } else if (collection.geometries) {
        isFeatureUnderCoordinate(coord, collection, epsilon, result);
      }
    }
    return result;
  }
};
exports.default = _default;