view client/src/application/lib/geo.js @ 904:e4b72a199258

New default bottleneck colors Mainly to make the stroke color one actually selectable in the ui. In addition the pink does better match the collors used on the ECDIS layer.
author Sascha Wilde <wilde@intevation.de>
date Tue, 02 Oct 2018 13:34:59 +0200
parents dedf252b3e01
children ca628dce90dd
line wrap: on
line source

/**
 *
 * Distance calculations
 * JS transposition of cross.go functions
 *
 */

const EARTHRADIUS = 6378137.0;

/**
 * Converts to radiant
 * @param {degree} d
 */
const deg2rad = d => {
  return (d * Math.PI) / 180.0;
};

/**
 * Calculates the difference between two points in m
 *
 * Points are given with {lat: $lat, lon: $lon}
 *
 * @param {object} P1
 * @param {object} P2
 */
const distanceBetween = (P1, P2) => {
  const dLat = deg2rad(P2.lat - P1.lat);
  let dLng = Math.abs(deg2rad(P2.lon - P1.lon));
  if (dLng > Math.PI) {
    dLng = 2 * Math.PI - dLng;
  }
  const x = dLng * Math.cos(deg2rad((P1.lat + P2.lat) / 2.0));
  return Math.sqrt(dLat * dLat + x * x) * EARTHRADIUS;
};

/**
 * Takes a triple of [lat, long, alt] and generates
 * an object with according attributes
 * {
 *  lat: $lat,
 *  lon: $lon,
 *  alt: $alt
 * }
 *
 * @param {array} coords
 */
const Point = coords => {
  return {
    lon: coords[0],
    lat: coords[1],
    alt: coords[2]
  };
};

/**
 * Has geoJSON as its input and transforms
 * given coordinates into points representing
 * distance from startpoint / altitude information
 *
 * a) extracting the minimum altitude
 * b) extracting the maximum altitude
 * c) calculating the total length of the given profile
 * d) transposes the datapoints relative to a given start point
 *
 * The calculation of total equals the sum of partial distances between points
 *
 * The x-value of a point is equal to the total distance up to this point
 *
 * The distance between the last point of the last segment and the end point is added
 * to the total
 *
 * @param {object} geoJSON, startPoint, endPoint
 */
const transform = ({ geoJSON, startPoint, endPoint }) => {
  const lineSegments = geoJSON.geometry.coordinates;
  let segmentPoints = [];
  let lengthPolyLine = 0;
  let referencePoint = Point(startPoint);
  let minAlt = Math.abs(lineSegments[0][0][2]);
  let maxAlt = Math.abs(lineSegments[0][0][2]);
  let currentPoint = null;
  for (let segment of lineSegments) {
    let points = [];
    for (let coordinateTriplet of segment) {
      currentPoint = Point(coordinateTriplet);
      lengthPolyLine += distanceBetween(referencePoint, currentPoint);
      let y = Math.abs(currentPoint.alt);
      points.push({
        x: lengthPolyLine,
        y: y
      });
      if (y < minAlt) minAlt = y;
      if (y > maxAlt) maxAlt = y;
      referencePoint = currentPoint;
    }
    segmentPoints.push(points);
  }
  lengthPolyLine += distanceBetween(currentPoint, Point(endPoint));
  return { segmentPoints, lengthPolyLine, minAlt, maxAlt };
};

/**
 * Prepare profile takes geoJSON data in form of
 * a MultiLineString, e.g.
 *
 * {
 *   type: "Feature",
 *   geometry: {
 *   type: "MultiLineString",
 *   coordinates: [
 *                 [
 *                  [16.53593398, 48.14694085, -146.52392755]
 *                  ...
 *                  ]]
 *
 * and transforms it to a structure representing the number of sections
 * where data is present with according lengths and the points
 *
 * {
 *  { points:
 *           [
 *            [ { x: 0.005798201616417183, y: -146.52419461 },
 *              { x: 0, y: -146.52394016 }
 *              ...
 *            ]
 *           ]
 *   lengthPolyLine: 160.06814078495722,
 *   minAlt: -146.73122231,
 *   maxAlt: -145.65155866
 *  }
 *
 * @param {object} geoJSON
 */
const prepareProfile = ({ geoJSON, startPoint, endPoint }) => {
  const { segmentPoints, lengthPolyLine, minAlt, maxAlt } = transform({
    geoJSON,
    startPoint,
    endPoint
  });
  return {
    points: segmentPoints,
    lengthPolyLine: lengthPolyLine,
    minAlt: minAlt,
    maxAlt: maxAlt
  };
};

export { prepareProfile };