kopia lustrzana https://github.com/jakecoppinger/safe-cycling-map
Porównaj commity
19 Commity
84a8f2b452
...
bdca4fbecc
Autor | SHA1 | Data |
---|---|---|
Jake Coppinger | bdca4fbecc | |
Jake Coppinger | eb01aac738 | |
Jake Coppinger | 0234c6a747 | |
Jake Coppinger | 0c13b5b909 | |
Jake Coppinger | 68cd11a980 | |
Jake Coppinger | e484e23299 | |
Jake Coppinger | a7110eef7f | |
Jake Coppinger | 7fbd8ac1d3 | |
Jake Coppinger | 66a3ad80f3 | |
Jake Coppinger | 8f4446b462 | |
Jake Coppinger | 865be63b88 | |
Jake Coppinger | 9861006cf1 | |
Jake Coppinger | 26feb770aa | |
Jake Coppinger | 751eae09ab | |
Jake Coppinger | 43166ef98a | |
Jake Coppinger | f5accf4a8f | |
Jake Coppinger | d2efd8ad67 | |
Jake Coppinger | dd7edf9b72 | |
Jake Coppinger | 0b9ffb2ed1 |
21
README.md
21
README.md
|
@ -1,8 +1,26 @@
|
||||||
Safe Cycling Map
|
Safe Cycling Map
|
||||||
================
|
================
|
||||||
|
|
||||||
Work in progress! PRs and forks very welcome :)
|
A map showing how safe a street is for cycling, based on (arbitrary) metrics. See the
|
||||||
|
[key](https://github.com/jakecoppinger/safe-cycling-map/blob/main/docs/key.md) for how street safety is calculated.
|
||||||
|
|
||||||
|
This is a work in progress side project. This data is not guaranteed to be accurate.
|
||||||
|
|
||||||
|
When zoomed in close, individual road and bicycle lanes are shown. When zoomed out, streets are
|
||||||
|
coloured by their safety ratings.
|
||||||
|
|
||||||
|
# Disclaimer
|
||||||
|
Warning: This is an arbitrary rating system. Data is open source and not guaranteed to be accurate.
|
||||||
|
|
||||||
|
This map uses OpenStreetMap data. It is not a complete or accurate map of the world and should not
|
||||||
|
be used in such a manner that deficiencies, omissions, inaccuracies or errors could result in death,
|
||||||
|
loss or injury. The maps are an iterative ongoing work-in-progress and everyone is welcome to
|
||||||
|
contribute editing the OpenStreetMap data if you spot inaccuracies. (warning courtesy of [CyclOSM](https://www.cyclosm.org/))
|
||||||
|
|
||||||
|
# Contributing: Found a mislabelled street? You can fix it!
|
||||||
|
|
||||||
|
Head to https://bikemaps.org/blog/post/improving-bicycling-data-on-openstreetmap for instructions
|
||||||
|
on how to fix OpenStreetMap data.
|
||||||
|
|
||||||
![Screenshot of map](img/safe-cycling-map-2022-01-05-v2.jpg)
|
![Screenshot of map](img/safe-cycling-map-2022-01-05-v2.jpg)
|
||||||
|
|
||||||
|
@ -12,6 +30,7 @@ Uses [osm2streets-vector-tileserver](https://github.com/jakecoppinger/osm2street
|
||||||
a vector tileserver I wrote to generate Protobuf GeoJSON vector tiles using the JS bindings to
|
a vector tileserver I wrote to generate Protobuf GeoJSON vector tiles using the JS bindings to
|
||||||
osm2streets (which is written in Rust).
|
osm2streets (which is written in Rust).
|
||||||
|
|
||||||
|
|
||||||
# Local development
|
# Local development
|
||||||
|
|
||||||
See instructions for setting up the backend tileserve at
|
See instructions for setting up the backend tileserve at
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
Safe Cycling Map
|
||||||
|
================
|
||||||
|
Zoom in to see individual lanes, zoom out a little to see street safety.
|
||||||
|
|
||||||
|
Warning: This is an arbitrary rating system. Data is open source and not guaranteed to be accurate.
|
||||||
|
|
||||||
|
This map uses OpenStreetMap data. It is not a complete or accurate map of the world and should not
|
||||||
|
be used in such a manner that deficiencies, omissions, inaccuracies or errors could result in death,
|
||||||
|
loss or injury. The maps are an iterative ongoing work-in-progress and everyone is welcome to
|
||||||
|
contribute editing the OpenStreetMap data if you spot inaccuracies. (warning courtesy of [CyclOSM](https://www.cyclosm.org/))
|
||||||
|
|
||||||
|
See the README at https://github.com/jakecoppinger/safe-cycling-map for how to fix the data (or
|
||||||
|
propose improvments to the safety rating system).
|
||||||
|
|
||||||
|
# Key
|
||||||
|
## Safe streets - green
|
||||||
|
The street satisfies any of the below conditions:
|
||||||
|
- The speed is less than or equal to 30kph
|
||||||
|
- It's a [living street](https://wiki.openstreetmap.org/wiki/Tag:highway%3Dliving_street)
|
||||||
|
- Is has a separated cycleway
|
||||||
|
- It has a cycle lane separated from the road
|
||||||
|
- Is has a shared path (bikes + pedestrians allowed)
|
||||||
|
|
||||||
|
## More dangerous streets
|
||||||
|
The street satisfies any of the below conditions:
|
||||||
|
- It has a speed limit less than 40kph and greater than 30kmh
|
||||||
|
- It has an on road, painted (non-separated) bike lane
|
||||||
|
|
||||||
|
## Dangerous streets
|
||||||
|
The street satisfies any of the below conditions:
|
||||||
|
- It has a speed higher than 40kmh
|
||||||
|
- It is a residental street with the default speed limit (assumed to be 50kph)
|
||||||
|
|
||||||
|
## TODO: Banned streets
|
||||||
|
Currently banned streets (eg. motorways/Sydney Harbour Bridge) are currently displayed as red.
|
||||||
|
|
||||||
|
# Technical details
|
||||||
|
|
||||||
|
These safety ratings are calculated in
|
||||||
|
https://github.com/jakecoppinger/safe-cycling-map/blob/main/src/osm-selectors.ts
|
||||||
|
|
||||||
|
PRs are very welcome!
|
|
@ -12,6 +12,8 @@
|
||||||
"@mapbox/mapbox-gl-directions": "^4.1.1",
|
"@mapbox/mapbox-gl-directions": "^4.1.1",
|
||||||
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
|
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
|
||||||
"@svgr/webpack": "4.3.3",
|
"@svgr/webpack": "4.3.3",
|
||||||
|
"@types/debounce": "^1.2.1",
|
||||||
|
"@types/osmtogeojson": "^2.2.30",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||||
"@typescript-eslint/parser": "^2.10.0",
|
"@typescript-eslint/parser": "^2.10.0",
|
||||||
"babel-eslint": "10.1.0",
|
"babel-eslint": "10.1.0",
|
||||||
|
@ -22,6 +24,7 @@
|
||||||
"camelcase": "^5.3.1",
|
"camelcase": "^5.3.1",
|
||||||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||||
"css-loader": "3.4.2",
|
"css-loader": "3.4.2",
|
||||||
|
"debounce": "^1.2.1",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"dotenv-expand": "5.1.0",
|
"dotenv-expand": "5.1.0",
|
||||||
"eslint": "^6.6.0",
|
"eslint": "^6.6.0",
|
||||||
|
@ -43,6 +46,7 @@
|
||||||
"mapbox-gl": "^2.9.2",
|
"mapbox-gl": "^2.9.2",
|
||||||
"mini-css-extract-plugin": "0.9.0",
|
"mini-css-extract-plugin": "0.9.0",
|
||||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||||
|
"osmtogeojson": "^3.0.0-beta.5",
|
||||||
"pnp-webpack-plugin": "1.6.4",
|
"pnp-webpack-plugin": "1.6.4",
|
||||||
"postcss-flexbugs-fixes": "4.1.0",
|
"postcss-flexbugs-fixes": "4.1.0",
|
||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
|
@ -2396,6 +2400,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/debounce": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA=="
|
||||||
|
},
|
||||||
"node_modules/@types/eslint-visitor-keys": {
|
"node_modules/@types/eslint-visitor-keys": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||||
|
@ -2405,7 +2414,7 @@
|
||||||
"version": "7946.0.10",
|
"version": "7946.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
|
||||||
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
|
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/glob": {
|
"node_modules/@types/glob": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
|
@ -2514,6 +2523,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
|
||||||
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
|
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/osmtogeojson": {
|
||||||
|
"version": "2.2.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/osmtogeojson/-/osmtogeojson-2.2.30.tgz",
|
||||||
|
"integrity": "sha512-2TE8PjRSqDvlSCNUU3jln9KVKERXwOAVR7Yhxaww0ST9WSqylIAT0itivECUyvuK83/JkIsCnExS4yNobP8SVg=="
|
||||||
|
},
|
||||||
"node_modules/@types/parse-json": {
|
"node_modules/@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
@ -3001,6 +3015,14 @@
|
||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xmldom/xmldom": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xtuc/ieee754": {
|
"node_modules/@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
|
@ -5092,6 +5114,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 6.0"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/confusing-browser-globals": {
|
"node_modules/confusing-browser-globals": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
|
||||||
|
@ -5741,6 +5777,11 @@
|
||||||
"webidl-conversions": "^4.0.2"
|
"webidl-conversions": "^4.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/debounce": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
|
@ -8054,6 +8095,18 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/geojson-numeric": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/geojson-numeric/-/geojson-numeric-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-rvItMp3W7pe16o2EQTnRw54v6WHdiE4bYjUsdr3FZskFb6oPC7gjLe4zginP+Wd1B/HLl2acTukfn16Lmwn7lg==",
|
||||||
|
"dependencies": {
|
||||||
|
"concat-stream": "2.0.0",
|
||||||
|
"optimist": "~0.3.5"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"geojson-numeric": "geojson-numeric"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/geojson-vt": {
|
"node_modules/geojson-vt": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
|
||||||
|
@ -10277,6 +10330,31 @@
|
||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonparse": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-fw7Q/8gFR8iSekUi9I+HqWIap6mywuoe7hQIg3buTVjuZgALKj4HAmm0X6f+TaL4c9NJbvyFQdaI2ppr5p6dnQ==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 0.2.0"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"node_modules/JSONStream": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-PiV28BpoUorz9kKFwRbD7+wg0t/k0ITHKn0DgCU44YZ/GaGAZRPt9q5PzoifC85gE55SEPIdMu0Labfxevj8cw==",
|
||||||
|
"dependencies": {
|
||||||
|
"jsonparse": "0.0.5",
|
||||||
|
"through": "~2.2.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/JSONStream/node_modules/through": {
|
||||||
|
"version": "2.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz",
|
||||||
|
"integrity": "sha512-JIR0m0ybkmTcR8URann+HbwKmodP+OE8UCbsifQDYMLD5J3em1Cdn3MYPpbEd5elGDwmP98T+WbqP/tvzA5Mjg=="
|
||||||
|
},
|
||||||
"node_modules/jsprim": {
|
"node_modules/jsprim": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||||
|
@ -11958,6 +12036,14 @@
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/optimist": {
|
||||||
|
"version": "0.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
|
||||||
|
"integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"wordwrap": "~0.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/optimize-css-assets-webpack-plugin": {
|
"node_modules/optimize-css-assets-webpack-plugin": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
|
||||||
|
@ -12020,6 +12106,84 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/osm-polygon-features": {
|
||||||
|
"version": "0.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/osm-polygon-features/-/osm-polygon-features-0.9.2.tgz",
|
||||||
|
"integrity": "sha512-5zNEFCq+G6X2TDkqbKYLF1+GtWVCCLA8zX+FVhSogsiTRsGquyaGRy5cYNW4BE3ci0MKOLvNTkFNsjsCNtgz0A=="
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson": {
|
||||||
|
"version": "3.0.0-beta.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osmtogeojson/-/osmtogeojson-3.0.0-beta.5.tgz",
|
||||||
|
"integrity": "sha512-izvaUWnunrYvMB4LB0ZN15O1+g90c628yHS4SeSR3daVSBF9vdTHL7iVHfg0wEr1uEYjQ+lMJHCiYFusL5yKVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@mapbox/geojson-rewind": "0.5.2",
|
||||||
|
"@xmldom/xmldom": "0.8.3",
|
||||||
|
"concat-stream": "2.0.0",
|
||||||
|
"geojson-numeric": "0.2.1",
|
||||||
|
"htmlparser2": "3.5.1",
|
||||||
|
"JSONStream": "0.8.0",
|
||||||
|
"optimist": "~0.3.5",
|
||||||
|
"osm-polygon-features": "^0.9.1",
|
||||||
|
"tiny-osmpbf": "^0.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"osmtogeojson": "osmtogeojson"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.5"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@types/geojson": "^7946.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson/node_modules/domhandler": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-MFFBQFGkyTuNe3vL9WEw9JdlCwIoBYpOGESLeZAvc/jClYNsOl6P1KzevJbWg76GovdEycfR7/2/Ra7NnqtMKw==",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson/node_modules/domutils": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-1UdPmldjSGewOuWE40YYFZB1Q4im4LZoCMXGYeTeLz3R9hvxrDYJPRcPHXR4yBbubQebgGNCY2hwpJxmAiUMzQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson/node_modules/htmlparser2": {
|
||||||
|
"version": "3.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.5.1.tgz",
|
||||||
|
"integrity": "sha512-9ouaQ6sjVJZS4NhPC65zNm2JCJotiH6BVm6iFvI90hRcsIEISMrgjqMUrPpU9G1VS4vTspH4dyaqSRf6JLQPbg==",
|
||||||
|
"dependencies": {
|
||||||
|
"domelementtype": "1",
|
||||||
|
"domhandler": "2.2",
|
||||||
|
"domutils": "1.3",
|
||||||
|
"readable-stream": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson/node_modules/isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson/node_modules/readable-stream": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||||
|
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.1",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"string_decoder": "~0.10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/osmtogeojson/node_modules/string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
||||||
|
},
|
||||||
"node_modules/p-cancelable": {
|
"node_modules/p-cancelable": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||||
|
@ -16423,6 +16587,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||||
|
},
|
||||||
|
"node_modules/tiny-osmpbf": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-osmpbf/-/tiny-osmpbf-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-Sl0xuDdM0+bnrYPhTAWnQ5eui8+2cpYCnsBxq0EFR1/IgmfB7+FiC23I8aa7tdP4AjaWvBUMK34kfXdY6C1LCQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"pbf": "^3.0.4",
|
||||||
|
"tiny-inflate": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tinyqueue": {
|
"node_modules/tinyqueue": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
|
||||||
|
@ -17848,6 +18026,14 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wordwrap": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/workbox-background-sync": {
|
"node_modules/workbox-background-sync": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz",
|
||||||
|
@ -20079,6 +20265,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
|
||||||
},
|
},
|
||||||
|
"@types/debounce": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA=="
|
||||||
|
},
|
||||||
"@types/eslint-visitor-keys": {
|
"@types/eslint-visitor-keys": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||||
|
@ -20088,7 +20279,7 @@
|
||||||
"version": "7946.0.10",
|
"version": "7946.0.10",
|
||||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz",
|
||||||
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
|
"integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"@types/glob": {
|
"@types/glob": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
|
@ -20197,6 +20388,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
|
||||||
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
|
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="
|
||||||
},
|
},
|
||||||
|
"@types/osmtogeojson": {
|
||||||
|
"version": "2.2.30",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/osmtogeojson/-/osmtogeojson-2.2.30.tgz",
|
||||||
|
"integrity": "sha512-2TE8PjRSqDvlSCNUU3jln9KVKERXwOAVR7Yhxaww0ST9WSqylIAT0itivECUyvuK83/JkIsCnExS4yNobP8SVg=="
|
||||||
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||||
|
@ -20602,6 +20798,11 @@
|
||||||
"@xtuc/long": "4.2.2"
|
"@xtuc/long": "4.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@xmldom/xmldom": {
|
||||||
|
"version": "0.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.3.tgz",
|
||||||
|
"integrity": "sha512-Lv2vySXypg4nfa51LY1nU8yDAGo/5YwF+EY/rUZgIbfvwVARcd67ttCM8SMsTeJy51YhHYavEq+FS6R0hW9PFQ=="
|
||||||
|
},
|
||||||
"@xtuc/ieee754": {
|
"@xtuc/ieee754": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
|
||||||
|
@ -22303,6 +22504,17 @@
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||||
},
|
},
|
||||||
|
"concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"requires": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"confusing-browser-globals": {
|
"confusing-browser-globals": {
|
||||||
"version": "1.0.9",
|
"version": "1.0.9",
|
||||||
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
|
"resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
|
||||||
|
@ -22833,6 +23045,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"debounce": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
|
@ -24711,6 +24928,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
|
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
|
||||||
"integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg=="
|
"integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg=="
|
||||||
},
|
},
|
||||||
|
"geojson-numeric": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/geojson-numeric/-/geojson-numeric-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-rvItMp3W7pe16o2EQTnRw54v6WHdiE4bYjUsdr3FZskFb6oPC7gjLe4zginP+Wd1B/HLl2acTukfn16Lmwn7lg==",
|
||||||
|
"requires": {
|
||||||
|
"concat-stream": "2.0.0",
|
||||||
|
"optimist": "~0.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"geojson-vt": {
|
"geojson-vt": {
|
||||||
"version": "3.2.1",
|
"version": "3.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
|
||||||
|
@ -26467,6 +26693,27 @@
|
||||||
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
|
||||||
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
|
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
|
||||||
},
|
},
|
||||||
|
"jsonparse": {
|
||||||
|
"version": "0.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz",
|
||||||
|
"integrity": "sha512-fw7Q/8gFR8iSekUi9I+HqWIap6mywuoe7hQIg3buTVjuZgALKj4HAmm0X6f+TaL4c9NJbvyFQdaI2ppr5p6dnQ=="
|
||||||
|
},
|
||||||
|
"JSONStream": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-PiV28BpoUorz9kKFwRbD7+wg0t/k0ITHKn0DgCU44YZ/GaGAZRPt9q5PzoifC85gE55SEPIdMu0Labfxevj8cw==",
|
||||||
|
"requires": {
|
||||||
|
"jsonparse": "0.0.5",
|
||||||
|
"through": "~2.2.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"through": {
|
||||||
|
"version": "2.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.2.7.tgz",
|
||||||
|
"integrity": "sha512-JIR0m0ybkmTcR8URann+HbwKmodP+OE8UCbsifQDYMLD5J3em1Cdn3MYPpbEd5elGDwmP98T+WbqP/tvzA5Mjg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"jsprim": {
|
"jsprim": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||||
|
@ -27828,6 +28075,14 @@
|
||||||
"is-wsl": "^1.1.0"
|
"is-wsl": "^1.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"optimist": {
|
||||||
|
"version": "0.3.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz",
|
||||||
|
"integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==",
|
||||||
|
"requires": {
|
||||||
|
"wordwrap": "~0.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"optimize-css-assets-webpack-plugin": {
|
"optimize-css-assets-webpack-plugin": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz",
|
||||||
|
@ -27878,6 +28133,78 @@
|
||||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
|
||||||
},
|
},
|
||||||
|
"osm-polygon-features": {
|
||||||
|
"version": "0.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/osm-polygon-features/-/osm-polygon-features-0.9.2.tgz",
|
||||||
|
"integrity": "sha512-5zNEFCq+G6X2TDkqbKYLF1+GtWVCCLA8zX+FVhSogsiTRsGquyaGRy5cYNW4BE3ci0MKOLvNTkFNsjsCNtgz0A=="
|
||||||
|
},
|
||||||
|
"osmtogeojson": {
|
||||||
|
"version": "3.0.0-beta.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/osmtogeojson/-/osmtogeojson-3.0.0-beta.5.tgz",
|
||||||
|
"integrity": "sha512-izvaUWnunrYvMB4LB0ZN15O1+g90c628yHS4SeSR3daVSBF9vdTHL7iVHfg0wEr1uEYjQ+lMJHCiYFusL5yKVg==",
|
||||||
|
"requires": {
|
||||||
|
"@mapbox/geojson-rewind": "0.5.2",
|
||||||
|
"@types/geojson": "^7946.0",
|
||||||
|
"@xmldom/xmldom": "0.8.3",
|
||||||
|
"concat-stream": "2.0.0",
|
||||||
|
"geojson-numeric": "0.2.1",
|
||||||
|
"htmlparser2": "3.5.1",
|
||||||
|
"JSONStream": "0.8.0",
|
||||||
|
"optimist": "~0.3.5",
|
||||||
|
"osm-polygon-features": "^0.9.1",
|
||||||
|
"tiny-osmpbf": "^0.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"domhandler": {
|
||||||
|
"version": "2.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.2.1.tgz",
|
||||||
|
"integrity": "sha512-MFFBQFGkyTuNe3vL9WEw9JdlCwIoBYpOGESLeZAvc/jClYNsOl6P1KzevJbWg76GovdEycfR7/2/Ra7NnqtMKw==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"domutils": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-1UdPmldjSGewOuWE40YYFZB1Q4im4LZoCMXGYeTeLz3R9hvxrDYJPRcPHXR4yBbubQebgGNCY2hwpJxmAiUMzQ==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"htmlparser2": {
|
||||||
|
"version": "3.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.5.1.tgz",
|
||||||
|
"integrity": "sha512-9ouaQ6sjVJZS4NhPC65zNm2JCJotiH6BVm6iFvI90hRcsIEISMrgjqMUrPpU9G1VS4vTspH4dyaqSRf6JLQPbg==",
|
||||||
|
"requires": {
|
||||||
|
"domelementtype": "1",
|
||||||
|
"domhandler": "2.2",
|
||||||
|
"domutils": "1.3",
|
||||||
|
"readable-stream": "1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isarray": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
|
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
|
||||||
|
},
|
||||||
|
"readable-stream": {
|
||||||
|
"version": "1.1.14",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
||||||
|
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
|
||||||
|
"requires": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.1",
|
||||||
|
"isarray": "0.0.1",
|
||||||
|
"string_decoder": "~0.10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"string_decoder": {
|
||||||
|
"version": "0.10.31",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||||
|
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"p-cancelable": {
|
"p-cancelable": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
|
||||||
|
@ -31440,6 +31767,20 @@
|
||||||
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
|
||||||
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
|
||||||
},
|
},
|
||||||
|
"tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||||
|
},
|
||||||
|
"tiny-osmpbf": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-osmpbf/-/tiny-osmpbf-0.1.0.tgz",
|
||||||
|
"integrity": "sha512-Sl0xuDdM0+bnrYPhTAWnQ5eui8+2cpYCnsBxq0EFR1/IgmfB7+FiC23I8aa7tdP4AjaWvBUMK34kfXdY6C1LCQ==",
|
||||||
|
"requires": {
|
||||||
|
"pbf": "^3.0.4",
|
||||||
|
"tiny-inflate": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"tinyqueue": {
|
"tinyqueue": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
|
||||||
|
@ -32588,6 +32929,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
|
||||||
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
"integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="
|
||||||
},
|
},
|
||||||
|
"wordwrap": {
|
||||||
|
"version": "0.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||||
|
"integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw=="
|
||||||
|
},
|
||||||
"workbox-background-sync": {
|
"workbox-background-sync": {
|
||||||
"version": "4.3.1",
|
"version": "4.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-4.3.1.tgz",
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
"@mapbox/mapbox-gl-directions": "^4.1.1",
|
"@mapbox/mapbox-gl-directions": "^4.1.1",
|
||||||
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
|
"@mapbox/mapbox-gl-geocoder": "^5.0.1",
|
||||||
"@svgr/webpack": "4.3.3",
|
"@svgr/webpack": "4.3.3",
|
||||||
|
"@types/debounce": "^1.2.1",
|
||||||
|
"@types/osmtogeojson": "^2.2.30",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||||
"@typescript-eslint/parser": "^2.10.0",
|
"@typescript-eslint/parser": "^2.10.0",
|
||||||
"babel-eslint": "10.1.0",
|
"babel-eslint": "10.1.0",
|
||||||
|
@ -17,6 +19,7 @@
|
||||||
"camelcase": "^5.3.1",
|
"camelcase": "^5.3.1",
|
||||||
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
"case-sensitive-paths-webpack-plugin": "2.3.0",
|
||||||
"css-loader": "3.4.2",
|
"css-loader": "3.4.2",
|
||||||
|
"debounce": "^1.2.1",
|
||||||
"dotenv": "8.2.0",
|
"dotenv": "8.2.0",
|
||||||
"dotenv-expand": "5.1.0",
|
"dotenv-expand": "5.1.0",
|
||||||
"eslint": "^6.6.0",
|
"eslint": "^6.6.0",
|
||||||
|
@ -38,6 +41,7 @@
|
||||||
"mapbox-gl": "^2.9.2",
|
"mapbox-gl": "^2.9.2",
|
||||||
"mini-css-extract-plugin": "0.9.0",
|
"mini-css-extract-plugin": "0.9.0",
|
||||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||||
|
"osmtogeojson": "^3.0.0-beta.5",
|
||||||
"pnp-webpack-plugin": "1.6.4",
|
"pnp-webpack-plugin": "1.6.4",
|
||||||
"postcss-flexbugs-fixes": "4.1.0",
|
"postcss-flexbugs-fixes": "4.1.0",
|
||||||
"postcss-loader": "3.0.0",
|
"postcss-loader": "3.0.0",
|
||||||
|
|
|
@ -104,7 +104,7 @@ h1 {
|
||||||
bottom: 15px;
|
bottom: 15px;
|
||||||
}
|
}
|
||||||
.mapboxgl-ctrl-top-left {
|
.mapboxgl-ctrl-top-left {
|
||||||
margin-top: 50px;
|
margin-top: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mapboxgl-ctrl-geocoder--icon.mapboxgl-ctrl-geocoder--icon-search {
|
.mapboxgl-ctrl-geocoder--icon.mapboxgl-ctrl-geocoder--icon-search {
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
import debounce from "debounce";
|
||||||
|
import { LoadingStatusType, OverpassResponse} from "./interfaces";
|
||||||
|
|
||||||
|
import * as http from "https";
|
||||||
|
import { addStreetLayers, removeStreetLayers } from "./drawing";
|
||||||
|
import { safeCycleways } from "./overpass-requests";
|
||||||
|
|
||||||
|
import osmtogeojson from 'osmtogeojson';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make request to Overpass Turbo.
|
||||||
|
* @param overpassQuery Overpass turbo query string
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function getOSMData(overpassQuery: string): Promise<OverpassResponse> {
|
||||||
|
// overpass.kumi.systems
|
||||||
|
// hostname: "overpass-api.de",
|
||||||
|
const options = {
|
||||||
|
hostname: "overpass.kumi.systems",
|
||||||
|
port: 443,
|
||||||
|
path: "/api/interpreter",
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
// "Content-Type": "application/json",
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var req = http.request(options, function (res) {
|
||||||
|
var body = "";
|
||||||
|
res.setEncoding("utf8");
|
||||||
|
res.on("data", (chunk) => (body += chunk));
|
||||||
|
res.on("end", function () {
|
||||||
|
if (res.statusCode !== 200) {
|
||||||
|
console.log("error code", res.statusCode);
|
||||||
|
reject(res.statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const jsonResponse = JSON.parse(body);
|
||||||
|
resolve(jsonResponse);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.on("error", function (e) {
|
||||||
|
reject(e.message);
|
||||||
|
});
|
||||||
|
req.write(new URLSearchParams({
|
||||||
|
'data': overpassQuery,
|
||||||
|
}).toString());
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const debouncedFetchAndDrawMarkers = debounce(fetchAndDrawMarkers, 2000);
|
||||||
|
|
||||||
|
async function fetchAndDrawMarkers(
|
||||||
|
map: mapboxgl.Map,
|
||||||
|
markers: React.MutableRefObject<mapboxgl.Marker[]>,
|
||||||
|
setLoadingStatus: React.Dispatch<React.SetStateAction<LoadingStatusType>>
|
||||||
|
) {
|
||||||
|
setLoadingStatus("loading");
|
||||||
|
const bounds = map.getBounds();
|
||||||
|
const southernLat = bounds.getSouth();
|
||||||
|
const westLong = bounds.getWest();
|
||||||
|
const northLat = bounds.getNorth();
|
||||||
|
const eastLong = bounds.getEast();
|
||||||
|
|
||||||
|
let safeRoutes: OverpassResponse;
|
||||||
|
|
||||||
|
const overpassBounds = [southernLat, westLong, northLat, eastLong];
|
||||||
|
const boundsStr = overpassBounds.join(",");
|
||||||
|
const safeRoutesOverpassQuery = safeCycleways(boundsStr);;
|
||||||
|
|
||||||
|
console.log("Started POST request...");
|
||||||
|
try {
|
||||||
|
safeRoutes = await getOSMData(safeRoutesOverpassQuery);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Error:", e);
|
||||||
|
setLoadingStatus("unknownerror");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const geoJson = osmtogeojson(safeRoutes, {})
|
||||||
|
console.log(geoJson);
|
||||||
|
console.log("Adding geojson to map...");
|
||||||
|
|
||||||
|
removeStreetLayers(map);
|
||||||
|
addStreetLayers(map, geoJson);
|
||||||
|
|
||||||
|
setLoadingStatus("success");
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
import mapboxgl from "mapbox-gl";
|
||||||
|
import {
|
||||||
|
RawOverpassNode,
|
||||||
|
} from "./interfaces";
|
||||||
|
|
||||||
|
import { FeatureCollection, Geometry, GeoJsonProperties, Feature, GeometryObject } from 'geojson';
|
||||||
|
import { isGreenRoad, isOrangeRoad, isRedRoad } from "./osm-selectors";
|
||||||
|
|
||||||
|
export function drawMarkerAndCard(
|
||||||
|
item: RawOverpassNode,
|
||||||
|
map: mapboxgl.Map
|
||||||
|
): mapboxgl.Marker {
|
||||||
|
const { lat, lon } = item;
|
||||||
|
|
||||||
|
let markerOptions: mapboxgl.MarkerOptions = {};
|
||||||
|
markerOptions.color = "gray";
|
||||||
|
|
||||||
|
const defaultScale = 0.5;
|
||||||
|
markerOptions.scale = defaultScale;
|
||||||
|
|
||||||
|
if (item.tags && item.tags.capacity !== undefined) {
|
||||||
|
const capacity = parseInt(item.tags.capacity);
|
||||||
|
console.log({ capacity });
|
||||||
|
let possibleScale = defaultScale + capacity / 30;
|
||||||
|
if (possibleScale > 2) {
|
||||||
|
possibleScale = 2;
|
||||||
|
}
|
||||||
|
markerOptions.scale = possibleScale;
|
||||||
|
|
||||||
|
if (item.tags && item.tags.covered === "yes") {
|
||||||
|
markerOptions.color = "green";
|
||||||
|
}
|
||||||
|
if (item.tags && item.tags.lit === "yes") {
|
||||||
|
markerOptions.color = "yellow";
|
||||||
|
}
|
||||||
|
if (item.tags && item.tags.bicycle_parking === "shed") {
|
||||||
|
markerOptions.color = "#00ec18";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const marker = new mapboxgl.Marker(markerOptions)
|
||||||
|
.setLngLat([lon, lat])
|
||||||
|
.addTo(map);
|
||||||
|
|
||||||
|
if (window.orientation !== undefined) {
|
||||||
|
marker.getElement().addEventListener("click", (e) => {
|
||||||
|
map.flyTo({
|
||||||
|
center: [lon, lat],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return marker;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeMarkers(markers: mapboxgl.Marker[]): void {
|
||||||
|
markers.map((marker) => marker.remove());
|
||||||
|
}
|
||||||
|
export function removeStreetLayers(map: mapboxgl.Map): void {
|
||||||
|
try {
|
||||||
|
console.log("Removing sources...");
|
||||||
|
map.removeLayer('greenRoadsId');
|
||||||
|
map.removeLayer('redRoadsId');
|
||||||
|
map.removeLayer('orangeRoadsId');
|
||||||
|
|
||||||
|
map.removeSource('greenRoads');
|
||||||
|
map.removeSource('redRoads');
|
||||||
|
map.removeSource('orangeRoads');
|
||||||
|
} catch (e) {
|
||||||
|
console.log("not removing sources - at least one doesn't exist yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addStreetLayers(map: mapboxgl.Map, geoJson: FeatureCollection<Geometry, GeoJsonProperties>) {
|
||||||
|
/** Add below first vector layer */
|
||||||
|
const layerToAddBefore = 'SharedUse';
|
||||||
|
map.addSource('redRoads', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
features: geoJson.features.filter(isRedRoad),
|
||||||
|
type: "FeatureCollection"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map.addSource('orangeRoads', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
features: geoJson.features.filter(isOrangeRoad)
|
||||||
|
,
|
||||||
|
type: "FeatureCollection"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
map.addSource('greenRoads', {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
features: geoJson.features.filter(isGreenRoad)
|
||||||
|
,
|
||||||
|
type: "FeatureCollection"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Add a new layer to visualize the polygon.
|
||||||
|
map.addLayer({
|
||||||
|
'id': 'redRoadsId',
|
||||||
|
'type': 'line',
|
||||||
|
'source': 'redRoads', // reference the data source
|
||||||
|
'layout': {},
|
||||||
|
'paint': {
|
||||||
|
"line-color": "red",
|
||||||
|
"line-width": 3,
|
||||||
|
'line-opacity': 0.3
|
||||||
|
},
|
||||||
|
}, layerToAddBefore);
|
||||||
|
|
||||||
|
map.addLayer({
|
||||||
|
'id': 'orangeRoadsId',
|
||||||
|
'type': 'line',
|
||||||
|
'source': 'orangeRoads', // reference the data source
|
||||||
|
'layout': {},
|
||||||
|
'paint': {
|
||||||
|
"line-color": "orange",
|
||||||
|
"line-width": 3,
|
||||||
|
'line-opacity': 0.5
|
||||||
|
},
|
||||||
|
}, layerToAddBefore);
|
||||||
|
|
||||||
|
|
||||||
|
// Add a new layer to visualize the polygon.
|
||||||
|
map.addLayer({
|
||||||
|
'id': 'greenRoadsId',
|
||||||
|
'type': 'line',
|
||||||
|
'source': 'greenRoads', // reference the data source
|
||||||
|
'layout': {},
|
||||||
|
'paint': {
|
||||||
|
"line-color": "#00FF00",
|
||||||
|
"line-width": 7,
|
||||||
|
'line-opacity': 0.8
|
||||||
|
},
|
||||||
|
}, layerToAddBefore);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export function drawMarkersAndCards(
|
||||||
|
map: mapboxgl.Map,
|
||||||
|
items: RawOverpassNode[]
|
||||||
|
): mapboxgl.Marker[] {
|
||||||
|
const markers = items
|
||||||
|
.filter((item) => item.type === "node")
|
||||||
|
.map((node: RawOverpassNode) => {
|
||||||
|
return drawMarkerAndCard(node, map);
|
||||||
|
});
|
||||||
|
|
||||||
|
return markers;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { RawOverpassNode, RawOverpassWay } from "./interfaces.js";
|
||||||
|
|
||||||
|
export function wayToNode(way: RawOverpassWay, allItems: (RawOverpassNode | RawOverpassWay)[]): RawOverpassNode | null {
|
||||||
|
console.log("Converting way...");
|
||||||
|
console.log(way)
|
||||||
|
const nodes: number[] = way.nodes;
|
||||||
|
const firstNodeId = nodes[0];
|
||||||
|
const firstNode = allItems
|
||||||
|
.find(item => item.type === 'node' && item.id === firstNodeId) as (RawOverpassNode | undefined)
|
||||||
|
if (firstNode === undefined) {
|
||||||
|
console.error(`Unable to find node with id ${firstNodeId}`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const representativeNode: RawOverpassNode = firstNode;
|
||||||
|
const { lat, lon } = representativeNode;
|
||||||
|
const { id, tags } = way;
|
||||||
|
const output: RawOverpassNode = {
|
||||||
|
id,
|
||||||
|
lat,
|
||||||
|
lon,
|
||||||
|
type: 'node',
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
console.log({ output });
|
||||||
|
return output;
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
interface BicycleParkingInterface {
|
||||||
|
amenity?: "bicycle_parking";
|
||||||
|
capacity?: string;
|
||||||
|
covered?: "yes" | "no";
|
||||||
|
lit?: "yes" | "no";
|
||||||
|
bicycle_parking?:
|
||||||
|
| "stands"
|
||||||
|
| "wall_loops"
|
||||||
|
| "rack"
|
||||||
|
| "safe_loops"
|
||||||
|
| "shed"
|
||||||
|
| "bollard"
|
||||||
|
| "lockers"
|
||||||
|
| "building";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RawOverpassNode {
|
||||||
|
type: "node";
|
||||||
|
id: number;
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
tags?: BicycleParkingInterface;
|
||||||
|
}
|
||||||
|
export interface RawOverpassWay {
|
||||||
|
type: "way";
|
||||||
|
id: number;
|
||||||
|
nodes: number[],
|
||||||
|
tags?: BicycleParkingInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OverpassResponse = {
|
||||||
|
elements: (RawOverpassNode | RawOverpassWay)[]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type LoadingStatusType = "loading" | "success" | "429error" | "unknownerror" | "too_zoomed_out" | "ready_to_load"
|
|
@ -1,5 +1,6 @@
|
||||||
// TODO: Find the layer of the road labels for the maptiler background
|
// TODO: Find the layer of the road labels for the maptiler background
|
||||||
const layerToAddAfter = undefined;
|
const layerToAddAfter = undefined;
|
||||||
|
// const layerToAddAfter = 'greenRoadsId'; //undefined;
|
||||||
|
|
||||||
function addLayer(
|
function addLayer(
|
||||||
map: mapboxgl.Map,
|
map: mapboxgl.Map,
|
||||||
|
@ -207,4 +208,9 @@ export const mapOnLoad = (map: mapboxgl.Map) => () => {
|
||||||
},
|
},
|
||||||
filter: ["==", "$type", "Polygon"],
|
filter: ["==", "$type", "Polygon"],
|
||||||
}, layerToAddAfter);
|
}, layerToAddAfter);
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
95
src/map.tsx
95
src/map.tsx
|
@ -9,14 +9,23 @@ import "@mapbox/mapbox-gl-directions/dist/mapbox-gl-directions.css";
|
||||||
|
|
||||||
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
|
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
|
||||||
import "@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css";
|
import "@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css";
|
||||||
|
import { debouncedFetchAndDrawMarkers } from "./api";
|
||||||
|
import { LoadingStatusType } from "./interfaces";
|
||||||
|
|
||||||
const MAPBOX_TOKEN =
|
const MAPBOX_TOKEN =
|
||||||
"pk.eyJ1IjoiamFrZWMiLCJhIjoiY2tkaHplNGhjMDAyMDJybW4ybmRqbTBmMyJ9.AR_fnEuka8-cFb4Snp3upw";
|
"pk.eyJ1IjoiamFrZWMiLCJhIjoiY2tkaHplNGhjMDAyMDJybW4ybmRqbTBmMyJ9.AR_fnEuka8-cFb4Snp3upw";
|
||||||
|
|
||||||
|
const min_overpass_turbo_zoom = 15;
|
||||||
|
/** Also the min zoom of the vector tileserver */
|
||||||
|
// const max_overpass_turbo_zoom = 15;
|
||||||
|
|
||||||
mapboxgl.accessToken = MAPBOX_TOKEN;
|
mapboxgl.accessToken = MAPBOX_TOKEN;
|
||||||
export function Map() {
|
export function Map() {
|
||||||
const mapContainer = React.useRef<HTMLDivElement>(null);
|
const mapContainer = React.useRef<HTMLDivElement>(null);
|
||||||
const mapRef = React.useRef<mapboxgl.Map | null>(null);
|
const mapRef = React.useRef<mapboxgl.Map | null>(null);
|
||||||
|
const markers = React.useRef<mapboxgl.Marker[]>([]);
|
||||||
|
const [loadingStatus, setLoadingStatus] =
|
||||||
|
useState<LoadingStatusType>("ready_to_load");
|
||||||
|
|
||||||
const [lng, setLng] = useState(151.2160755932166);
|
const [lng, setLng] = useState(151.2160755932166);
|
||||||
const [lat, setLat] = useState(-33.88056647217827);
|
const [lat, setLat] = useState(-33.88056647217827);
|
||||||
|
@ -36,29 +45,7 @@ export function Map() {
|
||||||
center: [lng, lat],
|
center: [lng, lat],
|
||||||
zoom: zoom,
|
zoom: zoom,
|
||||||
hash: true,
|
hash: true,
|
||||||
style: {
|
style: "mapbox://styles/mapbox/dark-v11",
|
||||||
version: 8,
|
|
||||||
sources: {
|
|
||||||
"raster-tiles": {
|
|
||||||
type: "raster",
|
|
||||||
tiles: [
|
|
||||||
"https://a.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
|
|
||||||
],
|
|
||||||
tileSize: 256,
|
|
||||||
attribution:
|
|
||||||
'© <a target="_blank" href="https://www.cyclosm.org">CyclOSM</a>, <a target="_blank" href="https://github.com/a-b-street/osm2streets">osm2streets</a>, <a target="_blank" href="https://openstreetmap.org/">OpenStreetMap contributors</a>',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
layers: [
|
|
||||||
{
|
|
||||||
id: "simple-tiles",
|
|
||||||
type: "raster",
|
|
||||||
source: "raster-tiles",
|
|
||||||
minzoom: 0,
|
|
||||||
maxzoom: 22,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const map = mapRef.current;
|
const map = mapRef.current;
|
||||||
|
@ -73,12 +60,12 @@ export function Map() {
|
||||||
}),
|
}),
|
||||||
"top-left"
|
"top-left"
|
||||||
);
|
);
|
||||||
map.addControl(
|
// map.addControl(
|
||||||
new MapboxDirections({
|
// new MapboxDirections({
|
||||||
accessToken: mapboxgl.accessToken,
|
// accessToken: mapboxgl.accessToken,
|
||||||
}),
|
// }),
|
||||||
"top-left"
|
// "top-left"
|
||||||
);
|
// );
|
||||||
map.addControl(
|
map.addControl(
|
||||||
new mapboxgl.GeolocateControl({
|
new mapboxgl.GeolocateControl({
|
||||||
positionOptions: {
|
positionOptions: {
|
||||||
|
@ -94,34 +81,70 @@ export function Map() {
|
||||||
}
|
}
|
||||||
const { lng, lat } = map.getCenter();
|
const { lng, lat } = map.getCenter();
|
||||||
const zoom = map.getZoom();
|
const zoom = map.getZoom();
|
||||||
|
if (zoom < min_overpass_turbo_zoom) {
|
||||||
|
setLoadingStatus("too_zoomed_out");
|
||||||
|
} else {
|
||||||
|
setLoadingStatus("ready_to_load");
|
||||||
|
}
|
||||||
console.log(lng, lat, zoom);
|
console.log(lng, lat, zoom);
|
||||||
|
|
||||||
setLng(map.getCenter().lng);
|
setLng(map.getCenter().lng);
|
||||||
setLat(map.getCenter().lat);
|
setLat(map.getCenter().lat);
|
||||||
setZoom(map.getZoom());
|
setZoom(map.getZoom());
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
|
if (map.getZoom() < min_overpass_turbo_zoom) {
|
||||||
|
setLoadingStatus("too_zoomed_out");
|
||||||
|
} else {
|
||||||
|
console.log(`zoom is ${map.getZoom()}`);
|
||||||
|
debouncedFetchAndDrawMarkers(map, markers, setLoadingStatus);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.on("moveend", async () => {
|
||||||
|
if (map === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const zoom = map.getZoom();
|
||||||
|
if (zoom > min_overpass_turbo_zoom) {
|
||||||
|
console.log(`zoom is ${zoom}`);
|
||||||
|
debouncedFetchAndDrawMarkers(map, markers, setLoadingStatus);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const statusMessages = {
|
||||||
|
loading: "Loading safety ratings...",
|
||||||
|
success: "Done loading safety ratings",
|
||||||
|
ready_to_load: "About to load ratings...",
|
||||||
|
too_zoomed_out: "Zoom in to see street safety ratings",
|
||||||
|
unknownerror: "Error loading. Please wait a bit",
|
||||||
|
"429error": "Too many requests, please try in a bit",
|
||||||
|
};
|
||||||
|
|
||||||
|
const statusText = statusMessages[loadingStatus];
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
<label>
|
<label>
|
||||||
A work in progress side project by{" "}
|
<span color="red">Warning:</span> Data is open source and not guaranteed to be
|
||||||
|
accurate.
|
||||||
|
<br></br>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://jakecoppinger.com/"
|
href="https://github.com/jakecoppinger/safe-cycling-map/blob/main/docs/key.md"
|
||||||
>
|
>
|
||||||
Jake Coppinger
|
View map key and how safety is calculated
|
||||||
</a>{" "}
|
</a>
|
||||||
| Open source on{" "}
|
<br></br>
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
href="https://github.com/jakecoppinger/safe-cycling-map"
|
href="https://github.com/jakecoppinger/safe-cycling-map"
|
||||||
>
|
>
|
||||||
Github
|
About this map
|
||||||
</a>
|
</a>
|
||||||
|
<br></br>
|
||||||
|
{statusText}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div ref={mapContainer} className="map-container" />
|
<div ref={mapContainer} className="map-container" />
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { Geometry, GeoJsonProperties, Feature } from 'geojson';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is a red (dangerous) road if:
|
||||||
|
* - Speed is higher than 40kmh
|
||||||
|
* - Road is a residental street with default speed limit (50kph)
|
||||||
|
* @param feature
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isRedRoad(feature: Feature<Geometry, GeoJsonProperties>): boolean {
|
||||||
|
const p = feature.properties;
|
||||||
|
if (p === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(p.highway === 'primary' && p.maxspeed === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.maxspeed > 40) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.highway === 'residential' && p.maxspeed === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is an orange (caution) road if:
|
||||||
|
* - Road has a speed limit less than 40kph and greater than 30kmh
|
||||||
|
* - Has an on road, painted (non-separated) bike lane
|
||||||
|
* @param feature
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isOrangeRoad(feature: Feature<Geometry, GeoJsonProperties>): boolean {
|
||||||
|
const p = feature.properties;
|
||||||
|
if (p === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.maxspeed <= 40) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.cycleway === 'lane') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is a green (safe) road if:
|
||||||
|
* - Speed is less than or equal to 30kph
|
||||||
|
* - Is a [living street](https://wiki.openstreetmap.org/wiki/Tag:highway%3Dliving_street)
|
||||||
|
* - Is a separated cycleway
|
||||||
|
* - Is a cycle lane separated from the road
|
||||||
|
* - Is a shared path (bikes + pedestrians allowed)
|
||||||
|
* @param feature
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function isGreenRoad(feature: Feature<Geometry, GeoJsonProperties>): boolean {
|
||||||
|
const p = feature.properties;
|
||||||
|
if (p === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (p.maxspeed <= 30) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.highway === 'cycleway') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.highway === 'shared_lane') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.bicycle === 'designated' && p.highway === 'cycleway') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.highway === 'living_street') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (p.cycleway === 'track' || p['cycleway:left'] === 'track' || p['cycleway:right'] === 'track') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
/** where boundsStr is in the format of `lat,lon,lat,lon` */
|
||||||
|
export const bicycleParking = (boundsStr: string) => `
|
||||||
|
[out:json][timeout:25];
|
||||||
|
(
|
||||||
|
// query part for: “bicycle_parking=*”
|
||||||
|
node["bicycle_parking"](${boundsStr});
|
||||||
|
way["bicycle_parking"](${boundsStr});
|
||||||
|
relation["bicycle_parking"](${boundsStr});
|
||||||
|
// query part for: “amenity=bicycle_parking”
|
||||||
|
node["amenity"="bicycle_parking"](${boundsStr});
|
||||||
|
way["amenity"="bicycle_parking"](${boundsStr});
|
||||||
|
relation["amenity"="bicycle_parking"](${boundsStr});
|
||||||
|
);
|
||||||
|
out body;
|
||||||
|
>;
|
||||||
|
out skel qt;
|
||||||
|
`
|
||||||
|
|
||||||
|
/** where boundsStr is in the format of `lat,lon,lat,lon` */
|
||||||
|
export const safeCycleways = (boundsStr: string) => `
|
||||||
|
[out:json][timeout:60][bbox:${boundsStr}];
|
||||||
|
|
||||||
|
/* Select road types to display */
|
||||||
|
|
||||||
|
(
|
||||||
|
way[highway];
|
||||||
|
way["highway"="residential"];
|
||||||
|
|
||||||
|
way[highway=cycleway];
|
||||||
|
way["highway"~"cycleway|path|footway|pedestrian"]["bicycle"~"yes|designated"];
|
||||||
|
way[highway=proposed][proposed=cycleway];
|
||||||
|
way[highway=construction][construction=cycleway];
|
||||||
|
way[proposed=cycleway];
|
||||||
|
way[cycleway=lane];
|
||||||
|
way["cycleway:left"=track];
|
||||||
|
way[cycleway=track];
|
||||||
|
|
||||||
|
);
|
||||||
|
|
||||||
|
// print results
|
||||||
|
out body;
|
||||||
|
>;
|
||||||
|
out skel qt;
|
||||||
|
`
|
Ładowanie…
Reference in New Issue