view client/src/application/lib/geo.js @ 935:430d52c2f6ef

client: move layer isolines to be drawn at the top * Move layer isolones to be drawn last (and thus being "on top") so that the bottleneck (position) layer will not interfere that much with the colours. It also allows to set a white background with high opacity on the bottleneck polygon in order to get highly visible isolines.
author Bernhard Reiter <bernhard@intevation.de>
date Mon, 08 Oct 2018 17:20:42 +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 };