view client/src/application/lib/geo.js @ 666:4c36b0e39d78

feat: prepareProfile converts geoJSON to diagram data. PrepareProfile assumes data given in geoJSON format. It extracts heigth and width information for the diagram and transposes every coordinate given relative to the first coordinate in the first set.
author Thomas Junk <thomas.junk@intevation.de>
date Tue, 18 Sep 2018 13:43:24 +0200
parents
children 3605af94d1ee
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 generatePoint = coords => {
  return {
    lon: coords[0],
    lat: coords[1],
    alt: coords[2]
  };
};

/**
 * Has geoJSON as its input and transforms
 * given coordinates
 *
 * a) extracting the minimum altitude
 * b) extracting the maximum altitude
 * c) calculating the total length of the given profile
 * d) transposes the datapoints given to the first point of the first section
 *
 * @param {object} feature
 */
const transform = feature => {
  const sections = feature.geometry.coordinates;
  let firstPoint = generatePoint(sections[0][0]);
  let coordinates = [];
  let totalLength = 0;
  let minAlt = firstPoint.alt;
  let maxAlt = firstPoint.alt;
  for (let section of sections) {
    let sectionCoordinates = [];
    let previousPoint = generatePoint(section[0]);
    for (let coords of section) {
      const currentPoint = generatePoint(coords);
      let x = distanceBetween(firstPoint, currentPoint);
      let y = coords[2];
      sectionCoordinates.push({
        x: x,
        y: y
      });
      totalLength += distanceBetween(currentPoint, previousPoint);
      previousPoint = currentPoint;
      if (y < minAlt) minAlt = y;
      if (y > maxAlt) maxAlt = y;
    }
    coordinates.push(sectionCoordinates);
  }
  return { coordinates, totalLength, 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 }
 *              ...
 *            ]
 *           ]
 *   totalLength: 160.06814078495722,
 *   minAlt: -146.73122231,
 *   maxAlt: -145.65155866
 *  }
 *
 * @param {object} geoJSON
 */
const prepareProfile = geoJSON => {
  const { coordinates, totalLength, minAlt, maxAlt } = transform(geoJSON);
  return {
    points: coordinates,
    totalLength: totalLength,
    minAlt: minAlt,
    maxAlt: maxAlt
  };
};

export { prepareProfile };