UNPKG

sharedstreets

Version:

SharedStreets, a 'digital commons' for the street

744 lines (743 loc) 29 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.round = exports.getCoords = exports.getFormOfWayNumber = exports.getEndCoord = exports.getStartCoord = exports.getFormOfWay = exports.getFormOfWayString = exports.getRoadClassNumber = exports.getRoadClassString = exports.generateHash = exports.coordsToLonlats = exports.lonlatsToCoords = exports.distanceToNextRef = exports.inboundBearing = exports.outboundBearing = exports.metadata = exports.locationReference = exports.backReference = exports.forwardReference = exports.reference = exports.referenceMessage = exports.referenceId = exports.intersection = exports.intersectionMessage = exports.intersectionId = exports.geometry = exports.geometryMessage = exports.geometryId = void 0; var command_1 = require("@oclif/command"); Object.defineProperty(exports, "run", { enumerable: true, get: function () { return command_1.run; } }); const bearing_1 = __importDefault(require("@turf/bearing")); const helpers_1 = require("@turf/helpers"); const invariant_1 = require("@turf/invariant"); const length_1 = __importDefault(require("@turf/length")); const along_1 = __importDefault(require("@turf/along")); const bignumber_js_1 = __importDefault(require("bignumber.js")); const crypto_1 = require("crypto"); const sharedstreets_types_1 = require("sharedstreets-types"); const util_1 = require("util"); var graph_1 = require("./graph"); Object.defineProperty(exports, "Graph", { enumerable: true, get: function () { return graph_1.Graph; } }); Object.defineProperty(exports, "PathCandidate", { enumerable: true, get: function () { return graph_1.PathCandidate; } }); Object.defineProperty(exports, "GraphMode", { enumerable: true, get: function () { return graph_1.GraphMode; } }); Object.defineProperty(exports, "ReferenceSideOfStreet", { enumerable: true, get: function () { return graph_1.ReferenceSideOfStreet; } }); var tile_index_1 = require("./tile_index"); Object.defineProperty(exports, "TileIndex", { enumerable: true, get: function () { return tile_index_1.TileIndex; } }); var tiles_1 = require("./tiles"); Object.defineProperty(exports, "TilePathGroup", { enumerable: true, get: function () { return tiles_1.TilePathGroup; } }); Object.defineProperty(exports, "TilePath", { enumerable: true, get: function () { return tiles_1.TilePath; } }); Object.defineProperty(exports, "TilePathParams", { enumerable: true, get: function () { return tiles_1.TilePathParams; } }); Object.defineProperty(exports, "TileType", { enumerable: true, get: function () { return tiles_1.TileType; } }); /** * Shared Streets Java implementation * * @private * Intersection * https://github.com/sharedstreets/sharedstreets-builder/blob/master/src/main/java/io/sharedstreets/data/SharedStreetsIntersection.java#L42-L49 * * Geometry * https://github.com/sharedstreets/sharedstreets-builder/blob/master/src/main/java/io/sharedstreets/data/SharedStreetsGeometry.java#L98-L108 * * Reference * https://github.com/sharedstreets/sharedstreets-builder/blob/master/src/main/java/io/sharedstreets/data/SharedStreetsReference.java#L323-L340 * * Location Reference * https://github.com/sharedstreets/sharedstreets-builder/blob/master/src/main/java/io/sharedstreets/data/SharedStreetsLocationReference.java * * OpenLR White Paper * http://www.openlr.org/data/docs/OpenLR-Whitepaper_v1.5.pdf */ /** * Geometry Id * * @param {Feature<LineString>|Array<Array<number>>} line Line Geometry as a GeoJSON LineString or an Array of Positions Array<<longitude, latitude>>. * @returns {string} SharedStreets Geometry Id * @example * const id = sharedstreets.geometryId([[110, 45], [115, 50], [120, 55]]); * id // => "ce9c0ec1472c0a8bab3190ab075e9b21" */ function geometryId(line) { const message = geometryMessage(line); return generateHash(message); } exports.geometryId = geometryId; /** * Geometry Message * * @private */ function geometryMessage(line) { const coords = getCoords(line); return "Geometry " + coords.map(([x, y]) => `${round(x)} ${round(y)}`).join(" "); } exports.geometryMessage = geometryMessage; /** * geometry * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString Feature * @param {Object} [options={}] Optional parameters * @param {string} [options.formOfWay] Property field that contains FormOfWay value (number/string). * @param {string} [options.roadClass] Property field that contains RoadClass value (number/string). * @returns {SharedStreetsGeometry} SharedStreets Geometry * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const geom = sharedstreets.geometry(line); * geom.id; // => "ce9c0ec1472c0a8bab3190ab075e9b21" * geom.lonlats; // => [ 110, 45, 115, 50, 120, 55 ] */ function geometry(line, options = {}) { let properties = {}; const coords = getCoords(line); // Extract Properties from GeoJSON LineString Feature if (!util_1.isArray(line) && line.type === "Feature") { properties = line.properties || {}; } // FormOfWay needs to be extracted from the GeoJSON properties. let formOfWay = 0; if (options.formOfWay && properties[options.formOfWay]) { formOfWay = properties[options.formOfWay]; } // RoadClass needs to be extracted from the GeoJSON properties. let roadClass = "Other"; if (options.roadClass && properties[options.roadClass]) { roadClass = properties[options.roadClass]; } const forwardRef = forwardReference(line, { formOfWay: formOfWay }); const backRef = backReference(line, { formOfWay: formOfWay }); const forwardReferenceId = forwardRef.id; const backReferenceId = backRef.id; const fromIntersectionId = forwardRef.locationReferences[0].intersectionId; const toIntersectionId = forwardRef.locationReferences[forwardRef.locationReferences.length - 1].intersectionId; // Save Results const id = geometryId(line); const lonlats = coordsToLonlats(coords); return { id, fromIntersectionId, toIntersectionId, forwardReferenceId, backReferenceId, roadClass, lonlats, }; } exports.geometry = geometry; /** * Intersection Id * * @param {Feature<Point>|Array<number>} pt Point location reference as a GeoJSON Point or an Array of numbers <longitude, latitude>. * @returns {string} SharedStreets Intersection Id * @example * const id = sharedstreets.intersectionId([110, 45]); * id // => "71f34691f182a467137b3d37265cb3b6" */ function intersectionId(pt, id) { const message = intersectionMessage(pt, id); return generateHash(message); } exports.intersectionId = intersectionId; /** * Intersection Message * * @private */ function intersectionMessage(pt, id) { const [lon, lat] = invariant_1.getCoord(pt); var message = `Intersection ${round(lon)} ${round(lat)}`; if (id != undefined) message = message + ' ' + id; return message; } exports.intersectionMessage = intersectionMessage; /** * Intersection * * @param {Feature<Point>|Array<number>} pt Point location reference as a GeoJSON Point or an Array of numbers <longitude, latitude>. * @param {Object} [options={}] Optional parameters * @param {string} [options.nodeId] Define NodeId for Intersection * @returns {SharedStreetsIntersection} SharedStreets Intersection * @example * const intersection = sharedstreets.intersection([110, 45]); * intersection.id // => "71f34691f182a467137b3d37265cb3b6" */ function intersection(pt, options = {}) { // Default params const inboundReferences = options.inboundReferences || []; const outboundReferences = options.outboundReferencesIds || []; const nodeId = options.nodeId; // Main const [lon, lat] = invariant_1.getCoord(pt); const id = intersectionId(pt, nodeId); const inboundReferenceIds = inboundReferences.map((ref) => ref.intersectionId); const outboundReferenceIds = outboundReferences.map((ref) => ref.intersectionId); const data = { id, lon, lat, inboundReferenceIds, outboundReferenceIds, }; if (nodeId !== undefined) { data.nodeId = nodeId; } return data; } exports.intersection = intersection; /** * Reference Id * * @param {Array<LocationReference>} locationReferences An Array of Location References. * @param {FormOfWay} [formOfWay=0] Form Of Way * @returns {string} SharedStreets Reference Id * @example * const locationReferences = [ * sharedstreets.locationReference([-74.0048213, 40.7416415], {outboundBearing: 208, distanceToNextRef: 9279}), * sharedstreets.locationReference([-74.0051265, 40.7408505], {inboundBearing: 188}) * ]; * const formOfWay = 2; // => "MultipleCarriageway" * * const id = sharedstreets.referenceId(locationReferences, formOfWay); * id // => "ef209661aeebadfb4e0a2cb93153493f" */ function referenceId(locationReferences, formOfWay = sharedstreets_types_1.FormOfWay.Undefined) { const message = referenceMessage(locationReferences, formOfWay); const hash = generateHash(message); return hash; } exports.referenceId = referenceId; /** * Reference Message * * @private */ function referenceMessage(locationReferences, formOfWay = sharedstreets_types_1.FormOfWay.Undefined) { // Convert FormOfWay to Number if encoding ID if (typeof formOfWay !== "number") { formOfWay = getFormOfWayNumber(formOfWay); } let message = `Reference ${formOfWay}`; locationReferences.forEach((lr) => { message += ` ${round(lr.lon)} ${round(lr.lat)}`; if (lr.outboundBearing !== null && lr.outboundBearing !== undefined && lr.distanceToNextRef !== null && lr.distanceToNextRef !== undefined) { message += ` ${Math.round(lr.outboundBearing)}`; message += ` ${Math.round(Math.round(lr.distanceToNextRef / 100))}`; // distanceToNextRef stored in centimeter but using meters to compute ref Id } }); return message; } exports.referenceMessage = referenceMessage; /** * Reference * * @param {SharedStreetsGeometry} geom SharedStreets Geometry * @param {Array<LocationReference>} locationReferences An Array of Location References. * @param {number} [formOfWay=0] Form Of Way (default Undefined) * @returns {SharedStreetsReference} SharedStreets Reference * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const geom = sharedstreets.geometry(line); * const locationReferences = [ * sharedstreets.locationReference([-74.0048213, 40.7416415], {outboundBearing: 208, distanceToNextRef: 9279}), * sharedstreets.locationReference([-74.0051265, 40.7408505], {inboundBearing: 188}), * ]; * const formOfWay = 2; // => "MultipleCarriageway" * const ref = sharedstreets.reference(geom, locationReferences, formOfWay); * ref.id // => "ef209661aeebadfb4e0a2cb93153493f" */ function reference(geom, locationReferences, formOfWay = sharedstreets_types_1.FormOfWay.Undefined) { return { id: referenceId(locationReferences, formOfWay), geometryId: geom.id, formOfWay, locationReferences, }; } exports.reference = reference; /** * Forward Reference * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString Feature or an Array of Positions * @param {Object} [options={}] Optional parameters * @param {number|string} [options.formOfWay=0] Form of Way (default "Undefined") * @returns {SharedStreetsReference} Forward SharedStreets Reference * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const ref = sharedstreets.forwardReference(line); * ref.id // => "3f652e4585aa7d7df3c1fbe4f55cea0a" */ function forwardReference(line, options = {}) { const lineLength = Math.round(length_1.default(line, { units: "meters" }) * 100); const formOfWay = getFormOfWay(line, options); const geomId = geometryId(line); // lines over 15 are divided into smaller segments const MAX_SEGMENT_LENGTH = 15000; var segmentCount = Math.ceil(lineLength / MAX_SEGMENT_LENGTH); var locationReferences = []; for (var i = 0; i < segmentCount + 1; i++) { var refProperties = {}; if (i < segmentCount) { refProperties.outboundBearing = outboundBearing(line, lineLength, i * (lineLength / segmentCount)); refProperties.distanceToNextRef = Math.round((lineLength / segmentCount) * 100); } if (i > 0) { refProperties.inboundBearing = inboundBearing(line, lineLength, i * (lineLength / segmentCount)); } var lrCoord; if (i == 0) lrCoord = getStartCoord(line); else if (i == segmentCount) lrCoord = getEndCoord(line); else { var pos = i * (lineLength / segmentCount); var pt = along_1.default(line, pos); lrCoord = invariant_1.getCoord(pt); } var ref = locationReference(lrCoord, refProperties); locationReferences.push(ref); } const id = referenceId(locationReferences, formOfWay); return { id, geometryId: geomId, formOfWay, locationReferences }; } exports.forwardReference = forwardReference; /** * Back Reference * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString Feature or an Array of Positions * @param {Object} [options={}] Optional parameters * @param {number|string} [options.formOfWay=0] Form of Way (default "Undefined") * @returns {SharedStreetsReference} Back SharedStreets Reference * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const ref = sharedstreets.backReference(line); * ref.id // => "a18b2674e41cad630f5693154837baf4" */ function backReference(line, options = {}) { var geomId = geometryId(line); var reversedLine = JSON.parse(JSON.stringify(line)); reversedLine.geometry.coordinates.reverse(); var ref = forwardReference(reversedLine, options); ref.geometryId = geomId; return ref; } exports.backReference = backReference; /** * Location Reference * * @private * @param {Feature<Point>|Array<number>} pt Point as a GeoJSON Point or an Array of numbers <longitude, latitude>. * @param {Object} [options={}] Optional parameters * @param {string} [options.intersectionId] Intersection Id - Fallbacks to input's point `id` or generates Intersection Id. * @param {number} [options.inboundBearing] Inbound bearing of the street geometry for the 20 meters immediately following the location reference. * @param {number} [options.outboundBearing] Outbound bearing. * @param {number} [options.distanceToNextRef] Distance to next Location Reference (distance must be defined in centimeters). * @returns {LocationReference} SharedStreets Location Reference * @example * const options = { * outboundBearing: 208, * distanceToNextRef: 9279 * }; * const locRef = sharedstreets.locationReference([-74.00482177734375, 40.741641998291016], options); * locRef.intersectionId // => "5c88d4fa3900a083355c46c54da8f584" */ function locationReference(pt, options = {}) { const coord = invariant_1.getCoord(pt); const id = options.intersectionId || intersectionId(coord); // Include extra properties & Reference ID to GeoJSON Properties const locRef = { intersectionId: id, lat: coord[1], lon: coord[0], }; if (options.inboundBearing !== undefined) { locRef.inboundBearing = options.inboundBearing; } if (options.outboundBearing !== undefined) { locRef.outboundBearing = options.outboundBearing; } if (options.distanceToNextRef !== undefined) { locRef.distanceToNextRef = options.distanceToNextRef; } if (locRef.outboundBearing !== undefined && locRef.distanceToNextRef === undefined) { throw new Error("distanceToNextRef is required if outboundBearing is present"); } return locRef; } exports.locationReference = locationReference; /** * Metadata * * @param {SharedStreetsGeometry} geom SharedStreets Geometry * @param {OSMMetadata} [osmMetadata={}] OSM Metadata * @param {Array<GISMetadata>} [gisMetadata=[]] GIS Metadata * @param {} * @returns {SharedStreetsMetadata} SharedStreets Metadata * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const geom = sharedstreets.geometry(line); * const metadata = sharedstreets.metadata(geom) */ function metadata(geom, osmMetadata = {}, gisMetadata = []) { return { geometryId: geom.id, osmMetadata, gisMetadata, }; } exports.metadata = metadata; /** * Calculates outbound bearing from a LineString * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString or an Array of Positions * @param {number} len length of line * @param {number} dist distance along line to sample outbound bearing * @returns {number} Outbound Bearing * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const outboundBearing = sharedstreets.outboundBearing(line); * if line is less than 20 meters long bearing is from start to end of line * outboundBearing; // => 208 */ function outboundBearing(line, len, dist) { // LRs describe the compass bearing of the street geometry for the 20 meters immediately following the LR. if (len > 20) { const start = along_1.default(line, dist, { units: "meters" }); const end = along_1.default(line, dist + 20, { units: "meters" }); // Calculate outbound & inbound return helpers_1.bearingToAzimuth(Math.round(bearing_1.default(start, end))); } else { const start = along_1.default(line, 0, { units: "meters" }); const end = along_1.default(line, len, { units: "meters" }); // Calculate outbound & inbound return helpers_1.bearingToAzimuth(Math.round(bearing_1.default(start, end))); } } exports.outboundBearing = outboundBearing; /** * Calculates inbound bearing from a LineString * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString or an Array of Positions * @param {number} len length of line * @param {number} dist distance along line to sample outbound bearing * @returns {number} Inbound Bearing * @example * const line = [[110, 45], [115, 50], [120, 55]]; * const inboundBearing = sharedstreets.inboundBearing(line); * if line is less than 20 meters long bearing is from start to end of line * inboundBearing; // => 188 */ function inboundBearing(line, len, dist) { if (len > 20) { const start = along_1.default(line, dist - 20, { units: "meters" }); const end = along_1.default(line, dist, { units: "meters" }); return helpers_1.bearingToAzimuth(Math.round(bearing_1.default(start, end))); } else { const start = along_1.default(line, 0, { units: "meters" }); const end = along_1.default(line, len, { units: "meters" }); return helpers_1.bearingToAzimuth(Math.round(bearing_1.default(start, end))); } } exports.inboundBearing = inboundBearing; /** * Calculates inbound bearing from a LineString * * @param {Coord} start GeoJSON Point or an Array of numbers [Longitude/Latitude] * @param {Coord} end GeoJSON Point or an Array of numbers [Longitude/Latitude] * @returns {number} Distance to next Ref in centimeters * @example * const start = [110, 45]; * const end = [120, 55]; * const distanceToNextRef = sharedstreets.distanceToNextRef(start, end); * distanceToNextRef; // => 9279 */ function distanceToNextRef(line) { if (Array.isArray(line)) { line = helpers_1.lineString(line); } return Math.round(length_1.default(line, { units: "meters" }) * 100); } exports.distanceToNextRef = distanceToNextRef; /** * Converts lonlats to GeoJSON LineString Coords * * @param {Array<number>} lonlats Single Array of paired longitudes & latitude * @returns {Array<Array<number>>} GeoJSON LineString coordinates * @example * const coords = lonlatsToCoords([110, 45, 120, 55]); * coords // => [[110, 45], [120, 55]] */ function lonlatsToCoords(lonlats) { const coords = []; lonlats.reduce((lon, deg, index) => { if (index % 2 === 0) { return deg; } // Longitude coords.push([lon, deg]); return deg; // Latitude }); return coords; } exports.lonlatsToCoords = lonlatsToCoords; /** * Converts GeoJSON LineString Coords to lonlats * * @param {Array<Array<number, number>>} coords GeoJSON LineString coordinates * @returns {Array<number>} lonlats Single Array of paired longitudes & latitude * @example * const lonlats = coordsToLonlats([[110, 45], [120, 55]]); * lonlats // => [110, 45, 120, 55] */ function coordsToLonlats(coords) { const lonlats = []; coords.forEach((coord) => { lonlats.push(coord[0], coord[1]); }); return lonlats; } exports.coordsToLonlats = coordsToLonlats; /** * Generates Base16 Hash * * @param {string} message Message to hash * @returns {string} SharedStreets Reference ID * @example * const message = "Intersection -74.00482177734375 40.741641998291016"; * const hash = sharedstreets.generateHash(message); * hash // => "69f13f881649cb21ee3b359730790bb9" */ function generateHash(message) { return crypto_1.createHash("md5").update(message).digest("hex"); } exports.generateHash = generateHash; /** * Get RoadClass from a Number to a String * * @param {number} value Number value [between 0-8] * @returns {string} Road Class * @example * sharedstreets.getRoadClassString(0); // => "Motorway" * sharedstreets.getRoadClassString(5); // => "Residential" */ function getRoadClassString(value) { switch (value) { case 0: return "Motorway"; case 1: return "Trunk"; case 2: return "Primary"; case 3: return "Secondary"; case 4: return "Tertiary"; case 5: return "Residential"; case 6: return "Unclassified"; case 7: return "Service"; case 8: return "Other"; default: throw new Error(`[${value}] unknown RoadClass Number value`); } } exports.getRoadClassString = getRoadClassString; /** * Get RoadClass from a String to a Number * * @param {number} value String value ["Motorway", "Trunk", "Primary", etc...] * @returns {string} Road Class * @example * sharedstreets.getRoadClassNumber("Motorway"); // => 0 * sharedstreets.getRoadClassNumber("Residential"); // => 5 */ function getRoadClassNumber(value) { switch (value) { case "Motorway": return 0; case "Trunk": return 1; case "Primary": return 2; case "Secondary": return 3; case "Tertiary": return 4; case "Residential": return 5; case "Unclassified": return 6; case "Service": return 7; case "Other": return 8; default: throw new Error(`[${value}] unknown RoadClass String value`); } } exports.getRoadClassNumber = getRoadClassNumber; /** * Get FormOfWay from a GeoJSON LineString and/or Optional parameters * * @param {number} value Number value [between 0-7] * @returns {string} Form of Way * @example * sharedstreets.getFormOfWayString(0); // => "Undefined" * sharedstreets.getFormOfWayString(5); // => "TrafficSquare" */ function getFormOfWayString(value) { switch (value) { case undefined: case 0: return "Undefined"; case 1: return "Motorway"; case 2: return "MultipleCarriageway"; case 3: return "SingleCarriageway"; case 4: return "Roundabout"; case 5: return "TrafficSquare"; case 6: return "SlipRoad"; case 7: return "Other"; default: throw new Error(`[${value}] unknown FormOfWay Number value`); } } exports.getFormOfWayString = getFormOfWayString; /** * Get FormOfWay from a GeoJSON LineString * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString Feature or an Array of Positions * @param {Object} [options={}] Optional parameters * @param {number} [options.formOfWay=0] Form of Way * @example * const lineA = turf.lineString([[110, 45], [115, 50], [120, 55]], {formOfWay: 3}); * const lineB = turf.lineString([[110, 45], [115, 50], [120, 55]]); * const lineC = turf.lineString([[110, 45], [115, 50], [120, 55]], {formOfWay: "Motorway"}); * * sharedstreets.getFormOfWay(lineA); // => 3 * sharedstreets.getFormOfWay(lineB); // => 0 * sharedstreets.getFormOfWay(lineC); // => 1 */ function getFormOfWay(line, options = {}) { // Set default to Other (0) let formOfWay = sharedstreets_types_1.FormOfWay.Undefined; // Retrieve formOfWay from Optional Parameters (priority order since it is user defined) if (options.formOfWay !== undefined) { formOfWay = options.formOfWay; // Retrieve formOfWay via GeoJSON LineString properties } else if (!Array.isArray(line) && line.type === "Feature" && line.properties && line.properties.formOfWay) { formOfWay = line.properties.formOfWay; } // Assert value to Number if (typeof formOfWay === "string") { formOfWay = getFormOfWayNumber(formOfWay); } return formOfWay; } exports.getFormOfWay = getFormOfWay; /** * Get Start Coordinate from a GeoJSON LineString * * @param {Feature<LineString>|Array<Position>} line GeoJSON LineString or an Array of Positiosn * @returns {Position} Start Coordinate * @example * const line = turf.lineString([[110, 45], [115, 50], [120, 55]]); * const start = sharedstreets.getStartCoord(line); * start // => [110, 45] */ function getStartCoord(line) { // Array of Positions if (Array.isArray(line)) { return line[0]; // GeoJSON Feature } else if (line.type === "Feature" && line.geometry) { return line.geometry.coordinates[0]; // GeoJSON Geometry } else if (line.type === "LineString") { return line.coordinates[0]; } else { throw new Error("invalid line"); } } exports.getStartCoord = getStartCoord; /** * Get Start Coordinate from a GeoJSON LineString * * @param {Feature<LineString>|Array<Position>} line GeoJSON LineString or an Array of Positiosn * @returns {Position} Start Coordinate * @example * const line = turf.lineString([[110, 45], [115, 50], [120, 55]]); * const end = sharedstreets.getEndCoord(line); * end // => [120, 55] */ function getEndCoord(line) { // Array of Positions if (Array.isArray(line)) { return line[line.length - 1]; // GeoJSON Feature } else if (line.type === "Feature" && line.geometry) { return line.geometry.coordinates[line.geometry.coordinates.length - 1]; // GeoJSON Geometry } else if (line.type === "LineString") { return line.coordinates[line.coordinates.length - 1]; } else { throw new Error("invalid line"); } } exports.getEndCoord = getEndCoord; /** * Get FormOfWay from a String to a Number * * @param {number} value String value [ex: "Undefined", "Motorway", etc...] * @returns {number} Form of Way * @example * sharedstreets.getFormOfWayNumber("Undefined"); // => 0 * sharedstreets.getFormOfWayNumber("TrafficSquare"); // => 5 */ function getFormOfWayNumber(value) { switch (value) { case undefined: case "Undefined": return 0; case "Motorway": return 1; case "MultipleCarriageway": return 2; case "SingleCarriageway": return 3; case "Roundabout": return 4; case "TrafficSquare": return 5; case "SlipRoad": return 6; case "Other": return 7; default: throw new Error(`[${value}] unknown FormOfWay String value`); } } exports.getFormOfWayNumber = getFormOfWayNumber; /** * getCoords * * @param {Feature<LineString>|Array<Array<number>>} line GeoJSON LineString or an Array of positions * @returns {Array<Array<number>>} Array of positions * @example * const line = turf.lineString([[110, 45], [115, 50], [120, 55]]); * const coords = sharedstreets.getCoords(line); * coords; // => [[110, 45], [115, 50], [120, 55]] */ function getCoords(line) { // Deconstruct GeoJSON LineString let coords; if (util_1.isArray(line)) { coords = line; } else if (line.type === "Feature") { if (line.geometry === null) { throw new Error("line geometry cannot be null"); } coords = line.geometry.coordinates; } else { coords = line.coordinates; } return coords; } exports.getCoords = getCoords; /** * Round Number to 5 decimals * * @param {number} num Number to round * @param {number} [decimalPlaces=5] Decimal Places * @returns {string} Big Number fixed string * @example * sharedstreets.round(10.123456789) // => 10.123457 */ function round(num, decimalPlaces = 5) { return new bignumber_js_1.default(String(num)).toFixed(decimalPlaces); } exports.round = round;