import * as sharedstreetsPbf from 'sharedstreets-pbf' ; import { SharedStreetsIntersection , SharedStreetsGeometry } from 'sharedstreets-types' ; import * as turfHelpers from '@turf/helpers' ; import bbox from "@turf/bbox" ; import destination from '@turf/destination' ; import { existsSync , mkdirSync , writeFileSync , readFileSync } from 'fs' ; import { getJson , getPbf , resolveHome } from "./util" ; const chalk = require ( 'chalk' ) ; const path = require ( 'path' ) ; const SphericalMercator = require ( "@mapbox/sphericalmercator" ) ; const sphericalMercator = new SphericalMercator ( { size : 256 } ) ; const DEFAULT_ZLEVEL = 12 ; const SHST_ID_API_URL = 'https://api.sharedstreets.io/v0.1.0/id/' ; const SHST_TILE_URL = 'https://tiles.sharedstreets.io/' ; const USE_LOCAL_CACHE = true ; const SHST_TILE_CACHE_DIR = resolveHome ( '~/.shst/cache/tiles/' ) ; export enum TileType { REFERENCE = 'reference' , INTERSECTION = 'intersection' , GEOMETRY = 'geometry' , METADATA = 'metadata' } export async function getTilesForId ( id ) { var url = SHST_ID_API_URL + 'shst:' + id ; return getJson ( url ) ; } export function getTileIdsForPolygon ( polygon , buffer = 0 ) { var polyBound = bbox ( polygon ) ; var nwPoint = destination ( [ polyBound [ 0 ] , polyBound [ 1 ] ] , buffer , 315 , { 'units' : 'meters' } ) ; var sePoint = destination ( [ polyBound [ 2 ] , polyBound [ 3 ] ] , buffer , 135 , { 'units' : 'meters' } ) ; let bounds = [ nwPoint . geometry . coordinates [ 0 ] , nwPoint . geometry . coordinates [ 1 ] , sePoint . geometry . coordinates [ 0 ] , sePoint . geometry . coordinates [ 1 ] ] ; return getTileIdsForBounds ( bounds , false ) ; } export function getTileIdsForPoint ( point , buffer ) { if ( buffer > 0 ) { var nwPoint = destination ( point , buffer , 315 , { 'units' : 'meters' } ) ; var sePoint = destination ( point , buffer , 135 , { 'units' : 'meters' } ) ; let bounds = [ nwPoint . geometry . coordinates [ 0 ] , nwPoint . geometry . coordinates [ 1 ] , sePoint . geometry . coordinates [ 0 ] , sePoint . geometry . coordinates [ 1 ] ] ; return getTileIdsForBounds ( bounds , false ) ; } else { let bounds = [ point . geometry . coordinates [ 0 ] , point . geometry . coordinates [ 1 ] , point . geometry . coordinates [ 0 ] , point . geometry . coordinates [ 1 ] ] ; return getTileIdsForBounds ( bounds , false ) ; } } export function getTileIdsForBounds ( bounds , bufferEdge ) { let tileRange = sphericalMercator . xyz ( bounds , DEFAULT_ZLEVEL ) ; let tileIds = [ ] ; let bufferSize = 0 ; if ( bufferEdge ) bufferSize = 1 ; for ( var x = tileRange . minX - bufferSize ; x <= tileRange . maxX + bufferSize ; x ++ ) { for ( var y = tileRange . minY - bufferSize ; y <= tileRange . maxY + bufferSize ; y ++ ) { var tileId = DEFAULT_ZLEVEL + '-' + x + '-' + y ; tileIds . push ( tileId ) ; } } return tileIds ; } export async function getTile ( tilePath ) { var arrayBuffer ; var tileFilePath = path . join ( SHST_TILE_CACHE_DIR , tilePath . toPathString ( ) ) ; if ( USE_LOCAL_CACHE && existsSync ( tileFilePath ) ) { arrayBuffer = new Uint8Array ( readFileSync ( tileFilePath ) ) ; } else { try { arrayBuffer = await getPbf ( SHST_TILE_URL + tilePath . toPathString ( ) ) ; } catch ( e ) { return [ ] ; } if ( USE_LOCAL_CACHE ) { mkdirSync ( path . join ( SHST_TILE_CACHE_DIR , tilePath . source ) , { recursive : true } ) ; writeFileSync ( tileFilePath , arrayBuffer ) ; console . log ( chalk . keyword ( 'lightgreen' ) ( "     writing to cache: " + tileFilePath ) ) ; } } if ( arrayBuffer ) { if ( tilePath . tileType === TileType . GEOMETRY ) { var geometries = sharedstreetsPbf . geometry ( arrayBuffer ) ; return geometries ; } else if ( tilePath . tileType === TileType . INTERSECTION ) { var intersections = sharedstreetsPbf . intersection ( arrayBuffer ) ; return intersections ; } else if ( tilePath . tileType === TileType . REFERENCE ) { var references = sharedstreetsPbf . reference ( arrayBuffer ) ; return references ; } else if ( tilePath . tileType === TileType . METADATA ) { var metadata = sharedstreetsPbf . metadata ( arrayBuffer ) ; return metadata ; } } } export function getIdFromTilePath ( tilePath ) { var pathParts = tilePath . split ( "/" ) ; var fileParts = pathParts [ pathParts . length - 1 ] . split ( "." ) ; var tileId = fileParts [ fileParts . length - 4 ] ; return tileId ; } export function getTypeFromTilePath ( tilePath ) { var parts = tilePath . split ( "." ) ; var typeString = parts [ parts . length - 3 ] . toUpperCase ( ) ; var type = TileType [ typeString ] ; return type ; } export function getSourceFromTilePath ( tilePath ) { var pathParts = tilePath . split ( '/' ) ; var tileSource = pathParts [ 0 ] + '/' + pathParts [ 1 ] ; return tileSource ; } export function getHierarchyFromPath ( tilePath ) { var parts = tilePath . split ( "." ) ; return parseInt ( parts [ parts . length - 2 ] ) ; } export class TilePathParams { source : string ; tileHierarchy : number ; constructor ( params = null ) { if ( params ) this . setParams ( params ) ; } setParams ( params ) { this . source = params . source ; this . tileHierarchy = params . tileHierarchy ; } } export class TilePath extends TilePathParams { tileId : string ; tileType : TileType ; constructor ( path = null ) { super ( ) ; if ( path ) { this . tileId = getIdFromTilePath ( path ) ; this . tileType = getTypeFromTilePath ( path ) ; this . source = getSourceFromTilePath ( path ) ; this . tileHierarchy = getHierarchyFromPath ( path ) ; } } toPathString ( ) { return this . source + '/' + this . tileId + '.' + this . tileType + '.' + this . tileHierarchy + '.pbf' ; } } export class TilePathGroup extends TilePathParams { tileIds : string [ ] ; tileTypes : TileType [ ] ; constructor ( paths = null ) { super ( ) ; this . tileIds = [ ] ; this . tileTypes = [ ] ; if ( paths ) { for ( var path of paths ) { this . addPath ( path ) ; } } } * [ Symbol . iterator ] ( ) { this . tileTypes . sort ( ) ; this . tileIds . sort ( ) ; for ( var tileType of this . tileTypes ) { for ( var tileId of this . tileIds ) { var tilePath = new TilePath ( ) ; tilePath . setParams ( this ) ; tilePath . tileId = tileId ; tilePath . tileType = tileType ; yield tilePath ; } } } addType ( tileType ) { var typeSet = new Set ( this . tileTypes ) ; typeSet . add ( tileType ) ; this . tileTypes = [ ... typeSet . values ( ) ] ; } addTileId ( tileId ) { var idSet = new Set ( this . tileIds ) ; idSet . add ( tileId ) ; this . tileIds = [ ... idSet . values ( ) ] ; } addPath ( path ) { if ( this . source != undefined && this . source !== path . source ) throw "Path source does not match group" ; else this . source = path . source ; if ( this . tileHierarchy != undefined && this . tileHierarchy !== path . tileHierarchy ) throw "Path source does not match group" ; else this . tileHierarchy = path . tileHierarchy ; this . addType ( path . tileType ) ; this . addTileId ( path . tileId ) ; } static fromPolygon ( polygon , buffer , params ) { var tilePathGroup = new TilePathGroup ( ) ; tilePathGroup . setParams ( params ) ; tilePathGroup . tileIds = getTileIdsForPolygon ( polygon ) ; return tilePathGroup ; } static fromPoint ( point , buffer , params ) { var tilePathGroup = new TilePathGroup ( ) ; tilePathGroup . setParams ( params ) ; tilePathGroup . tileIds = getTileIdsForPoint ( point , buffer ) ; return tilePathGroup ; } }