├── src ├── intersections.ts ├── util.ts ├── commands │ └── extract.ts ├── routing.ts ├── geom.ts ├── tiles.ts └── compare.ts ├── bin ├── run.cmd └── run ├── .travis.yml ├── test ├── .DS_Store ├── pbf │ ├── 12-1170-1566.geometry.6.pbf │ ├── 12-1170-1566.metadata.6.pbf │ ├── 12-1170-1566.reference.6.pbf │ └── 12-1170-1566.intersection.6.pbf └── geojson │ ├── points_1.in.unmatched.geojson │ ├── roundabout.1a.out.geojson │ ├── polygon_1.geojson │ ├── line_2.in.geojson │ ├── line_3.in.geojson │ ├── line_5.in.geojson │ ├── line_1.in.geojson │ ├── line_4.in.geojson │ ├── roundabout.1b.out.geojson │ ├── line_1a.out.geojson │ ├── line_side_of_street_bidirectional.3.geojson │ ├── points_1.in.matched.geojson │ ├── points_1.in.geojson │ ├── roundabout.1a.unmatched.geojson │ ├── line_side_of_street_override.geojson │ ├── line_side_of_street.out.geojson │ ├── roundabout.1c.geojson │ ├── roundabout.1a.geojson │ ├── roundabout.1b.geojson │ ├── line_side_of_street_bidirectional.2.geojson │ ├── roundabout.1d.geojson │ ├── line_side_of_street_bidirectional.geojson │ ├── line_1.in.matched.geojson │ ├── test_route.geojson │ ├── roundabout.1a.matched.geojson │ ├── line_6.in.geojson │ ├── points_1a.out.geojson │ ├── line_side_of_street_bidirectional.matched.geojson │ ├── line_side_of_street_override.matched.geojson │ ├── points_1b.out.geojson │ ├── line_side_of_street_bidirectional.2.matched.geojson │ ├── sf_centerlines.1a.out.geojson │ ├── long-paths.geojson │ ├── line-directed-test.in.geojson │ ├── line_side_of_street.geojson │ ├── sf_centerlines.1b.out.geojson │ ├── sf_centerlines.sample.out.geojson │ ├── roundabout.1.geojson │ ├── line-directed-test-snapped.out.geojson │ ├── line_side_of_street.matched.geojson │ ├── line-directed-test-unsnapped.out.geojson │ └── line_side_of_street_bidirectional.4.unmatched.geojson ├── docs └── cli_matcher.png ├── tslint.json ├── tsconfig.json ├── LICENSE ├── .circleci └── config.yml ├── .gitignore ├── bench.ts ├── test_graph.ts ├── package.json ├── CHANGELOG.md ├── README.md └── test_match.ts /src/intersections.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bin/run.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | node "%~dp0\run" %* -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | cache: yarn 3 | node_js: 4 | - node -------------------------------------------------------------------------------- /test/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharedstreets/sharedstreets-js/HEAD/test/.DS_Store -------------------------------------------------------------------------------- /docs/cli_matcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharedstreets/sharedstreets-js/HEAD/docs/cli_matcher.png -------------------------------------------------------------------------------- /test/pbf/12-1170-1566.geometry.6.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharedstreets/sharedstreets-js/HEAD/test/pbf/12-1170-1566.geometry.6.pbf -------------------------------------------------------------------------------- /test/pbf/12-1170-1566.metadata.6.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharedstreets/sharedstreets-js/HEAD/test/pbf/12-1170-1566.metadata.6.pbf -------------------------------------------------------------------------------- /test/pbf/12-1170-1566.reference.6.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharedstreets/sharedstreets-js/HEAD/test/pbf/12-1170-1566.reference.6.pbf -------------------------------------------------------------------------------- /test/pbf/12-1170-1566.intersection.6.pbf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sharedstreets/sharedstreets-js/HEAD/test/pbf/12-1170-1566.intersection.6.pbf -------------------------------------------------------------------------------- /bin/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('@oclif/command').run() 4 | .then(require('@oclif/command/flush')) 5 | .catch(require('@oclif/errors/handle')) -------------------------------------------------------------------------------- /test/geojson/points_1.in.unmatched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"bearing":90},"geometry":{"type":"Point","coordinates":[-77.02804327011107,38.899216038578196]}}]} -------------------------------------------------------------------------------- /test/geojson/roundabout.1a.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-121.324797,44.062538100000005],[-121.3248324,44.062508400000006],[-121.3248564,44.0624734]]]}}]} -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": { 8 | "max-line-length": [false], 9 | "object-literal-sort-keys": [false] 10 | }, 11 | "rulesDirectory": [] 12 | } -------------------------------------------------------------------------------- /test/geojson/polygon_1.geojson: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "type": "Feature", 4 | "properties": {}, 5 | "geometry": { 6 | "type": "Polygon", 7 | "coordinates": [ 8 | [[-77.0511531829834,38.88588861057251], 9 | [-77.00746536254883, 38.88588861057251], 10 | [-77.00746536254883, 38.91407701203291], 11 | [-77.0511531829834, 38.91407701203291], 12 | [-77.0511531829834,38.88588861057251]] 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/geojson/line_2.in.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -77.03850656747818, 12 | 38.90562417127419 13 | ], 14 | [ 15 | -77.03853607177734, 16 | 38.90275623810778 17 | ] 18 | ] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/geojson/line_3.in.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -77.02697038650513, 12 | 38.89969614673712 13 | ], 14 | [ 15 | -77.02686309814453, 16 | 38.89840193471598 17 | ] 18 | ] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/geojson/line_5.in.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -77.02393412590025, 12 | 38.91162892244559 13 | ], 14 | [ 15 | -77.02195197343826, 16 | 38.9123155647889 17 | ] 18 | ] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/geojson/line_1.in.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -77.03854948282242, 12 | 38.905582426353035 13 | ], 14 | [ 15 | -77.03856021165848, 16 | 38.90479970453626 17 | ] 18 | ] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/geojson/line_4.in.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "properties": {}, 7 | "geometry": { 8 | "type": "LineString", 9 | "coordinates": [ 10 | [ 11 | -77.02819347381592, 12 | 38.900535284506695 13 | ], 14 | [ 15 | -77.02821493148804, 16 | 38.89623512049704 17 | ] 18 | ] 19 | } 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /test/geojson/roundabout.1b.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-121.32466336749997,44.06257825993773],[-121.32468250000001,44.0625775],[-121.324724,44.062569800000006],[-121.32476270000001,44.0625565],[-121.324797,44.062538100000005]],[[-121.324797,44.062538100000005],[-121.3248324,44.062508400000006],[-121.3248564,44.0624734]],[[-121.3248564,44.0624734],[-121.32486840000001,44.062414800000006],[-121.32485977653651,44.06238847335589]]]}}]} -------------------------------------------------------------------------------- /test/geojson/line_1a.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"referenceId":"74c27665b3d05728566384c95afbdf66","fromIntersectionId":"70c77e7cb099b32e14b695997f194735","toIntersectionId":"c2d7eeac1d46b3c7fe53b3f04f044cf1","roadClass":"Secondary","direction":"forward","geometryId":"6150e0b9dd7da196aca002bfb68b2365","referenceLength":99.47,"section":[7.742365192587979,99.47],"side":"unknown","score":5.073777661358422},"geometry":{"type":"LineString","coordinates":[[-77.03851016043451,38.90558237144512],[-77.0385102,38.905565200000005],[-77.0385114,38.9051545],[-77.03851249600262,38.90475744815141]]}}]} -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_bidirectional.3.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "line_side_of_street_bidirectional", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "GEO_ID": 14676337, "LFN_ID": 9747, "LF_NAME": "John St", "ADDRESS_L": "170-170", "ADDRESS_R": "165-169", "OE_FLAG_L": "E", "OE_FLAG_R": "O", "LONUML": 170, "HINUM_L": 170, "LONUMR": 165, "HINUM_R": 169, "FNODE": 13466701, "TNODE": 14676283, "ONE_WAY_DI": 0, "DIR_CODE_D": "Not One-Way", "FCODE": 201500, "FCODE_DESC": "Local", "JURIS_CODE": "CITY OF TORONTO", "OBJECTID": 55079.0 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -79.391278046, 43.650326533 ], [ -79.39133652, 43.650462603 ], [ -79.391361517, 43.650520752 ] ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/geojson/points_1.in.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"score":3.1799868991648736,"location":103.30809300066495,"referenceLength":133.47,"geometryId":"1d12bdab9cc895cf2ffee2f386a8fd41","referenceId":"4ef86ba3b7aedb47b4e6eb16cd082b8b","direction":"forward","bearing":90.30216185745682,"snappedSide":"right","interceptAngle":90.00001082771188},"geometry":{"type":"Point","coordinates":[-77.02842931440927,38.89981241618331]}},{"type":"Feature","properties":{"score":5.820410277808107,"location":72.30870109871125,"referenceLength":90.87,"geometryId":"32aabe41771a4a94aff41de3102debb8","referenceId":"7edae7bb25899c06ba232d2ae846dc92","direction":"forward","bearing":89.65786747273398,"snappedSide":"left","interceptAngle":270.0000074825405},"geometry":{"type":"Point","coordinates":[-77.02724893462616,38.89981497174624]}}]} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "outDir": "build/", 5 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | 8 | /* Strict Type-Checking Options */ 9 | "strict": false, 10 | 11 | /* Module Resolution Options */ 12 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ 13 | /* Additional Checks */ 14 | "noUnusedLocals": false, /* Report errors on unused locals. */ 15 | "noUnusedParameters": false, /* Report errors on unused parameters. */ 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test/geojson/points_1.in.geojson: -------------------------------------------------------------------------------- 1 | {"type": "FeatureCollection", 2 | "features": [ 3 | { 4 | "type": "Feature", 5 | "properties": {"bearing":90}, 6 | 7 | "geometry": { 8 | "type": "Point", 9 | "coordinates": [ 10 | -77.02842950820923, 11 | 38.89978381831128 12 | ] 13 | } 14 | }, 15 | { 16 | "type": "Feature", 17 | "properties": {"bearing":90}, 18 | "geometry": { 19 | "type": "Point", 20 | "coordinates": [ 21 | -77.02724933624268, 22 | 38.89986731494795 23 | ] 24 | } 25 | }, 26 | { 27 | "type": "Feature", 28 | "properties": {"bearing":90}, 29 | "geometry": { 30 | "type": "Point", 31 | "coordinates": [ 32 | -77.02804327011107, 33 | 38.899216038578196 34 | ] 35 | } 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /test/geojson/roundabout.1a.unmatched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"objectid":5896,"road":"80161","dir":"NW","name":"NEWPORT AVE","alt_dir":"NW","alt_name":"TRAFFIC CIRCLE #24","label":"NEWPORT AVE","owner":"City of Bend","surface":"AC","class":"City Arterial","cr_num":0,"row_width":0,"estdate":null,"row_source":null,"lid":null,"srd":null,"length_mi":0.014,"oid_copy":15209,"maintenanc":"City of Bend","alt_class":"City Arterial","autodate":"2010/02/25","autowho":"JOHNA","home_owner":null,"shape_stle":78.8388169725},"geometry":{"type":"LineString","coordinates":[[-121.32466275541064,44.06257030269731],[-121.32469340806084,44.06256087634114],[-121.32472155820828,44.06254801671064],[-121.32474647242987,44.062532058851026],[-121.3247675016128,44.062513418528596],[-121.32478409786661,44.062492581398175],[-121.32479582879776,44.062470090349734],[-121.32480238877481,44.06244653136369],[-121.3248036068908,44.0624225182438],[-121.32479945141537,44.06239867662513]]}}]} -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_override.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "features": [ 4 | { 5 | "type": "Feature", 6 | "geometry": { 7 | "type": "LineString", 8 | "coordinates": [ 9 | [ 10 | -0.013609, 11 | 51.5499 12 | ], 13 | [ 14 | -0.013782, 15 | 51.549835 16 | ] 17 | ] 18 | }, 19 | "properties": { 20 | "id": "b6039f07-a0d2-4be6-ae1e-963b52657255", 21 | "side": "right" 22 | } 23 | }, 24 | { 25 | "type": "Feature", 26 | "geometry": { 27 | "type": "LineString", 28 | "coordinates": [ 29 | [ 30 | -0.003578, 31 | 51.55121 32 | ], 33 | [ 34 | -0.003293, 35 | 51.551283 36 | ] 37 | ] 38 | }, 39 | "properties": { 40 | "id": "edf69fff-0c0b-4bed-b9b6-aeb061233562", 41 | "side": "left" 42 | } 43 | } 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.4074386,43.675831300000006],[-79.4073214,43.675527900000006],[-79.40724920000001,43.6753493],[-79.40709600000001,43.674965500000006],[-79.40706099646103,43.67486735087316]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.40706080000001,43.674866800000004],[-79.40709600000001,43.674965500000006],[-79.40724920000001,43.6753493],[-79.4073214,43.675527900000006],[-79.40743838834308,43.67583075207873]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.4074386,43.675831300000006],[-79.4073214,43.675527900000006],[-79.40724920000001,43.6753493],[-79.40709600000001,43.674965500000006],[-79.40706099646103,43.67486735087316]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.40706080000001,43.674866800000004],[-79.40709600000001,43.674965500000006],[-79.40724920000001,43.6753493],[-79.4073214,43.675527900000006],[-79.40743838834308,43.67583075207873]]]}}]} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 SharedStreets 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/node:11.15.0-stretch 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/mongo:3.4.4 16 | 17 | working_directory: ~/repo 18 | 19 | steps: 20 | - checkout 21 | 22 | # Download and cache dependencies 23 | - restore_cache: 24 | keys: 25 | - v1-dependencies-{{ checksum "package.json" }} 26 | # fallback to using the latest cache if no exact match is found 27 | - v1-dependencies- 28 | 29 | - run: yarn install 30 | 31 | - save_cache: 32 | paths: 33 | - node_modules 34 | key: v1-dependencies-{{ checksum "package.json" }} 35 | 36 | # run tests! 37 | - run: yarn test 38 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | import fetch from 'node-fetch'; 2 | const path = require('path'); 3 | 4 | export function resolveHome(filepath) { 5 | if (filepath[0] === '~') { 6 | return path.join(process.env.HOME, filepath.slice(1)); 7 | } 8 | console.log(filepath) 9 | return filepath; 10 | } 11 | 12 | function checkStatus(res) { 13 | if (res.ok) { // res.status >= 200 && res.status < 300 14 | return res; 15 | } else { 16 | throw "file not found"; 17 | } 18 | } 19 | 20 | export async function getJson(url):Promise<{}> { 21 | 22 | var data = await fetch(url, { 23 | method: 'GET' 24 | }); 25 | 26 | checkStatus(data); 27 | 28 | return data.json(); 29 | } 30 | 31 | export async function getPbf(url):Promise { 32 | 33 | var data = await fetch(url, { 34 | method: 'GET' 35 | }); 36 | 37 | checkStatus(data); 38 | 39 | return new Uint8Array(await data.buffer()); 40 | } 41 | 42 | 43 | export function rmse(values:number[]):number { 44 | var sum = 0; 45 | for(var value of values) { 46 | sum = sum + Math.pow(value, 2); 47 | } 48 | var mean = sum / values.length; 49 | return Math.sqrt(mean); 50 | } -------------------------------------------------------------------------------- /test/geojson/roundabout.1c.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "roundabout", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "OBJECTID": 7153, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.013, "OID_COPY": 15212, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 69.634831150899998 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324577547979672, 44.062278206520688 ], [ -121.32454495384674, 44.062282414775325 ], [ -121.324513719731272, 44.062290342338692 ], [ -121.324484656197754, 44.062301783480656 ], [ -121.32445851748254, 44.062316441289312 ], [ -121.324435981920658, 44.062333935375953 ], [ -121.324417634342183, 44.062353811746512 ], [ -121.324403950895103, 44.062375554583141 ], [ -121.324395286688315, 44.062398599630235 ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/geojson/roundabout.1a.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "roundabout", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "OBJECTID": 5896, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.014, "OID_COPY": 15209, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 78.838816972499998 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324662755410642, 44.06257030269731 ], [ -121.324693408060838, 44.062560876341138 ], [ -121.324721558208282, 44.062548016710643 ], [ -121.324746472429865, 44.062532058851026 ], [ -121.324767501612797, 44.062513418528596 ], [ -121.324784097866612, 44.062492581398175 ], [ -121.324795828797761, 44.062470090349734 ], [ -121.324802388774813, 44.06244653136369 ], [ -121.324803606890796, 44.0624225182438 ], [ -121.324799451415373, 44.062398676625129 ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/geojson/roundabout.1b.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "roundabout", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "OBJECTID": 1019, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.015, "OID_COPY": 15211, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 80.079999732199994 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324799451415373, 44.062398676625129 ], [ -121.324790553214456, 44.062375127655727 ], [ -121.324776416061738, 44.06235295099362 ], [ -121.324757423324357, 44.062332748010441 ], [ -121.324734090038504, 44.062315066556458 ], [ -121.324707048942557, 44.062300386104354 ], [ -121.324677033318892, 44.062289104747599 ], [ -121.324644857109391, 44.06228152840535 ], [ -121.324611392843792, 44.062277862526905 ], [ -121.324577547979672, 44.062278206520688 ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | *.class 8 | // Published bundle 9 | .rpt2_cache 10 | dist 11 | *.pyc 12 | 13 | // Typescript compiled files 14 | *.d.ts 15 | *.js 16 | 17 | # Runtime data 18 | pids 19 | *.pid 20 | *.seed 21 | *.pid.lock 22 | 23 | # Directory for instrumented libs generated by jscoverage/JSCover 24 | lib-cov 25 | 26 | # Coverage directory used by tools like istanbul 27 | coverage 28 | 29 | # nyc test coverage 30 | .nyc_output 31 | 32 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 33 | .grunt 34 | 35 | # Bower dependency directory (https://bower.io/) 36 | bower_components 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (http://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules/ 46 | jspm_packages/ 47 | 48 | # Typescript v1 declaration files 49 | typings/ 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional REPL history 58 | .node_repl_history 59 | 60 | # Output of 'npm pack' 61 | *.tgz 62 | 63 | # Yarn Integrity file 64 | .yarn-integrity 65 | 66 | # dotenv environment variables file 67 | .env 68 | 69 | # cache directory 70 | shst 71 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_bidirectional.2.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "line_side_of_street_bidirectional", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "GEO_ID": 14020386, "LFN_ID": 3584, "LF_NAME": "Grenville St", "ADDRESS_L": "9-25", "ADDRESS_R": "14-32", "OE_FLAG_L": "O", "OE_FLAG_R": "E", "LONUML": 9, "HINUM_L": 25, "LONUMR": 14, "HINUM_R": 32, "FNODE": 14020388, "TNODE": 14020387, "ONE_WAY_DI": 0, "DIR_CODE_D": "Not One-Way", "FCODE": 201500, "FCODE_DESC": "Local", "JURIS_CODE": "CITY OF TORONTO", "OBJECTID": 55474.0 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -79.384022436, 43.662005971 ], [ -79.385361657, 43.661721383 ] ] ] } }, 7 | { "type": "Feature", "properties": { "GEO_ID": 14020385, "LFN_ID": 3584, "LF_NAME": "Grenville St", "ADDRESS_L": "1-7", "ADDRESS_R": "10-10", "OE_FLAG_L": "O", "OE_FLAG_R": "E", "LONUML": 1, "HINUM_L": 7, "LONUMR": 10, "HINUM_R": 10, "FNODE": 13464794, "TNODE": 14020388, "ONE_WAY_DI": 0, "DIR_CODE_D": "Not One-Way", "FCODE": 201500, "FCODE_DESC": "Local", "JURIS_CODE": "CITY OF TORONTO", "OBJECTID": 128910.0 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -79.383421798, 43.662136087 ], [ -79.384022436, 43.662005971 ] ] ] } } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/geojson/roundabout.1d.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "roundabout", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "OBJECTID": 4008, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.021, "OID_COPY": 15208, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 113.814449524 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324395286688315, 44.062398599630235 ], [ -121.32439111453202, 44.062422426161909 ], [ -121.324392309806001, 44.062446426404577 ], [ -121.32439884140986, 44.062469975757459 ], [ -121.324410539365985, 44.062492461353379 ], [ -121.324427099242627, 44.062513298008547 ], [ -121.324448090076103, 44.062531943452058 ], [ -121.324472965586253, 44.062547912438468 ], [ -121.324501078393084, 44.062560789376477 ], [ -121.324531696864781, 44.062570239144847 ], [ -121.324564024158448, 44.06257601581391 ], [ -121.32459721895836, 44.062577969046274 ], [ -121.324630417371466, 44.062576048009092 ], [ -121.324662755410642, 44.06257030269731 ] ] } } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /bench.ts: -------------------------------------------------------------------------------- 1 | import * as Benchmark from "benchmark"; 2 | import { FormOfWay } from "sharedstreets-types"; 3 | import * as sharedstreets from "./src/"; 4 | 5 | // Fixtures 6 | const message = "Intersection 110.000000 45.000000"; 7 | const coord = [110, 45]; 8 | const geom = [[110, 45], [115, 50], [120, 55]]; 9 | const locationReferences = [ 10 | sharedstreets.locationReference([-74.00482177734375, 40.741641998291016], {outboundBearing: 208, distanceToNextRef: 9279}), 11 | sharedstreets.locationReference([-74.005126953125, 40.74085235595703], {inboundBearing: 188}), 12 | ]; 13 | const formOfWay = FormOfWay.MultipleCarriageway; 14 | 15 | sharedstreets.referenceId(locationReferences, formOfWay); 16 | /** 17 | * Benchmark Results 18 | * 19 | * generateHash x 475,542 ops/sec ±4.91% (71 runs sampled) 20 | * intersectionId x 177,663 ops/sec ±17.20% (62 runs sampled) 21 | * geometryId x 90,787 ops/sec ±15.06% (62 runs sampled) 22 | * referenceId x 76,479 ops/sec ±5.55% (74 runs sampled) 23 | */ 24 | const suite = new Benchmark.Suite("sharedstreets"); 25 | suite 26 | .add("generateHash", () => sharedstreets.generateHash(message)) 27 | .add("intersectionId", () => sharedstreets.intersectionId(coord)) 28 | .add("geometryId", () => sharedstreets.geometryId(geom)) 29 | .add("referenceId", () => sharedstreets.referenceId(locationReferences, formOfWay)) 30 | .on("cycle", (e: any) => { process.stdout.write(String(e.target) + "\n"); }) 31 | .run(); 32 | -------------------------------------------------------------------------------- /test_graph.ts: -------------------------------------------------------------------------------- 1 | import { lineString } from "@turf/helpers"; 2 | import length from "@turf/length"; 3 | import * as fs from "fs"; 4 | import * as glob from "glob"; 5 | import * as path from "path"; 6 | import * as sharedstreetsPbf from "sharedstreets-pbf"; 7 | import * as sharedstreets from "./src/index"; 8 | 9 | import * as turfHelpers from '@turf/helpers'; 10 | import envelope from '@turf/envelope'; 11 | 12 | 13 | import { TileIndex } from './src/index'; 14 | import { TilePathGroup, TileType, TilePathParams } from './src/index'; 15 | 16 | import { CleanedPoints, CleanedLines } from "./src/geom"; 17 | import { polygon } from "@turf/envelope/node_modules/@turf/helpers"; 18 | import { Graph, GraphMode } from "./src/graph"; 19 | 20 | const test = require('tape'); 21 | 22 | test("sharedstreets -- graph test", async (t:any) => { 23 | 24 | var params = new TilePathParams(); 25 | params.source = 'osm/planet-181224'; 26 | params.tileHierarchy = 7; 27 | 28 | // test polygon (dc area) 29 | const content = fs.readFileSync('test/geojson/test_route.geojson'); 30 | var lineIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 31 | var graph = new Graph(envelope(lineIn), params); 32 | await graph.buildGraph(); 33 | 34 | t.equal(graph.id, 'd626d5b0-0dec-3e6f-97ff-d9712228a282'); 35 | 36 | var results = await graph.matchGeom(lineIn.features[0]); 37 | lineIn.features[0].geometry.coordinates.reverse(); 38 | var results2 = await graph.matchGeom(lineIn.features[0]); 39 | 40 | 41 | 42 | t.end(); 43 | 44 | }); 45 | 46 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_bidirectional.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "line_side_of_street_bidirectional", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "row_number": 4911, "id": 12533, "objectid": 549301.0, "city": "MT", "deleted": "FALSE", "bylawno_ol": "(M.T. 21)", "bylawno": null, "chapter": "950", "schedule": "14", "schedule_n": "No Stopping", "col1c": "Spadina Road", "col2c": "Both", "col3c": "Dupont Street and Macpherson Avenue", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2019\/01\/15", "anomalies": null, "symbol_id": "950_14", "legend_id": "950_14", "last_update": "2019\/01\/14" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.407373334821187, 43.675810785454104 ], [ -79.407083955008758, 43.67508519714265 ], [ -79.407027581278513, 43.674937952690733 ] ] } }, 7 | { "type": "Feature", "properties": { "row_number": 4912, "id": 12533, "objectid": 549301.0, "city": "MT", "deleted": "FALSE", "bylawno_ol": "(M.T. 21)", "bylawno": null, "chapter": "950", "schedule": "14", "schedule_n": "No Stopping", "col1c": "Spadina Road", "col2c": "Both", "col3c": "Dupont Street and Macpherson Avenue", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2019\/01\/15", "anomalies": null, "symbol_id": "950_14", "legend_id": "950_14", "last_update": "2019\/01\/14" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.40754873226237, 43.675811422517427 ], [ -79.40753578485554, 43.675779079301734 ], [ -79.407261433481082, 43.675094099551536 ], [ -79.407197394223246, 43.674931339686729 ] ] } } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /test/geojson/line_1.in.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"shstReferenceId":"74c27665b3d05728566384c95afbdf66","shstGeometryId":"6150e0b9dd7da196aca002bfb68b2365","shstFromIntersectionId":"70c77e7cb099b32e14b695997f194735","shstToIntersectionId":"c2d7eeac1d46b3c7fe53b3f04f044cf1","referenceLength":99.47,"section":[7.742365192587979,94.78027759526125],"gisReferenceId":"bb3d8b8ae94378b67534da8bc905f3fe","gisGeometryId":"56e68625e9bd224fd9f4b8ad2ef3cc95","gisTotalSegments":1,"gisSegmentIndex":1,"gisFromIntersectionId":"be0e0c2439a01037504b32a33e39748a","gisToIntersectionId":"56e68438af1d8dbbba08c1b708072c0f","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":3.09,"matchType":"hmm"},"geometry":{"type":"LineString","coordinates":[[-77.03851016043451,38.90558237144512],[-77.0385102,38.905565200000005],[-77.0385114,38.9051545],[-77.03851237958392,38.90479962368269]]}},{"type":"Feature","properties":{"shstReferenceId":"34284cfb3e115d19cc1ba33d70d34353","shstGeometryId":"6150e0b9dd7da196aca002bfb68b2365","shstFromIntersectionId":"c2d7eeac1d46b3c7fe53b3f04f044cf1","shstToIntersectionId":"70c77e7cb099b32e14b695997f194735","referenceLength":99.47,"section":[4.689722404738745,91.72763480741202],"gisReferenceId":"720e26876b08bbd5fae5ee747698cb78","gisGeometryId":"56e68625e9bd224fd9f4b8ad2ef3cc95","gisTotalSegments":1,"gisSegmentIndex":1,"gisFromIntersectionId":"56e68438af1d8dbbba08c1b708072c0f","gisToIntersectionId":"be0e0c2439a01037504b32a33e39748a","startSideOfStreet":"left","endSideOfStreet":"left","sideOfStreet":"left","score":3.09,"matchType":"hmm"},"geometry":{"type":"LineString","coordinates":[[-77.03851238358132,38.90479817553129],[-77.0385114,38.9051545],[-77.0385102,38.905565200000005],[-77.03851016377128,38.9055809232927]]}}]} -------------------------------------------------------------------------------- /test/geojson/test_route.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection", "features":[ 2 | 3 | { 4 | "type": "Feature", 5 | "properties": { 6 | "OBJECTID": 27764, 7 | "ASSETID": 28197, 8 | "INT_ID_FRO": 19624, 9 | "INT_ID_TO": 19840, 10 | "STNUM": 4405, 11 | "MAPSHEET": "139-5A201", 12 | "ID": 33097, 13 | "ADRF": 300, 14 | "ADRT": 338, 15 | "ZIP_R": 90026, 16 | "ADLF": 301, 17 | "ADLT": 339, 18 | "ZIP_L": 90026, 19 | "TDIR": "N", 20 | "STNAME": "MICHELTORENA", 21 | "STSFX": "ST", 22 | "SFXDIR": null, 23 | "STNAME_A": null, 24 | "STSFX_A": null, 25 | "STATUS": "O", 26 | "TEMP_": null, 27 | "SECT_ID": 3681500, 28 | "DEDRQ": 1, 29 | "REMARKS": null, 30 | "SV_STATUS": null, 31 | "ST_SUBTYPE": 1, 32 | "CRTN_DT": "1995-04-11T00:00:00.000Z", 33 | "LST_MODF_D": null, 34 | "OLD_STREET": "Local Street", 35 | "PLANNING_S": null, 36 | "DEDRQ_DESC": "No", 37 | "TOOLTIP": "MICHELTORENA ST\\nStreet Designation: Local Street - Standard", 38 | "NLA_URL": "navigatela/reports/centerline_mb.cfm?pk=28197&t=3681500", 39 | "Planning_A": 28197, 40 | "TYPE": 70, 41 | "MODIFIED": 0, 42 | "Street_Des": "Local Street - Standard", 43 | "Street_D_1": "Local Street - Standard" 44 | }, 45 | "geometry": { 46 | "type": "LineString", 47 | "coordinates": [ 48 | [ 49 | -118.28265684254498, 50 | 34.076759468193124 51 | ], 52 | [ 53 | -118.28305660375169, 54 | 34.07613105871959 55 | ] 56 | ] 57 | } 58 | } 59 | 60 | ]} 61 | -------------------------------------------------------------------------------- /test/geojson/roundabout.1a.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"segments":[{"referenceId":"45b63f60e777410878738bfc00f0ebbe","geometryId":"1fc6f2261b05436adb3f63cfd9dc6b4d","referenceLength":17.16,"section":[5.28770403659774,17.16],"fromIntersectionId":"6e0ba77e9297c70865d35e7c385ca509","toIntersectionId":"c47c4e4cb6499f417ac98f8da151bf0f"},{"referenceId":"363221e6abefa6d77dbe2f507ae8ce99","geometryId":"2418c6bb94d0c160e9e7d571882acad3","referenceLength":8.69,"section":[0,8.69],"fromIntersectionId":"c47c4e4cb6499f417ac98f8da151bf0f","toIntersectionId":"eacdc5ac380c409c28849079d793fb4d"},{"referenceId":"a9ab221d954649f8ddbea47cdd1c1cce","geometryId":"ace949e9a907172a28a48228a6253893","referenceLength":13.17,"section":[0,9.532499734793085],"fromIntersectionId":"eacdc5ac380c409c28849079d793fb4d","toIntersectionId":"78a0b0c43307966ddf2be7e41b95fddb"}],"score":0.00016469897718718318,"matchType":"hmm","pp_objectid":5896,"pp_road":"80161","pp_dir":"NW","pp_name":"NEWPORT AVE","pp_alt_dir":"NW","pp_alt_name":"TRAFFIC CIRCLE #24","pp_label":"NEWPORT AVE","pp_owner":"City of Bend","pp_surface":"AC","pp_class":"City Arterial","pp_cr_num":0,"pp_row_width":0,"pp_estdate":null,"pp_row_source":null,"pp_lid":null,"pp_srd":null,"pp_length_mi":0.014,"pp_oid_copy":15209,"pp_maintenanc":"City of Bend","pp_alt_class":"City Arterial","pp_autodate":"2010/02/25","pp_autowho":"JOHNA","pp_home_owner":null,"pp_shape_stle":78.8388169725},"geometry":{"type":"MultiLineString","coordinates":[[[-121.32466302105388,44.06257827369844],[-121.32468250000001,44.0625775],[-121.324724,44.062569800000006],[-121.32476270000001,44.0625565],[-121.324797,44.062538100000005]],[[-121.324797,44.062538100000005],[-121.3248324,44.062508400000006],[-121.3248564,44.0624734]],[[-121.3248564,44.0624734],[-121.32486840000001,44.062414800000006],[-121.32485995171413,44.06238900815741]]]}}]} -------------------------------------------------------------------------------- /test/geojson/line_6.in.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection", "features":[ 2 | 3 | 4 | { 5 | "type": "Feature", 6 | "properties": { 7 | "OBJECTID": 78072, 8 | "ASSETID": 59023, 9 | "INT_ID_FRO": 18576, 10 | "INT_ID_TO": 18624, 11 | "STNUM": 8773, 12 | "MAPSHEET": "138B197", 13 | "ID": 31420, 14 | "ADRF": 3801, 15 | "ADRT": 3831, 16 | "ZIP_R": 90004, 17 | "ADLF": 3800, 18 | "ADLT": 3898, 19 | "ZIP_L": 90004, 20 | "TDIR": "W", 21 | "STNAME": "BEVERLY", 22 | "STSFX": "BLVD", 23 | "SFXDIR": null, 24 | "STNAME_A": null, 25 | "STSFX_A": null, 26 | "STATUS": "O", 27 | "TEMP_": null, 28 | "SECT_ID": "0576000", 29 | "DEDRQ": 2, 30 | "REMARKS": null, 31 | "SV_STATUS": null, 32 | "ST_SUBTYPE": 1, 33 | "CRTN_DT": "2000-10-18T00:00:00.000Z", 34 | "LST_MODF_D": null, 35 | "OLD_STREET": "Major Highway - Class II", 36 | "PLANNING_S": null, 37 | "DEDRQ_DESC": "Yes", 38 | "TOOLTIP": "BEVERLY BLVD\\nStreet Designation: Avenue II", 39 | "NLA_URL": "navigatela/reports/centerline_mb.cfm?pk=59023&t=0576000", 40 | "Planning_A": 59023, 41 | "TYPE": 40, 42 | "MODIFIED": 0, 43 | "Street_Des": "Avenue II", 44 | "Street_D_1": "Avenue II" 45 | }, 46 | "geometry": { 47 | "type": "LineString", 48 | "coordinates": [ 49 | [ 50 | -118.29169749020073, 51 | 34.07633471218885 52 | ], 53 | [ 54 | -118.29234142928871, 55 | 34.07633041395482 56 | ], 57 | [ 58 | -118.29249002144039, 59 | 34.076329731508366 60 | ], 61 | [ 62 | -118.29296916326467, 63 | 34.07632753724597 64 | ] 65 | ] 66 | } 67 | } 68 | 69 | ]} 70 | -------------------------------------------------------------------------------- /test/geojson/points_1a.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"score":3.1799868991648736,"location":103.30809300066495,"referenceLength":133.47,"geometryId":"1d12bdab9cc895cf2ffee2f386a8fd41","referenceId":"4ef86ba3b7aedb47b4e6eb16cd082b8b","direction":"forward","bearing":90.30216185745682,"sideOfStreet":"right","interceptAngle":90.00001082771188},"geometry":{"type":"Point","coordinates":[-77.02842931440927,38.89981241618331]}},{"type":"Feature","properties":{"score":3.1799868991648736,"location":30.161906999335045,"referenceLength":133.47,"geometryId":"1d12bdab9cc895cf2ffee2f386a8fd41","referenceId":"1f621430356ba66efee4b83379a147b1","direction":"backward","bearing":270.30216185745684,"sideOfStreet":"left","interceptAngle":270.0000108277119},"geometry":{"type":"Point","coordinates":[-77.02842931440927,38.89981241618331]}},{"type":"Feature","properties":{"score":5.820410277808107,"location":18.56129890128875,"referenceLength":90.87,"geometryId":"32aabe41771a4a94aff41de3102debb8","referenceId":"e2ff5fedf35bd749f1db4e301f9a56b5","direction":"backward","bearing":269.657867472734,"sideOfStreet":"right","interceptAngle":90.00000748254052},"geometry":{"type":"Point","coordinates":[-77.02724893462616,38.89981497174624]}},{"type":"Feature","properties":{"score":5.820410277808107,"location":72.30870109871125,"referenceLength":90.87,"geometryId":"32aabe41771a4a94aff41de3102debb8","referenceId":"7edae7bb25899c06ba232d2ae846dc92","direction":"forward","bearing":89.65786747273398,"sideOfStreet":"left","interceptAngle":270.0000074825405},"geometry":{"type":"Point","coordinates":[-77.02724893462616,38.89981497174624]}},{"type":"Feature","properties":{"score":3.56113832131912,"location":99.56772042985736,"referenceLength":165.45,"geometryId":"0283824017780cbdf9febed3ec5a2450","referenceId":"0e7016db2d00589705b3af197c80040e","direction":"forward","bearing":359.99410728693755,"sideOfStreet":"right","interceptAngle":89.99551496760006},"geometry":{"type":"Point","coordinates":[-77.02808442137345,38.89921603278467]}}]} -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_bidirectional.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"gisReferenceId":"517a6aa5d537dd268edbebf3ceef8b51","gisGeometryId":"aa49222ded4e45ac5ac3cecf057b84cc","gisSegementIndex":1,"gisFromIntersectionId":"af04a87425df5d7fad93d4950a30ff11","gisToIntersectionId":"9e693b7884b4f8d47f6f2c33c10fa4b3","gisTotalSegments":1,"shstReferenceId":"307eea41a137153cd5661ea2350b747f","shstGeometryId":"0705ae4cc8c41b35f0cd95ba1d6c7650","shstFromIntersectionId":"c7ed504536aebd8d82f913c0d229f800","shstToIntersectionId":"350485c32ac04cfa3e47a969eb9ef6b6","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":3.68,"matchType":"hmm","pp_row_number":4911,"pp_id":12533,"pp_objectid":549301,"pp_city":"MT","pp_deleted":"FALSE","pp_bylawno_ol":"(M.T. 21)","pp_bylawno":null,"pp_chapter":"950","pp_schedule":"14","pp_schedule_n":"No Stopping","pp_col1c":"Spadina Road","pp_col2c":"Both","pp_col3c":"Dupont Street and Macpherson Avenue","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2019/01/15","pp_anomalies":null,"pp_symbol_id":"950_14","pp_legend_id":"950_14","pp_last_update":"2019/01/14"},"geometry":{"type":"LineString","coordinates":[[-79.40703997059165,43.67494225687234],[-79.40705392790353,43.674981392816996],[-79.40720747462824,43.67536606143931],[-79.40727958061301,43.67554442887536],[-79.40738436643515,43.67581569198074]]}},{"type":"Feature","properties":{"gisReferenceId":"13eca9e098eb0fc0430e1c03a524b7db","gisGeometryId":"bb31cfa556f0e4e749751cfc7574377a","gisSegementIndex":1,"gisFromIntersectionId":"42f4bf1e9285019afef87247a483943c","gisToIntersectionId":"52e8d3ee6fef2598ca21d3f812184afd","gisTotalSegments":1,"shstReferenceId":"e658318d63ce8af823578ead7c4332bd","shstGeometryId":"0705ae4cc8c41b35f0cd95ba1d6c7650","shstFromIntersectionId":"350485c32ac04cfa3e47a969eb9ef6b6","shstToIntersectionId":"c7ed504536aebd8d82f913c0d229f800","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":7.34,"matchType":"hmm","pp_row_number":4912,"pp_id":12533,"pp_objectid":549301,"pp_city":"MT","pp_deleted":"FALSE","pp_bylawno_ol":"(M.T. 21)","pp_bylawno":null,"pp_chapter":"950","pp_schedule":"14","pp_schedule_n":"No Stopping","pp_col1c":"Spadina Road","pp_col2c":"Both","pp_col3c":"Dupont Street and Macpherson Avenue","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2019/01/15","pp_anomalies":null,"pp_symbol_id":"950_14","pp_legend_id":"950_14","pp_last_update":"2019/01/14"},"geometry":{"type":"LineString","coordinates":[[-79.40748054527408,43.67581509701344],[-79.40736321938515,43.67551137112008],[-79.40729092537178,43.67533253856069],[-79.40713807209548,43.67494960718049],[-79.40713325575568,43.67493610229279]]}}]} -------------------------------------------------------------------------------- /src/commands/extract.ts: -------------------------------------------------------------------------------- 1 | import {Command, flags} from '@oclif/command' 2 | import { readFileSync, writeFileSync } from 'fs'; 3 | 4 | import { TilePathParams, TileType, TilePathGroup } from '../index' 5 | import { TileIndex } from '../index' 6 | 7 | import geomLength from '@turf/length'; 8 | 9 | const chalk = require('chalk'); 10 | 11 | export default class Extract extends Command { 12 | static description = 'extracts SharedStreets streets using polygon boundary and returns GeoJSON output of all intersecting features' 13 | 14 | static examples = [ 15 | `$ shst extract polygon.geojson --out=output.geojson 16 | 🌏 Loading polygon... 17 | 🗄️ Loading SharedStreets tiles... 18 | 🔍 Searching data... 19 | `, 20 | ] 21 | 22 | static flags = { 23 | help: flags.help({char: 'h'}), 24 | 25 | out: flags.string({char: 'o', description: 'output file'}), 26 | 'tile-source': flags.string({description: 'SharedStreets tile source', default: 'osm/planet-181224'}), 27 | 'tile-hierarchy': flags.integer({description: 'SharedStreets tile hierarchy', default: 6}), 28 | metadata: flags.boolean({description: 'Include SharedStreets OpenStreetMap metadata in output', default: false}), 29 | tiles: flags.boolean({description: 'Export list of tiles intersecting with bounding box', default: false}) 30 | 31 | } 32 | 33 | static args = [{name: 'file'}] 34 | 35 | async run() { 36 | const {args, flags} = this.parse(Extract) 37 | 38 | if(flags.out) 39 | this.log(chalk.bold.keyword('green')(' 🌏 Loading polygon...')); 40 | 41 | var inFile = args.file; 42 | 43 | var content = readFileSync(inFile); 44 | var polygon = JSON.parse(content.toLocaleString()); 45 | 46 | var outFile = flags.out; 47 | 48 | if(!outFile) 49 | outFile = inFile; 50 | 51 | if(outFile.toLocaleLowerCase().endsWith(".geojson")) 52 | outFile = outFile.split(".").slice(0, -1).join("."); 53 | 54 | 55 | this.log(chalk.bold.keyword('green')(' 🗄️ Loading SharedStreets tiles...')); 56 | 57 | var params = new TilePathParams(); 58 | params.source = flags['tile-source']; 59 | params.tileHierarchy = flags['tile-hierarchy'] 60 | 61 | var tileIndex = new TileIndex(); 62 | 63 | this.log(chalk.bold.keyword('green')(' 🔍 Searching data...')); 64 | 65 | if(flags.metadata) 66 | tileIndex.addTileType(TileType.METADATA) 67 | 68 | var data = await tileIndex.intersects(polygon, TileType.GEOMETRY, 0, params); 69 | 70 | for(var feature of data.features) { 71 | var geometryProperties = tileIndex.objectIndex.get(feature.properties.id); 72 | feature.properties = geometryProperties; 73 | 74 | if(flags.metadata) { 75 | feature.properties.metadata = tileIndex.metadataIndex.get(feature.properties.id) 76 | } 77 | } 78 | 79 | console.log(chalk.bold.keyword('blue')(' ✏️ Writing ' + data.features.length + ' features to: ' + outFile + '.out.geojson')); 80 | var jsonOut = JSON.stringify(data); 81 | writeFileSync(outFile + '.out.geojson', jsonOut); 82 | 83 | 84 | if(flags['tiles']) { 85 | var tiles = Array.from(tileIndex.tiles.values()); 86 | console.log(chalk.bold.keyword('blue')(' ✏️ Writing ' + tiles.length + ' tile paths to: ' + outFile + '.tiles.txt')); 87 | writeFileSync(outFile + '.tiles.txt', tiles.join('\n')); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_override.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"shstReferenceId":"6dde00521a749fd1e42849d8a426f41e","shstGeometryId":"5d5605a07cad15990516d2ff42fb27ec","shstFromIntersectionId":"fe88c4beb110889bc1ff024d8acabbb9","shstToIntersectionId":"92cb33ccc2b3bfa01e931e4ac88bf9b2","gisReferenceId":"0c4f898cb0aba4dd220843c30eb6a944","gisGeometryId":"d47b280950b3819a6339efb36156894a","gisTotalSegments":1,"gisSegmentIndex":1,"gisFromIntersectionId":"15e3c1a95ec7e6c390ca2cb8912414d7","gisToIntersectionId":"58778d10526b1b48478c190fce74fc71","startSideOfStreet":"right","endSideOfStreet":"left","sideOfStreet":"right","score":0.01,"matchType":"hmm","pp_id":"b6039f07-a0d2-4be6-ae1e-963b52657255","pp_side":"right"},"geometry":{"type":"LineString","coordinates":[[-0.013608889392227319,51.54989988444145],[-0.013782149577639369,51.549835153408274]]}},{"type":"Feature","properties":{"shstReferenceId":"4e4a2d9622c2e7a46128772c0a8e3aff","shstGeometryId":"5d5605a07cad15990516d2ff42fb27ec","shstFromIntersectionId":"92cb33ccc2b3bfa01e931e4ac88bf9b2","shstToIntersectionId":"fe88c4beb110889bc1ff024d8acabbb9","gisReferenceId":"33fdda25781c48afa4717611c355de3a","gisGeometryId":"d47b280950b3819a6339efb36156894a","gisTotalSegments":1,"gisSegmentIndex":1,"gisFromIntersectionId":"58778d10526b1b48478c190fce74fc71","gisToIntersectionId":"15e3c1a95ec7e6c390ca2cb8912414d7","startSideOfStreet":"right","endSideOfStreet":"left","sideOfStreet":"right","score":0.01,"matchType":"hmm","pp_id":"b6039f07-a0d2-4be6-ae1e-963b52657255","pp_side":"right"},"geometry":{"type":"LineString","coordinates":[[-0.013769846642495878,51.54983974987233],[-0.01359658642207254,51.549904480887385]]}},{"type":"Feature","properties":{"shstReferenceId":"9ae35902a98ae5d377a05ff7bb266f1c","shstGeometryId":"0a62b4573f21ae138d3f407f62d97550","shstFromIntersectionId":"6b016bfbf604499ca081e69bac3190d7","shstToIntersectionId":"4553786a78aecaad7b1434a9e34138b0","gisReferenceId":"7f6f4c5fd7e9036519cc27d7fb8f5871","gisGeometryId":"50e0a3db349fdfdda8e876014281a1c3","gisTotalSegments":1,"gisSegmentIndex":1,"gisFromIntersectionId":"97ade9445405c0e5628dbdd1c9c765a0","gisToIntersectionId":"b0a796b35c7be5bdf6000c165956a674","startSideOfStreet":"left","endSideOfStreet":"right","sideOfStreet":"left","score":0.03,"matchType":"hmm","pp_id":"edf69fff-0c0b-4bed-b9b6-aeb061233562","pp_side":"left"},"geometry":{"type":"LineString","coordinates":[[-0.0035669045396667376,51.55121257178458],[-0.0033463000000000004,51.5512687],[-0.003282444255060908,51.551286313158805]]}},{"type":"Feature","properties":{"shstReferenceId":"7049a5d6eac7612903ce63a957ea14c9","shstGeometryId":"0a62b4573f21ae138d3f407f62d97550","shstFromIntersectionId":"4553786a78aecaad7b1434a9e34138b0","shstToIntersectionId":"6b016bfbf604499ca081e69bac3190d7","gisReferenceId":"4c084a8afeca75ba077c008599056628","gisGeometryId":"50e0a3db349fdfdda8e876014281a1c3","gisTotalSegments":1,"gisSegmentIndex":1,"gisFromIntersectionId":"b0a796b35c7be5bdf6000c165956a674","gisToIntersectionId":"97ade9445405c0e5628dbdd1c9c765a0","startSideOfStreet":"left","endSideOfStreet":"right","sideOfStreet":"left","score":0.03,"matchType":"hmm","pp_id":"edf69fff-0c0b-4bed-b9b6-aeb061233562","pp_side":"left"},"geometry":{"type":"LineString","coordinates":[[-0.0032932633416626065,51.55128332896257],[-0.0033463000000000004,51.5512687],[-0.003577858737032213,51.55120978470356]]}}]} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sharedstreets", 3 | "version": "0.15.2", 4 | "description": "SharedStreets, a 'digital commons' for the street", 5 | "main": "build/src/index.js", 6 | "bin": { 7 | "shst": "./bin/run" 8 | }, 9 | "engines": { 10 | "node": ">=10.0.0" 11 | }, 12 | "files": [ 13 | "/oclif.manifest.json", 14 | "/build", 15 | "/bin", 16 | "/src" 17 | ], 18 | "oclif": { 19 | "commands": "./build/src/commands", 20 | "bin": "shst", 21 | "plugins": [ 22 | "@oclif/plugin-help" 23 | ] 24 | }, 25 | "scripts": { 26 | "pretest": "tsc", 27 | "prepack": "rm -rf build && tsc && mkdir -p build/src/proto && cp src/proto/*.js build/src/proto/ && oclif-dev manifest", 28 | "test": "tsc && colortape build/test_core.js", 29 | "test-match": "tsc && colortape build/test_match.js", 30 | "test-graph": "tsc && colortape build/test_graph.js", 31 | "docs": "tsc && documentation readme build/index.js --section=API", 32 | "bench": "tsc && node bench.js" 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/sharedstreets/sharedstreets-js.git" 37 | }, 38 | "keywords": [ 39 | "sharedstreets", 40 | "streets", 41 | "highway", 42 | "reference" 43 | ], 44 | "author": "SharedStreets", 45 | "contributors": [ 46 | "Denis Carriere <@DenisCarriere>", 47 | "Kevin Webb <@kpwebb>", 48 | "Morgan Herlocker <@morganherlocker>" 49 | ], 50 | "license": "MIT", 51 | "bugs": { 52 | "url": "https://github.com/sharedstreets/sharedstreets-js/issues" 53 | }, 54 | "homepage": "https://github.com/sharedstreets/sharedstreets-js#readme", 55 | "devDependencies": { 56 | "@oclif/dev-cli": "^1.22.2", 57 | "@types/benchmark": "*", 58 | "@types/glob": "*", 59 | "@types/node": "*", 60 | "@types/tape": "*", 61 | "benchmark": "^2.1.4", 62 | "colortape": "^0.1.2", 63 | "documentation": "^13.0.0", 64 | "tap": "^14.10.7", 65 | "tape": "*", 66 | "ts-node": "^8.1.0", 67 | "tslint": "*", 68 | "typescript": "^3.9.2" 69 | }, 70 | "dependencies": { 71 | "@mapbox/sphericalmercator": "^1.1.0", 72 | "@oclif/command": "^1.5.13", 73 | "@oclif/config": "^1.12.12", 74 | "@oclif/plugin-help": "^2.1.6", 75 | "@turf/along": "^6.0.1", 76 | "@turf/bbox": "^6.0.1", 77 | "@turf/bbox-polygon": "^6.0.1", 78 | "@turf/bearing": "^6.0.1", 79 | "@turf/buffer": "^5.1.5", 80 | "@turf/envelope": "^5.1.5", 81 | "@turf/helpers": "^6.0.1", 82 | "@turf/invariant": "^6.0.1", 83 | "@turf/length": "^6.0.1", 84 | "@turf/line-offset": "^5.1.5", 85 | "@turf/line-slice-along": "^5.1.5", 86 | "@turf/nearest-point-on-line": "^6.0.2", 87 | "@types/leveldown": "^4.0.0", 88 | "@types/levelup": "^3.1.0", 89 | "@types/osrm": "^5.12.0", 90 | "bignumber.js": "7.x", 91 | "chalk": "^2.4.2", 92 | "cli-progress": "^2.1.1", 93 | "const": "^1.0.0", 94 | "expand-tilde": "^2.0.2", 95 | "jkstra": "^0.0.6", 96 | "leveldown": "^5.0.0", 97 | "levelup": "^4.0.1", 98 | "node-fetch": "^2.3.0", 99 | "osrm": "^5.22.0", 100 | "rbush": "^3.0.0", 101 | "sharedstreets-pbf": "^0.8.0", 102 | "sharedstreets-types": "^1.3.1", 103 | "simple-statistics": "^7.0.2", 104 | "tslib": "^1.9.3", 105 | "uuid-by-string": "^2.1.0", 106 | "xml": "^1.0.1" 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 2019-03-21 - 0.11.0 4 | 5 | - mirgrated/refactored cache library from api 6 | 7 | 8 | ## 2019-03-19 - 0.10.0 9 | - Update test suite 10 | - Update directory stucture: move code to `./src` 11 | - Remove uncessary dependencies 12 | 13 | ## 2018-06-19 - 0.9.3 14 | - Fixed bugs in location reference generation 15 | 16 | ## 2018-04-05 - 0.9.2 17 | - Update default roudning to five decimals 18 | 19 | ## 2018-02-21 - 0.9.1 20 | 21 | - Revert `nodeId` to `number` type. 22 | - Convert `bench.js` to Typescript 23 | 24 | ## 2018-02-20 - 0.9.0 25 | 26 | - `distanceToNextRef` has been changed to calculate distance along geometry instead of `start` to `end` points. 27 | 28 | ## 2018-02-19 - 0.8.2 29 | 30 | - Fix closed loops LineStrings: https://github.com/sharedstreets/sharedstreets-conflator/issues/8 31 | 32 | ## 2018-02-18 - 0.8.0 33 | 34 | - Implement `backReference` & `forwardReference` methods. 35 | - Implement `getStartCoord` & `getEndCoord` methods, easy way to extract coordinates from a GeoJSON LineString. 36 | - Implement `getFormOfWay` method, easy way to extract FormOfWay from a GeoJSON LineString. 37 | - Update `distanceToNextRef` to use `start` & `end` params. 38 | 39 | ## 2018-02-17 40 | 41 | - Add new helper methods `inboundBearing`, `outboundBearing` & `distanceToNextRef`. 42 | 43 | ## 2018-02-16 - 0.7.2 44 | 45 | - Enforce strict TSLint/Typescript configs 46 | 47 | ## 2018-02-15 - 0.7.0 48 | 49 | - Add new method `metadata` 50 | - Update docs for `reference` 51 | - Add new method `reference` 52 | 53 | ## 2018-02-12 54 | 55 | - Add new method `intersection` 56 | 57 | ## 2018-02-11 58 | 59 | - Add new method `geometry` 60 | - Convert tests to Typescript 61 | 62 | ## 2018-02-08 - 0.6.0 63 | 64 | - Make `formOfWay` optional for `referenceId` method. 65 | - Add `coordsToLonlats` method 66 | 67 | ## 2018-02-06 - 0.5.0 68 | 69 | - Enforce strict TSLint `tslint.json` 70 | - Enforce strict=true `tsconfig.json` 71 | https://github.com/sharedstreets/sharedstreets-js/issues/9 72 | 73 | ## 2018-01-30 - 0.4.0 74 | 75 | - Improved readability of testing (expectedId) 76 | - Handle FormOfWay as `undefined` 77 | - Split method names `getFormOfWay` => `getFormOfWayString` & `getFormOfWayNumber` 78 | - Split method names `getFormOfWay` => `getFormOfWayString` & `getFormOfWayNumber` 79 | - Update pbf sample data 80 | 81 | ## 2018-01-29 - 0.3.0 82 | 83 | - Fix `bignumber.js` precision loss issue 84 | - Add `.pbf` test cases (intersection 100%, geometry 100%, reference 0%) 85 | - Replace `latlonsToCoords` => `lonlatsToCoords` 86 | 87 | ## 2018-01-27 - 0.2.0 88 | 89 | - Add messages methods (helps troubleshoot library) 90 | - Add `geometryMessage` 91 | - Add `intersectionMessage` 92 | - Add `referenceMessage` 93 | 94 | ## 2018-01-25 95 | 96 | - Add `referenceId` 97 | - Add `locationReference` 98 | - Clean documentation 99 | 100 | ## 2018-01-23 101 | 102 | - Add `geometryId` 103 | - Add `intersectionId` 104 | 105 | ## 2018-01-11 106 | 107 | - Start implementation of SharedStreets Location Reference 108 | - Start implementation of SharedStreets Reference 109 | 110 | ## 2018-01-10 111 | 112 | - Drop Rollup bundler in favor of Browserify (Config too complex when including Crypto & Typescript) 113 | - Replaced Base58 with Base16 (hex) 114 | 115 | ## 2018-01-09 116 | 117 | - Implement `sharedstreets.geometry` 118 | 119 | ## 2018-01-05 120 | 121 | - Drop PBF & Typescript definition from core library 122 | 123 | ## 2018-01-03 124 | 125 | - Implement Geometry Pbf parser 126 | 127 | ## 2018-01-02 128 | 129 | - Implement Intersection Pbf parser 130 | 131 | ## 2018-01-01 132 | 133 | - Setup boilerplate 134 | - Add PBF test fixtures 135 | - Add initial JSDocs documentation 136 | -------------------------------------------------------------------------------- /test/geojson/points_1b.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"score":3.1799868991648736,"location":103.30809300066495,"referenceLength":133.47,"geometryId":"1d12bdab9cc895cf2ffee2f386a8fd41","referenceId":"4ef86ba3b7aedb47b4e6eb16cd082b8b","direction":"forward","bearing":90.30216185745682,"sideOfStreet":"right","interceptAngle":90.00001082771188},"geometry":{"type":"Point","coordinates":[-77.02842931440927,38.89981241618331]}},{"type":"Feature","properties":{"score":3.1799868991648736,"location":30.161906999335045,"referenceLength":133.47,"geometryId":"1d12bdab9cc895cf2ffee2f386a8fd41","referenceId":"1f621430356ba66efee4b83379a147b1","direction":"backward","bearing":270.30216185745684,"sideOfStreet":"left","interceptAngle":270.0000108277119},"geometry":{"type":"Point","coordinates":[-77.02842931440927,38.89981241618331]}},{"type":"Feature","properties":{"score":29.856311212715827,"location":162.7052754105483,"referenceLength":165.45,"geometryId":"0283824017780cbdf9febed3ec5a2450","referenceId":"0e7016db2d00589705b3af197c80040e","direction":"forward","bearing":359.99410728693755,"sideOfStreet":"left","interceptAngle":270.00101637940037},"geometry":{"type":"Point","coordinates":[-77.02808449641095,38.89978384167077]}},{"type":"Feature","properties":{"score":30.008594618988095,"location":0,"referenceLength":90.87,"geometryId":"32aabe41771a4a94aff41de3102debb8","referenceId":"7edae7bb25899c06ba232d2ae846dc92","direction":"forward","bearing":89.64389549319387,"sideOfStreet":"right","interceptAngle":174.57557159703018},"geometry":{"type":"Point","coordinates":[-77.0280845,38.899811]}},{"type":"Feature","properties":{"score":30.008594618988095,"location":90.87,"referenceLength":90.87,"geometryId":"32aabe41771a4a94aff41de3102debb8","referenceId":"e2ff5fedf35bd749f1db4e301f9a56b5","direction":"backward","bearing":269.6438954931939,"sideOfStreet":"left","interceptAngle":354.57557159703015},"geometry":{"type":"Point","coordinates":[-77.0280845,38.899811]}},{"type":"Feature","properties":{"score":30.008594618988095,"location":0,"referenceLength":96.02,"geometryId":"960144327553e0f7487e8a4adf56e54c","referenceId":"11854de0a281c98d50e48d7745216aa6","direction":"forward","bearing":0,"sideOfStreet":"left","interceptAngle":264.21946709022404},"geometry":{"type":"Point","coordinates":[-77.0280845,38.899811]}},{"type":"Feature","properties":{"score":77.055412546651,"location":78.33969936780356,"referenceLength":146.19,"geometryId":"453c30aa4cd2b9e2a65704ea94ad6798","referenceId":"34139d9c4d72d540f6ccc48d1633d202","direction":"forward","bearing":66.08274158025435,"sideOfStreet":"right","interceptAngle":89.99964467828553},"geometry":{"type":"Point","coordinates":[-77.02879050964661,38.90041728767828]}},{"type":"Feature","properties":{"score":91.98319618346457,"location":72.39665429706584,"referenceLength":145.52,"geometryId":"e373958592276406c70acc59e4d1b171","referenceId":"c3c584c377f0f635235c851688a91f7b","direction":"forward","bearing":246.67620786578303,"sideOfStreet":"left","interceptAngle":269.999633039488},"geometry":{"type":"Point","coordinates":[-77.02885035857211,38.9005434415667]}},{"type":"Feature","properties":{"score":103.33383932646024,"location":165.09727879687583,"referenceLength":167.01,"geometryId":"d047de8f7d38bb0efc54b34494a06aa0","referenceId":"b878500e619bf881c6991ea753f829b5","direction":"forward","bearing":0.8512041084756191,"sideOfStreet":"right","interceptAngle":89.9992466923876},"geometry":{"type":"Point","coordinates":[-77.02962347575352,38.899797617672725]}},{"type":"Feature","properties":{"score":103.33383932646024,"location":1.912721203124164,"referenceLength":167.01,"geometryId":"d047de8f7d38bb0efc54b34494a06aa0","referenceId":"b29c667e5cbfdcda80b7bfe4fcaa8bb1","direction":"backward","bearing":180.85120410847563,"sideOfStreet":"left","interceptAngle":269.9992466923876},"geometry":{"type":"Point","coordinates":[-77.02962347575352,38.899797617672725]}}]} -------------------------------------------------------------------------------- /src/routing.ts: -------------------------------------------------------------------------------- 1 | 2 | import distance from '@turf/distance'; 3 | import * as turfHelpers from '@turf/helpers'; 4 | const jkstra = require('jkstra'); 5 | 6 | class PathStackFrame { 7 | 8 | static defaultOptions = { 9 | direction: jkstra.OUT, 10 | edgeCost: (e, costDone) => 1, 11 | edgeFilter: null // take all edges 12 | } 13 | 14 | graph; 15 | edges; 16 | options; 17 | 18 | edge; 19 | startEdgeCost; 20 | currentEdgeCost; 21 | visitedNodes = []; 22 | 23 | 24 | constructor(graph, start, startCost, visitedNodes, options) { 25 | this.graph = graph; 26 | this.options = options; 27 | this.startEdgeCost = startCost; 28 | this.visitedNodes = JSON.parse(JSON.stringify(visitedNodes)); 29 | 30 | this.edges = this.graph.incidentEdges(start, jkstra.OUT, this.options.edgeFilter); 31 | } 32 | 33 | nextEdge() { 34 | if(this.edges.length > 0) { 35 | this.edge = this.edges.pop(); 36 | this.currentEdgeCost = this.options.edgeCost(this.edge); 37 | this.visitedNodes[this.edge.from.data] = true; 38 | return this.edge; 39 | } 40 | else 41 | return null; 42 | } 43 | 44 | isVisited(node) { 45 | return this.visitedNodes[node.data] == true ? true : false; 46 | } 47 | 48 | getCurrentEdge() { 49 | return this.edge; 50 | } 51 | 52 | getTotalEdgeCost() { 53 | return this.startEdgeCost + this.currentEdgeCost; 54 | } 55 | } 56 | 57 | export class PathSearch { 58 | 59 | graph; 60 | start; 61 | end; 62 | min; 63 | max; 64 | options; 65 | minCost = [] 66 | 67 | constructor(graph) { 68 | this.graph = graph; 69 | } 70 | 71 | findPath(start, end, endCoord:turfHelpers.Coord, min, max, options) { 72 | var results = []; 73 | 74 | var stack = []; 75 | var visitedNodes = {}; 76 | 77 | stack.push(new PathStackFrame(this.graph, start, 0, visitedNodes, options)); 78 | var currentStackFrame = stack[0]; 79 | 80 | while(stack.length > 0) { 81 | var nextEdge; 82 | while(nextEdge = currentStackFrame.nextEdge()) { 83 | 84 | var remainingDistance = distance(nextEdge.data.start, endCoord, {'units':'meters'}) 85 | 86 | if((currentStackFrame.getTotalEdgeCost() - currentStackFrame.currentEdgeCost > 0 ) && currentStackFrame.getTotalEdgeCost() - currentStackFrame.currentEdgeCost + remainingDistance > max) { 87 | continue; 88 | } 89 | else if(nextEdge.to.data == end.data) { 90 | if(currentStackFrame.getTotalEdgeCost() < max && currentStackFrame.getTotalEdgeCost() > min) { 91 | var path = stack.map((e) => {return e.edge.data.id}); 92 | results.push({path:path, length:currentStackFrame.getTotalEdgeCost()}); 93 | } 94 | else 95 | continue; // path found but too short... 96 | } 97 | else { 98 | if(!currentStackFrame.isVisited(nextEdge.to)) { 99 | currentStackFrame = new PathStackFrame(this.graph, nextEdge.to, currentStackFrame.getTotalEdgeCost(), currentStackFrame.visitedNodes, options); 100 | stack.push(currentStackFrame); 101 | } 102 | } 103 | 104 | } 105 | 106 | var lastFrame = stack.pop(); 107 | //minCost[lastFrame.edge.from] = lastFrame.minCost; 108 | currentStackFrame = stack[stack.length-1]; 109 | } 110 | return results; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_bidirectional.2.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"gisReferenceId":"c5d15fb57bcb2097db60a61e81f1cbac","gisGeometryId":"0a587d9c66317646fc27277f1de2ee43","gisSegementIndex":1,"gisFromIntersectionId":"8f1e71b552bd9dfe6df69accdcc31bf3","gisToIntersectionId":"27dbfadf76df59fa266dc04fc34109fc","gisTotalSegments":1,"shstReferenceId":"e383c3b5909cf499fc5a2dcf1e0fa4bb","shstGeometryId":"5e82ace85ab96ba5d396813ac5310c2e","shstFromIntersectionId":"640588783c1a12f0a727f708e28c4b29","shstToIntersectionId":"55731d15c03370627da7cdbe3f19f496","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":0.48,"matchType":"hmm","pp_geo_id":14020386,"pp_lfn_id":3584,"pp_lf_name":"Grenville St","pp_address_l":"9-25","pp_address_r":"14-32","pp_oe_flag_l":"O","pp_oe_flag_r":"E","pp_lonuml":9,"pp_hinum_l":25,"pp_lonumr":14,"pp_hinum_r":32,"pp_fnode":14020388,"pp_tnode":14020387,"pp_one_way_di":0,"pp_dir_code_d":"Not One-Way","pp_fcode":201500,"pp_fcode_desc":"Local","pp_juris_code":"CITY OF TORONTO","pp_objectid":55474},"geometry":{"type":"LineString","coordinates":[[-79.38403130386628,43.66201884970438],[-79.38465657939078,43.661884484852926],[-79.38536930208835,43.661731281909766]]}},{"type":"Feature","properties":{"gisReferenceId":"9719c117131f19d242225aa6b50aed8b","gisGeometryId":"0a587d9c66317646fc27277f1de2ee43","gisSegementIndex":1,"gisFromIntersectionId":"27dbfadf76df59fa266dc04fc34109fc","gisToIntersectionId":"8f1e71b552bd9dfe6df69accdcc31bf3","gisTotalSegments":1,"shstReferenceId":"c4953b8f6cdfcf566f9fcb46285ac8d9","shstGeometryId":"5e82ace85ab96ba5d396813ac5310c2e","shstFromIntersectionId":"55731d15c03370627da7cdbe3f19f496","shstToIntersectionId":"640588783c1a12f0a727f708e28c4b29","startSideOfStreet":"left","endSideOfStreet":"left","sideOfStreet":"left","score":0.48,"matchType":"hmm","pp_geo_id":14020386,"pp_lfn_id":3584,"pp_lf_name":"Grenville St","pp_address_l":"9-25","pp_address_r":"14-32","pp_oe_flag_l":"O","pp_oe_flag_r":"E","pp_lonuml":9,"pp_hinum_l":25,"pp_lonumr":14,"pp_hinum_r":32,"pp_fnode":14020388,"pp_tnode":14020387,"pp_one_way_di":0,"pp_dir_code_d":"Not One-Way","pp_fcode":201500,"pp_fcode_desc":"Local","pp_juris_code":"CITY OF TORONTO","pp_objectid":55474},"geometry":{"type":"LineString","coordinates":[[-79.38535529163168,43.66169749904382],[-79.38464902065702,43.661849315136806],[-79.38401729540573,43.661985065942915]]}},{"type":"Feature","properties":{"gisReferenceId":"35db081d81810cd6dc7102ef6ba82821","gisGeometryId":"c9d662d57d5553a5fdac35c898e74919","gisSegementIndex":1,"gisFromIntersectionId":"ef7ab340f8e1683673f7d8517bbed779","gisToIntersectionId":"8f1e71b552bd9dfe6df69accdcc31bf3","gisTotalSegments":1,"shstReferenceId":"e383c3b5909cf499fc5a2dcf1e0fa4bb","shstGeometryId":"5e82ace85ab96ba5d396813ac5310c2e","shstFromIntersectionId":"640588783c1a12f0a727f708e28c4b29","shstToIntersectionId":"55731d15c03370627da7cdbe3f19f496","startSideOfStreet":"left","endSideOfStreet":"right","sideOfStreet":"unknown","score":0.57,"matchType":"hmm","pp_geo_id":14020385,"pp_lfn_id":3584,"pp_lf_name":"Grenville St","pp_address_l":"1-7","pp_address_r":"10-10","pp_oe_flag_l":"O","pp_oe_flag_r":"E","pp_lonuml":1,"pp_hinum_l":7,"pp_lonumr":10,"pp_hinum_r":10,"pp_fnode":13464794,"pp_tnode":14020388,"pp_one_way_di":0,"pp_dir_code_d":"Not One-Way","pp_fcode":201500,"pp_fcode_desc":"Local","pp_juris_code":"CITY OF TORONTO","pp_objectid":128910},"geometry":{"type":"LineString","coordinates":[[-79.38343565245738,43.662159975421964],[-79.38352369771172,43.662139813618445],[-79.38401738009733,43.66202184172395],[-79.38403130381117,43.662018849716226]]}},{"type":"Feature","properties":{"gisReferenceId":"1c95cae04ed120dfbe6d15ee11b38b69","gisGeometryId":"c9d662d57d5553a5fdac35c898e74919","gisSegementIndex":1,"gisFromIntersectionId":"8f1e71b552bd9dfe6df69accdcc31bf3","gisToIntersectionId":"ef7ab340f8e1683673f7d8517bbed779","gisTotalSegments":1,"shstReferenceId":"c4953b8f6cdfcf566f9fcb46285ac8d9","shstGeometryId":"5e82ace85ab96ba5d396813ac5310c2e","shstFromIntersectionId":"55731d15c03370627da7cdbe3f19f496","shstToIntersectionId":"640588783c1a12f0a727f708e28c4b29","startSideOfStreet":"left","endSideOfStreet":"right","sideOfStreet":"unknown","score":0.57,"matchType":"hmm","pp_geo_id":14020385,"pp_lfn_id":3584,"pp_lf_name":"Grenville St","pp_address_l":"1-7","pp_address_r":"10-10","pp_oe_flag_l":"O","pp_oe_flag_r":"E","pp_lonuml":1,"pp_hinum_l":7,"pp_lonumr":10,"pp_hinum_r":10,"pp_fnode":13464794,"pp_tnode":14020388,"pp_one_way_di":0,"pp_dir_code_d":"Not One-Way","pp_fcode":201500,"pp_fcode_desc":"Local","pp_juris_code":"CITY OF TORONTO","pp_objectid":128910},"geometry":{"type":"LineString","coordinates":[[-79.38401729546085,43.66198506593107],[-79.3840094199029,43.661986758276],[-79.38351550228866,43.66210478638147],[-79.38342120708951,43.66212637937946]]}}]} -------------------------------------------------------------------------------- /test/geojson/sf_centerlines.1a.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4916233,37.7554491],[-122.4915191,37.7539606],[-122.4914928388634,37.75358640359542]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4925664,37.7535356],[-122.4924363371244,37.75167279626022]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49282670000001,37.7572666],[-122.49269683358442,37.755405048367]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4897437,37.759273],[-122.48961323936788,37.75741201460372]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4926966,37.7554017],[-122.49256663595139,37.75353898187404]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48867010000001,37.7593204],[-122.48853993858401,37.75745931143476]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4885397,37.757455900000004],[-122.4884094375583,37.755594095439854]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4893514,37.7536773],[-122.4892208381964,37.75181469820863]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4882786,37.753724600000005],[-122.48849530000001,37.7537151],[-122.4891393,37.7536867],[-122.4893514,37.7536773]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4905535,37.7554962],[-122.4916233,37.7554491]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.489613,37.757408600000005],[-122.489482538533,37.755546804154385]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4916233,37.7554491],[-122.4926966,37.7554017]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4918844,37.7591784],[-122.49175413839217,37.75731740589688]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4894823,37.7555434],[-122.48940110000001,37.754385500000005],[-122.4893785,37.7540631],[-122.48935163969213,37.7536807123135]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4884092,37.7555907],[-122.4894823,37.7555434]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4914926,37.753583],[-122.4925664,37.7535356]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4894823,37.7555434],[-122.4905535,37.7554962]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4925664,37.7535356],[-122.49347560000001,37.7534955],[-122.4936375,37.7534884]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4893514,37.7536773],[-122.4902644,37.7536371],[-122.49042270000001,37.7536301]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49042270000001,37.7536301],[-122.4905765,37.7536233],[-122.49133970000001,37.7535897],[-122.4914926,37.753583]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4874681,37.7575033],[-122.4885397,37.757455900000004]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4906842,37.757361200000005],[-122.4917539,37.757314]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4917539,37.757314],[-122.49282670000001,37.7572666]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4905535,37.7554962],[-122.49042293886774,37.75363350796853]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4885397,37.757455900000004],[-122.489613,37.757408600000005]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49042270000001,37.7536301],[-122.4902922390705,37.751767313654]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49081480000001,37.7592257],[-122.49068443956033,37.75736462014902]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.489613,37.757408600000005],[-122.4906842,37.757361200000005]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4906842,37.757361200000005],[-122.49055373717127,37.7554995843576]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49282670000001,37.7572666],[-122.4938994,37.7572192]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4914926,37.753583],[-122.49146560000001,37.7531972],[-122.49136213911716,37.75172021365601]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4926966,37.7554017],[-122.4937685,37.7553545]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4917539,37.757314],[-122.4916235360043,37.75545247010317]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49295690000001,37.759131100000005],[-122.49282693761201,37.75727000275548]]]}}]} -------------------------------------------------------------------------------- /test/geojson/long-paths.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "bluetooth", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "analysis_id": 1455175 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.39300461099999, 43.640562822 ], [ -79.393344172, 43.641344022 ], [ -79.393399833, 43.641479699 ], [ -79.393425824, 43.641543067 ], [ -79.39351551199999, 43.64176172 ], [ -79.393690503, 43.642171107 ], [ -79.393928862, 43.642738992 ], [ -79.393928862737795, 43.642738993729203 ] ] } }, 7 | { "type": "Feature", "properties": { "analysis_id": 1455369 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.384806858, 43.647662148 ], [ -79.385186699, 43.648281766 ], [ -79.38534882899999, 43.648539452 ], [ -79.385359964, 43.648557142 ], [ -79.385556196, 43.648869021 ], [ -79.38629115, 43.650087001 ], [ -79.38662863099999, 43.650852846 ] ] } }, 8 | { "type": "Feature", "properties": { "analysis_id": 1455195 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.393928862737795, 43.642738993729203 ], [ -79.39411357, 43.64317192 ], [ -79.39429227, 43.643590779 ], [ -79.394487438, 43.644030259 ], [ -79.394575663, 43.644282195 ], [ -79.394624906, 43.644422816 ], [ -79.394772023, 43.644788098 ], [ -79.39478540099999, 43.644821332 ], [ -79.394881799, 43.645060706 ], [ -79.395046842, 43.64547051 ] ] } }, 9 | { "type": "Feature", "properties": { "analysis_id": 1455400 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.388314517, 43.654790183 ], [ -79.388320862, 43.654804105 ], [ -79.388599167, 43.655465528 ], [ -79.388688402, 43.655677598 ], [ -79.388967833, 43.656347946 ], [ -79.388971806, 43.656357474 ], [ -79.389327877, 43.657131936 ], [ -79.389489383, 43.657529566 ], [ -79.389647893, 43.657919864 ], [ -79.389935996, 43.658590747 ], [ -79.38998391, 43.658701215 ], [ -79.39030705899999, 43.659446242 ], [ -79.390494598972296, 43.659858836939101 ] ] } }, 10 | { "type": "Feature", "properties": { "analysis_id": 1454853 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.446219418, 43.63871533 ], [ -79.446190906, 43.638806396 ], [ -79.446186143, 43.638868567 ], [ -79.446189843, 43.638931211 ], [ -79.44622021399999, 43.639036956 ], [ -79.446286404, 43.639200594 ], [ -79.446361263, 43.639385625 ], [ -79.446413934, 43.639515845 ], [ -79.44667921, 43.64019962 ], [ -79.447016163, 43.641076507 ], [ -79.44735314499999, 43.641932508 ], [ -79.447704505, 43.642830252 ], [ -79.448041515, 43.643707102 ], [ -79.44837136699999, 43.64455264 ], [ -79.448712538, 43.645446842 ], [ -79.449059782, 43.646337685 ], [ -79.449393578, 43.647204807 ], [ -79.449726744, 43.648054857 ], [ -79.450027955, 43.648832542 ], [ -79.450070972, 43.648947383 ], [ -79.450350668, 43.649662452 ], [ -79.450659052, 43.650450597 ], [ -79.45106952, 43.651515981 ], [ -79.451211301, 43.651880711 ], [ -79.451303051, 43.652119666 ], [ -79.451388213, 43.65234959 ], [ -79.451443795, 43.652533487 ], [ -79.45177804799999, 43.653557645 ], [ -79.45184606799999, 43.653882472 ] ] } }, 11 | { "type": "Feature", "properties": { "analysis_id": 1455213 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.395046842, 43.64547051 ], [ -79.39525172, 43.645948887 ], [ -79.395365615, 43.646214851 ], [ -79.39557079399999, 43.646740586 ], [ -79.395829745, 43.647397808 ], [ -79.39596965, 43.647807823 ], [ -79.396074544, 43.64803314 ], [ -79.396199716, 43.648331684 ], [ -79.396212309, 43.648361715 ], [ -79.396381621, 43.648765518 ] ] } }, 12 | { "type": "Feature", "properties": { "analysis_id": 1455385 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.38662863099999, 43.650852846 ], [ -79.386996289, 43.651755917 ], [ -79.387245216, 43.65231419 ], [ -79.387551565, 43.653034557 ], [ -79.387582021, 43.653107886 ], [ -79.387938516, 43.653966597 ], [ -79.388314517, 43.654790183 ] ] } }, 13 | { "type": "Feature", "properties": { "analysis_id": 1455275 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.38662863099999, 43.650852846 ], [ -79.38629115, 43.650087001 ], [ -79.385556196, 43.648869021 ], [ -79.385359964, 43.648557142 ], [ -79.38534882899999, 43.648539452 ], [ -79.385186699, 43.648281766 ], [ -79.384806858, 43.647662148 ] ] } }, 14 | { "type": "Feature", "properties": { "analysis_id": 1454832 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.45184606799999, 43.653882472 ], [ -79.45177804799999, 43.653557645 ], [ -79.451443795, 43.652533487 ], [ -79.451388213, 43.65234959 ], [ -79.451303051, 43.652119666 ], [ -79.451211301, 43.651880711 ], [ -79.45106952, 43.651515981 ], [ -79.450659052, 43.650450597 ], [ -79.450350668, 43.649662452 ], [ -79.450070972, 43.648947383 ], [ -79.450027955, 43.648832542 ], [ -79.449726744, 43.648054857 ], [ -79.449393578, 43.647204807 ], [ -79.449059782, 43.646337685 ], [ -79.448712538, 43.645446842 ], [ -79.44837136699999, 43.64455264 ], [ -79.448041515, 43.643707102 ], [ -79.447704505, 43.642830252 ], [ -79.44735314499999, 43.641932508 ], [ -79.447016163, 43.641076507 ], [ -79.44667921, 43.64019962 ], [ -79.446413934, 43.639515845 ], [ -79.446361263, 43.639385625 ], [ -79.446286404, 43.639200594 ], [ -79.44622021399999, 43.639036956 ], [ -79.446189843, 43.638931211 ], [ -79.446186143, 43.638868567 ], [ -79.446190906, 43.638806396 ], [ -79.446219418, 43.63871533 ] ] } }, 15 | { "type": "Feature", "properties": { "analysis_id": 1455158 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.393928862737795, 43.642738993729203 ], [ -79.393928862, 43.642738992 ], [ -79.393690503, 43.642171107 ], [ -79.39351551199999, 43.64176172 ], [ -79.393425824, 43.641543067 ], [ -79.393399833, 43.641479699 ], [ -79.393344172, 43.641344022 ], [ -79.39300461099999, 43.640562822 ] ] } }, 16 | { "type": "Feature", "properties": { "analysis_id": 1455256 }, "geometry": { "type": "LineString", "coordinates": [ [ -79.388314517, 43.654790183 ], [ -79.387938516, 43.653966597 ], [ -79.387582021, 43.653107886 ], [ -79.387551565, 43.653034557 ], [ -79.387245216, 43.65231419 ], [ -79.386996289, 43.651755917 ], [ -79.38662863099999, 43.650852846 ] ] } } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/geojson/line-directed-test.in.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34439,43.67168],[-79.34435,43.67157]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34205,43.67211],[-79.34258,43.67198]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34623,43.67125],[-79.34683,43.67111]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34439,43.67168],[-79.34623,43.67125]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34457,43.67208],[-79.34439,43.67168]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34473,43.67242],[-79.34457,43.67208]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34648,43.67023],[-79.34599,43.67034]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34599,43.67034],[-79.34587,43.67037]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34561,43.6695],[-79.34613,43.66939]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34579,43.66859],[-79.34525,43.66871]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34525,43.66871],[-79.34382,43.66902]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34382,43.66902],[-79.34337,43.66912]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34417,43.66982],[-79.34561,43.6695]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.3437,43.66991],[-79.34417,43.66982]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34363,43.66975],[-79.34302,43.66988]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34302,43.66988],[-79.34363,43.66975]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34302,43.66988],[-79.34183,43.67014]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34183,43.67014],[-79.34302,43.66988]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34183,43.67014],[-79.34125,43.67027]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34125,43.67027],[-79.34183,43.67014]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34402,43.6707],[-79.34352,43.6708],[-79.34295,43.67093]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34295,43.67093],[-79.34352,43.6708],[-79.34402,43.6707]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34295,43.67093],[-79.34241,43.67105]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34241,43.67105],[-79.34295,43.67093]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34241,43.67105],[-79.34171,43.67122]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34171,43.67122],[-79.34241,43.67105]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34384,43.67025],[-79.34402,43.6707]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34402,43.6707],[-79.34384,43.67025]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34363,43.66975],[-79.3437,43.66991]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.3437,43.66991],[-79.34363,43.66975]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.3437,43.66991],[-79.34374,43.67001]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34374,43.67001],[-79.3437,43.66991]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34374,43.67001],[-79.34384,43.67025]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34384,43.67025],[-79.34374,43.67001]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34309,43.66842],[-79.34323,43.66876]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34323,43.66876],[-79.34309,43.66842]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34383,43.6717],[-79.34435,43.67157]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34258,43.67198],[-79.34383,43.6717]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34323,43.66876],[-79.34332,43.66899]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34332,43.66899],[-79.34323,43.66876]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34332,43.66899],[-79.34337,43.66912]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34337,43.66912],[-79.34332,43.66899]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34337,43.66912],[-79.34342,43.66924]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34342,43.66924],[-79.34337,43.66912]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34419,43.67116],[-79.34402,43.6707]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34435,43.67157],[-79.34419,43.67116]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34587,43.67037],[-79.3449,43.67054]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.3449,43.67054],[-79.34459,43.6706],[-79.34402,43.6707]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34342,43.66924],[-79.34351,43.66945]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34351,43.66945],[-79.34342,43.66924]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34351,43.66945],[-79.34363,43.66975]]}},{"type":"Feature","properties":{},"geometry":{"type":"LineString","coordinates":[[-79.34363,43.66975],[-79.34351,43.66945]]}}]} -------------------------------------------------------------------------------- /src/geom.ts: -------------------------------------------------------------------------------- 1 | import destination from '@turf/destination'; 2 | import envelope from '@turf/envelope'; 3 | import * as turfHelpers from '@turf/helpers'; 4 | import bbox from '@turf/bbox'; 5 | 6 | 7 | export function envelopeBufferFromPoint(point, radius):turfHelpers.Feature { 8 | var nwPoint = destination(point, radius, 315, {'units':'meters'}); 9 | var sePoint = destination(point, radius, 135, {'units':'meters'}); 10 | return envelope(turfHelpers.featureCollection([nwPoint, sePoint])); 11 | } 12 | 13 | export function bboxFromPolygon(polygon) { 14 | var bboxCoords = bbox(polygon) 15 | return {"minX": bboxCoords[0], "minY": bboxCoords[1], "maxX":bboxCoords[2], "maxY":bboxCoords[3]} 16 | } 17 | 18 | function cleanProperties(og_props:{}) { 19 | var new_props:{} = {}; 20 | for(var prop of Object.keys(og_props)) { 21 | new_props[prop.toLocaleLowerCase().replace(" ", "_")] = og_props[prop]; 22 | } 23 | return new_props; 24 | } 25 | 26 | export function reverseLineString(line:turfHelpers.Feature):turfHelpers.Feature|turfHelpers.LineString { 27 | var reverseLineFeature:turfHelpers.Feature = JSON.parse(JSON.stringify(line)); 28 | 29 | if(reverseLineFeature.geometry && reverseLineFeature.geometry.coordinates) { 30 | reverseLineFeature.geometry.coordinates.reverse(); 31 | return reverseLineFeature; 32 | } 33 | else { 34 | var reverseLine:turfHelpers.LineString = JSON.parse(JSON.stringify(line)); 35 | reverseLine.coordinates.reverse(); 36 | return reverseLine; 37 | } 38 | } 39 | 40 | export class CleanedPoints { 41 | clean:turfHelpers.Feature[] = []; 42 | invalid:any[] = []; 43 | 44 | constructor(inputData:any) { 45 | try { 46 | var inputFeatures = []; 47 | if(inputData.type === "FeatureCollection") { 48 | inputFeatures = inputFeatures.concat(inputData.features); 49 | } 50 | else if(inputData.type === "Feature") { 51 | 52 | inputFeatures.push(inputData); 53 | } 54 | else if(inputData.type === "GeometryCollection") { 55 | for(var geometry of inputData.geometies) { 56 | inputFeatures.push({type:"Feature", properties: {}, geometry: geometry}); 57 | } 58 | } 59 | else if (inputData.type === "Point") { 60 | inputFeatures.push({type:"Feature", properties: {}, geometry: inputData}); 61 | } 62 | else { 63 | this.invalid.push(inputData); 64 | } 65 | 66 | for(var inputFeature of inputFeatures) { 67 | 68 | // move properties to lowercase 69 | inputFeature.properties = cleanProperties(inputFeature.properties); 70 | 71 | if (inputFeature.geometry.type === "Point"){ 72 | this.clean.push(inputFeature); 73 | } 74 | else { 75 | this.invalid.push(inputFeature); 76 | } 77 | } 78 | } 79 | catch(e) { 80 | throw e; 81 | } 82 | 83 | } 84 | } 85 | 86 | export class CleanedLines { 87 | clean:turfHelpers.Feature[] = []; 88 | invalid:any[]; 89 | 90 | constructor(inputData:any) { 91 | 92 | this.invalid = []; 93 | 94 | try { 95 | var inputFeatures = []; 96 | if(inputData.type === "FeatureCollection") { 97 | inputFeatures = inputFeatures.concat(inputData.features); 98 | } 99 | else if(inputData.type === "Feature") { 100 | inputFeatures.push(inputData); 101 | } 102 | else if(inputData.type === "GeometryCollection") { 103 | for(var geometry of inputData.geometies) { 104 | inputFeatures.push({type:"Feature", properties: {}, geometry: geometry}); 105 | } 106 | } 107 | else if (inputData.type === "LineString" || inputData.type === "MultiLineString") { 108 | inputFeatures.push({type:"Feature", properties: {}, geometry: inputData}); 109 | } 110 | else { 111 | this.invalid.push[inputData]; 112 | } 113 | 114 | for(var inputFeature of inputFeatures) { 115 | 116 | // move properties to lowercase 117 | inputFeature.properties = cleanProperties(inputFeature.properties); 118 | 119 | if (inputFeature.geometry.type === "LineString"){ 120 | if(this.validLength(inputFeature)) 121 | this.clean.push(inputFeature); 122 | else 123 | this.invalid.push(inputFeature); 124 | } 125 | else if (inputFeature.geometry.type === "MultiLineString"){ 126 | // convert multi linestring features to linestrings 127 | if(inputFeature.geometry.coordinates.length == 1){ 128 | 129 | // only contains a single line, just remove one level of array heirachy 130 | inputFeature.geometry.coordinates = inputFeature.geometry.coordinates[0]; 131 | inputFeature.geometry.type = "LineString"; 132 | if(this.validLength(inputFeature)) 133 | this.clean.push(inputFeature); 134 | else 135 | this.invalid.push(inputFeature); 136 | 137 | } 138 | else if(inputFeature.geometry.coordinates.length > 1) { 139 | 140 | // make copy of feature 141 | var newFeature = JSON.parse(JSON.stringify(inputFeature));; 142 | newFeature.geometry.type = "LineString"; 143 | newFeature.geometry.coordinates = []; 144 | 145 | for(var lineStringCoordinates of inputFeature.geometry.coordinates) { 146 | 147 | if(newFeature.geometry.coordinates.length == 0) { 148 | newFeature.geometry.coordinates = lineStringCoordinates; 149 | } 150 | else if(newFeature.geometry.coordinates[newFeature.geometry.coordinates.length-1][0] === 151 | lineStringCoordinates[0][0] && 152 | newFeature.geometry.coordinates[newFeature.geometry.coordinates.length-1][1] === 153 | lineStringCoordinates[0][1]) { 154 | 155 | // continous line feature -- merge 156 | 157 | // remove duplicate end point 158 | newFeature.geometry.coordinates.splice(-1); 159 | newFeature.geometry.coordinates = newFeature.geometry.coordinates.concat(lineStringCoordinates); 160 | } 161 | else { 162 | // disjoint line feature -- save current line and start over with new feature 163 | if(this.validLength(newFeature)) 164 | this.clean.push(newFeature); 165 | else 166 | this.invalid.push(newFeature); 167 | newFeature = JSON.parse(JSON.stringify(inputFeature));; 168 | newFeature.geometry.type = "LineString"; 169 | newFeature.geometry.coordinates = lineStringCoordinates; 170 | } 171 | } 172 | 173 | if(newFeature.geometry.coordinates.length > 0){ 174 | if(this.validLength(newFeature)) 175 | this.clean.push(newFeature); 176 | else 177 | this.invalid.push(newFeature); 178 | } 179 | } 180 | else { 181 | this.invalid.push(inputFeature); 182 | } 183 | } 184 | else { 185 | this.invalid.push(inputFeature); 186 | } 187 | } 188 | } 189 | catch(e) { 190 | throw e; 191 | } 192 | } 193 | 194 | validLength(line:turfHelpers.Feature):boolean { 195 | if(line.geometry.coordinates.length > 1) 196 | return true; 197 | else 198 | return false; 199 | } 200 | } 201 | 202 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "name": "line_side_of_street", 4 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 5 | "features": [ 6 | { "type": "Feature", "properties": { "row_number": 1757, "id": 11556, "objectid": 548324.0, "city": null, "deleted": "FALSE", "bylawno_ol": null, "bylawno": null, "chapter": "950", "schedule": "14", "schedule_n": "No Stopping", "col1c": "Davenport Road", "col2c": "Both", "col3c": "Macpherson Avenue and Kendal Avenue", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2019\/01\/15", "anomalies": null, "symbol_id": "950_14", "legend_id": "950_14", "last_update": "2019\/01\/14" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.404995578301069, 43.677768016099272 ], [ -79.404878413753366, 43.677698050436646 ], [ -79.404843546676389, 43.677677399398824 ], [ -79.404802213088331, 43.677652071281315 ], [ -79.404770608724235, 43.677630855855192 ], [ -79.40474083911549, 43.677610137021702 ], [ -79.404712001567489, 43.6775882307834 ], [ -79.404671009480111, 43.677558780299215 ] ] } }, 7 | { "type": "Feature", "properties": { "row_number": 312, "id": 64, "objectid": 536832.0, "city": null, "deleted": "FALSE", "bylawno_ol": null, "bylawno": null, "chapter": "886", "schedule": "D", "schedule_n": "Designated Lanes for Bicycles", "col1c": "Davenport Road", "col2c": "MacPherson Avenue and Kendal Avenue", "col3c": "Northerly Westbound", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2015\/07\/09", "anomalies": null, "symbol_id": "886_D", "legend_id": "886_D", "last_update": "2015\/07\/08" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.404995578301069, 43.677768016099272 ], [ -79.404878413753366, 43.677698050436646 ], [ -79.404843546676389, 43.677677399398824 ], [ -79.404802213088331, 43.677652071281315 ], [ -79.404770608724235, 43.677630855855192 ], [ -79.40474083911549, 43.677610137021702 ], [ -79.404712001567489, 43.6775882307834 ], [ -79.404671009480111, 43.677558780299215 ] ] } }, 8 | { "type": "Feature", "properties": { "row_number": 313, "id": 65, "objectid": 536833.0, "city": null, "deleted": "FALSE", "bylawno_ol": null, "bylawno": null, "chapter": "886", "schedule": "D", "schedule_n": "Designated Lanes for Bicycles", "col1c": "Davenport Road", "col2c": "MacPherson Avenue and Madison Avenue", "col3c": "Southerly Eastbound", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2015\/07\/09", "anomalies": null, "symbol_id": "886_D", "legend_id": "886_D", "last_update": "2015\/07\/08" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.403784023914568, 43.67667963449874 ], [ -79.40392380453541, 43.676797417309771 ], [ -79.404021115506538, 43.676879879126858 ], [ -79.40429007268331, 43.67710154607515 ], [ -79.404694116768681, 43.677439919162367 ], [ -79.404701745978841, 43.677446550492917 ], [ -79.404731929688381, 43.677471995374162 ], [ -79.404735818767008, 43.677475275095027 ], [ -79.404765930057195, 43.677499477724986 ], [ -79.404770822671594, 43.677503415376869 ], [ -79.404806732945644, 43.677530935311765 ], [ -79.404843537187105, 43.677557834888447 ], [ -79.40488119823236, 43.677584087071253 ], [ -79.404919716081281, 43.677609691859345 ], [ -79.404959065972065, 43.677634622227274 ], [ -79.404999223114345, 43.677658869152339 ], [ -79.405033659324516, 43.677677512490995 ], [ -79.405061814092335, 43.677692073005623 ], [ -79.405076585441591, 43.677699709388648 ], [ -79.405120219662294, 43.677721177751103 ], [ -79.405164524807688, 43.677741899544145 ], [ -79.405209500905158, 43.677761856764299 ], [ -79.405255110775471, 43.677781031377378 ], [ -79.405301329598984, 43.677799432363379 ], [ -79.405318087448208, 43.677805738165745 ], [ -79.405348132598348, 43.677817041698432 ], [ -79.405395494996156, 43.67783384135867 ], [ -79.405690571893558, 43.677918544545967 ] ] } }, 9 | { "type": "Feature", "properties": { "row_number": 1750, "id": 11556, "objectid": 548324.0, "city": null, "deleted": "FALSE", "bylawno_ol": null, "bylawno": null, "chapter": "950", "schedule": "14", "schedule_n": "No Stopping", "col1c": "Davenport Road", "col2c": "Both", "col3c": "Macpherson Avenue and Kendal Avenue", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2019\/01\/15", "anomalies": null, "symbol_id": "950_14", "legend_id": "950_14", "last_update": "2019\/01\/14" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.403673574447751, 43.6767246470895 ], [ -79.403864399907675, 43.676882312812019 ], [ -79.404169777392497, 43.677137630591346 ], [ -79.404305238791096, 43.677250827634616 ], [ -79.404431334521703, 43.677357841781252 ], [ -79.404584052234412, 43.67748418583831 ], [ -79.404636276683291, 43.67753174737534 ] ] } }, 10 | { "type": "Feature", "properties": { "row_number": 309, "id": 64, "objectid": 536832.0, "city": null, "deleted": "FALSE", "bylawno_ol": null, "bylawno": null, "chapter": "886", "schedule": "D", "schedule_n": "Designated Lanes for Bicycles", "col1c": "Davenport Road", "col2c": "MacPherson Avenue and Kendal Avenue", "col3c": "Northerly Westbound", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2015\/07\/09", "anomalies": null, "symbol_id": "886_D", "legend_id": "886_D", "last_update": "2015\/07\/08" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.403673574447751, 43.6767246470895 ], [ -79.403864399907675, 43.676882312812019 ], [ -79.404169777392497, 43.677137630591346 ], [ -79.404305238791096, 43.677250827634616 ], [ -79.404431334521703, 43.677357841781252 ], [ -79.404584052234412, 43.67748418583831 ], [ -79.404636276683291, 43.67753174737534 ] ] } }, 11 | { "type": "Feature", "properties": { "row_number": 1749, "id": 11556, "objectid": 548324.0, "city": null, "deleted": "FALSE", "bylawno_ol": null, "bylawno": null, "chapter": "950", "schedule": "14", "schedule_n": "No Stopping", "col1c": "Davenport Road", "col2c": "Both", "col3c": "Macpherson Avenue and Kendal Avenue", "col4c": "Anytime", "col5c": null, "col6c": null, "gis": "2019\/01\/15", "anomalies": null, "symbol_id": "950_14", "legend_id": "950_14", "last_update": "2019\/01\/14" }, "geometry": { "type": "LineString", "coordinates": [ [ -79.403784023914568, 43.67667963449874 ], [ -79.40392380453541, 43.676797417309771 ], [ -79.404021115506538, 43.676879879126858 ], [ -79.40429007268331, 43.67710154607515 ], [ -79.404694116768681, 43.677439919162367 ], [ -79.404701745978841, 43.677446550492917 ], [ -79.404731929688381, 43.677471995374162 ], [ -79.404735818767008, 43.677475275095027 ], [ -79.404765930057195, 43.677499477724986 ], [ -79.404770822671594, 43.677503415376869 ], [ -79.404806732945644, 43.677530935311765 ], [ -79.404843537187105, 43.677557834888447 ], [ -79.40488119823236, 43.677584087071253 ], [ -79.404919716081281, 43.677609691859345 ], [ -79.404959065972065, 43.677634622227274 ], [ -79.404999223114345, 43.677658869152339 ], [ -79.405033659324516, 43.677677512490995 ], [ -79.405061814092335, 43.677692073005623 ], [ -79.405076585441591, 43.677699709388648 ], [ -79.405120219662294, 43.677721177751103 ], [ -79.405164524807688, 43.677741899544145 ], [ -79.405209500905158, 43.677761856764299 ], [ -79.405255110775471, 43.677781031377378 ], [ -79.405301329598984, 43.677799432363379 ], [ -79.405318087448208, 43.677805738165745 ], [ -79.405348132598348, 43.677817041698432 ], [ -79.405395494996156, 43.67783384135867 ], [ -79.405690571893558, 43.677918544545967 ] ] } } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/geojson/sf_centerlines.1b.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49162301305874,37.75544500111797],[-122.4915191,37.7539606],[-122.4914928388634,37.75358640359542]],[[-122.4914926,37.753583],[-122.49149229038538,37.75357857597396]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49256609225175,37.753531192438],[-122.4924363371244,37.75167279626022]],[[-122.4924361,37.751669400000004],[-122.49243552864982,37.75166121305171]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49282607861717,37.757257693099646],[-122.49269683358442,37.755405048367]],[[-122.4926966,37.7554017],[-122.49269630995995,37.755397543086744]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48974306355763,37.75926392155024],[-122.48961323936788,37.75741201460372]],[[-122.489613,37.757408600000005],[-122.4896123613131,37.75739948562444]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49269630995995,37.755397543086744],[-122.49256663595139,37.75353898187404]],[[-122.4925664,37.7535356],[-122.49256609225175,37.753531192438]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48866946416139,37.759311308809],[-122.48853993858401,37.75745931143476]],[[-122.4885397,37.757455900000004],[-122.48853906488966,37.757446822773694]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48853906488966,37.757446822773694],[-122.4884094375583,37.755594095439854]],[[-122.4884092,37.7555907],[-122.48840894486236,37.755587055625924]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48935109645107,37.75367296966352],[-122.4892208381964,37.75181469820863]],[[-122.48922060000001,37.7518113],[-122.48922020620944,37.751805679264066]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48826438575266,37.75372522647144],[-122.4882786,37.753724600000005]],[[-122.4882786,37.753724600000005],[-122.48849530000001,37.7537151],[-122.4891393,37.7536867],[-122.48932401802277,37.75367851355561]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49053461892238,37.75549703203635],[-122.4905535,37.7554962]],[[-122.4905535,37.7554962],[-122.49160512633392,37.755449900211794]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4896123613131,37.75739948562444],[-122.489482538533,37.755546804154385]],[[-122.4894823,37.7555434],[-122.48948203621853,37.755539638573985]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49160512633392,37.755449900211794],[-122.4916233,37.7554491]],[[-122.4916233,37.7554491],[-122.49267567859486,37.755402624042716]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49188377057955,37.75916940795395],[-122.49175413839217,37.75731740589688]],[[-122.4917539,37.757314],[-122.49175327200457,37.75730503277754]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48948203621853,37.755539638573985],[-122.48940110000001,37.754385500000005],[-122.4893785,37.7540631],[-122.48935163969213,37.7536807123135]],[[-122.4893514,37.7536773],[-122.48935109645107,37.75367296966352]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4883860712396,37.75559172109267],[-122.4884092,37.7555907]],[[-122.4884092,37.7555907],[-122.48945900567229,37.75554442686912]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49147547639677,37.753583750357436],[-122.4914926,37.753583]],[[-122.4914926,37.753583],[-122.49254817976863,37.75353640436464]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48945900567229,37.75554442686912],[-122.4894823,37.7555434]],[[-122.4894823,37.7555434],[-122.49053461892238,37.75549703203635]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49254817976863,37.75353640436464],[-122.4925664,37.7535356]],[[-122.4925664,37.7535356],[-122.49347560000001,37.7534955],[-122.4936188535625,37.75348921773653]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48932401802277,37.75367851355561],[-122.4893514,37.7536773]],[[-122.4893514,37.7536773],[-122.4902644,37.7536371],[-122.49040092196827,37.75363106303362]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49040092196827,37.75363106303362],[-122.49042270000001,37.7536301]],[[-122.49042270000001,37.7536301],[-122.4905765,37.7536233],[-122.49133970000001,37.7535897],[-122.49147547639677,37.753583750357436]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4874497554696,37.75750411139337],[-122.4874681,37.7575033]],[[-122.4874681,37.7575033],[-122.48851787630073,37.7574568654234]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49066449572473,37.757362071991224],[-122.4906842,37.757361200000005]],[[-122.4906842,37.757361200000005],[-122.49173742178876,37.75731472716696]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49173742178876,37.75731472716696],[-122.4917539,37.757314]],[[-122.4917539,37.757314],[-122.49280660634267,37.75726748789693]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4905532253105,37.75549228115316],[-122.49042293886774,37.75363350796853]],[[-122.49042270000001,37.7536301],[-122.49042239433098,37.75362573561435]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48851787630073,37.7574568654234],[-122.4885397,37.757455900000004]],[[-122.4885397,37.757455900000004],[-122.4895919715706,37.75740952681047]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49042239433098,37.75362573561435],[-122.4902922390705,37.751767313654]],[[-122.49029200000001,37.7517639],[-122.49029155504994,37.75175754904342]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.490814165175,37.75921663720125],[-122.49068443956033,37.75736462014902]],[[-122.4906842,37.757361200000005],[-122.49068357141842,37.75735223079391]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4895919715706,37.75740952681047],[-122.489613,37.757408600000005]],[[-122.489613,37.757408600000005],[-122.49066449572473,37.757362071991224]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49068357141842,37.75735223079391],[-122.49055373717127,37.7554995843576]],[[-122.4905535,37.7554962],[-122.4905532253105,37.75549228115316]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49280660634267,37.75726748789693],[-122.49282670000001,37.7572666]],[[-122.49282670000001,37.7572666],[-122.4938780679552,37.757220142706444]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49149229038538,37.75357857597396],[-122.49146560000001,37.7531972],[-122.49136213911716,37.75172021365601]],[[-122.4913619,37.751716800000004],[-122.49136138741613,37.751709477669415]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49267567859486,37.755402624042716],[-122.4926966,37.7554017]],[[-122.4926966,37.7554017],[-122.4937510756434,37.75535526734149]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49175327200457,37.75730503277754],[-122.4916235360043,37.75545247010317]],[[-122.4916233,37.7554491],[-122.49162301305874,37.75544500111797]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49295626768316,37.75912204527563],[-122.49282693761201,37.75727000275548]],[[-122.49282670000001,37.7572666],[-122.49282607861717,37.757257693099646]]]}}]} -------------------------------------------------------------------------------- /test/geojson/sf_centerlines.sample.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49162301305874,37.75544500111797],[-122.4915191,37.7539606],[-122.4914928388634,37.75358640359542]],[[-122.4914926,37.753583],[-122.49149229038538,37.75357857597396]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49256609225175,37.753531192438],[-122.4924363371244,37.75167279626022]],[[-122.4924361,37.751669400000004],[-122.49243552864982,37.75166121305171]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49282607861717,37.757257693099646],[-122.49269683358442,37.755405048367]],[[-122.4926966,37.7554017],[-122.49269630995995,37.755397543086744]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48974306355763,37.75926392155024],[-122.48961323936788,37.75741201460372]],[[-122.489613,37.757408600000005],[-122.4896123613131,37.75739948562444]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49269630995995,37.755397543086744],[-122.49256663595139,37.75353898187404]],[[-122.4925664,37.7535356],[-122.49256609225175,37.753531192438]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48866946416139,37.759311308809],[-122.48853993858401,37.75745931143476]],[[-122.4885397,37.757455900000004],[-122.48853906488966,37.757446822773694]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48853906488966,37.757446822773694],[-122.4884094375583,37.755594095439854]],[[-122.4884092,37.7555907],[-122.48840894486236,37.755587055625924]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48935109645107,37.75367296966352],[-122.4892208381964,37.75181469820863]],[[-122.48922060000001,37.7518113],[-122.48922020620944,37.751805679264066]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48826438575266,37.75372522647144],[-122.4882786,37.753724600000005]],[[-122.4882786,37.753724600000005],[-122.48849530000001,37.7537151],[-122.4891393,37.7536867],[-122.48932401802277,37.75367851355561]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49053461892238,37.75549703203635],[-122.4905535,37.7554962]],[[-122.4905535,37.7554962],[-122.49160512633392,37.755449900211794]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4896123613131,37.75739948562444],[-122.489482538533,37.755546804154385]],[[-122.4894823,37.7555434],[-122.48948203621853,37.755539638573985]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49160512633392,37.755449900211794],[-122.4916233,37.7554491]],[[-122.4916233,37.7554491],[-122.49267567859486,37.755402624042716]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49188377057955,37.75916940795395],[-122.49175413839217,37.75731740589688]],[[-122.4917539,37.757314],[-122.49175327200457,37.75730503277754]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48948203621853,37.755539638573985],[-122.48940110000001,37.754385500000005],[-122.4893785,37.7540631],[-122.48935163969213,37.7536807123135]],[[-122.4893514,37.7536773],[-122.48935109645107,37.75367296966352]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4883860712396,37.75559172109267],[-122.4884092,37.7555907]],[[-122.4884092,37.7555907],[-122.48945900567229,37.75554442686912]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49147547639677,37.753583750357436],[-122.4914926,37.753583]],[[-122.4914926,37.753583],[-122.49254817976863,37.75353640436464]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48945900567229,37.75554442686912],[-122.4894823,37.7555434]],[[-122.4894823,37.7555434],[-122.49053461892238,37.75549703203635]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49254817976863,37.75353640436464],[-122.4925664,37.7535356]],[[-122.4925664,37.7535356],[-122.49347560000001,37.7534955],[-122.4936188535625,37.75348921773653]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48932401802277,37.75367851355561],[-122.4893514,37.7536773]],[[-122.4893514,37.7536773],[-122.4902644,37.7536371],[-122.49040092196827,37.75363106303362]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49040092196827,37.75363106303362],[-122.49042270000001,37.7536301]],[[-122.49042270000001,37.7536301],[-122.4905765,37.7536233],[-122.49133970000001,37.7535897],[-122.49147547639677,37.753583750357436]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4874497554696,37.75750411139337],[-122.4874681,37.7575033]],[[-122.4874681,37.7575033],[-122.48851787630073,37.7574568654234]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49066449572473,37.757362071991224],[-122.4906842,37.757361200000005]],[[-122.4906842,37.757361200000005],[-122.49173742178876,37.75731472716696]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49173742178876,37.75731472716696],[-122.4917539,37.757314]],[[-122.4917539,37.757314],[-122.49280660634267,37.75726748789693]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4905532253105,37.75549228115316],[-122.49042293886774,37.75363350796853]],[[-122.49042270000001,37.7536301],[-122.49042239433098,37.75362573561435]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.48851787630073,37.7574568654234],[-122.4885397,37.757455900000004]],[[-122.4885397,37.757455900000004],[-122.4895919715706,37.75740952681047]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49042239433098,37.75362573561435],[-122.4902922390705,37.751767313654]],[[-122.49029200000001,37.7517639],[-122.49029155504994,37.75175754904342]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.490814165175,37.75921663720125],[-122.49068443956033,37.75736462014902]],[[-122.4906842,37.757361200000005],[-122.49068357141842,37.75735223079391]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.4895919715706,37.75740952681047],[-122.489613,37.757408600000005]],[[-122.489613,37.757408600000005],[-122.49066449572473,37.757362071991224]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49068357141842,37.75735223079391],[-122.49055373717127,37.7554995843576]],[[-122.4905535,37.7554962],[-122.4905532253105,37.75549228115316]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49280660634267,37.75726748789693],[-122.49282670000001,37.7572666]],[[-122.49282670000001,37.7572666],[-122.4938780679552,37.757220142706444]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49149229038538,37.75357857597396],[-122.49146560000001,37.7531972],[-122.49136213911716,37.75172021365601]],[[-122.4913619,37.751716800000004],[-122.49136138741613,37.751709477669415]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49267567859486,37.755402624042716],[-122.4926966,37.7554017]],[[-122.4926966,37.7554017],[-122.4937510756434,37.75535526734149]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49175327200457,37.75730503277754],[-122.4916235360043,37.75545247010317]],[[-122.4916233,37.7554491],[-122.49162301305874,37.75544500111797]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-122.49295626768316,37.75912204527563],[-122.49282693761201,37.75727000275548]],[[-122.49282670000001,37.7572666],[-122.49282607861717,37.757257693099646]]]}}]} -------------------------------------------------------------------------------- /test/geojson/roundabout.1.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "FeatureCollection", 3 | "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } }, 4 | "features": [ 5 | { "type": "Feature", "properties": { "OBJECTID": 4627, "ROAD": "08072", "DIR": "NW", "NAME": "NASHVILLE AVE", "ALT_DIR": null, "ALT_NAME": null, "LABEL": "NW NASHVILLE AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Local", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.031, "OID_COPY": 15215, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Local", "AUTODATE": null, "AUTOWHO": null, "HOME_OWNER": null, "SHAPE_STLe": 148.09164361699999 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324577547979672, 44.062278206520688 ], [ -121.324555336240181, 44.062191096567261 ], [ -121.324473054786139, 44.062094766758982 ], [ -121.324388110069037, 44.062040491658301 ], [ -121.32431772836425, 44.062002562611973 ], [ -121.324251773800782, 44.061967098730094 ] ] } }, 6 | { "type": "Feature", "properties": { "OBJECTID": 1858, "ROAD": "08078", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": null, "ALT_NAME": null, "LABEL": "NW NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "Macadam", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.056, "OID_COPY": 3472, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": null, "AUTOWHO": null, "HOME_OWNER": null, "SHAPE_STLe": 299.51499051100001 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.326251221066684, 44.062398016123126 ], [ -121.325111826738876, 44.062397544056232 ] ] } }, 7 | { "type": "Feature", "properties": { "OBJECTID": 5191, "ROAD": "08078", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": null, "ALT_NAME": null, "LABEL": "NW NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "Macadam", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.011, "OID_COPY": 15216, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": null, "AUTOWHO": null, "HOME_OWNER": null, "SHAPE_STLe": 82.115784231000006 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.325111826738876, 44.062397544056232 ], [ -121.324799451415373, 44.062398676625129 ] ] } }, 8 | { "type": "Feature", "properties": { "OBJECTID": 1284, "ROAD": "08078", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": null, "ALT_NAME": null, "LABEL": "NW NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.055, "OID_COPY": 15217, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": null, "AUTOWHO": null, "HOME_OWNER": null, "SHAPE_STLe": 272.16943692500001 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324395286688315, 44.062398599630235 ], [ -121.323797247798382, 44.062398332377064 ], [ -121.323359923705553, 44.062396775164665 ] ] } }, 9 | { "type": "Feature", "properties": { "OBJECTID": 4313, "ROAD": "08074", "DIR": "NW", "NAME": "UNION ST", "ALT_DIR": null, "ALT_NAME": null, "LABEL": "NW UNION ST", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Local", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.081, "OID_COPY": 3618, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Local", "AUTODATE": "2012\/12\/14", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 431.37401846300003 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.325111826738876, 44.062397544056232 ], [ -121.325243348078686, 44.062278653380787 ], [ -121.326133936256511, 44.061471809435318 ] ] } }, 10 | { "type": "Feature", "properties": { "OBJECTID": 4008, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.021, "OID_COPY": 15208, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 113.814449524 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324395286688315, 44.062398599630235 ], [ -121.32439111453202, 44.062422426161909 ], [ -121.324392309806001, 44.062446426404577 ], [ -121.32439884140986, 44.062469975757459 ], [ -121.324410539365985, 44.062492461353379 ], [ -121.324427099242627, 44.062513298008547 ], [ -121.324448090076103, 44.062531943452058 ], [ -121.324472965586253, 44.062547912438468 ], [ -121.324501078393084, 44.062560789376477 ], [ -121.324531696864781, 44.062570239144847 ], [ -121.324564024158448, 44.06257601581391 ], [ -121.32459721895836, 44.062577969046274 ], [ -121.324630417371466, 44.062576048009092 ], [ -121.324662755410642, 44.06257030269731 ] ] } }, 11 | { "type": "Feature", "properties": { "OBJECTID": 4467, "ROAD": "08307", "DIR": "NW", "NAME": "9TH ST", "ALT_DIR": null, "ALT_NAME": null, "LABEL": "NW 9TH ST", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Collector", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.048, "OID_COPY": 15210, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Collector", "AUTODATE": null, "AUTOWHO": null, "HOME_OWNER": null, "SHAPE_STLe": 261.22723833800001 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324804232466846, 44.063273790435616 ], [ -121.324788423948107, 44.062948968667726 ], [ -121.32472713313777, 44.062813670774773 ], [ -121.324697343604853, 44.062748227510056 ], [ -121.324668558878997, 44.062656187215204 ], [ -121.324662755410642, 44.06257030269731 ] ] } }, 12 | { "type": "Feature", "properties": { "OBJECTID": 7153, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.013, "OID_COPY": 15212, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 69.634831150899998 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324577547979672, 44.062278206520688 ], [ -121.32454495384674, 44.062282414775325 ], [ -121.324513719731272, 44.062290342338692 ], [ -121.324484656197754, 44.062301783480656 ], [ -121.32445851748254, 44.062316441289312 ], [ -121.324435981920658, 44.062333935375953 ], [ -121.324417634342183, 44.062353811746512 ], [ -121.324403950895103, 44.062375554583141 ], [ -121.324395286688315, 44.062398599630235 ] ] } }, 13 | { "type": "Feature", "properties": { "OBJECTID": 5896, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.014, "OID_COPY": 15209, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 78.838816972499998 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324662755410642, 44.06257030269731 ], [ -121.324693408060838, 44.062560876341138 ], [ -121.324721558208282, 44.062548016710643 ], [ -121.324746472429865, 44.062532058851026 ], [ -121.324767501612797, 44.062513418528596 ], [ -121.324784097866612, 44.062492581398175 ], [ -121.324795828797761, 44.062470090349734 ], [ -121.324802388774813, 44.06244653136369 ], [ -121.324803606890796, 44.0624225182438 ], [ -121.324799451415373, 44.062398676625129 ] ] } }, 14 | { "type": "Feature", "properties": { "OBJECTID": 1019, "ROAD": "80161", "DIR": "NW", "NAME": "NEWPORT AVE", "ALT_DIR": "NW", "ALT_NAME": "TRAFFIC CIRCLE #24", "LABEL": "NEWPORT AVE", "OWNER": "City of Bend", "SURFACE": "AC", "CLASS": "City Arterial", "CR_NUM": 0, "ROW_WIDTH": 0, "ESTDATE": null, "ROW_SOURCE": null, "LID": null, "SRD": null, "LENGTH_MI": 0.015, "OID_COPY": 15211, "MAINTENANC": "City of Bend", "ALT_CLASS": "City Arterial", "AUTODATE": "2010\/02\/25", "AUTOWHO": "JOHNA", "HOME_OWNER": null, "SHAPE_STLe": 80.079999732199994 }, "geometry": { "type": "LineString", "coordinates": [ [ -121.324799451415373, 44.062398676625129 ], [ -121.324790553214456, 44.062375127655727 ], [ -121.324776416061738, 44.06235295099362 ], [ -121.324757423324357, 44.062332748010441 ], [ -121.324734090038504, 44.062315066556458 ], [ -121.324707048942557, 44.062300386104354 ], [ -121.324677033318892, 44.062289104747599 ], [ -121.324644857109391, 44.06228152840535 ], [ -121.324611392843792, 44.062277862526905 ], [ -121.324577547979672, 44.062278206520688 ] ] } } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /test/geojson/line-directed-test-snapped.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3444095446276,43.67167559375915],[-79.34438389800209,43.671616059627524]],[[-79.34438370000001,43.6716156],[-79.34436244334816,43.671567142744856]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34205850000001,43.6720958],[-79.34214800000001,43.672078500000005],[-79.34257810000001,43.671995200000005],[-79.34258602250092,43.67199332924065]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34623281064727,43.67125654764029],[-79.3462889,43.671244],[-79.3468174,43.6711273]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34438370000001,43.6716156],[-79.3445437,43.6716344],[-79.34623281064727,43.67125654764029]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3445799904888,43.67207809592418],[-79.34456390000001,43.6720339],[-79.34438389800209,43.671616059627524]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3447076,43.6724286],[-79.3445799904888,43.67207809592418]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34646860000001,43.6702181],[-79.34600920000001,43.670316],[-79.34598319768361,43.670320826562964]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34598319768361,43.670320826562964],[-79.3458729,43.670341300000004],[-79.34585892385606,43.67034447666824]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34560920298513,43.66949816096571],[-79.3456182,43.6694961],[-79.3456664,43.6694839],[-79.3457872,43.6694557],[-79.34612320000001,43.669385000000005]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34580480000001,43.6686137],[-79.3452816,43.6687327],[-79.3452618415049,43.668737198114684]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3452618415049,43.668737198114684],[-79.3438746,43.669053000000005],[-79.34383799902662,43.669061337902875]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34383799902662,43.669061337902875],[-79.3433689,43.6691682]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3441727902617,43.66982692237662],[-79.3441876,43.6698238],[-79.34560920298513,43.66949816096571]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3436853,43.669929700000004],[-79.3441727902617,43.66982692237662]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.343609,43.669760800000006],[-79.3430359,43.6698876],[-79.34301894311605,43.66989144393169]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34302442504635,43.669890201241095],[-79.3430359,43.6698876],[-79.343609,43.669760800000006]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34301894311605,43.66989144393169],[-79.34185760000001,43.670154700000005],[-79.34183256736107,43.670160246998655]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34183806024801,43.670159029826834],[-79.34185760000001,43.670154700000005],[-79.34302442504635,43.669890201241095]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34183256736107,43.670160246998655],[-79.3414018,43.670255700000006],[-79.3413147,43.670275000000004]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3413147,43.670275000000004],[-79.3414018,43.670255700000006],[-79.34183806024801,43.670159029826834]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3439846,43.6707057],[-79.34386930000001,43.670738],[-79.34295927045147,43.670950735190125]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3429647600495,43.67094945192755],[-79.34386930000001,43.670738],[-79.3439846,43.6707057]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34295927045147,43.670950735190125],[-79.34242179377489,43.67107637558565]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34242728339586,43.671075092348794],[-79.3429647600495,43.67094945192755]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34242179377489,43.67107637558565],[-79.34179710000001,43.671222400000005],[-79.34170060000001,43.671245000000006]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34170060000001,43.671245000000006],[-79.34179710000001,43.671222400000005],[-79.34242728339586,43.671075092348794]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34381091188955,43.67025537853698],[-79.34398442486405,43.670705245928254]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3439846,43.6707057],[-79.3438110870229,43.670255832609016]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34360650514004,43.66975464240338],[-79.3436088750617,43.66976049163818]],[[-79.343609,43.669760800000006],[-79.3436334,43.669814900000006],[-79.34367864304627,43.66991497520337]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34367867425001,43.669915044224226],[-79.3436334,43.669814900000006],[-79.34360903113667,43.66976086903669]],[[-79.343609,43.669760800000006],[-79.34360663007831,43.66975495076523]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34367864304627,43.66991497520337],[-79.34368526879626,43.669929630979134]],[[-79.3436853,43.669929700000004],[-79.34371780773382,43.67001398434176]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34371798286575,43.67001443841393],[-79.34368547513144,43.66993015407221]],[[-79.3436853,43.669929700000004],[-79.34367867425001,43.669915044224226]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3436853,43.669929700000004],[-79.34381091188955,43.67025537853698]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3438110870229,43.670255832609016],[-79.34368547513144,43.66993015407221]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3430735,43.6684495],[-79.34321342390213,43.668763482875505]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34321357780017,43.66876382821379],[-79.34307365389643,43.66844984533847]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34382990700226,43.671699762723804],[-79.34426020000001,43.671610900000005],[-79.34438370000001,43.6716156]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34258602250092,43.67199332924065],[-79.3438244,43.671700900000005],[-79.34382990700226,43.671699762723804]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34321342390213,43.668763482875505],[-79.34321410000001,43.668765],[-79.3432822,43.668942400000006],[-79.34330174063656,43.66899329145433]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34330187487296,43.66899364105806],[-79.3432822,43.668942400000006],[-79.34321410000001,43.668765],[-79.34321357780017,43.66876382821379]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34330174063656,43.66899329145433],[-79.34335166251118,43.66912330711887]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34335179674815,43.66912365672254],[-79.34330187487296,43.66899364105806]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34335166251118,43.66912330711887],[-79.34336876576282,43.66916785039635]],[[-79.3433689,43.6691682],[-79.34339960544769,43.66924398597129]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34339973038385,43.66924429433335],[-79.34336902493582,43.66916850836208]],[[-79.3433689,43.6691682],[-79.34335179674815,43.66912365672254]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3441931,43.671181100000005],[-79.34398470638287,43.67070594256515]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34438370000001,43.6716156],[-79.34419320509332,43.671181339577096]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34585892385606,43.67034447666824],[-79.34490887104258,43.67056041146413]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34490887104258,43.67056041146413],[-79.3441275,43.670738],[-79.3439846,43.6707057]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3433689,43.6691682],[-79.343485078941,43.669454947215065]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34348520387803,43.669455255577034],[-79.34336902493582,43.66916850836208]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.343485078941,43.669454947215065],[-79.3436088750617,43.66976049163818]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.343609,43.669760800000006],[-79.34348520387803,43.669455255577034]]]}}]} -------------------------------------------------------------------------------- /src/tiles.ts: -------------------------------------------------------------------------------- 1 | import * as sharedstreetsPbf from 'sharedstreets-pbf'; 2 | import {SharedStreetsIntersection, SharedStreetsGeometry } from 'sharedstreets-types'; 3 | 4 | 5 | import * as turfHelpers from '@turf/helpers'; 6 | import bbox from "@turf/bbox"; 7 | import destination from '@turf/destination'; 8 | import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'; 9 | 10 | import { getJson, getPbf, resolveHome } from "./util"; 11 | 12 | const chalk = require('chalk'); 13 | const path = require('path'); 14 | 15 | const SphericalMercator = require("@mapbox/sphericalmercator"); 16 | const sphericalMercator = new SphericalMercator({ 17 | size: 256 18 | }); 19 | 20 | const DEFAULT_ZLEVEL = 12; 21 | 22 | const SHST_ID_API_URL = 'https://api.sharedstreets.io/v0.1.0/id/'; 23 | const SHST_TILE_URL = 'https://tiles.sharedstreets.io/'; 24 | 25 | const USE_LOCAL_CACHE = true; 26 | const SHST_TILE_CACHE_DIR = resolveHome('~/.shst/cache/tiles/'); 27 | 28 | export enum TileType { 29 | REFERENCE = 'reference', 30 | INTERSECTION = 'intersection', 31 | GEOMETRY = 'geometry', 32 | METADATA = 'metadata' 33 | } 34 | 35 | export async function getTilesForId(id:string) { 36 | var url = SHST_ID_API_URL + 'shst:' + id; 37 | return getJson(url); 38 | } 39 | 40 | export function getTileIdsForPolygon(polygon:turfHelpers.Feature, buffer:number=0):string[] { 41 | 42 | var polyBound = bbox(polygon) 43 | 44 | var nwPoint = destination([polyBound[0],polyBound[1]], buffer, 315, {'units':'meters'}); 45 | var sePoint = destination([polyBound[2],polyBound[3]], buffer, 135, {'units':'meters'}); 46 | let bounds = [nwPoint.geometry.coordinates[0], nwPoint.geometry.coordinates[1], sePoint.geometry.coordinates[0], sePoint.geometry.coordinates[1]]; 47 | 48 | return getTileIdsForBounds(bounds, false); 49 | 50 | } 51 | 52 | export function getTileIdsForPoint(point:turfHelpers.Feature, buffer:number):string[] { 53 | 54 | if(buffer > 0) { 55 | var nwPoint = destination(point, buffer, 315, {'units':'meters'}); 56 | var sePoint = destination(point, buffer, 135, {'units':'meters'}); 57 | let bounds = [nwPoint.geometry.coordinates[0], nwPoint.geometry.coordinates[1], sePoint.geometry.coordinates[0], sePoint.geometry.coordinates[1]]; 58 | return getTileIdsForBounds(bounds, false); 59 | } 60 | else{ 61 | let bounds = [point.geometry.coordinates[0], point.geometry.coordinates[1], point.geometry.coordinates[0], point.geometry.coordinates[1]]; 62 | return getTileIdsForBounds(bounds, false); 63 | } 64 | } 65 | 66 | export function getTileIdsForBounds(bounds:number[], bufferEdge:boolean):string[] { 67 | 68 | let tileRange = sphericalMercator.xyz(bounds, DEFAULT_ZLEVEL); 69 | let tileIds = []; 70 | 71 | // if buffer extend tile range to +/- 1 72 | let bufferSize = 0; 73 | if(bufferEdge) 74 | bufferSize = 1; 75 | 76 | for(var x = tileRange.minX - bufferSize; x <= tileRange.maxX + bufferSize; x++){ 77 | for(var y = tileRange.minY - bufferSize; y <= tileRange.maxY + bufferSize; y++){ 78 | var tileId = DEFAULT_ZLEVEL + '-' + x + '-' + y; 79 | tileIds.push(tileId); 80 | } 81 | } 82 | 83 | return tileIds; 84 | } 85 | 86 | 87 | export async function getTile(tilePath:TilePath):Promise { 88 | 89 | // TODO use generator/yield pattern + protobuf decodeDelimited 90 | 91 | var arrayBuffer:Uint8Array; 92 | var tileFilePath = path.join(SHST_TILE_CACHE_DIR, tilePath.toPathString()); 93 | if(USE_LOCAL_CACHE && existsSync(tileFilePath)) { 94 | arrayBuffer = new Uint8Array(readFileSync(tileFilePath)); 95 | //console.log(chalk.keyword('lightgreen')(" reading from cached: " + SHST_TILE_CACHE_DIR + tilePath.toPathString())); 96 | } 97 | else { 98 | 99 | try { 100 | arrayBuffer = await getPbf(SHST_TILE_URL + tilePath.toPathString()); 101 | } catch(e) { 102 | return []; 103 | } 104 | 105 | if(USE_LOCAL_CACHE) { 106 | mkdirSync(path.join(SHST_TILE_CACHE_DIR, tilePath.source), { recursive: true }); 107 | writeFileSync(tileFilePath, arrayBuffer); 108 | console.log(chalk.keyword('lightgreen')(" writing to cache: " + tileFilePath)); 109 | } 110 | } 111 | 112 | if(arrayBuffer) { 113 | 114 | if(tilePath.tileType === TileType.GEOMETRY) { 115 | var geometries:any[] = sharedstreetsPbf.geometry(arrayBuffer); 116 | return geometries; 117 | } 118 | else if(tilePath.tileType === TileType.INTERSECTION) { 119 | var intersections:any[] = sharedstreetsPbf.intersection(arrayBuffer); 120 | return intersections; 121 | } 122 | else if(tilePath.tileType === TileType.REFERENCE) { 123 | var references:any[] = sharedstreetsPbf.reference(arrayBuffer); 124 | return references; 125 | } 126 | else if(tilePath.tileType === TileType.METADATA) { 127 | var metadata:any[] = sharedstreetsPbf.metadata(arrayBuffer); 128 | return metadata; 129 | } 130 | } 131 | } 132 | 133 | export function getIdFromTilePath(tilePath:string):string { 134 | var pathParts = tilePath.split("/"); 135 | var fileParts = pathParts[pathParts.length-1].split("."); 136 | var tileId = fileParts[fileParts.length-4]; 137 | return tileId; 138 | } 139 | 140 | 141 | export function getTypeFromTilePath(tilePath:string):TileType { 142 | var parts = tilePath.split("."); 143 | var typeString = parts[parts.length-3].toUpperCase(); 144 | var type:TileType = TileType[typeString] 145 | return type; 146 | } 147 | 148 | 149 | export function getSourceFromTilePath(tilePath:string):string { 150 | var pathParts = tilePath.split('/'); 151 | var tileSource = pathParts[0] + '/' + pathParts[1]; 152 | return tileSource; 153 | } 154 | 155 | 156 | export function getHierarchyFromPath(tilePath:string):number { 157 | var parts = tilePath.split("."); 158 | return parseInt(parts[parts.length-2]) 159 | } 160 | 161 | 162 | export class TilePathParams { 163 | source:string; 164 | tileHierarchy:number; 165 | constructor(params:TilePathParams=null) { 166 | if(params) 167 | this.setParams(params); 168 | } 169 | 170 | setParams(params:TilePathParams) { 171 | this.source = params.source; 172 | this.tileHierarchy = params.tileHierarchy; 173 | } 174 | } 175 | 176 | export class TilePath extends TilePathParams{ 177 | tileId:string; 178 | tileType:TileType; 179 | 180 | 181 | constructor(path:string=null) { 182 | super(); 183 | 184 | if(path) { 185 | this.tileId = getIdFromTilePath(path); 186 | this.tileType = getTypeFromTilePath(path); 187 | this.source = getSourceFromTilePath(path); 188 | this.tileHierarchy = getHierarchyFromPath(path); 189 | } 190 | } 191 | 192 | toPathString():string { 193 | return this.source + '/' + this.tileId + '.' + this.tileType + '.' + this.tileHierarchy + '.pbf' 194 | } 195 | } 196 | 197 | export class TilePathGroup extends TilePathParams { 198 | tileIds:string[]; 199 | tileTypes:TileType[]; 200 | 201 | constructor(paths:TilePath[]=null){ 202 | super(); 203 | this.tileIds = []; 204 | this.tileTypes = []; 205 | 206 | if(paths) { 207 | for(var path of paths) { 208 | this.addPath(path); 209 | } 210 | } 211 | } 212 | 213 | *[Symbol.iterator]() { 214 | 215 | this.tileTypes.sort(); 216 | this.tileIds.sort(); 217 | 218 | for(var tileType of this.tileTypes) { 219 | for(var tileId of this.tileIds) { 220 | var tilePath:TilePath = new TilePath(); 221 | tilePath.setParams(this); 222 | tilePath.tileId = tileId; 223 | tilePath.tileType = tileType; 224 | 225 | yield tilePath; 226 | } 227 | } 228 | } 229 | 230 | addType(tileType:TileType) { 231 | var typeSet:Set = new Set(this.tileTypes); 232 | typeSet.add(tileType); 233 | this.tileTypes = [...typeSet.values()]; 234 | } 235 | 236 | addTileId(tileId:string) { 237 | var idSet:Set = new Set(this.tileIds); 238 | idSet.add(tileId); 239 | this.tileIds = [...idSet.values()]; 240 | } 241 | 242 | addPath(path:TilePath) { 243 | if(this.source != undefined && this.source !== path.source) 244 | throw "Path source does not match group"; 245 | else 246 | this.source = path.source; 247 | 248 | if(this.tileHierarchy != undefined && this.tileHierarchy !== path.tileHierarchy) 249 | throw "Path source does not match group"; 250 | else 251 | this.tileHierarchy = path.tileHierarchy; 252 | 253 | this.addType(path.tileType); 254 | this.addTileId(path.tileId); 255 | } 256 | 257 | static fromPolygon(polygon:turfHelpers.Feature, buffer:number, params:TilePathParams):TilePathGroup { 258 | 259 | var tilePathGroup = new TilePathGroup(); 260 | tilePathGroup.setParams(params); 261 | tilePathGroup.tileIds = getTileIdsForPolygon(polygon); 262 | 263 | return tilePathGroup; 264 | } 265 | 266 | static fromPoint(point:turfHelpers.Feature, buffer:number, params:TilePathParams):TilePathGroup { 267 | 268 | var tilePathGroup = new TilePathGroup(); 269 | tilePathGroup.setParams(params); 270 | tilePathGroup.tileIds = getTileIdsForPoint(point, buffer); 271 | 272 | return tilePathGroup; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street.matched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"gisReferenceId":"6ab1f9e4b68c745cf5075cf490a0c43f","gisGeometryId":"fb477731d91ab9c191f096f81b1fa0a1","gisSegementIndex":1,"gisFromIntersectionId":"aca27b3f62c34c9005de90e082b4f525","gisToIntersectionId":"fd43f1df977e3d96a0fe64e7cdf52da0","gisTotalSegments":1,"shstReferenceId":"5ec00ed941c9e0f1d3df5a64bd085391","shstGeometryId":"b29554c65c583b7b3dc8fae344536d0d","shstFromIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","shstToIntersectionId":"5c5adb9691f0140541db6a1347af9a2e","startSideOfStreet":"left","endSideOfStreet":"left","sideOfStreet":"left","score":4.6,"matchType":"hmm","pp_row_number":1757,"pp_id":11556,"pp_objectid":548324,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"950","pp_schedule":"14","pp_schedule_n":"No Stopping","pp_col1c":"Davenport Road","pp_col2c":"Both","pp_col3c":"Macpherson Avenue and Kendal Avenue","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2019/01/15","pp_anomalies":null,"pp_symbol_id":"950_14","pp_legend_id":"950_14","pp_last_update":"2019/01/14"},"geometry":{"type":"LineString","coordinates":[[-79.40502224819124,43.67776095910699],[-79.4050040970608,43.67775119959175],[-79.40472668200034,43.677601884716545],[-79.40469578888185,43.67757583978819],[-79.40468475571508,43.67756661733978]]}},{"type":"Feature","properties":{"gisReferenceId":"6ab1f9e4b68c745cf5075cf490a0c43f","gisGeometryId":"fb477731d91ab9c191f096f81b1fa0a1","gisSegementIndex":1,"gisFromIntersectionId":"aca27b3f62c34c9005de90e082b4f525","gisToIntersectionId":"fd43f1df977e3d96a0fe64e7cdf52da0","gisTotalSegments":1,"shstReferenceId":"5ec00ed941c9e0f1d3df5a64bd085391","shstGeometryId":"b29554c65c583b7b3dc8fae344536d0d","shstFromIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","shstToIntersectionId":"5c5adb9691f0140541db6a1347af9a2e","startSideOfStreet":"left","endSideOfStreet":"left","sideOfStreet":"left","score":4.6,"matchType":"hmm","pp_row_number":312,"pp_id":64,"pp_objectid":536832,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"886","pp_schedule":"D","pp_schedule_n":"Designated Lanes for Bicycles","pp_col1c":"Davenport Road","pp_col2c":"MacPherson Avenue and Kendal Avenue","pp_col3c":"Northerly Westbound","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2015/07/09","pp_anomalies":null,"pp_symbol_id":"886_D","pp_legend_id":"886_D","pp_last_update":"2015/07/08"},"geometry":{"type":"LineString","coordinates":[[-79.40502224819124,43.67776095910699],[-79.4050040970608,43.67775119959175],[-79.40472668200034,43.677601884716545],[-79.40469578888185,43.67757583978819],[-79.40468475571508,43.67756661733978]]}},{"type":"Feature","properties":{"gisReferenceId":"9fb76a1eec9a67079c313b6fa1eea323","gisGeometryId":"56cc4051fe8f8b6e8ef906947cb04986","gisSegementIndex":1,"gisFromIntersectionId":"9487b3553023770a3a7b1aa1ada90c26","gisToIntersectionId":"c5375f88b460d6f027bc0c048eaa44dd","gisTotalSegments":2,"shstReferenceId":"8b2057ba5f9727f3dad5ace80ac05595","shstGeometryId":"d983386c5aebbee1a6a7d0254bff592b","shstFromIntersectionId":"c3c91930fa0128b9edb72c54c0c0a0f9","shstToIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":3.83,"matchType":"hmm","pp_row_number":313,"pp_id":65,"pp_objectid":536833,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"886","pp_schedule":"D","pp_schedule_n":"Designated Lanes for Bicycles","pp_col1c":"Davenport Road","pp_col2c":"MacPherson Avenue and Madison Avenue","pp_col3c":"Southerly Eastbound","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2015/07/09","pp_anomalies":null,"pp_symbol_id":"886_D","pp_legend_id":"886_D","pp_last_update":"2015/07/08"},"geometry":{"type":"LineString","coordinates":[[-79.40569157529917,43.677894220646294],[-79.40557898672584,43.677875560300244],[-79.4053575428077,43.67782501966985],[-79.40516245612373,43.67773432490755]]}},{"type":"Feature","properties":{"gisReferenceId":"9fb76a1eec9a67079c313b6fa1eea323","gisGeometryId":"56cc4051fe8f8b6e8ef906947cb04986","gisSegementIndex":2,"gisFromIntersectionId":"9487b3553023770a3a7b1aa1ada90c26","gisToIntersectionId":"c5375f88b460d6f027bc0c048eaa44dd","gisTotalSegments":2,"shstReferenceId":"5ec00ed941c9e0f1d3df5a64bd085391","shstGeometryId":"b29554c65c583b7b3dc8fae344536d0d","shstFromIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","shstToIntersectionId":"5c5adb9691f0140541db6a1347af9a2e","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":3.83,"matchType":"hmm","pp_row_number":313,"pp_id":65,"pp_objectid":536833,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"886","pp_schedule":"D","pp_schedule_n":"Designated Lanes for Bicycles","pp_col1c":"Davenport Road","pp_col2c":"MacPherson Avenue and Madison Avenue","pp_col3c":"Southerly Eastbound","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2015/07/09","pp_anomalies":null,"pp_symbol_id":"886_D","pp_legend_id":"886_D","pp_last_update":"2015/07/08"},"geometry":{"type":"LineString","coordinates":[[-79.40516479438023,43.67773549580625],[-79.40504670291797,43.67767200039683],[-79.40477751799968,43.67752711528346],[-79.40475361127785,43.67750696034646],[-79.40376123109313,43.676677429971804]]}},{"type":"Feature","properties":{"gisReferenceId":"84ba57f131c7d456d5ecba9ef9889506","gisGeometryId":"5fa99402a9881a503bc4d4b2a7a1c2f6","gisSegementIndex":1,"gisFromIntersectionId":"6e9e6dccff30ba919caeaa990b84ad98","gisToIntersectionId":"011a26b0afb909d27dc6f5525ef9dca7","gisTotalSegments":1,"shstReferenceId":"5ec00ed941c9e0f1d3df5a64bd085391","shstGeometryId":"b29554c65c583b7b3dc8fae344536d0d","shstFromIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","shstToIntersectionId":"5c5adb9691f0140541db6a1347af9a2e","startSideOfStreet":"left","endSideOfStreet":"left","sideOfStreet":"left","score":3.75,"matchType":"hmm","pp_row_number":1750,"pp_id":11556,"pp_objectid":548324,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"950","pp_schedule":"14","pp_schedule_n":"No Stopping","pp_col1c":"Davenport Road","pp_col2c":"Both","pp_col3c":"Macpherson Avenue and Kendal Avenue","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2019/01/15","pp_anomalies":null,"pp_symbol_id":"950_14","pp_legend_id":"950_14","pp_last_update":"2019/01/14"},"geometry":{"type":"LineString","coordinates":[[-79.40465139133673,43.677538728563526],[-79.40368706238635,43.67673264526863]]}},{"type":"Feature","properties":{"gisReferenceId":"84ba57f131c7d456d5ecba9ef9889506","gisGeometryId":"5fa99402a9881a503bc4d4b2a7a1c2f6","gisSegementIndex":1,"gisFromIntersectionId":"6e9e6dccff30ba919caeaa990b84ad98","gisToIntersectionId":"011a26b0afb909d27dc6f5525ef9dca7","gisTotalSegments":1,"shstReferenceId":"5ec00ed941c9e0f1d3df5a64bd085391","shstGeometryId":"b29554c65c583b7b3dc8fae344536d0d","shstFromIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","shstToIntersectionId":"5c5adb9691f0140541db6a1347af9a2e","startSideOfStreet":"left","endSideOfStreet":"left","sideOfStreet":"left","score":3.75,"matchType":"hmm","pp_row_number":309,"pp_id":64,"pp_objectid":536832,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"886","pp_schedule":"D","pp_schedule_n":"Designated Lanes for Bicycles","pp_col1c":"Davenport Road","pp_col2c":"MacPherson Avenue and Kendal Avenue","pp_col3c":"Northerly Westbound","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2015/07/09","pp_anomalies":null,"pp_symbol_id":"886_D","pp_legend_id":"886_D","pp_last_update":"2015/07/08"},"geometry":{"type":"LineString","coordinates":[[-79.40465139133673,43.677538728563526],[-79.40368706238635,43.67673264526863]]}},{"type":"Feature","properties":{"gisReferenceId":"9fb76a1eec9a67079c313b6fa1eea323","gisGeometryId":"56cc4051fe8f8b6e8ef906947cb04986","gisSegementIndex":1,"gisFromIntersectionId":"9487b3553023770a3a7b1aa1ada90c26","gisToIntersectionId":"c5375f88b460d6f027bc0c048eaa44dd","gisTotalSegments":2,"shstReferenceId":"8b2057ba5f9727f3dad5ace80ac05595","shstGeometryId":"d983386c5aebbee1a6a7d0254bff592b","shstFromIntersectionId":"c3c91930fa0128b9edb72c54c0c0a0f9","shstToIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":3.83,"matchType":"hmm","pp_row_number":1749,"pp_id":11556,"pp_objectid":548324,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"950","pp_schedule":"14","pp_schedule_n":"No Stopping","pp_col1c":"Davenport Road","pp_col2c":"Both","pp_col3c":"Macpherson Avenue and Kendal Avenue","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2019/01/15","pp_anomalies":null,"pp_symbol_id":"950_14","pp_legend_id":"950_14","pp_last_update":"2019/01/14"},"geometry":{"type":"LineString","coordinates":[[-79.40569157529917,43.677894220646294],[-79.40557898672584,43.677875560300244],[-79.4053575428077,43.67782501966985],[-79.40516245612373,43.67773432490755]]}},{"type":"Feature","properties":{"gisReferenceId":"9fb76a1eec9a67079c313b6fa1eea323","gisGeometryId":"56cc4051fe8f8b6e8ef906947cb04986","gisSegementIndex":2,"gisFromIntersectionId":"9487b3553023770a3a7b1aa1ada90c26","gisToIntersectionId":"c5375f88b460d6f027bc0c048eaa44dd","gisTotalSegments":2,"shstReferenceId":"5ec00ed941c9e0f1d3df5a64bd085391","shstGeometryId":"b29554c65c583b7b3dc8fae344536d0d","shstFromIntersectionId":"84aecbcfbf0c8701e354f13d3f0a8336","shstToIntersectionId":"5c5adb9691f0140541db6a1347af9a2e","startSideOfStreet":"right","endSideOfStreet":"right","sideOfStreet":"right","score":3.83,"matchType":"hmm","pp_row_number":1749,"pp_id":11556,"pp_objectid":548324,"pp_city":null,"pp_deleted":"FALSE","pp_bylawno_ol":null,"pp_bylawno":null,"pp_chapter":"950","pp_schedule":"14","pp_schedule_n":"No Stopping","pp_col1c":"Davenport Road","pp_col2c":"Both","pp_col3c":"Macpherson Avenue and Kendal Avenue","pp_col4c":"Anytime","pp_col5c":null,"pp_col6c":null,"pp_gis":"2019/01/15","pp_anomalies":null,"pp_symbol_id":"950_14","pp_legend_id":"950_14","pp_last_update":"2019/01/14"},"geometry":{"type":"LineString","coordinates":[[-79.40516479438023,43.67773549580625],[-79.40504670291797,43.67767200039683],[-79.40477751799968,43.67752711528346],[-79.40475361127785,43.67750696034646],[-79.40376123109313,43.676677429971804]]}}]} -------------------------------------------------------------------------------- /src/compare.ts: -------------------------------------------------------------------------------- 1 | 2 | // class ExtendableSharedStreetsGeometryProperties { 3 | // id:string; 4 | // roadClass:RoadClass; 5 | // metadata:SharedStreetsMetadata; 6 | // forwardReference:SharedStreetsReference; 7 | // backReference:SharedStreetsReference; 8 | // toIntersection:SharedStreetsIntersection; 9 | // fromIntersection:SharedStreetsIntersection; 10 | 11 | // constructor() { 12 | // this.id = null; 13 | // // this.roadClass = null; 14 | // // this.metadata = null; 15 | // // this.forwardReference = null; 16 | // // this.backReference = null; 17 | // // this.toIntersection = null; 18 | // // this.fromIntersection = null; 19 | // } 20 | // } 21 | 22 | // export class ExtendableSharedStreetsGeometry implements Feature { 23 | // type; 24 | // geometry; 25 | // properties:ExtendableSharedStreetsGeometryProperties; 26 | 27 | // constructor(geom:ExtendableSharedStreetsGeometry) { 28 | // this.geometry = null; 29 | // this.properties = new ExtendableSharedStreetsGeometryProperties(); 30 | 31 | // if(geom){ 32 | // this.type = "Feature"; 33 | // this.geometry = geom.geometry; 34 | // this.properties = Object.assign(this.properties, geom.properties); 35 | // } 36 | // } 37 | 38 | // isGeomSame(otherGeom:ExtendableSharedStreetsGeometry):boolean { 39 | // if(this.properties.id !== otherGeom.properties.id) 40 | // return false; 41 | // if(this.properties.forwardReference.id !== otherGeom.properties.forwardReference.id) 42 | // return false; 43 | 44 | // return true; 45 | // } 46 | 47 | // isRefSame(otherGeom:ExtendableSharedStreetsGeometry):boolean { 48 | // // XOR backReference -- coerce undefined 49 | // if(!this.properties.backReference != !otherGeom.properties.backReference) 50 | // return false; 51 | 52 | // if(this.properties.backReference && this.properties.backReference.id !== otherGeom.properties.backReference.id) 53 | // return false; 54 | 55 | // return true; 56 | // } 57 | 58 | // isSame(otherGeom:ExtendableSharedStreetsGeometry):boolean { 59 | // if(!this.isGeomSame(otherGeom)) 60 | // return false; 61 | 62 | // if(!this.isRefSame(otherGeom)) 63 | // return false; 64 | 65 | // return true; 66 | // } 67 | // } 68 | 69 | // export enum MatchType { 70 | // UNMATCHED = "unmatched", 71 | // UNCHANGED = "unchanged", 72 | // MATCHED_REF_CHANGED = "matched_ref_changed", 73 | // MATCHED_GEOM_CHANGED = "matched_geom_changed", 74 | // MATCHED_CANDIDATES = "matched_candidates", 75 | // } 76 | 77 | 78 | // class MatchedSharedStreetsGeometryProperties extends ExtendableSharedStreetsGeometryProperties{ 79 | 80 | // matchType:MatchType; 81 | // matchedGeom:ExtendableSharedStreetsGeometry; 82 | // matchedForwardCandidateSegments:PathSegment[]; 83 | // forwardCandidateScore:number; 84 | // matchedBackCandidateSegments:PathSegment[]; 85 | // backCandidateScore:number; 86 | 87 | // constructor() { 88 | // super() 89 | // } 90 | // } 91 | 92 | // export class MatchedSharedStreetsGeometry extends ExtendableSharedStreetsGeometry { 93 | 94 | // properties:MatchedSharedStreetsGeometryProperties 95 | 96 | // constructor(geom1:ExtendableSharedStreetsGeometry) { 97 | // super(geom1); 98 | // } 99 | // } 100 | 101 | // export class SharedStreetsGeometryWithMetadataCollection { 102 | // data:ExtendableSharedStreetsGeometry[]; 103 | 104 | // intersectionIdIndex = new Map(); 105 | // referencedIdIndex = new Map(); 106 | // geometryIdIndex = new Map(); 107 | 108 | // constructor(data:ExtendableSharedStreetsGeometry[]) { 109 | // this.data = data; 110 | // for(var item of data) { 111 | // this.geometryIdIndex.set(item.properties.id, item); 112 | 113 | // this.intersectionIdIndex.set(item.properties.fromIntersection.id, item.properties.fromIntersection); 114 | // this.intersectionIdIndex.set(item.properties.toIntersection.id, item.properties.toIntersection); 115 | 116 | // this.referencedIdIndex.set(item.properties.forwardReference.id, item.properties.forwardReference); 117 | // if(item.properties.backReference) 118 | // this.referencedIdIndex.set(item.properties.backReference.id, item.properties.backReference); 119 | 120 | // } 121 | // } 122 | 123 | // async getMatch(feature1:ExtendableSharedStreetsGeometry, cache:LocalCache, matchTileBuild:string, matchTileHierarchy:number):Promise { 124 | // var matchedGeom = new MatchedSharedStreetsGeometry(feature1); 125 | // if(this.geometryIdIndex.has(feature1.properties.id)) { 126 | 127 | // var feature2 = this.geometryIdIndex.get(feature1.properties.id); 128 | 129 | // if(feature1.isSame(feature2)) { 130 | // // matched + UNCHANGED 131 | // matchedGeom.properties.matchType = MatchType.UNCHANGED; 132 | // matchedGeom.properties.matchedGeom = feature2; 133 | // return matchedGeom; 134 | // } 135 | 136 | // // method 1: check geomId + ref changes (e.g. 2-way -> 1-way) 137 | // if(feature1.isGeomSame(feature2)) { 138 | // if(!feature1.isRefSame(feature2)) { 139 | // matchedGeom.properties.matchType = MatchType.MATCHED_REF_CHANGED; 140 | // matchedGeom.properties.matchedGeom = feature2; 141 | // return matchedGeom; 142 | // } 143 | // } 144 | 145 | // } 146 | // else { 147 | 148 | // // not an exact match try fallback methods 149 | // // method 2: uses referenceIds 150 | 151 | // var ref1:SharedStreetsReference = this.referencedIdIndex.get(feature1.properties.forwardReference.id); 152 | 153 | // if(ref1) { 154 | // matchedGeom.properties.matchType = MatchType.MATCHED_GEOM_CHANGED; 155 | // matchedGeom.properties.matchedGeom = this.geometryIdIndex.get(ref1.geometryId); 156 | // return matchedGeom; 157 | // } 158 | 159 | // if(feature1.properties.backReference) { 160 | // var ref2:SharedStreetsReference = this.referencedIdIndex.get(feature1.properties.backReference.id); 161 | 162 | // if(ref2) { 163 | // matchedGeom.properties.matchType = MatchType.MATCHED_GEOM_CHANGED; 164 | // matchedGeom.properties.matchedGeom = this.geometryIdIndex.get(ref2.geometryId); 165 | // return matchedGeom; 166 | // } 167 | // } 168 | 169 | 170 | // // method 3: use matcher 171 | 172 | // // TODO allow setting matcher config 173 | // var matcher = new Matcher(cache); 174 | // matcher.bearingTolerance = 45; 175 | // matcher.snapToIntersections = true; 176 | // matcher.tileBuild = matchTileBuild; 177 | // matcher.tileHierarchy = matchTileHierarchy; 178 | // matcher.tileSource = "osm"; 179 | 180 | 181 | // var sortCandidates = (l) => { 182 | 183 | // return l.sort((p1:PathCandidate, p2:PathCandidate) => { 184 | // p1.calcScore(); 185 | // p2.calcScore(); 186 | 187 | // if(p1 && p2 && p1.score > p2.score) { 188 | // return 1; 189 | // } 190 | // else if(p1 && p2 && p1.score < p2.score) { 191 | // return -1; 192 | // } 193 | // else { 194 | // if(p1 && p2 && p1.sideOfStreet == ReferenceSideOfStreet.UNKNOWN && p2.sideOfStreet != ReferenceSideOfStreet.UNKNOWN) 195 | // return 1; 196 | // if(p1 && p2 && p2.sideOfStreet == ReferenceSideOfStreet.UNKNOWN && p1.sideOfStreet != ReferenceSideOfStreet.UNKNOWN) 197 | // return -1; 198 | // else 199 | // return 0; 200 | // } 201 | 202 | // })}; 203 | 204 | // var forwardCandidates = await matcher.getCandidatesForRef(feature1.properties.forwardReference, feature1, null) 205 | // var sortedForwardCandidates = sortCandidates(forwardCandidates); 206 | 207 | 208 | // var sortedBackwardCandidates = []; 209 | // if(feature1.properties.backReference) { 210 | // var backwardCandidates = []; 211 | // backwardCandidates = await matcher.getCandidatesForRef(feature1.properties.backReference, feature1, null); 212 | // sortedBackwardCandidates = sortCandidates(backwardCandidates); 213 | // } 214 | 215 | // matchedGeom.properties.matchedForwardCandidateSegments = []; 216 | // matchedGeom.properties.matchedBackCandidateSegments = []; 217 | 218 | // if(sortedForwardCandidates.length > 0) { 219 | // for(var pathSegment of sortedForwardCandidates[0].segments) { 220 | // matchedGeom.properties.matchedForwardCandidateSegments.push(pathSegment); 221 | // } 222 | // matchedGeom.properties.matchType = MatchType.MATCHED_CANDIDATES; 223 | // matchedGeom.properties.forwardCandidateScore = sortedForwardCandidates[0].calcScore(); 224 | // } 225 | 226 | 227 | // if(sortedBackwardCandidates.length != 0) { 228 | // for(var pathSegment of sortedBackwardCandidates[0].segments) { 229 | // matchedGeom.properties.matchedBackCandidateSegments.push(pathSegment); 230 | // } 231 | // matchedGeom.properties.matchType = MatchType.MATCHED_CANDIDATES; 232 | // matchedGeom.properties.backCandidateScore = sortedBackwardCandidates[0].calcScore(); 233 | // } 234 | 235 | // if(matchedGeom.properties.matchType == MatchType.MATCHED_CANDIDATES) 236 | // return matchedGeom; 237 | 238 | // } 239 | // matchedGeom.properties.matchType = MatchType.UNMATCHED; 240 | // return matchedGeom; 241 | // } 242 | // } -------------------------------------------------------------------------------- /test/geojson/line-directed-test-unsnapped.out.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3444095446276,43.67167559375915],[-79.34438389800209,43.671616059627524]],[[-79.34438370000001,43.6716156],[-79.34436244334816,43.671567142744856]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34204891510545,43.672098061993196],[-79.34205850000001,43.6720958]],[[-79.34205850000001,43.6720958],[-79.34214800000001,43.672078500000005],[-79.34257810000001,43.671995200000005],[-79.34258602250092,43.67199332924065]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34623281064727,43.67125654764029],[-79.3462889,43.671244],[-79.3468174,43.6711273]],[[-79.3468174,43.6711273],[-79.34683582513428,43.67112314349226]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34438370000001,43.6716156],[-79.3445437,43.6716344],[-79.34623281064727,43.67125654764029]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3445799904888,43.67207809592418],[-79.34456390000001,43.6720339],[-79.3444095446276,43.67167559375915]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3447061241125,43.67242454621522],[-79.3445799904888,43.67207809592418]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34647452681355,43.67021677756024],[-79.34646860000001,43.6702181]],[[-79.34646860000001,43.6702181],[-79.34600920000001,43.670316],[-79.34598319768361,43.670320826562964]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34598319768361,43.670320826562964],[-79.3458729,43.670341300000004],[-79.34585892385606,43.67034447666824]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34560920298513,43.66949816096571],[-79.3456182,43.6694961],[-79.3456664,43.6694839],[-79.3457872,43.6694557],[-79.34612320000001,43.669385000000005]],[[-79.34612320000001,43.669385000000005],[-79.34612700427141,43.66938400526977]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34580070811525,43.668614630695885],[-79.3452816,43.6687327],[-79.3452618415049,43.668737198114684]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3452618415049,43.668737198114684],[-79.3438746,43.669053000000005],[-79.34383799902662,43.669061337902875]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34383799902662,43.669061337902875],[-79.34338899386185,43.669163622608046]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3441727902617,43.66982692237662],[-79.3441876,43.6698238],[-79.34560920298513,43.66949816096571]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34370616635732,43.669925300793345],[-79.3441727902617,43.66982692237662]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34360650514004,43.66975464240338],[-79.3436088750617,43.66976049163818]],[[-79.343609,43.669760800000006],[-79.3430359,43.6698876],[-79.34301894311605,43.66989144393169]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34302442504635,43.669890201241095],[-79.3430359,43.6698876],[-79.343609,43.669760800000006]],[[-79.343609,43.669760800000006],[-79.34360663007831,43.66975495076523]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34301894311605,43.66989144393169],[-79.34185760000001,43.670154700000005],[-79.34183256736107,43.670160246998655]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34183806024801,43.670159029826834],[-79.34185760000001,43.670154700000005],[-79.34302442504635,43.669890201241095]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34183256736107,43.670160246998655],[-79.3414018,43.670255700000006],[-79.3413147,43.670275000000004]],[[-79.3413147,43.670275000000004],[-79.34130672131629,43.67025690992466]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34130660002687,43.67025663492497],[-79.3413145787105,43.67027472500033]],[[-79.3413147,43.670275000000004],[-79.3414018,43.670255700000006],[-79.34183806024801,43.670159029826834]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34401460716575,43.67071248260304],[-79.3439846,43.6707057]],[[-79.3439846,43.6707057],[-79.34386930000001,43.670738],[-79.34295927045147,43.670950735190125]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3429647600495,43.67094945192755],[-79.34386930000001,43.670738],[-79.3439846,43.6707057]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34295927045147,43.670950735190125],[-79.34242179377489,43.67107637558565]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34242728339586,43.671075092348794],[-79.3429647600495,43.67094945192755]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34242179377489,43.67107637558565],[-79.34179710000001,43.671222400000005],[-79.3417192371731,43.67124063523966]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34172472583056,43.67123934981469],[-79.34179710000001,43.671222400000005],[-79.34242728339586,43.671075092348794]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34381091188955,43.67025537853698],[-79.34398442486405,43.670705245928254]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34398556457072,43.670707899331966],[-79.34398470638287,43.67070594256515]],[[-79.3439846,43.6707057],[-79.3438110870229,43.670255832609016]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34360650514004,43.66975464240338],[-79.3436088750617,43.66976049163818]],[[-79.343609,43.669760800000006],[-79.3436334,43.669814900000006],[-79.34367864304627,43.66991497520337]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34367867425001,43.669915044224226],[-79.3436334,43.669814900000006],[-79.34360903113667,43.66976086903669]],[[-79.343609,43.669760800000006],[-79.34360663007831,43.66975495076523]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34367864304627,43.66991497520337],[-79.34368526879626,43.669929630979134]],[[-79.3436853,43.669929700000004],[-79.34371780773382,43.67001398434176]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34371798286575,43.67001443841393],[-79.34368547513144,43.66993015407221]],[[-79.3436853,43.669929700000004],[-79.34367867425001,43.669915044224226]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34371780773382,43.67001398434176],[-79.34381091188955,43.67025537853698]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.3438110870229,43.670255832609016],[-79.34371798286575,43.67001443841393]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34306361420644,43.66842521412306],[-79.34307334988232,43.668449131214416]],[[-79.3430735,43.6684495],[-79.34321342390213,43.668763482875505]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34321357780017,43.66876382821379],[-79.34307365389643,43.66844984533847]],[[-79.3430735,43.6684495],[-79.34306376432401,43.668425582908654]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34382990700226,43.671699762723804],[-79.34426020000001,43.671610900000005],[-79.344346785124,43.67161419515639]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34258602250092,43.67199332924065],[-79.3438244,43.671700900000005],[-79.34382990700226,43.671699762723804]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34321342390213,43.668763482875505],[-79.34321410000001,43.668765],[-79.3432822,43.668942400000006],[-79.34330174063656,43.66899329145433]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34330187487296,43.66899364105806],[-79.3432822,43.668942400000006],[-79.34321410000001,43.668765],[-79.34321357780017,43.66876382821379]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34330174063656,43.66899329145433],[-79.34335166251118,43.66912330711887]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34335179674815,43.66912365672254],[-79.34330187487296,43.66899364105806]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34335166251118,43.66912330711887],[-79.34336876576282,43.66916785039635]],[[-79.3433689,43.6691682],[-79.34339960544769,43.66924398597129]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34339973038385,43.66924429433335],[-79.34336902493582,43.66916850836208]],[[-79.3433689,43.6691682],[-79.34335179674815,43.66912365672254]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34418440779798,43.671161281103636],[-79.34398556457072,43.670707899331966]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34436244334816,43.671567142744856],[-79.34419320509332,43.671181339577096]],[[-79.3441931,43.671181100000005],[-79.34418440779798,43.671161281103636]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34585892385606,43.67034447666824],[-79.34490887104258,43.67056041146413]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34490887104258,43.67056041146413],[-79.3441275,43.670738],[-79.34401460716575,43.67071248260304]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34339960544769,43.66924398597129],[-79.343485078941,43.669454947215065]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34348520387803,43.669455255577034],[-79.34339973038385,43.66924429433335]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.343485078941,43.669454947215065],[-79.34360650514004,43.66975464240338]]]}},{"type":"Feature","properties":{},"geometry":{"type":"MultiLineString","coordinates":[[[-79.34360663007831,43.66975495076523],[-79.34348520387803,43.669455255577034]]]}}]} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SharedStreets (Node.js & JavaScript) 2 | 3 | [![npm version](https://badge.fury.io/js/sharedstreets.svg)](https://badge.fury.io/js/sharedstreets) 4 | [![CircleCI](https://circleci.com/gh/sharedstreets/sharedstreets-js.svg?style=svg)](https://circleci.com/gh/sharedstreets/sharedstreets-js) 5 | 6 | Node.js & JavaScript implementation of [SharedStreets Reference System](https://github.com/sharedstreets/sharedstreets-ref-system). 7 | 8 | # Command Line Interface (CLI) 9 | ![SharedStreets CLI Matcher](docs/cli_matcher.png) 10 | 11 | ## Description 12 | 13 | The CLI is the primary tool for users to match GIS data to SharedStreets. The CLI is installed and run locally. Compared to the hosted API, the CLI can process much larger datasets and runs more quickly. 14 | 15 | The CLI is currently runs on macOS and Linux. It does not (yet) support Windows. 16 | 17 | Usage: 18 | ```sh 19 | shst [options ... ] 20 | ``` 21 | 22 | For detailed examples of how to use this tool, see the [SharedStreets blog](http://www.medium.com/sharedstreets). 23 | 24 | ## Install 25 | 26 | The CLI requires Node v10+ on MacOS or Linux. Windows is not currently supported. On supported platforms it can be installed using either `npm` or `yarn`, or follow using Docker for unsupported environments. 27 | 28 | 29 | #### NPM 30 | To install using `npm`: 31 | 32 | ```sh 33 | npm install -g sharedstreets 34 | ``` 35 | 36 | #### Yarn 37 | To install using `yarn`: 38 | ```sh 39 | yarn global add sharedstreets 40 | ``` 41 | This will install the CLI as `shst`. 42 | 43 | #### Docker 44 | To install using Docker create the following Dockerfile: 45 | 46 | ``` 47 | FROM node:11 48 | 49 | ENV NPM_CONFIG_PREFIX=/home/node/.npm-global 50 | ENV PATH=$PATH:/home/node/.npm-global/bin 51 | 52 | USER node 53 | RUN npm install -g sharedstreets 54 | ``` 55 | 56 | Docker image build: 57 | ``` 58 | docker build --tag shst-image . 59 | ``` 60 | 61 | Run match on local data file: 62 | ``` 63 | docker run -it -v [/path/to/data/on/host/]:/data/ --rm shst-image shst match /data/[input_file.geojson] --out=/data/output.geojson 64 | ``` 65 | 66 | 67 | ## Available commands 68 | 69 | Available commands include: 70 | - **extract**: extracts SharedStreets streets using polygon boundary and returns GeoJSON output of all intersecting features (this includes features that are in but not fully contained by the polygon) 71 | - **help**: displays help for `shst` 72 | - **match**: matches point and line features to SharedStreets references 73 | 74 | ### extract 75 | Extracts SharedStreets streets using polygon boundary and returns GeoJSON routput of all intersecting features from the SharedStreets reference tile set. 76 | 77 | The input polygon(s) must be in GeoJSON format, using the WGS84 datum (EPSG:4326) in decimal degrees. "Extracts" includes features that are inside but not fully contained by the polygon. 78 | 79 | Output is given as a GeoJSON file: 80 | - `[output filename].geojson`: contains features that intersected with the input polygon, including the SharedStreets ReferenceID 81 | 82 | #### Usage: 83 | ```sh 84 | shst extract [options] 85 | ``` 86 | 87 | ### help 88 | Displays help for `shst` 89 | 90 | #### Usage: 91 | ```sh 92 | shst help 93 | ``` 94 | 95 | #### Options: 96 | The following options may be appended to the command: 97 | - **-h, --help**: displays help for the `intersects` command 98 | - **-o, --out=[filename.geojson]**: names the output file and allows user to place it in a different location 99 | - **-s, --stats**: not sure 100 | 101 | #### Example: 102 | ```sh 103 | shst extract ~/Desktop/project/city_boundary.geojson --out=streets_in_city.geojson 104 | ``` 105 | 106 | 107 | ### match 108 | Matches point and line features, from GIS, to SharedStreets references. This can be used to match city centerlines, parking meters, and other street features. Input data must be in GeoJSON format, using the WGS84 datum (EPSG:4326) in decimal degrees. Any standard GIS program (e.g. ArcGIS, QGIS) can convert from more common GIS file formats, like shapefile and geodatabase, into GeoJSON. 109 | 110 | Output is given as up to three GeoJSON files: 111 | - `[output filename].matched.geojson`: contains features that matched, including the SharedStreets IDs and properties, as well as the original properties of the matched features (by default) 112 | - `[output filename].unmatched.geojson`: contains any unmatched features, if applicable 113 | - `[output filename].invalid.geojson`: contains any invalid input data, if applicable 114 | 115 | #### Usage: 116 | ```sh 117 | shst match [options] 118 | ``` 119 | 120 | #### Options: 121 | 122 | Additional options allow users to change the modality for matching, incorporate a city's directionality information to support better matching, snap or offset results, cluster points along a street section, etc. 123 | 124 | The following options may be appended to the command: 125 | - **-h, --help**: displays help for the `intersects` command 126 | - **-o, --out=[filename.geojson]**: names the output file and allows user to place it in a different location 127 | - **-p, --skip-port-properties**: do not port input feature properties into the matched output (these are output fields normally preceeded by "pp_") 128 | - **--bearing-field=[bearing field]**: [default: bearing] name of optional point property containing bearing in decimal degrees. If converting from GIS, this is the name of the appropriate field in the attribute table. Example: `--bearing-field=degrees`. 129 | - **--best-direction**: only match one direction, based on best score 130 | - **--buffer-points**: used to snap points to the street and buffer them by a specified length (in meters), turning them into a street segment. For example, this could be used to transform a parking meter point into a parking space with a set length. This optional flag indicates that the points should be buffered, and the following flags provide additional information about how length is determined. 131 | - **--buffer-points-length=** [default: 5] the length (as in diameter) of the buffered point (in meters). This flag is used when all points in the dataset should be given the same length. 132 | - **--buffer-points-length-field=**: [default: length] name of property containing buffered points (in meters). This flag is used when points in the dataset should be given different, specified lengths. For example, this could be used for transforming single-space and multi-space parking meter points into parking spaces of different lengths. 133 | - **--buffer-merge**: When point data is buffered and transformed into street segments, there can be overlap between resulting segments. For example, a group of parking meter points may be buffered into parking spaces with given length. Overlap between resulting features may be undesirable. This flag will dissolve buffered length results that have the same specified attributes, producing one street length instead of many overlapping lengths. Requires related buffer-merge-match-fields to be defined 134 | - **--buffer-merge-match-fields=** a comma-seperated list of fields to *match* values when merging buffered points 135 | - **--buffer-merge-group-fields=** a comma-separated list of fields to *group* values when merging buffered points 136 | - **--center-of-street-value=** [default: center] value of "side-of-street-field" for center features 137 | - **--cluster-points=[number of meters]**: target sub-segment length for clustering points (in meters). Since road segments will rarely divide evenly into the target length, actual lengths may vary slightly from the target. 138 | - **--direction-field=[direction field]**: name of optional line property describing segment directionality. Use in conjunction with the related `one-way-*-value` and `two-way-value` properties 139 | - **--follow-line-direction**: only match using line direction 140 | - **--join-points** allows a series of points to be snapped to the street and transformed into a street segment. Requires the relationship between points to be defined using *join-points-sequence-field* and *join-points-match-field* 141 | - **--join-point-sequence-field=**: [default: point_sequence] specifies the name of the field containing point sequence info (e.g. 1=start, 2=middle, 3=terminus) 142 | - **--join-points-match-fields=**: When turning points into line segments, there may be multiple sequences of points on one street. This flag tells the CLI hoow to sort like with like by specifying the fields that must match in order for points to considered part of the same segment and merged into a line. Expressed as a comma-separated list of fields to match values when joining points 143 | - **--left-side-driving**: directionality assumes left-side driving 144 | - **--match-bike**: match using bike routing rules in [OSRM](http://project-osrm.org/), which excludes motorways and includes features like off-street paths 145 | - **--match-car**: matching will use car routing rules in [OSRM](http://project-osrm.org/) 146 | - **--match-motorway-only**: only match against motorway segments 147 | - **--match-pedestrian**: match using pedestrian routing rules 148 | - **--match-surface-streets-only**: only match against surface street segments 149 | - **--offset-line=[offset in meters]**: offset geometry based on direction of matched line (in meters). This visually offsets the result to make it easier to see on a map (for example, could be used for sidewalks and curbs). 150 | - **--one-way-against-direction-value=[one-way-against-direction value]**: name of optional value of `direction-field` indicating a one-way street against line direction 151 | - **--one-way-with-direction-value=one-way-with-direction value**: name of optional value of `direction-field` indicating a one-way street with line direction 152 | - **--search-radius=[number of meters]**: [default: 10] the search radius, in meters, for snapping points, lines, and traces to the street 153 | - **--snap-intersections**: snap line end-points to nearest intersection 154 | - **--snap-side-of-street**: snap line to side of street 155 | - **--tile-hierarchy=[number]**: [default: 6] SharedStreets tile hierarchy, which refers to the [OSM data model](https://github.com/sharedstreets/sharedstreets-builder/blob/a554983e96010d32b71d7d23504fa88c6fbbad10/src/main/java/io/sharedstreets/tools/builder/osm/model/Way.java#L61). Level 6 includes unclassified roads and above. Level 7 includes service roads and above. Level 8 includes other features, like bike and pedestrian paths. 156 | - **--tile-source=[osm/planet-DATE]**: [default: osm/planet-181224] SharedStreets tile source, which is derived from OSM at the date specified (in `yymmdd` format). A new tile source is created roughly once a month and a list can be found [here](https://github.com/sharedstreets/sharedstreets-api_). 157 | - **--trim-intersections-radius** buffer radius of a given length (in meters) around each intersection, and trim these lengths off of the results. 158 | - **--two-way-value=[two-way-value]**: name of optional value of `direction-field` indicating a two-way street 159 | 160 | 161 | #### Examples: 162 | 163 | Matching city centerlines to SharedStreets: 164 | ```sh 165 | $ shst match ~/Desktop/project/city_centerlines.geojson --out=city_centerlines.geojson 166 | ``` 167 | 168 | Matching city bike facilities to SharedStreets: 169 | ```sh 170 | $ shst match ~/Desktop/project/city_bikeways.geojson --out=city_bikeways.geojson --match-bike --tile-hierarchy=8 171 | ``` 172 | 173 | ## Development 174 | 175 | For developers: 176 | 177 | ### Install 178 | ```sh 179 | git clone https://github.com/sharedstreets/sharedstreets-js.git 180 | yarn install 181 | yarn prepack 182 | yarn global add /Users/username/github/sharedstreets-js 183 | ``` 184 | 185 | ### Test 186 | 187 | You can test your installation using the following: 188 | ```sh 189 | yarn test 190 | ``` 191 | ### Troubleshooting 192 | 193 | - When updating `sharedstreets-js`, remove the cache of graphs and data that were created using previous versions. Do this by deleting the entire `sharedstreets-js/shst` directory. 194 | 195 | 196 | ### Build docs 197 | 198 | ```sh 199 | yarn run docs 200 | ``` 201 | 202 | ### Benchmark 203 | 204 | ```sh 205 | yarn run bench 206 | ``` 207 | -------------------------------------------------------------------------------- /test_match.ts: -------------------------------------------------------------------------------- 1 | import { lineString } from "@turf/helpers"; 2 | import length from "@turf/length"; 3 | import * as fs from "fs"; 4 | import * as glob from "glob"; 5 | import * as path from "path"; 6 | import * as sharedstreetsPbf from "sharedstreets-pbf"; 7 | import * as sharedstreets from "./src/index"; 8 | 9 | import * as turfHelpers from '@turf/helpers'; 10 | 11 | import { TileIndex } from './src/index'; 12 | import { TilePathGroup, TileType, TilePathParams, } from './src/index'; 13 | 14 | import { CleanedPoints, CleanedLines } from "./src/geom"; 15 | import { Graph, GraphMode } from "./src/index"; 16 | import envelope from "@turf/envelope"; 17 | 18 | const test = require('tape'); 19 | 20 | const BUILD_TEST_OUPUT = false; 21 | 22 | test("match points", async (t:any) => { 23 | 24 | // test polygon (dc area) 25 | const content = fs.readFileSync('test/geojson/points_1.in.geojson'); 26 | var pointsIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 27 | var cleanedPoints = new CleanedPoints(pointsIn); 28 | 29 | var points:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedPoints.clean); 30 | 31 | var params = new TilePathParams(); 32 | params.source = 'osm/planet-180430'; 33 | params.tileHierarchy = 6; 34 | 35 | // test matcher point candidates 36 | var matcher = new Graph(null, params); 37 | 38 | var matchedPoints:turfHelpers.Feature[] = []; 39 | for(let searchPoint of points.features) { 40 | let matches = await matcher.matchPoint(searchPoint, null, 3); 41 | for(let match of matches) { 42 | matchedPoints.push(match.toFeature()); 43 | } 44 | } 45 | const matchedPointFeatureCollection_1a:turfHelpers.FeatureCollection = turfHelpers.featureCollection(matchedPoints); 46 | 47 | const expected_1a_file = 'test/geojson/points_1a.out.geojson'; 48 | if(BUILD_TEST_OUPUT) { 49 | var expected_1a_out:string = JSON.stringify(matchedPointFeatureCollection_1a); 50 | fs.writeFileSync(expected_1a_file, expected_1a_out); 51 | } 52 | 53 | const expected_1a_in = fs.readFileSync(expected_1a_file); 54 | const expected_1a:turfHelpers.FeatureCollection = JSON.parse(expected_1a_in.toLocaleString()); 55 | 56 | t.deepEqual(expected_1a, matchedPointFeatureCollection_1a); 57 | 58 | matcher.searchRadius = 1000; 59 | 60 | var matchedPoints:turfHelpers.Feature[] = []; 61 | let matches = await matcher.matchPoint(points.features[0], null, 10); 62 | 63 | for(let match of matches) { 64 | matchedPoints.push(match.toFeature()); 65 | } 66 | const matchedPointFeatureCollection_1b:turfHelpers.FeatureCollection = turfHelpers.featureCollection(matchedPoints); 67 | 68 | const expected_1b_file = 'test/geojson/points_1b.out.geojson'; 69 | 70 | if(BUILD_TEST_OUPUT) { 71 | var expected_1b_out:{} = JSON.stringify(matchedPointFeatureCollection_1b); 72 | fs.writeFileSync(expected_1b_file, expected_1b_out); 73 | } 74 | 75 | const expected_1b_in = fs.readFileSync(expected_1b_file); 76 | const expected_1b:{} = JSON.parse(expected_1b_in.toLocaleString()); 77 | 78 | t.deepEqual(expected_1b, matchedPointFeatureCollection_1b); 79 | 80 | 81 | t.end(); 82 | }); 83 | 84 | 85 | test("match lines 1", async (t:any) => { 86 | 87 | // test polygon (dc area) 88 | const content = fs.readFileSync('test/geojson/sf_centerlines.sample.geojson'); 89 | var linesIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 90 | 91 | var cleanedLines = new CleanedLines(linesIn); 92 | var lines:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedLines.clean); 93 | 94 | var params = new TilePathParams(); 95 | params.source = 'osm/planet-180430'; 96 | params.tileHierarchy = 6; 97 | 98 | //test matcher point candidates 99 | var matcher = new Graph(envelope(lines), params); 100 | await matcher.buildGraph(); 101 | 102 | var matchedLines = turfHelpers.featureCollection([]); 103 | for(var line of lines.features) { 104 | var pathCandidate = await matcher.matchGeom(line); 105 | matchedLines.features.push(pathCandidate.matchedPath); 106 | } 107 | 108 | const expected_1a_file = 'test/geojson/sf_centerlines.sample.out.geojson'; 109 | if(BUILD_TEST_OUPUT) { 110 | var expected_1a_out:string = JSON.stringify(matchedLines); 111 | fs.writeFileSync(expected_1a_file, expected_1a_out); 112 | } 113 | 114 | const expected_1a_in = fs.readFileSync(expected_1a_file); 115 | const expected_1a:{} = JSON.parse(expected_1a_in.toLocaleString()); 116 | t.deepEqual(matchedLines, expected_1a); 117 | 118 | t.end(); 119 | }); 120 | 121 | test("match lines 2 -- snapping and directed edges", async (t:any) => { 122 | 123 | // test polygon (dc area) 124 | const content = fs.readFileSync('test/geojson/line-directed-test.in.geojson'); 125 | var linesIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 126 | 127 | var cleanedLines = new CleanedLines(linesIn); 128 | var lines:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedLines.clean); 129 | 130 | var params = new TilePathParams(); 131 | params.source = 'osm/planet-180430'; 132 | params.tileHierarchy = 6; 133 | 134 | //test matcher point candidates 135 | var matcher = new Graph(envelope(lines), params); 136 | await matcher.buildGraph(); 137 | 138 | var matchedLines = turfHelpers.featureCollection([]); 139 | for(var line of lines.features) { 140 | var pathCandidate = await matcher.matchGeom(line); 141 | matchedLines.features.push(pathCandidate.matchedPath); 142 | } 143 | 144 | 145 | const expected_1a_file = 'test/geojson/line-directed-test-snapped.out.geojson'; 146 | if(BUILD_TEST_OUPUT) { 147 | var expected_1a_out:string = JSON.stringify(matchedLines); 148 | fs.writeFileSync(expected_1a_file, expected_1a_out); 149 | } 150 | 151 | 152 | const expected_1a_in = fs.readFileSync(expected_1a_file); 153 | const expected_1a:{} = JSON.parse(expected_1a_in.toLocaleString()); 154 | t.deepEqual(matchedLines, expected_1a); 155 | 156 | matcher.snapIntersections = false; 157 | 158 | var matchedLines = turfHelpers.featureCollection([]); 159 | for(var line of lines.features) { 160 | var pathCandidate = await matcher.matchGeom(line); 161 | matchedLines.features.push(pathCandidate.matchedPath); 162 | } 163 | 164 | 165 | const expected_1b_file = 'test/geojson/line-directed-test-unsnapped.out.geojson'; 166 | if(BUILD_TEST_OUPUT) { 167 | var expected_1b_out:string = JSON.stringify(matchedLines); 168 | fs.writeFileSync(expected_1b_file, expected_1b_out); 169 | } 170 | 171 | const expected_1b_in = fs.readFileSync(expected_1b_file); 172 | const expected_1b:{} = JSON.parse(expected_1b_in.toLocaleString()); 173 | t.deepEqual(matchedLines, expected_1b); 174 | 175 | 176 | t.end(); 177 | }); 178 | 179 | 180 | 181 | // test("match grid", async (t:any) => { 182 | 183 | // // test polygon (dc area) 184 | // const content = fs.readFileSync('test/geojson/sf_centerlines.sample.geojson'); 185 | // var linesIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 186 | 187 | // var cleanedLines = new CleanedLines(linesIn); 188 | // var lines:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedLines.clean); 189 | 190 | // var params = new TilePathParams(); 191 | // params.source = 'osm/planet-180430'; 192 | // params.tileHierarchy = 6; 193 | 194 | // //test matcher point candidates 195 | // var matcher = new Graph(envelope(lines), params); 196 | // await matcher.buildGraph(); 197 | 198 | // var matchedLines = turfHelpers.featureCollection([]); 199 | // for(var line of lines.features) { 200 | 201 | // var pathCandidate = await matcher.match(line); 202 | // matchedLines.features.push(pathCandidate.matchedPath); 203 | // } 204 | 205 | 206 | 207 | // const expected_1a_file = 'test/geojson/sf_centerlines.1a.out.geojson'; 208 | // if(BUILD_TEST_OUPUT) { 209 | // var expected_1a_out:string = JSON.stringify(matchedLines); 210 | // fs.writeFileSync(expected_1a_file, expected_1a_out); 211 | // } 212 | 213 | 214 | // const expected_1a_in = fs.readFileSync(expected_1a_file); 215 | // const expected_1a:{} = JSON.parse(expected_1a_in.toLocaleString()); 216 | // t.deepEqual(matchedLines, expected_1a); 217 | 218 | // matcher.snapIntersections = false; 219 | 220 | // var matchedLines = turfHelpers.featureCollection([]); 221 | // for(var line of lines.features) { 222 | // var pathCandidate = await matcher.match(line); 223 | // matchedLines.features.push(pathCandidate.matchedPath); 224 | // } 225 | 226 | 227 | // const expected_1b_file = 'test/geojson/sf_centerlines.1b.out.geojson'; 228 | // if(BUILD_TEST_OUPUT) { 229 | // var expected_1b_out:string = JSON.stringify(matchedLines); 230 | // fs.writeFileSync(expected_1b_file, expected_1b_out); 231 | // } 232 | 233 | // const expected_1b_in = fs.readFileSync(expected_1b_file); 234 | // const expected_1b:{} = JSON.parse(expected_1b_in.toLocaleString()); 235 | // t.deepEqual(matchedLines, expected_1b); 236 | 237 | 238 | // t.end(); 239 | // }); 240 | 241 | 242 | // test("match roundabout", async (t:any) => { 243 | 244 | // // test polygon (dc area) 245 | // const content = fs.readFileSync('test/geojson/roundabout.1a.geojson'); 246 | // var linesIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 247 | 248 | // var cleanedLines = new CleanedLines(linesIn); 249 | // var lines:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedLines.clean); 250 | 251 | // var params = new TilePathParams(); 252 | // params.source = 'osm/planet-180430'; 253 | // params.tileHierarchy = 6; 254 | 255 | // //test matcher point candidates 256 | // var matcher = new Graph(envelope(lines), params); 257 | // await matcher.buildGraph(); 258 | 259 | // var matchedLines = turfHelpers.featureCollection([]); 260 | // for(var line of lines.features) { 261 | // var pathCandidate = await matcher.match(line); 262 | // matchedLines.features.push(pathCandidate.matchedPath); 263 | // } 264 | 265 | // const expected_1a_file = 'test/geojson/roundabout.1a.out.geojson'; 266 | // if(BUILD_TEST_OUPUT) { 267 | // var expected_1a_out:string = JSON.stringify(matchedLines); 268 | // fs.writeFileSync(expected_1a_file, expected_1a_out); 269 | // } 270 | 271 | 272 | // const expected_1a_in = fs.readFileSync(expected_1a_file); 273 | // const expected_1a:{} = JSON.parse(expected_1a_in.toLocaleString()); 274 | // t.deepEqual(matchedLines, expected_1a); 275 | 276 | // matcher.snapIntersections = false; 277 | 278 | // var matchedLines = turfHelpers.featureCollection([]); 279 | // for(var line of lines.features) { 280 | // var pathCandidate = await matcher.match(line); 281 | // matchedLines.features.push(pathCandidate.matchedPath); 282 | // } 283 | 284 | 285 | // const expected_1b_file = 'test/geojson/roundabout.1b.out.geojson'; 286 | // if(BUILD_TEST_OUPUT) { 287 | // var expected_1b_out:string = JSON.stringify(matchedLines); 288 | // fs.writeFileSync(expected_1b_file, expected_1b_out); 289 | // } 290 | 291 | // const expected_1b_in = fs.readFileSync(expected_1b_file); 292 | // const expected_1b:{} = JSON.parse(expected_1b_in.toLocaleString()); 293 | // t.deepEqual(matchedLines, expected_1b); 294 | 295 | 296 | // t.end(); 297 | // }); 298 | 299 | 300 | 301 | // test("match long paths", async (t:any) => { 302 | 303 | // // test polygon (dc area) 304 | // const content = fs.readFileSync('test/geojson/long-paths.geojson'); 305 | // var linesIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 306 | 307 | // console.log("1"); 308 | // var cleanedLines = new CleanedLines(linesIn); 309 | // var lines:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedLines.clean); 310 | 311 | // var params = new TilePathParams(); 312 | // params.source = 'osm/planet-180430'; 313 | // params.tileHierarchy = 6; 314 | 315 | // //test matcher point candidates 316 | // var matcher = new Graph(envelope(lines), params); 317 | // matcher.searchRadius = 20; 318 | // await matcher.buildGraph(); 319 | 320 | // var matchedLines = turfHelpers.featureCollection([]); 321 | // for(var line of lines.features) { 322 | // if(line.properties['analysis_id'] == 1454853) 323 | // console.log('1454853') 324 | // var pathCandidate = await matcher.match(line); 325 | // //matchedLines.features.push(pathCandidate.matchedPath); 326 | // } 327 | 328 | // const BUILD_TEST_OUPUT = false; 329 | 330 | // // const expected_1a_file = 'test/geojson/line-directed-test-snapped.out.geojson'; 331 | // // if(BUILD_TEST_OUPUT) { 332 | // // var expected_1a_out:string = JSON.stringify(matchedLines); 333 | // // fs.writeFileSync(expected_1a_file, expected_1a_out); 334 | // // } 335 | 336 | 337 | // // const expected_1a_in = fs.readFileSync(expected_1a_file); 338 | // // const expected_1a:{} = JSON.parse(expected_1a_in.toLocaleString()); 339 | // // t.deepEqual(matchedLines, expected_1a); 340 | 341 | // // matcher.snapIntersections = false; 342 | 343 | // // var matchedLines = turfHelpers.featureCollection([]); 344 | // // for(var line of lines.features) { 345 | // // var pathCandidate = await matcher.match(line); 346 | // // matchedLines.features.push(pathCandidate.matchedPath); 347 | // // } 348 | 349 | 350 | // // const expected_1b_file = 'test/geojson/line-directed-test-unsnapped.out.geojson'; 351 | // // if(BUILD_TEST_OUPUT) { 352 | // // var expected_1b_out:string = JSON.stringify(matchedLines); 353 | // // fs.writeFileSync(expected_1b_file, expected_1b_out); 354 | // // } 355 | 356 | // // const expected_1b_in = fs.readFileSync(expected_1b_file); 357 | // // const expected_1b:{} = JSON.parse(expected_1b_in.toLocaleString()); 358 | // // t.deepEqual(matchedLines, expected_1b); 359 | 360 | 361 | // t.end(); 362 | // }); 363 | 364 | 365 | // test("match long paths", async (t:any) => { 366 | 367 | // // test polygon (dc area) 368 | // const content = fs.readFileSync('test/geojson/expressways.geojson'); 369 | // var linesIn:turfHelpers.FeatureCollection = JSON.parse(content.toLocaleString()); 370 | 371 | // console.log("1"); 372 | // var cleanedLines = new CleanedLines(linesIn); 373 | // var lines:turfHelpers.FeatureCollection = turfHelpers.featureCollection(cleanedLines.clean); 374 | 375 | // var params = new TilePathParams(); 376 | // params.source = 'osm/planet-180430'; 377 | // params.tileHierarchy = 6; 378 | 379 | // //test matcher point candidates 380 | // var matcher = new Graph(envelope(lines), params); 381 | // matcher.graphMode = GraphMode.CAR_MOTORWAY_ONLY; 382 | // matcher.searchRadius = 20; 383 | // await matcher.buildGraph(); 384 | 385 | // var matchedLines = turfHelpers.featureCollection([]); 386 | // for(var line of lines.features) { 387 | // if(line.properties['analysis_id'] == 1454853) 388 | // console.log('1454853') 389 | // var pathCandidate = await matcher.match(line); 390 | // //matchedLines.features.push(pathCandidate.matchedPath); 391 | // } 392 | 393 | // const BUILD_TEST_OUPUT = false; 394 | 395 | // // const expected_1a_file = 'test/geojson/line-directed-test-snapped.out.geojson'; 396 | // // if(BUILD_TEST_OUPUT) { 397 | // // var expected_1a_out:string = JSON.stringify(matchedLines); 398 | // // fs.writeFileSync(expected_1a_file, expected_1a_out); 399 | // // } 400 | 401 | 402 | // // const expected_1a_in = fs.readFileSync(expected_1a_file); 403 | // // const expected_1a:{} = JSON.parse(expected_1a_in.toLocaleString()); 404 | // // t.deepEqual(matchedLines, expected_1a); 405 | 406 | // // matcher.snapIntersections = false; 407 | 408 | // // var matchedLines = turfHelpers.featureCollection([]); 409 | // // for(var line of lines.features) { 410 | // // var pathCandidate = await matcher.match(line); 411 | // // matchedLines.features.push(pathCandidate.matchedPath); 412 | // // } 413 | 414 | 415 | // // const expected_1b_file = 'test/geojson/line-directed-test-unsnapped.out.geojson'; 416 | // // if(BUILD_TEST_OUPUT) { 417 | // // var expected_1b_out:string = JSON.stringify(matchedLines); 418 | // // fs.writeFileSync(expected_1b_file, expected_1b_out); 419 | // // } 420 | 421 | // // const expected_1b_in = fs.readFileSync(expected_1b_file); 422 | // // const expected_1b:{} = JSON.parse(expected_1b_in.toLocaleString()); 423 | // // t.deepEqual(matchedLines, expected_1b); 424 | 425 | 426 | // t.end(); 427 | // }); 428 | 429 | -------------------------------------------------------------------------------- /test/geojson/line_side_of_street_bidirectional.4.unmatched.geojson: -------------------------------------------------------------------------------- 1 | {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"geo_id":14676295,"lfn_id":12133,"lf_name":"Ln N Queen E Beverley","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":14676269,"tnode":14676283,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":27321},"geometry":{"type":"LineString","coordinates":[[-79.391878588,43.650410705],[-79.391361517,43.650520752]]}},{"type":"Feature","properties":{"geo_id":1145451,"lfn_id":9580,"lf_name":"Planning Boundary","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466749,"tnode":13466684,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":207001,"fcode_desc":"Geostatistical line","juris_code":"CITY OF TORONTO","objectid":18204},"geometry":{"type":"LineString","coordinates":[[-79.390706642,43.649988827],[-79.390855084,43.65041414]]}},{"type":"Feature","properties":{"geo_id":14050973,"lfn_id":13054,"lf_name":"Ln S Queen W Duncan","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":14025319,"tnode":14050974,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":18207},"geometry":{"type":"LineString","coordinates":[[-79.389318821,43.649818493],[-79.389956123,43.649693554]]}},{"type":"Feature","properties":{"geo_id":1145403,"lfn_id":4268,"lf_name":"Renfrew Pl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466684,"tnode":13466701,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":122986},"geometry":{"type":"LineString","coordinates":[[-79.390855084,43.65041414],[-79.391278046,43.650326533]]}},{"type":"Feature","properties":{"geo_id":30107239,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107236,"tnode":30107238,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216327},"geometry":{"type":"LineString","coordinates":[[-79.390646392,43.650591063],[-79.390648455,43.650602153],[-79.390647215,43.650609073],[-79.390642803,43.650615459],[-79.390637426,43.650620421],[-79.390631791,43.650623961],[-79.390622747,43.650627678],[-79.390614923,43.650629092],[-79.390605154,43.650629434],[-79.39059416,43.65062836],[-79.390586591,43.650626049],[-79.390580968,43.650623199],[-79.390577316,43.650620361],[-79.390573158,43.65061645],[-79.39057097,43.650613082],[-79.39056902,43.650608283]]}},{"type":"Feature","properties":{"geo_id":14041513,"lfn_id":4268,"lf_name":"Renfrew Pl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":14041514,"tnode":13466650,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":122675},"geometry":{"type":"LineString","coordinates":[[-79.389330281,43.650704909],[-79.389897556,43.650581846]]}},{"type":"Feature","properties":{"geo_id":2842544,"lfn_id":9747,"lf_name":"John St","address_l":null,"address_r":"197-205","oe_flag_l":"N","oe_flag_r":"O","lonuml":0,"hinum_l":0,"lonumr":197,"hinum_r":205,"fnode":13466558,"tnode":13466547,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201500,"fcode_desc":"Local","juris_code":"CITY OF TORONTO","objectid":214431},"geometry":{"type":"LineString","coordinates":[[-79.39162349,43.651196623],[-79.391674966,43.651254853]]}},{"type":"Feature","properties":{"geo_id":1145381,"lfn_id":9580,"lf_name":"Planning Boundary","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466684,"tnode":13466539,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":207001,"fcode_desc":"Geostatistical line","juris_code":"CITY OF TORONTO","objectid":214432},"geometry":{"type":"LineString","coordinates":[[-79.390855084,43.65041414],[-79.391190711,43.651288995]]}},{"type":"Feature","properties":{"geo_id":30104758,"lfn_id":20658,"lf_name":"Grange Park Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466547,"tnode":30104757,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":214434},"geometry":{"type":"LineString","coordinates":[[-79.391674966,43.651254853],[-79.39173801,43.651414672]]}},{"type":"Feature","properties":{"geo_id":30104755,"lfn_id":4484,"lf_name":"Stephanie St","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30104753,"tnode":13466558,"one_way_di":-1,"dir_code_d":"Against-Digitization","fcode":201500,"fcode_desc":"Local","juris_code":"CITY OF TORONTO","objectid":214435},"geometry":{"type":"LineString","coordinates":[[-79.391496798,43.651223667],[-79.39162349,43.651196623]]}},{"type":"Feature","properties":{"geo_id":30104767,"lfn_id":20658,"lf_name":"Grange Park Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30104753,"tnode":30104757,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":214440},"geometry":{"type":"LineString","coordinates":[[-79.391496798,43.651223667],[-79.391587436,43.651444537],[-79.39173801,43.651414672]]}},{"type":"Feature","properties":{"geo_id":30107237,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107220,"tnode":30107236,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216328},"geometry":{"type":"LineString","coordinates":[[-79.390738918,43.65057105],[-79.390646392,43.650591063]]}},{"type":"Feature","properties":{"geo_id":30107243,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107238,"tnode":30107217,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216329},"geometry":{"type":"LineString","coordinates":[[-79.39056902,43.650608283],[-79.390496583,43.650624697]]}},{"type":"Feature","properties":{"geo_id":14020226,"lfn_id":4268,"lf_name":"Renfrew Pl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":14020227,"tnode":13466684,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216319},"geometry":{"type":"LineString","coordinates":[[-79.390678853,43.650430452],[-79.390855084,43.65041414]]}},{"type":"Feature","properties":{"geo_id":30107224,"lfn_id":4268,"lf_name":"Renfrew Pl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466673,"tnode":30107223,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216313},"geometry":{"type":"LineString","coordinates":[[-79.390432385,43.650480019],[-79.390547639,43.650456843]]}},{"type":"Feature","properties":{"geo_id":30107221,"lfn_id":12325,"lf_name":"St Patricks Market","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":14020227,"tnode":30107220,"one_way_di":-1,"dir_code_d":"Against-Digitization","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216314},"geometry":{"type":"LineString","coordinates":[[-79.390678853,43.650430452],[-79.390738918,43.65057105]]}},{"type":"Feature","properties":{"geo_id":14040940,"lfn_id":12325,"lf_name":"St Patricks Market","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":14020220,"tnode":14020227,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204002,"fcode_desc":"Walkway","juris_code":"CITY OF TORONTO","objectid":216315},"geometry":{"type":"LineString","coordinates":[[-79.390504409,43.650030093],[-79.390678853,43.650430452]]}},{"type":"Feature","properties":{"geo_id":30107218,"lfn_id":11280,"lf_name":"St Patricks Sq","address_l":"14-14","address_r":"9-11","oe_flag_l":"E","oe_flag_r":"O","lonuml":14,"hinum_l":14,"lonumr":9,"hinum_r":11,"fnode":13466673,"tnode":30107217,"one_way_di":1,"dir_code_d":"Follow-Digitization","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216316},"geometry":{"type":"LineString","coordinates":[[-79.390432385,43.650480019],[-79.390496583,43.650624697]]}},{"type":"Feature","properties":{"geo_id":6361763,"lfn_id":11280,"lf_name":"St Patricks Sq","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466736,"tnode":13466673,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204002,"fcode_desc":"Walkway","juris_code":"CITY OF TORONTO","objectid":216318},"geometry":{"type":"LineString","coordinates":[[-79.390255334,43.650081767],[-79.390432385,43.650480019]]}},{"type":"Feature","properties":{"geo_id":1145364,"lfn_id":4268,"lf_name":"Renfrew Pl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":13466650,"tnode":13466673,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216320},"geometry":{"type":"LineString","coordinates":[[-79.389897556,43.650581846],[-79.390432385,43.650480019]]}},{"type":"Feature","properties":{"geo_id":30107225,"lfn_id":4268,"lf_name":"Renfrew Pl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107223,"tnode":14020227,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216321},"geometry":{"type":"LineString","coordinates":[[-79.390547639,43.650456843],[-79.390678853,43.650430452]]}},{"type":"Feature","properties":{"geo_id":30107269,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107262,"tnode":30107268,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216341},"geometry":{"type":"LineString","coordinates":[[-79.390843216,43.650798661],[-79.390758387,43.65081951]]}},{"type":"Feature","properties":{"geo_id":30107274,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107223,"tnode":30107253,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216345},"geometry":{"type":"LineString","coordinates":[[-79.390547639,43.650456843],[-79.390597431,43.650486611],[-79.390629632,43.650564235],[-79.390614554,43.650572967]]}},{"type":"Feature","properties":{"geo_id":30107271,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107268,"tnode":30107270,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216342},"geometry":{"type":"LineString","coordinates":[[-79.390758387,43.65081951],[-79.390680638,43.650839124]]}},{"type":"Feature","properties":{"geo_id":30107272,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107270,"tnode":30107259,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216343},"geometry":{"type":"LineString","coordinates":[[-79.390680638,43.650839124],[-79.390592026,43.6508613]]}},{"type":"Feature","properties":{"geo_id":30107273,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107268,"tnode":30107270,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216344},"geometry":{"type":"LineString","coordinates":[[-79.390758387,43.65081951],[-79.390762268,43.650832646],[-79.390763725,43.650842583],[-79.390763214,43.650850385],[-79.390760515,43.650855711],[-79.39075366,43.650862085],[-79.390745112,43.650866334],[-79.390737037,43.65086899],[-79.390728964,43.650870403],[-79.390720658,43.650870746],[-79.390711375,43.650869674],[-79.390702827,43.650866822],[-79.390696487,43.650863089],[-79.390691363,43.650858476],[-79.39068722,43.650853323],[-79.390684291,43.650848353],[-79.390680638,43.650839124]]}},{"type":"Feature","properties":{"geo_id":30107275,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107223,"tnode":30107256,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216346},"geometry":{"type":"LineString","coordinates":[[-79.390547639,43.650456843],[-79.39052927,43.650496515],[-79.390560993,43.650578043],[-79.390576506,43.650581524]]}},{"type":"Feature","properties":{"geo_id":30107264,"lfn_id":12325,"lf_name":"St Patricks Market","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107262,"tnode":20032746,"one_way_di":-1,"dir_code_d":"Against-Digitization","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216331},"geometry":{"type":"LineString","coordinates":[[-79.390843216,43.650798661],[-79.391063054,43.651329817]]}},{"type":"Feature","properties":{"geo_id":30107258,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107256,"tnode":30107238,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216332},"geometry":{"type":"LineString","coordinates":[[-79.390576506,43.650581524],[-79.390575675,43.650582036],[-79.390571749,43.650587],[-79.390568316,43.650593027],[-79.390567326,43.650599236],[-79.390567567,43.650603143],[-79.390567811,43.650605095],[-79.39056902,43.650608283]]}},{"type":"Feature","properties":{"geo_id":30107263,"lfn_id":12325,"lf_name":"St Patricks Market","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107220,"tnode":30107262,"one_way_di":-1,"dir_code_d":"Against-Digitization","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216330},"geometry":{"type":"LineString","coordinates":[[-79.390738918,43.65057105],[-79.390843216,43.650798661]]}},{"type":"Feature","properties":{"geo_id":30107260,"lfn_id":11280,"lf_name":"St Patricks Sq","address_l":"20-20","address_r":"11-23","oe_flag_l":"E","oe_flag_r":"O","lonuml":20,"hinum_l":20,"lonumr":11,"hinum_r":23,"fnode":30107217,"tnode":30107259,"one_way_di":1,"dir_code_d":"Follow-Digitization","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216333},"geometry":{"type":"LineString","coordinates":[[-79.390496583,43.650624697],[-79.390592026,43.6508613]]}},{"type":"Feature","properties":{"geo_id":30107261,"lfn_id":11280,"lf_name":"St Patricks Sq","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107259,"tnode":13466521,"one_way_di":1,"dir_code_d":"Follow-Digitization","fcode":201700,"fcode_desc":"Laneway","juris_code":"CITY OF TORONTO","objectid":216334},"geometry":{"type":"LineString","coordinates":[[-79.390592026,43.6508613],[-79.390830955,43.651378288]]}},{"type":"Feature","properties":{"geo_id":30107254,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107236,"tnode":30107253,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216335},"geometry":{"type":"LineString","coordinates":[[-79.390646392,43.650591063],[-79.390640666,43.650583874],[-79.390632123,43.650578016],[-79.390621618,43.6505741],[-79.390614554,43.650572967]]}},{"type":"Feature","properties":{"geo_id":30107257,"lfn_id":20726,"lf_name":"St. Patricks Square Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30107253,"tnode":30107256,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":216336},"geometry":{"type":"LineString","coordinates":[[-79.390614554,43.650572967],[-79.390613811,43.650572849],[-79.390606474,43.650572131],[-79.390599145,43.650572655],[-79.390591816,43.65057442],[-79.390583741,43.650577076],[-79.390576506,43.650581524]]}},{"type":"Feature","properties":{"geo_id":30110647,"lfn_id":20658,"lf_name":"Grange Park Trl","address_l":null,"address_r":null,"oe_flag_l":"N","oe_flag_r":"N","lonuml":0,"hinum_l":0,"lonumr":0,"hinum_r":0,"fnode":30104757,"tnode":30110646,"one_way_di":0,"dir_code_d":"Not One-Way","fcode":204001,"fcode_desc":"Trail","juris_code":"CITY OF TORONTO","objectid":219315},"geometry":{"type":"LineString","coordinates":[[-79.39173801,43.651414672],[-79.392017548,43.65212327]]}}]} --------------------------------------------------------------------------------