Mercurial > gemma
view client/src/lib/geo.js @ 4777:702f06d827a8
Updated requested scale for legend elements to avoid duplicates.
author | Raimund Renkert <raimund@renkert.org> |
---|---|
date | Mon, 21 Oct 2019 13:33:15 +0200 |
parents | 9e296d686f16 |
children | 5d9c7fcda566 |
line wrap: on
line source
/* This is Free Software under GNU Affero General Public License v >= 3.0 * without warranty, see README.md and license for details. * * SPDX-License-Identifier: AGPL-3.0-or-later * License-Filename: LICENSES/AGPL-3.0.txt * * Copyright (C) 2018, 2019 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Thomas Junk <thomas.junk@intevation.de> * Raimund Renkert <raimund.renkert@intevation.de> */ /** * * Distance calculations * JS transposition of cross.go functions * */ import { GeoJSON } from "ol/format"; import Feature from "ol/Feature"; import distance from "@turf/distance"; import { lineString as turfLineString, polygon as turfPolygon, point as turfPoint } from "@turf/helpers"; import lineSplit from "@turf/line-split"; import lineSlice from "@turf/line-slice"; import lineIntersect from "@turf/line-intersect"; // import booleanWithin from "@turf/boolean-within"; import booleanContains from "@turf/boolean-contains"; 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 }; }; const generateFeatureRequest = (profileLine, bottleneck_id, date_info) => { const feature = new Feature({ geometry: profileLine, bottleneck: bottleneck_id, date: date_info }); return new GeoJSON({ geometryName: "geometry" }).writeFeature(feature); }; const calculateFairwayCoordinates = (profileLine, fairwayGeometry, depth) => { // both geometries have to be in EPSG:4326 // uses turfjs distance() function let fairwayCoordinates = []; var line = turfLineString(profileLine.getCoordinates()); var polygon = turfPolygon(fairwayGeometry.getCoordinates()); var intersects = lineIntersect(line, polygon); let opts = { units: "meters" }; let pStartPoint = profileLine.getCoordinates()[0]; if (intersects.features.length == 2) { let line1 = lineSlice( intersects.features[0].geometry, intersects.features[1].geometry, line ); fairwayCoordinates.push([ distance(pStartPoint, line1.geometry.coordinates[0], opts), distance(pStartPoint, line1.geometry.coordinates[1], opts), depth ]); } else if ( booleanContains(polygon, turfPoint(line.geometry.coordinates[0])) && booleanContains(polygon, turfPoint(line.geometry.coordinates[1])) ) { fairwayCoordinates.push([ distance(pStartPoint, line.geometry.coordinates[0], opts), distance(pStartPoint, line.geometry.coordinates[1], opts), depth ]); } else if (lineSplit(line, polygon).features.length > 0) { let split = lineSplit(line, polygon); for (let feature of split.features) { if (booleanContains(polygon, feature)) { fairwayCoordinates.push([ distance(pStartPoint, feature.geometry.coordinates[0], opts), distance(pStartPoint, feature.geometry.coordinates[1], opts), depth ]); } } } return fairwayCoordinates; }; const featureToFairwayCoordinates = (feature, profileLine) => { // need to use EPSG:3857 which is the proj of vectorSource var intersectingPolygon = feature .getGeometry() .clone() .transform("EPSG:3857", "EPSG:4326"); const fairwayCoordinates = calculateFairwayCoordinates( profileLine, intersectingPolygon, feature.get("min_depth") ? feature.get("min_depth") / 100 : 2.5 ); return fairwayCoordinates; }; export { generateFeatureRequest, prepareProfile, calculateFairwayCoordinates, featureToFairwayCoordinates };