export { run } from '@oclif/command' ; import bearing from "@turf/bearing" ; import { bearingToAzimuth , Feature , lineString , LineString , Point , Position } from "@turf/helpers" ; import { getCoord } from "@turf/invariant" ; import length from "@turf/length" ; import along from "@turf/along" ; import BigNumber from "bignumber.js" ; import { createHash } from "crypto" ; import { FormOfWay , GISMetadata , LocationReference , OSMMetadata , SharedStreetsGeometry , SharedStreetsIntersection , SharedStreetsMetadata , SharedStreetsReference , } from "sharedstreets-types" ; import { isArray } from "util" ; export { Graph , PathCandidate , GraphMode , ReferenceSideOfStreet } from './graph' ; export { TileIndex } from './tile_index' ; export { TilePathGroup , TilePath , TilePathParams , TileType } from './tiles' ; private param { Feature < LineString > | Array < Array < number > > } line returns { string } example export function geometryId ( line ) { const message = geometryMessage ( line ) ; return generateHash ( message ) ; } private export function geometryMessage ( line ) { const coords = getCoords ( line ) ; return "Geometry " + coords . map ( ( [ x , y ] ) => `${ round ( x ) } ${ round ( y ) }` ) . join ( " " ) ; } param { Feature < LineString > | Array < Array < number > > } line param options param { string } options . formOfWay param { string } options . roadClass returns { SharedStreetsGeometry } example export function geometry ( line , options = { } ) { let properties = { } ; const coords = getCoords ( line ) ; if ( ! isArray ( line ) && line . type === "Feature" ) { properties = line . properties || { } ; } let formOfWay = 0 ; if ( options . formOfWay && properties [ options . formOfWay ] ) { formOfWay = properties [ options . formOfWay ] ; } 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 ; const id = geometryId ( line ) ; const lonlats = coordsToLonlats ( coords ) ; return { id , fromIntersectionId , toIntersectionId , forwardReferenceId , backReferenceId , roadClass , lonlats , } ; } param { Feature < Point > | Array < number > } pt returns { string } example export function intersectionId ( pt , id ? ) { const message = intersectionMessage ( pt , id ) ; return generateHash ( message ) ; } private export function intersectionMessage ( pt , id ? ) { const [ lon , lat ] = getCoord ( pt ) ; var message = `Intersection ${ round ( lon ) } ${ round ( lat ) }` ; if ( id != undefined ) message = message + ' ' + id ; return message ; } param { Feature < Point > | Array < number > } pt param options param { string } options . nodeId returns { SharedStreetsIntersection } example export function intersection ( pt , options = { } ) { const inboundReferences = options . inboundReferences || [ ] ; const outboundReferences = options . outboundReferencesIds || [ ] ; const nodeId = options . nodeId ; const [ lon , lat ] = 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 ; } param { Array < LocationReference > } locationReferences param { FormOfWay } formOfWay returns { string } example export function referenceId ( locationReferences , formOfWay = FormOfWay . Undefined ) { const message = referenceMessage ( locationReferences , formOfWay ) ; const hash = generateHash ( message ) ; return hash ; } private export function referenceMessage ( locationReferences , formOfWay = FormOfWay . Undefined ) { 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 ) ) }` ; } } ) ; return message ; } param { SharedStreetsGeometry } geom param { Array < LocationReference > } locationReferences param { number } formOfWay returns { SharedStreetsReference } example export function reference ( geom , locationReferences , formOfWay = FormOfWay . Undefined ) { return { id : referenceId ( locationReferences , formOfWay ) , geometryId : geom . id , formOfWay , locationReferences , } ; } param { Feature < LineString > | Array < Array < number > > } line param options param { number | string } options . formOfWay returns { SharedStreetsReference } example export function forwardReference ( line , options = { } ) { const lineLength = Math . round ( length ( line , { units : "meters" } ) * 100 ) ; const formOfWay = getFormOfWay ( line , options ) ; const geomId = geometryId ( line ) ; 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 ( line , pos ) ; lrCoord = getCoord ( pt ) ; } var ref = locationReference ( lrCoord , refProperties ) ; locationReferences . push ( ref ) ; } const id = referenceId ( locationReferences , formOfWay ) ; return { id , geometryId : geomId , formOfWay , locationReferences } ; } param { Feature < LineString > | Array < Array < number > > } line param options param { number | string } options . formOfWay returns { SharedStreetsReference } example export 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 ; } private param { Feature < Point > | Array < number > } pt param options param { string } options . intersectionId param { number } options . inboundBearing param { number } options . outboundBearing param { number } options . distanceToNextRef returns { LocationReference } example export function locationReference ( pt , options = { } ) { const coord = getCoord ( pt ) ; const id = options . intersectionId || intersectionId ( coord ) ; 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 ; } param { SharedStreetsGeometry } geom param { OSMMetadata } osmMetadata param { Array < GISMetadata > } gisMetadata param { } returns { SharedStreetsMetadata } example export function metadata ( geom , osmMetadata = { } , gisMetadata = [ ] ) { return { geometryId : geom . id , osmMetadata , gisMetadata , } ; } param { Feature < LineString > | Array < Array < number > > } line param { number } len param { number } dist returns { number } example export function outboundBearing ( line , len , dist ) { if ( len > 20 ) { const start = along ( line , dist , { units : "meters" } ) ; const end = along ( line , dist + 20 , { units : "meters" } ) ; return bearingToAzimuth ( Math . round ( bearing ( start , end ) ) ) ; } else { const start = along ( line , 0 , { units : "meters" } ) ; const end = along ( line , len , { units : "meters" } ) ; return bearingToAzimuth ( Math . round ( bearing ( start , end ) ) ) ; } } param { Feature < LineString > | Array < Array < number > > } line param { number } len param { number } dist returns { number } example export function inboundBearing ( line , len , dist ) { if ( len > 20 ) { const start = along ( line , dist - 20 , { units : "meters" } ) ; const end = along ( line , dist , { units : "meters" } ) ; return bearingToAzimuth ( Math . round ( bearing ( start , end ) ) ) ; } else { const start = along ( line , 0 , { units : "meters" } ) ; const end = along ( line , len , { units : "meters" } ) ; return bearingToAzimuth ( Math . round ( bearing ( start , end ) ) ) ; } } param { Coord } start param { Coord } end returns { number } example export function distanceToNextRef ( line ) { if ( Array . isArray ( line ) ) { line = lineString ( line ) ; } return Math . round ( length ( line , { units : "meters" } ) * 100 ) ; } param { Array < number > } lonlats returns { Array < Array < number > > } example export function lonlatsToCoords ( lonlats ) { const coords = [ ] ; lonlats . reduce ( ( lon , deg , index ) => { if ( index % 2 === 0 ) { return deg ; } coords . push ( [ lon , deg ] ) ; return deg ; } ) ; return coords ; } param { Array < Array < number , number > > } coords returns { Array < number > } example export function coordsToLonlats ( coords ) { const lonlats = [ ] ; coords . forEach ( ( coord ) => { lonlats . push ( coord [ 0 ] , coord [ 1 ] ) ; } ) ; return lonlats ; } param { string } message returns { string } example export function generateHash ( message ) { return createHash ( "md5" ) . update ( message ) . digest ( "hex" ) ; } param { number } value returns { string } example export 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` ) ; } } param { number } value returns { string } example export 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` ) ; } } param { number } value returns { string } example export 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` ) ; } } param { Feature < LineString > | Array < Array < number > > } line param options param { number } options . formOfWay example export function getFormOfWay ( line , options = { } ) { let formOfWay = FormOfWay . Undefined ; if ( options . formOfWay !== undefined ) { formOfWay = options . formOfWay ; } else if ( ! Array . isArray ( line ) && line . type === "Feature" && line . properties && line . properties . formOfWay ) { formOfWay = line . properties . formOfWay ; } if ( typeof formOfWay === "string" ) { formOfWay = getFormOfWayNumber ( formOfWay ) ; } return formOfWay ; } param { Feature < LineString > | Array < Position > } line returns { Position } example export function getStartCoord ( line ) { if ( Array . isArray ( line ) ) { return line [ 0 ] ; } else if ( line . type === "Feature" && line . geometry ) { return line . geometry . coordinates [ 0 ] ; } else if ( line . type === "LineString" ) { return line . coordinates [ 0 ] ; } else { throw new Error ( "invalid line" ) ; } } param { Feature < LineString > | Array < Position > } line returns { Position } example export function getEndCoord ( line ) { if ( Array . isArray ( line ) ) { return line [ line . length - 1 ] ; } else if ( line . type === "Feature" && line . geometry ) { return line . geometry . coordinates [ line . geometry . coordinates . length - 1 ] ; } else if ( line . type === "LineString" ) { return line . coordinates [ line . coordinates . length - 1 ] ; } else { throw new Error ( "invalid line" ) ; } } param { number } value returns { number } example export 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` ) ; } } param { Feature < LineString > | Array < Array < number > > } line returns { Array < Array < number > > } example export function getCoords ( line ) { let coords ; if ( 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 ; } param { number } num param { number } decimalPlaces returns { string } example export function round ( num , decimalPlaces = 5 ) { return new BigNumber ( String ( num ) ) . toFixed ( decimalPlaces ) ; }