Merge branch 'master' into master

This commit is contained in:
abrensch 2019-08-05 17:20:54 +02:00 committed by GitHub
commit 42abbbcb75
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
104 changed files with 26871 additions and 1753 deletions

2
.gitignore vendored
View file

@ -1,4 +1,3 @@
bower_components/
node_modules/ node_modules/
nbproject/ nbproject/
.idea/ .idea/
@ -7,3 +6,4 @@ nbproject/
/keys.js /keys.js
/dist /dist
brouter-web.*.zip brouter-web.*.zip
yarn-error.log

10
.prettierignore Normal file
View file

@ -0,0 +1,10 @@
LICENSE
dist/
*.zip
yarn.lock
.idea
.gitignore
.prettierignore
.tx/
layers/
locales/*.json

4
.prettierrc Normal file
View file

@ -0,0 +1,4 @@
{
"singleQuote": true,
"tabWidth": 4
}

4
.travis.yml Normal file
View file

@ -0,0 +1,4 @@
language: node_js
node_js:
- lts/*
cache: yarn

10
.tx/config Normal file
View file

@ -0,0 +1,10 @@
[main]
host = https://www.transifex.com
minimum_perc = 1
lang_map = fr_CA:fr-CA,pt_BR:pt-BR,zh_CN:zh-CN,zh_HK:zh-HK,zh_TW:zh-TW,da_DK:da-DK,sv_SE:sv-SE,kn_IN:kn-IN,nl_NL:nl-NL,en_NL:en-NL,gl_ES:gl-ES
[brouter-web.brouter-website]
file_filter = locales/<lang>.json
source_file = locales/en.json
source_lang = en
type = JSON

View file

@ -1,17 +1,137 @@
BRouter-Web Changelog # BRouter-Web Changelog
=====================
## 0.10.3 (2019-06-27)
See also [milestone 0.10.3](https://github.com/nrenner/brouter-web/milestone/11?closed=1)
### Bugfixes
- Warn when special characters in export name will get removed ([#194](https://github.com/nrenner/brouter-web/issues/194), [#202](https://github.com/nrenner/brouter-web/issues/202))
- Fix %-encoded export file name in Microsoft Edge ([#201](https://github.com/nrenner/brouter-web/issues/201))
- Fix error when no elevation data above 60° north, causing empty stats and disabled export, by implementing own missing data handling for elevation diagram ([#203](https://github.com/nrenner/brouter-web/issues/203))
### Improvements
- Reduce tile.openstreetmap.org usage ([#205](https://github.com/nrenner/brouter-web/issues/205))
- use a worldwide monolingual layer (de, fr, ru) as default when matching the browser language
- remember the last selected layers (like map view), so it doesn't load the default layer next time
- default zoom level 5 instead of 6, which seems to be cached longer
- Upgrade Gulp (build tool) to version 4.0.2 - by [@Phyks](https://github.com/Phyks) ([#209](https://github.com/nrenner/brouter-web/pull/209))
- Upgrade leaflet geocoder to properly parse lat/lng - by [@bagage](https://github.com/bagage) ([#134](https://github.com/nrenner/brouter-web/issues/134))
- Upgrade to latest Bootstrap (front-end framework) - by [@bagage](https://github.com/bagage) ([#186](https://github.com/nrenner/brouter-web/pull/186))
## 0.10.2 (2019-06-02)
See also [milestone 0.10.2](https://github.com/nrenner/brouter-web/milestone/10?closed=1)
### New Features
- Polish formatting and behaviour of track statistics bar - by [@rkflx](https://github.com/rkflx) ([#200](https://github.com/nrenner/brouter-web/pull/200))
### Bugfixes
- Fix unintentional shortcut activations when typing text - by [@rkflx](https://github.com/rkflx) ([#198](https://github.com/nrenner/brouter-web/pull/198))
- Fix export button translation - by [@bagage](https://github.com/bagage) ([#195](https://github.com/nrenner/brouter-web/issues/195))
- Fix downloads in Microsoft Edge - by [@bagage](https://github.com/bagage) ([#193](https://github.com/nrenner/brouter-web/issues/193))
## 0.10.1 (2019-05-22)
### Bugfixes
- Really ignore missing elevation points in elevation chart - by [@bagage](https://github.com/bagage)/[@nrenner](https://github.com/nrenner) ([#147](https://github.com/nrenner/brouter-web/issues/147))
## 0.10.0 (2019-05-21)
See also [milestone 0.10.0](https://github.com/nrenner/brouter-web/milestone/9?closed=1)
### New Features
- Export dialog with input field for file name and track title (replaces Download dropdown) - by [@bagage](https://github.com/bagage) ([#96](https://github.com/nrenner/brouter-web/issues/96))
### Bugfixes
- Fix broken nogo's - by [@bagage](https://github.com/bagage)/[@nrenner](https://github.com/nrenner) ([#183](https://github.com/nrenner/brouter-web/issues/183))
## 0.9.0 (2019-05-18)
See also [milestone 0.9.0](https://github.com/nrenner/brouter-web/milestone/8?closed=1)
### New Features
- Add delete last point button - by [@bagage](https://github.com/bagage) ([#33](https://github.com/nrenner/brouter-web/issues/33))
- Add reverse route button - by [@bagage](https://github.com/bagage) ([#54](https://github.com/nrenner/brouter-web/issues/54))
### Improvements
- Improve about dialog texts - by [@bagage](https://github.com/bagage) ([#176](https://github.com/nrenner/brouter-web/pull/176))
- Replace | with ; in URL - by [@bagage](https://github.com/bagage) ([#109](https://github.com/nrenner/brouter-web/issues/109))
### Bugfixes
- Ignore missing elevation points in elevation chart - by [@bagage](https://github.com/bagage) ([#147](https://github.com/nrenner/brouter-web/issues/147))
- Fix loading nogos with weight - by [@Phyks](https://github.com/Phyks) ([#174](https://github.com/nrenner/brouter-web/issues/174))
- Fix wrong version under tag ([#140](https://github.com/nrenner/brouter-web/issues/140))
## 0.8.0 (2019-05-04)
See also [milestone 0.8.0](https://github.com/nrenner/brouter-web/milestone/6?closed=1)
### New Features
- Optional layers tree ([#146](https://github.com/nrenner/brouter-web/issues/146))
- Let user upload GeoJSON file of nogos - by [@Phyks](https://github.com/Phyks) ([#161](https://github.com/nrenner/brouter-web/pull/161))
- Translations: make website localizable (i18n) - by [@bagage](https://github.com/bagage) ([#63](https://github.com/nrenner/brouter-web/issues/63))
- Fix polygon edition - by [@Phyks](https://github.com/Phyks) ([#158](https://github.com/nrenner/brouter-web/pull/158))
- Render polygons from URL hash and pass it to BRouter server - by [@Phyks](https://github.com/Phyks) ([#157](https://github.com/nrenner/brouter-web/pull/157))
- Start support of nogos polylines/polygons - by [@Phyks](https://github.com/Phyks) ([#148](https://github.com/nrenner/brouter-web/pull/148))
### Improvements
- Show line numbers in profile editor to help locating error message line ([81f2c08](https://github.com/nrenner/brouter-web/commit/81f2c0863f2569fa9079e5c96f4c9b09ef4c26e2))
- Hide StravaSegments control when layer is not active ([eaba5a0](https://github.com/nrenner/brouter-web/commit/eaba5a08217fd026fb7f83ec7beb7c1f1fdc2d69))
- Show strava error + update translations - by [@bagage](https://github.com/bagage) ([#163](https://github.com/nrenner/brouter-web/pull/163))
- Replace Bower with Yarn/npm - by [@bagage](https://github.com/bagage) ([#116](https://github.com/nrenner/brouter-web/issues/116))
- Add strava layer in overlays - by [@bagage](https://github.com/bagage) ([#152](https://github.com/nrenner/brouter-web/pull/152))
- Fix release script - by [@bagage](https://github.com/bagage) ([#150](https://github.com/nrenner/brouter-web/pull/150))
### Bugfixes
- Overlays hidden under custom layer ([#143](https://github.com/nrenner/brouter-web/issues/143))
## 0.7.0 (2018-10-10)
See also [milestone 0.7.0](https://github.com/nrenner/brouter-web/milestone/4?closed=1)
### New Features
- Redesign of the user interface to also support mobile devices - by [@bagage](https://github.com/bagage) and [@RoPP](https://github.com/RoPP) ([#34](https://github.com/nrenner/brouter-web/issues/34), [#66](https://github.com/nrenner/brouter-web/issues/66))
- Permalink replaced with auto-updating URL address bar - by [@bagage](https://github.com/bagage) ([#62](https://github.com/nrenner/brouter-web/issues/62))
- Allow user to add custom layers - by [@bagage](https://github.com/bagage) ([#77](https://github.com/nrenner/brouter-web/pull/77))
- Profile and data table now in a collapsible, full-height sidebar ([#90](https://github.com/nrenner/brouter-web/issues/90), [#114](https://github.com/nrenner/brouter-web/issues/114))
- No-go areas individually editable and deletable ([#100](https://github.com/nrenner/brouter-web/issues/100))
### Improvements
- New gulp debug task and watch CSS folder - by [@bagage](https://github.com/bagage) ([#58](https://github.com/nrenner/brouter-web/pull/58))
- Locate button not shown when no https ([#60](https://github.com/nrenner/brouter-web/issues/60))
- Support Leaflet 1.0 ([#65](https://github.com/nrenner/brouter-web/issues/65), [#69](https://github.com/nrenner/brouter-web/issues/69))
- Add a gulp command for release - by [@RoPP](https://github.com/RoPP) ([#85](https://github.com/nrenner/brouter-web/pull/85))
- Use https scheme whenever possible, to avoid mixed content issues - by [@bagage](https://github.com/bagage) ([#87](https://github.com/nrenner/brouter-web/pull/87))
- Add car-eco/fast profiles + display energy/time - by [@abrensch](https://github.com/abrensch) ([#95](https://github.com/nrenner/brouter-web/pull/95))
- Improve error message if no route found - by [@bagage](https://github.com/bagage) ([#99](https://github.com/nrenner/brouter-web/issues/99))
- Support zoom 19 for German style - by [@giggls](https://github.com/giggls) ([#128](https://github.com/nrenner/brouter-web/pull/128))
## 0.6.3 (2017-03-16) ## 0.6.3 (2017-03-16)
* Fix data tab showing only two rows (regression from v0.6.2) ([#72](https://github.com/nrenner/brouter-web/issues/72)) - Fix data tab showing only two rows (regression from v0.6.2) ([#72](https://github.com/nrenner/brouter-web/issues/72))
## 0.6.2 (2017-03-14) ## 0.6.2 (2017-03-14)
* Fix "API Key Required" in OpenCycleMap & Outdoors by registering for Thunderforest "Hobby Project" plan ([#70](https://github.com/nrenner/brouter-web/issues/70)) - Fix "API Key Required" in OpenCycleMap & Outdoors by registering for Thunderforest "Hobby Project" plan ([#70](https://github.com/nrenner/brouter-web/issues/70))
## 0.6.1 (2016-12-12) ## 0.6.1 (2016-12-12)
* Add Esri World Imagery layer (DigitalGlobe is now also blocked because monthly usage limit is exceeded) - Add Esri World Imagery layer (DigitalGlobe is now also blocked because monthly usage limit is exceeded)
## 0.6.0 (2016-10-11) ## 0.6.0 (2016-10-11)
@ -19,66 +139,65 @@ See also [milestone 0.6.0](https://github.com/nrenner/brouter-web/milestone/1?cl
### Features/Improvements ### Features/Improvements
* Update OpenTopoMap zoom range to 0-17 - Update OpenTopoMap zoom range to 0-17
* [local installation] Option to remove default base layers ([#27](https://github.com/nrenner/brouter-web/issues/27)) - [local installation] Option to remove default base layers ([#27](https://github.com/nrenner/brouter-web/issues/27))
* Add tooltip to display length in meter precision (3 digits) ([#38](https://github.com/nrenner/brouter-web/issues/38)) - Add tooltip to display length in meter precision (3 digits) ([#38](https://github.com/nrenner/brouter-web/issues/38))
* Add "mean cost" to route statistics ([#39](https://github.com/nrenner/brouter-web/issues/39)) - Add "mean cost" to route statistics ([#39](https://github.com/nrenner/brouter-web/issues/39))
* Set route transparency slider to partially transparent by default ([#36](https://github.com/nrenner/brouter-web/issues/36)) - Set route transparency slider to partially transparent by default ([#36](https://github.com/nrenner/brouter-web/issues/36))
* Show position in elevation diagram when hovering path on map ([#29](https://github.com/nrenner/brouter-web/issues/29)) - Show position in elevation diagram when hovering path on map ([#29](https://github.com/nrenner/brouter-web/issues/29))
* [local installation] Added ability to specify custom overlays in configuration - by [@saesh](https://github.com/saesh) ([#46](https://github.com/nrenner/brouter-web/pull/46)) - [local installation] Added ability to specify custom overlays in configuration - by [@saesh](https://github.com/saesh) ([#46](https://github.com/nrenner/brouter-web/pull/46))
* Add button to get/follow the current location (leaflet.locatecontrol plugin) - by [@bagage](https://github.com/bagage) ([#49](https://github.com/nrenner/brouter-web/pull/49)) - Add button to get/follow the current location (leaflet.locatecontrol plugin) - by [@bagage](https://github.com/bagage) ([#49](https://github.com/nrenner/brouter-web/pull/49))
* Save and restore last map position (leaflet.restoreview.js plugin) - by [@bagage](https://github.com/bagage) ([#49](https://github.com/nrenner/brouter-web/pull/49)) - Save and restore last map position (leaflet.restoreview.js plugin) - by [@bagage](https://github.com/bagage) ([#49](https://github.com/nrenner/brouter-web/pull/49))
* Toggle drawing mode via panel button - by [@bagage](https://github.com/bagage) ([#50](https://github.com/nrenner/brouter-web/pull/50)) - Toggle drawing mode via panel button - by [@bagage](https://github.com/bagage) ([#50](https://github.com/nrenner/brouter-web/pull/50))
* [local installation] add keys.js to configure API keys instead of bingkey request - [local installation] add keys.js to configure API keys instead of bingkey request
* Switch to new icon set (Font Awesome) with more options - Switch to new icon set (Font Awesome) with more options
### Bugfixes ### Bugfixes
* Replace Bing (usage limit exceeded) with DigitalGlobe Recent Imagery layer (newer images, but sometimes cloudy) - Replace Bing (usage limit exceeded) with DigitalGlobe Recent Imagery layer (newer images, but sometimes cloudy)
* [local installation] Show error message for invalid server response with custom profiles on Windows (still needs to be fixed) ([#53](https://github.com/nrenner/brouter-web/issues/53)) - [local installation] Show error message for invalid server response with custom profiles on Windows (still needs to be fixed) ([#53](https://github.com/nrenner/brouter-web/issues/53))
* Restrictive Cookie settings caused app to stop responding ([#47](https://github.com/nrenner/brouter-web/issues/47)) - Restrictive Cookie settings caused app to stop responding ([#47](https://github.com/nrenner/brouter-web/issues/47))
## 0.5.2 (2015-08-27) ## 0.5.2 (2015-08-27)
* switch search from MapQuest to Nominatim (MapQuest licensing change) - switch search from MapQuest to Nominatim (MapQuest licensing change)
## 0.5.1 (2015-07-24) ## 0.5.1 (2015-07-24)
* config option ``baseLayers`` to add custom base layers locally (#24) - config option `baseLayers` to add custom base layers locally (#24)
* reset slider on page load to minimum opacity (#22), - reset slider on page load to minimum opacity (#22),
customizable locally with config setting ``minOpacity`` customizable locally with config setting `minOpacity`
* set OpenTopoMap max zoom back to z15 while on fallback server (#21), - set OpenTopoMap max zoom back to z15 while on fallback server (#21),
also fix max zoom of other services also fix max zoom of other services
* overscale tiles to common max zoom (avoids gray screen when switching) - overscale tiles to common max zoom (avoids gray screen when switching)
## 0.5.0 (2015-07-01) ## 0.5.0 (2015-07-01)
### Features ### Features
* Load profile content for selected profile (needs extra server locally) - Load profile content for selected profile (needs extra server locally)
* Bing maps aerial layer (not working locally) - Bing maps aerial layer (not working locally)
* track color magenta instead of blue + white casing, for better contrast - track color magenta instead of blue + white casing, for better contrast
with background map (esp. OpenCycleMap) with background map (esp. OpenCycleMap)
* transparency slider for route track and markers - transparency slider for route track and markers
* button to delete route (#10) - button to delete route (#10)
* map scale - map scale
* download all dependencies in a bundle, instead using CDNs and separate files (#18) - download all dependencies in a bundle, instead using CDNs and separate files (#18)
* switch search plugin for result-dependent zoom - switch search plugin for result-dependent zoom
* "about" popup with a bit more infos and links - "about" popup with a bit more infos and links
* closable error/warning messages, profile messages in place - closable error/warning messages, profile messages in place
### Bugfixes ### Bugfixes
* keys to enable/disable drawing (d, q/esc) now always work, not only when map is focused - keys to enable/disable drawing (d, q/esc) now always work, not only when map is focused
* fix adding new waypoint after deleting the last (#11) - fix adding new waypoint after deleting the last (#11)
* fix profile/data scrolling on Firefox - fix profile/data scrolling on Firefox
* hide trailer over controls and outside map - hide trailer over controls and outside map
## BRouter 1.2 ## BRouter 1.2
* data/CSV aggregated over segments with same tags (for better performance) - data/CSV aggregated over segments with same tags (for better performance)
## 0.4.0 (2015-03-08) ## 0.4.0 (2015-03-08)
* data tab (slow with long routes, exp. on Firefox) - data tab (slow with long routes, exp. on Firefox)

View file

@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2016 Norbert Renner and contributors Copyright (c) 2018 Norbert Renner and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in this software and associated documentation files (the "Software"), to deal in

215
README.md
View file

@ -1,22 +1,33 @@
brouter-web # brouter-web
===========
Web client (by [@nrenner](https://github.com/nrenner)) for the BRouter routing engine (by [@abrensch](https://github.com/abrensch)). *Work in progress*. Web client (by [@nrenner](https://github.com/nrenner) and [contributors](https://github.com/nrenner/brouter-web/graphs/contributors)) for the BRouter routing engine (by [@abrensch](https://github.com/abrensch)). _Work in progress_.
**New web client available mobile-ready to test on [brouter.damsy.net](http://brouter.damsy.net). Instances:
Feedbacks are appreciated, do not hesitate to create issues about it!**
BRouter online service (provided by [@abrensch](https://github.com/abrensch)): - [brouter.de/brouter-web](http://brouter.de/brouter-web/) _(provided by [@abrensch](https://github.com/abrensch))_
http://brouter.de/brouter-web/ - [brouter.damsy.net](http://brouter.damsy.net) _(provided by [@bagage](https://github.com/bagage))_
This repository is only about the frontend. For the server/backend, BRouter routing engine, Android app, profiles, brouter.de site, see: **This repository is only about the frontend**.
For the server/backend, BRouter routing engine, Android app, profiles, brouter.de site, see:
https://github.com/abrensch/brouter https://github.com/abrensch/brouter
More information: More information:
http://brouter.de http://brouter.de
General BRouter discussions/questions, support: ## Contact
https://groups.google.com/group/osm-android-bikerouting
General BRouter discussions/questions, support:
- [`#brouter` on IRC OFTC](https://webchat.oftc.net/?channels=#brouter). You can also use [Riot](https://riot.im/app/#/room/#_oftc_#brouter:matrix.org).
- [Google Group](https://groups.google.com/group/osm-android-bikerouting)
## Translating
Translations are managed using the
[Transifex](https://www.transifex.com/openstreetmap/brouter-web/) platform. After
signing up, you can go to [BRouter's project
page](https://www.transifex.com/openstreetmap/brouter-web/dashboard/), select a language and
click **Translate** to start translating.
## Installation ## Installation
@ -24,116 +35,148 @@ As an alternative to the above online version, the standalone server of BRouter
### Install BRouter (server with routing engine) ### Install BRouter (server with routing engine)
1. download and unzip latest [BRouter revision](http://brouter.de/brouter/revisions.html) 1. download and unzip latest [BRouter revision](http://brouter.de/brouter/revisions.html)
e.g. for Linux (replace ``~/opt/`` with your preferred install dir and ``1_4_1`` with latest version): e.g. for Linux (replace `~/opt/` with your preferred install directory and `1_4_11` with latest version):
mkdir ~/opt/brouter mkdir ~/opt/brouter
cd ~/opt/brouter cd ~/opt/brouter
wget http://brouter.de/brouter_bin/brouter_1_4_1.zip wget http://brouter.de/brouter_bin/brouter_1_4_11.zip
unzip brouter_1_4_1.zip unzip brouter_1_4_11.zip
chmod +x ./standalone/server.sh chmod +x ./standalone/server.sh
2. download one or more [data file(s)](http://brouter.de/brouter/segments4/) (rd5) into ``segments4`` dir 2. download one or more [data file(s)](http://brouter.de/brouter/segments4/) (rd5) into `segments4` directory
### Install BRouter-Web (client) ### Install BRouter-Web (client)
1. download BRouter-Web as subdirectory ``brouter-web`` of the ``brouter`` directory 1. download BRouter-Web as subdirectory `brouter-web` of the `brouter` directory
* using the latest stable release - adjust to current version number - from
https://github.com/nrenner/brouter-web/releases:
wget https://github.com/nrenner/brouter-web/archive/0.6.3.zip - using the latest stable release - adjust to current version number - from
unzip 0.6.3.zip https://github.com/nrenner/brouter-web/releases:
mv brouter-web-0.6.3 brouter-web
* OR the current development state (potentially instable and without runtime distributables): wget https://github.com/nrenner/brouter-web/releases/download/0.7.0/brouter-web-0.7.0.zip
unzip brouter-web-0.7.0.zip -d brouter-web
wget https://github.com/nrenner/brouter-web/archive/master.zip - OR the current development state (potentially instable and without runtime distributables):
unzip master.zip
mv brouter-web-master brouter-web
* build the distributable files required for runtime (only for development state), see section [Build](#build) wget https://github.com/nrenner/brouter-web/archive/master.zip
unzip master.zip
mv brouter-web-master brouter-web
2. copy ``config.template.js`` to ``config.js`` - build the distributable files required for runtime (only for development state), see section [Build](#build)
3. configure URL to ``profiles2`` directory
set ``BR.conf.profilesUrl`` in config.js, e.g. uncomment:
BR.conf.profilesUrl = 'http://localhost:8000/profiles2/'; 2. copy `config.template.js` to `config.js`
3. configure URL to `profiles2` directory
set `BR.conf.profilesUrl` in config.js, e.g. uncomment:
4. add your API keys (optional) BR.conf.profilesUrl = 'http://localhost:8000/profiles2/';
copy ``keys.template.js`` to ``keys.js`` and edit to add your keys
4. add your API keys (optional)
copy `keys.template.js` to `keys.js` and edit to add your keys
### Run ### Run
1. start BRouter server in the ``standalone`` directory with ``./server.sh`` or ``server.cmd`` (Windows) 1. start BRouter server in the `standalone` directory with `./server.sh` or `server.cmd` (Windows)
2. serve the ``brouter`` directory for BRouter-Web 2. serve the `brouter` directory for BRouter-Web
This is needed for pre-loading the selected profile (unless you allowed local file access in the Browser). Depending on your setup (see [How to run things locally](https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally)), start a web server in the ``brouter`` directory, e.g.: This is needed for pre-loading the selected profile (unless you allowed local file access in the Browser). Depending on your setup (see [How to run things locally](https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally)), start a web server in the `brouter` directory, e.g.:
python -m SimpleHTTPServer python -m SimpleHTTPServer
2. open http://localhost:8000/brouter-web/ 3. open http://localhost:8000/brouter-web/
## Build ## Build
### Dependencies ### Dependencies
Requires [Node and npm](https://nodejs.org/) (or [io.js](https://iojs.org)), [Bower](https://bower.io/) and [Gulp](http://gulpjs.com/): Requires [Node.js](https://nodejs.org/) and [Yarn](https://yarnpkg.com/en/).
npm install -g bower
npm install -g gulp
### Install ### Install
npm install yarn
bower install
### Build ### Build
gulp #for release yarn build #for release
gulp debug #for development yarn build debug #for development
### Develop ### Develop
gulp watch yarn build watch
## License ## License
Copyright (c) 2016 Norbert Renner and [contributors](https://github.com/nrenner/brouter-web/graphs/contributors), licensed under the [MIT License (MIT)](LICENSE) Copyright (c) 2018 Norbert Renner and [contributors](https://github.com/nrenner/brouter-web/graphs/contributors), licensed under the [MIT License (MIT)](LICENSE)
## Credits and Licenses ## Credits and Licenses
* [BRouter](https://github.com/abrensch/brouter) (not included) - [BRouter](https://github.com/abrensch/brouter) (not included)
by abrensch; [GNU General Public License, version 3.0 (GPLv3)](https://github.com/abrensch/brouter/blob/master/LICENSE) by abrensch; [GNU General Public License, version 3.0 (GPLv3)](https://github.com/abrensch/brouter/blob/master/LICENSE)
* [Leaflet](http://leafletjs.com/) - [Leaflet](http://leafletjs.com/)
Copyright (c) 2010-2014, Vladimir Agafonkin; Copyright (c) 2010-2011, CloudMade; [2-clause BSD License](https://github.com/Leaflet/Leaflet/blob/master/LICENSE) Copyright (c) 2010-2014, Vladimir Agafonkin; Copyright (c) 2010-2011, CloudMade; [2-clause BSD License](https://github.com/Leaflet/Leaflet/blob/master/LICENSE)
* [leaflet-routing](https://github.com/Turistforeningen/leaflet-routing) - [leaflet-routing](https://github.com/Turistforeningen/leaflet-routing)
Copyright (c) 2013, Turistforeningen, Hans Kristian Flaatten. All rights reserved. [2-clause BSD License](https://github.com/Turistforeningen/leaflet-routing/blob/gh-pages/LICENSE) Copyright (c) 2013, Turistforeningen, Hans Kristian Flaatten. All rights reserved. [2-clause BSD License](https://github.com/Turistforeningen/leaflet-routing/blob/gh-pages/LICENSE)
* [Leaflet.Elevation](https://github.com/MrMufflon/Leaflet.Elevation) - [Leaflet.Elevation](https://github.com/MrMufflon/Leaflet.Elevation)
Copyright (c) 2013 Felix Bache; [MIT License](https://github.com/MrMufflon/Leaflet.Elevation/blob/master/LICENSE) Copyright (c) 2013 Felix Bache; [MIT License](https://github.com/MrMufflon/Leaflet.Elevation/blob/master/LICENSE)
* [D3.js](https://github.com/mbostock/d3) - [D3.js](https://github.com/mbostock/d3)
Copyright (c) 2013, Michael Bostock. All rights reserved.; [3-clause BSD License](https://github.com/mbostock/d3/blob/master/LICENSE) Copyright (c) 2013, Michael Bostock. All rights reserved.; [3-clause BSD License](https://github.com/mbostock/d3/blob/master/LICENSE)
* [Leaflet.draw](https://github.com/Leaflet/Leaflet.draw) - [Leaflet.Editable](https://github.com/Leaflet/Leaflet.Editable)
Copyright 2012 Jacob Toye; [MIT License](https://github.com/Leaflet/Leaflet.draw/blob/master/MIT-LICENCE.txt) Yohan Boniface; WTFPL licence
* [Leaflet Control Geocoder](https://github.com/perliedman/leaflet-control-geocoder) - [Leaflet Control Geocoder](https://github.com/perliedman/leaflet-control-geocoder)
Copyright (c) 2012 [sa3m](https://github.com/sa3m), Copyright (c) 2013 Per Liedman; [2-clause BSD License](https://github.com/perliedman/leaflet-control-geocoder/blob/master/LICENSE) Copyright (c) 2012 [sa3m](https://github.com/sa3m), Copyright (c) 2013 Per Liedman; [2-clause BSD License](https://github.com/perliedman/leaflet-control-geocoder/blob/master/LICENSE)
* [leaflet-plugins](https://github.com/shramov/leaflet-plugins) - [leaflet-plugins](https://github.com/shramov/leaflet-plugins)
Copyright (c) 2011-2012, Pavel Shramov; [2-clause BSD License](https://github.com/shramov/leaflet-plugins/blob/master/LICENSE) Copyright (c) 2011-2012, Pavel Shramov; [2-clause BSD License](https://github.com/shramov/leaflet-plugins/blob/master/LICENSE)
* [Async.js](https://github.com/caolan/async) - [Async.js](https://github.com/caolan/async)
Copyright (c) 2010-2014 Caolan McMahon; [MIT License](https://github.com/caolan/async/blob/master/LICENSE) Copyright (c) 2010-2014 Caolan McMahon; [MIT License](https://github.com/caolan/async/blob/master/LICENSE)
* [Bootstrap](https://getbootstrap.com/) - [Bootstrap](https://getbootstrap.com/)
Copyright (c) 2011-2014 Twitter, Inc; [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE) Copyright (c) 2011-2014 Twitter, Inc; [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE)
* [jQuery](https://github.com/jquery/jquery) - [jQuery](https://github.com/jquery/jquery)
Copyright 2005, 2014 jQuery Foundation and other contributors; [MIT License](https://github.com/jquery/jquery/blob/master/LICENSE.txt) Copyright 2005, 2014 jQuery Foundation and other contributors; [MIT License](https://github.com/jquery/jquery/blob/master/LICENSE.txt)
* [DataTables](https://github.com/DataTables/DataTables) - [DataTables](https://github.com/DataTables/DataTables)
Copyright (C) 2008-2014, SpryMedia Ltd.; [MIT License](https://www.datatables.net/license/MIT-LICENCE) Copyright (C) 2008-2014, SpryMedia Ltd.; [MIT License](https://www.datatables.net/license/MIT-LICENCE)
* [Leaflet.EasyButton](https://github.com/CliffCloud/Leaflet.EasyButton) - [Leaflet.EasyButton](https://github.com/CliffCloud/Leaflet.EasyButton)
Copyright (C) 2014 Daniel Montague; [MIT License](https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/LICENSE) Copyright (C) 2014 Daniel Montague; [MIT License](https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/LICENSE)
* [Bootbox](https://github.com/makeusabrew/bootbox) - [Bootbox](https://github.com/makeusabrew/bootbox)
Copyright (C) 2011-2014 by Nick Payne; [MIT License](https://github.com/makeusabrew/bootbox/blob/master/LICENSE.md) Copyright (C) 2011-2014 by Nick Payne; [MIT License](https://github.com/makeusabrew/bootbox/blob/master/LICENSE.md)
* [bootstrap-slider](https://github.com/seiyria/bootstrap-slider) - [bootstrap-slider](https://github.com/seiyria/bootstrap-slider)
Copyright (c) 2015 Kyle Kemp, Rohit Kalkur, and contributors; [MIT License](https://github.com/seiyria/bootstrap-slider/blob/master/LICENSE.md) Copyright (c) 2015 Kyle Kemp, Rohit Kalkur, and contributors; [MIT License](https://github.com/seiyria/bootstrap-slider/blob/master/LICENSE.md)
* [Leaflet.RestoreView](https://github.com/makinacorpus/Leaflet.RestoreView) - [Leaflet.RestoreView](https://github.com/makinacorpus/Leaflet.RestoreView)
Copyright (c) 2012 Makina Corpus, [MIT License](https://github.com/makinacorpus/Leaflet.RestoreView/blob/master/LICENSE) Copyright (c) 2012 Makina Corpus, [MIT License](https://github.com/makinacorpus/Leaflet.RestoreView/blob/master/LICENSE)
* [Leaflet.Locate](https://github.com/domoritz/leaflet-locatecontrol) - [Leaflet.Locate](https://github.com/domoritz/leaflet-locatecontrol)
Copyright (c) 2014 Dominik Moritz, [MIT License](https://github.com/domoritz/leaflet-locatecontrol/blob/gh-pages/LICENSE) Copyright (c) 2014 Dominik Moritz, [MIT License](https://github.com/domoritz/leaflet-locatecontrol/blob/gh-pages/LICENSE)
* [Font Awesome](http://fontawesome.io/license/) - [Font Awesome](http://fontawesome.io/license/)
by Dave Gandy; [SIL OFL 1.1](https://scripts.sil.org/OFL) (Font), MIT License (Code), CC BY 3.0 (Documentation) by Dave Gandy; [SIL OFL 1.1](https://scripts.sil.org/OFL) (Font), MIT License (Code), CC BY 3.0 (Documentation)
- [url-search-params](https://github.com/WebReflection/url-search-params)
Copyright (C) 2015-2017 Andrea Giammarchi - @WebReflection; [MIT License](https://github.com/WebReflection/url-search-params/blob/master/LICENSE.txt)
- [bootstrap-select](https://github.com/snapappointments/bootstrap-select)
Copyright (c) 2012-2018 SnapAppointments, LLC; [MIT License](https://github.com/snapappointments/bootstrap-select/blob/v1.13.0-dev/LICENSE)
- [leaflet-sidebar-v2](https://github.com/nickpeihl/leaflet-sidebar-v2)
Copyright (c) 2013 Tobias Bieniek; [MIT License](https://github.com/nickpeihl/leaflet-sidebar-v2/blob/master/LICENSE)
- [CodeMirror](https://github.com/codemirror/CodeMirror)
Copyright (C) 2017 by Marijn Haverbeke <marijnh@gmail.com> and others; [MIT License](https://github.com/codemirror/CodeMirror/blob/master/LICENSE)
- [Map BBCode](https://github.com/MapBBCode/mapbbcode)
Ilya Zverev; [Do What The F\*ck You Want To Public License](https://github.com/MapBBCode/mapbbcode/blob/master/LICENSE)
- [Leafet.StravaSegments](https://gitlab.com/bagage/leaflet.stravasegments)
Copyright (c) 2018 Gautier Pelloux-Prayer; [MIT License](https://gitlab.com/bagage/leaflet.stravasegments/blob/master/LICENSE)
- [polyline](https://github.com/mapbox/polyline)
Copyright (c), Development Seed; [BSD 3-Clause License](https://github.com/mapbox/polyline/blob/master/LICENSE)
- [leaflet-fullHash](https://github.com/KoGor/leaflet-fullHash)
Copyright (c) 2014 KoGor; [MIT License](https://github.com/KoGor/leaflet-fullHash/blob/master/LICENSE)
- [Turf.js](https://github.com/Turfjs/turf)
Copyright (c) 2019 Morgan Herlocker; [MIT License](https://github.com/Turfjs/turf/blob/master/LICENSE)
- [i18next](https://github.com/i18next/i18next), [i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector), [i18next-xhr-backend](https://github.com/i18next/i18next-xhr-backend), [jquery-i18next](https://github.com/i18next/jquery-i18next/blob/master/LICENSE)
Copyright (c) 2017 i18next; [MIT License](https://github.com/i18next/i18next/blob/master/LICENSE)
- [Leaflet TriangleMarker](https://github.com/themeler/leaflet-triangle-marker)
Copyright (c) 2018 Przemysław Melnarowicz; [MIT License](https://github.com/themeler/leaflet-triangle-marker/blob/master/LICENSE)
- [jsTree ](https://github.com/vakata/jstree)
Copyright (c) 2014 Ivan Bozhanov; [MIT License](https://github.com/vakata/jstree/blob/master/LICENSE-MIT)
- [Leaflet.snogylop](https://github.com/ebrelsford/leaflet.snogylop)
Copyright (c) 2014 Eric Brelsford; [MIT License](https://github.com/ebrelsford/Leaflet.snogylop/blob/master/LICENSE)
- [JOSM maps](https://josm.openstreetmap.de/wiki/Maps)
imagery database is licensed under [CC-BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)
- [LayersCollection](https://github.com/Edward17/LayersCollection/tree/gh-pages)
Copyright (c) 2016 Eduard &lt;edward17&gt;; [MIT License](https://github.com/Edward17/LayersCollection/blob/gh-pages/LICENSE.md)
- [Leaflet-providers](https://github.com/leaflet-extras/leaflet-providers)
Copyright (c) 2013 Leaflet Providers contributors All rights reserved.; [2-clause BSD License](https://github.com/leaflet-extras/leaflet-providers/blob/master/license.md)
- [Fetch polyfill](https://github.com/Github/fetch)
Copyright (c) 2014-2016 GitHub, Inc.; [MIT License](https://github.com/github/fetch/blob/master/LICENSE)
- [Promise Polyfill](https://github.com/taylorhakes/promise-polyfill)
Copyright (c) 2014 Taylor Hakes, Copyright (c) 2014 Forbes Lindesay; [MIT License](https://github.com/taylorhakes/promise-polyfill/blob/master/LICENSE)

View file

@ -1,117 +0,0 @@
{
"name": "brouter-web",
"version": "0.6.3",
"main": [
"dist/brouter-web.css",
"dist/brouter-web.js"
],
"ignore": [
"**/.*",
"bower_components"
],
"dependencies": {
"leaflet": "^1.0.3",
"leaflet-plugins": "~3.0.0",
"leaflet-routing": "nrenner/leaflet-routing#leaflet-1.0",
"async": "~0.9.2",
"d3": "~3.5.5",
"leaflet.draw": "~0.4.9",
"bootstrap": "4.0.0-alpha.5",
"DataTables": "~1.10.13",
"leaflet.elevation": "MrMufflon/Leaflet.Elevation#master",
"leaflet-control-geocoder": "1.5.3",
"Leaflet.EasyButton": "*",
"bootbox": "~4.4.0",
"seiyria-bootstrap-slider": "^9.8.1",
"url-search-params": "~0.5.0",
"Leaflet.RestoreView": "makinacorpus/Leaflet.RestoreView#master",
"leaflet.locatecontrol": "^0.60.0",
"font-awesome": "^4.7.0",
"bootstrap-select": "hugdx/bootstrap-select#patch-1",
"leaflet-sidebar": "^0.1.9",
"autosize": "^3.0.20"
},
"overrides": {
"leaflet": {
"main": [
"dist/leaflet-src.js",
"dist/leaflet.css",
"dist/images/*.png"
]
},
"leaflet-plugins": {
"main": [
"layer/tile/Bing.js"
]
},
"leaflet-routing": {
"main": [
"src/utils/LineUtil.Snapping.js",
"src/utils/Marker.Snapping.js",
"src/L.Routing.js",
"src/L.Routing.Draw.js",
"src/L.Routing.Edit.js"
]
},
"leaflet.draw": {
"main": [
"dist/leaflet.draw-src.js",
"dist/leaflet.draw.css",
"dist/images/*.png",
"dist/images/*.svg"
],
"dependencies": null
},
"bootstrap-select": {
"main": [
"js/bootstrap-select.js",
"dist/css/bootstrap-select.css"
]
},
"bootstrap": {
"main": [
"dist/js/bootstrap.js",
"dist/css/bootstrap.css"
]
},
"leaflet.elevation": {
"main": [
"dist/leaflet.elevation-0.0.4.src.js",
"dist/leaflet.elevation-0.0.4.css",
"dist/images/*.png"
],
"dependencies": null
},
"leaflet-control-geocoder": {
"main": [
"dist/Control.Geocoder.js",
"dist/Control.Geocoder.css",
"images/*.+(png|gif)"
]
},
"url-search-params": {
"main": "build/url-search-params.js"
},
"Leaflet.RestoreView": {
"main": "leaflet.restoreview.js"
},
"font-awesome": {
"main": [
"css/font-awesome.css",
"fonts/*"
]
},
"autosize": {
"main": "dist/autosize.js"
},
"seiyria-bootstrap-slider": {
"dependencies": {
"jquery": "*",
"bootstrap": "*"
}
}
},
"resolutions": {
"leaflet": "^1.0.3"
}
}

View file

@ -1,5 +1,4 @@
(function() { (function() {
var hostname = window.location.hostname; var hostname = window.location.hostname;
var params = new URLSearchParams(window.location.search.slice(1)); var params = new URLSearchParams(window.location.search.slice(1));
@ -13,13 +12,13 @@
//BR.conf.transit = params.has('transit') && (params.get('transit') === 'true'); //BR.conf.transit = params.has('transit') && (params.get('transit') === 'true');
if (hostname === 'brouter.de' ) { if (hostname === 'brouter.de' ) {
// online service (brouter.de) configuration // online service (brouter.de) configuration
BR.conf.profiles = [ BR.conf.profiles = [
'trekking', 'trekking',
'fastbike', 'fastbike',
'car-test', 'car-eco',
'car-fast',
'safety', 'safety',
'shortest', 'shortest',
'trekking-ignore-cr', 'trekking-ignore-cr',
@ -38,17 +37,20 @@
BR.conf.host = 'http://brouter.de:443'; BR.conf.host = 'http://brouter.de:443';
BR.conf.profilesUrl = 'http://brouter.de/brouter/profiles2/'; BR.conf.profilesUrl = 'http://brouter.de/brouter/profiles2/';
} else { } else {
// desktop configuration // desktop configuration
BR.conf.profiles = [ BR.conf.profiles = [
'trekking', 'trekking',
'fastbike', 'fastbike',
'car-eco',
'car-fast',
'shortest', 'shortest',
'moped', 'moped',
'car-test' 'vm-forum-liegerad-schnell',
'vm-forum-velomobil-schnell',
'fastbike-lowtraffic',
'fastbike-asia-pacific'
]; ];
BR.conf.host = 'http://0.0.0.0:17777'; BR.conf.host = 'http://0.0.0.0:17777';
@ -73,6 +75,9 @@
//'Mapsforge Tile Server': 'http://localhost:6090/{z}/{x}/{y}.png' //'Mapsforge Tile Server': 'http://localhost:6090/{z}/{x}/{y}.png'
}; };
// Base layer to show on start, as position number in the layer switcher, starting from 0, default is first
BR.conf.defaultBaseLayerIndex = 0;
// Initial route line transparency (0-1, overridden by stored slider setting) // Initial route line transparency (0-1, overridden by stored slider setting)
BR.conf.defaultOpacity = 0.67; BR.conf.defaultOpacity = 0.67;
@ -105,7 +110,6 @@
// transit (intermodal routing) demo config // transit (intermodal routing) demo config
if (BR.conf.transit) { if (BR.conf.transit) {
BR.conf.profiles = [ BR.conf.profiles = [
'../im/bike', '../im/bike',
'../im/foot', '../im/foot',
@ -117,6 +121,8 @@
'moped', 'moped',
'car-test' 'car-test'
]; ];
} }
// regex needs to be in sync with server, see ServerHandler.getTrackName()
BR.conf.tracknameAllowedChars = 'a-zA-Z0-9 \\._\\-';
})(); })();

View file

@ -1,5 +1,5 @@
div.line-mouse-marker { div.line-mouse-marker {
background-color: white; background-color: white;
border: 4px solid magenta; border: 4px solid magenta;
border-radius: 8px; border-radius: 8px;
} }

View file

@ -1,48 +1,83 @@
html, body, #map { html,
body,
#map {
height: 100%; height: 100%;
} }
/* This is important so that bootstrap-select list goes over leaflet buttons /* This is important so that bootstrap-select list goes over leaflet buttons
Since the list is in navbar and leaflet buttons within map, we create a Since the list is in navbar and leaflet buttons within map, we create a
stacking context on their common ancestor */ stacking context on their common ancestor */
body, #content { body,
#content {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.flexcolumn { #content {
display: -webkit-box; overflow: hidden;
display: -moz-box; }
display: -ms-flexbox;
display: -webkit-flex; .flexcolumn,
.leaflet-sidebar-pane.active,
.dataTables_wrapper,
.dataTables_scroll,
.dataTables_scrollBody {
display: flex; display: flex;
flex-flow: column; flex-flow: column;
} }
.flexrow { .flexrow {
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
#content { .flexgrow {
-webkit-box-flex: 1;
-moz-box-flex: 1;
-webkit-flex: 1;
-ms-flex: 1;
flex: 1; flex: 1;
} }
/* 'auto' (1 1 auto) instead of '1' (1 1 0) needed for DataTables tab in Firefox */
.dataTables_wrapper,
.dataTables_scroll,
.dataTables_scrollBody,
table.dataTable {
flex: auto;
}
/* wrap toolbar controls */
.leaflet-top.leaflet-left {
bottom: 0;
margin-bottom: 10px;
display: flex;
flex-direction: column;
align-items: flex-start;
flex-wrap: wrap;
}
/* bottom underneath top controls when overlapping */
.leaflet-bottom {
z-index: 999;
}
.navbar {
/* align with leaflet-control */
padding: 0.5rem 10px;
}
.nav-link .fa {
margin-right: 0.2em;
}
.navbar-dark .navbar-toggler {
background-image: none;
}
/* disabled style for "Custom" option, but not for selected items */
.bootstrap-select.btn-group .dropdown-menu li.disabled:not(.selected) a {
color: #777;
}
footer { footer {
-webkit-box-flex: none;
-moz-box-flex: none;
-webkit-flex: none;
-ms-flex: none;
flex: none; flex: none;
background-color: #f7f7f9; background-color: #f7f7f9;
@ -52,39 +87,76 @@ footer {
flex-grow: 1; flex-grow: 1;
margin: 0; margin: 0;
padding: 0; padding: 0;
text-align: center;
} }
#stats li { #stats li {
margin: 0 1rem; margin: 0 1rem;
display: inline-block;
}
@media (max-width: 767px) {
#stats li {
margin: 0 0.5rem;
}
#stats {
padding-top: 0.4em;
}
p.stats-label {
margin-bottom: 0.4em;
}
} }
#elevation-chart { .stats-label {
height: 175px; word-break: break-all;
font-size: 80%; font-weight: bold;
}
.stats-label abbr[title],
#distance {
text-decoration: none;
border-bottom: 1px dotted #818a91;
} }
.form-group { .form-group {
margin-bottom: 0 margin-bottom: 0;
} }
.bootstrap-select.btn-group:not(.input-group-btn), input#trackname:invalid,
.bootstrap-select.btn-group[class*="col-"] { input#trackname:focus:invalid {
margin-left: 10px; border-color: orange;
}
:not(output):-moz-ui-invalid:not(:focus),
:not(output):-moz-ui-invalid:-moz-focusring:not(:focus) {
box-shadow: none;
} }
.validation-warning {
color: orange;
font-size: small;
}
.axis path, /*
.axis line { * elevation diagram
stroke: #818a91; */
fill: none;
.steelblue-theme.leaflet-control.elevation .background {
display: block;
font-size: 80%;
border-radius: 0;
}
/* diagram colors lighter, less dominant */
.steelblue-theme.leaflet-control.elevation .background {
background-color: rgba(105, 155, 196, 0.2);
}
.steelblue-theme.leaflet-control.elevation .axis path,
.steelblue-theme.leaflet-control.elevation .axis line {
stroke: #8398aa;
shape-rendering: crispEdges; shape-rendering: crispEdges;
} }
.steelblue-theme.leaflet-control.elevation .axis text {
.axis text { fill: #8398aa;
fill: #818a91;
} }
.steelblue-theme.leaflet-control.elevation .area {
.area { fill: #699bc4;
fill: rgba(129, 138, 145, 0.45);
} }
#elevation-btn { #elevation-btn {
@ -96,24 +168,48 @@ footer {
cursor: crosshair; cursor: crosshair;
} }
#map {
/* center error message horizontally */
display: flex;
justify-content: center;
/* map click/drag selects text in controls in Firefox because of display flex */
-moz-user-select: none;
}
.leaflet-control-container,
#message .alert {
-moz-user-select: text;
}
#message { #message {
position: absolute; position: absolute;
left: 446px; /* 400 + 10 + 26 + 10 */ margin: 10px 46px; /* 10 + 26 + 10 */
top: 0px;
margin-top: 10px;
z-index: 3000; z-index: 3000;
font-size: 1rem;
cursor: auto;
}
#profile_buttons {
padding-top: 4px;
} }
/* track messages (data tab) */ /* track messages (data tab) */
#tab_data, #profile_upload { #tab_data,
.CodeMirror {
font-size: x-small; font-size: x-small;
} }
/* transit demo */
#itinerary pre {
font-size: small;
/* turn off bootstrap 'break-word' */
word-wrap: normal;
margin: 0;
}
/* dashed line animation, derived from Chris Coyier and others /* dashed line animation, derived from Chris Coyier and others
https://css-tricks.com/svg-line-animation-works/ https://css-tricks.com/svg-line-animation-works/
*/ */
.loading-trailer { .loading-trailer {
-webkit-animation: dash 0.4s linear infinite;
animation: dash 0.4s linear infinite; animation: dash 0.4s linear infinite;
} }
@-webkit-keyframes dash { @-webkit-keyframes dash {
@ -140,12 +236,19 @@ https://css-tricks.com/svg-line-animation-works/
.control-slider { .control-slider {
background: #fff; background: #fff;
border-radius: 10px; border-radius: 10px;
padding-top: 10px; }
padding-bottom: 10px;
.slider#overlay {
display: block;
} }
.slider.slider-vertical { .slider.slider-vertical {
height: 80px; height: 80px;
margin: 10px 0;
}
.slider.slider-horizontal {
width: 180px;
margin: 0 10px;
} }
/* invert track and selection styles to get partial gradient for "selection" */ /* invert track and selection styles to get partial gradient for "selection" */
@ -153,16 +256,22 @@ https://css-tricks.com/svg-line-animation-works/
width: 8px; width: 8px;
margin-left: 1px; margin-left: 1px;
background-image: linear-gradient(to right, #f0f0f0 0%, #e9e9e9 100%); background-image: linear-gradient(to right, #f0f0f0 0%, #e9e9e9 100%);
box-shadow: inset -1px -0px 1px rgba(55, 55, 55, 0.3), inset 1px 0px 1px rgba(230, 230, 230, 1); box-shadow: inset -1px -0px 1px rgba(55, 55, 55, 0.3),
inset 1px 0px 1px rgba(230, 230, 230, 1);
}
.slider.slider-horizontal .slider-track {
background-image: linear-gradient(to bottom, #f0f0f0 0%, #e9e9e9 100%);
box-shadow: inset -0px -1px 1px rgba(55, 55, 55, 0.3),
inset 0px 1px 1px rgba(230, 230, 230, 1);
} }
.control-slider:hover .slider-track, .control-slider:hover #route .slider-track,
.control-slider:active .slider-track { .control-slider:active #route .slider-track {
background-image: linear-gradient(to bottom, magenta 0%, white 100%); background-image: linear-gradient(to bottom, magenta 0%, white 100%);
} }
.slider-selection { .slider-selection {
background-color: #C6C6C6; background-color: #c6c6c6;
background-image: none; background-image: none;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.6); box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.6);
} }
@ -170,27 +279,56 @@ https://css-tricks.com/svg-line-animation-works/
.slider.slider-vertical .slider-tick, .slider.slider-vertical .slider-tick,
.slider.slider-vertical .slider-handle { .slider.slider-vertical .slider-handle {
cursor: ns-resize; cursor: ns-resize;
}
.slider .slider-tick,
.slider .slider-handle {
cursor: ew-resize;
box-sizing: border-box; box-sizing: border-box;
background: none; background: none;
outline: none; outline: none;
/* bootstrap .btn-default */ /* bootstrap .btn-secondary */
background-image: linear-gradient(to bottom,#fff 0,#e0e0e0 100%); background-image: linear-gradient(to bottom, #fff 0, #e0e0e0 100%);
background-repeat: repeat-x; background-repeat: repeat-x;
box-shadow: inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15),
0 1px 1px rgba(0, 0, 0, 0.075);
border: 1px solid #adadad; border: 1px solid #adadad;
} }
/* activated Font Awesome icon and Bootstrap button /* activated Font Awesome icon (for EasyButton add ' active' to icon class property) */
(for EasyButton add ' active' to icon class property) */ .fa.active {
.fa.active, .btn.active, .btn.active:hover, .btn.active:focus {
/* use same color as leaflet-locatecontrol */ /* use same color as leaflet-locatecontrol */
color: #2074B6; color: #2074b6;
}
/* Bootstrap button */
.btn-secondary:not(:disabled):not(.disabled).active,
.btn-secondary.active,
.btn-secondary:hover,
.btn-outline-secondary:not(:disabled):not(.disabled).active,
.btn-outline-secondary:hover {
/* use same color as leaflet-locatecontrol */
color: #2074b6;
background-color: #e6e6e6;
} }
.stats-label { button.btn {
word-break: break-all; box-shadow: none !important;
font-weight: bold; }
.leaflet-bar button {
/* override button for horizontal fa center in Firefox */
padding: 0;
}
.leaflet-bar .fa {
font-size: 14px;
/* override fa with Leaflet button height for vertical center */
line-height: 26px;
}
.leaflet-touch .leaflet-bar .fa {
font-size: 16px;
line-height: 30px;
} }
/* smaller tab height */ /* smaller tab height */
@ -198,10 +336,29 @@ https://css-tricks.com/svg-line-animation-works/
padding: 2px 15px; padding: 2px 15px;
} }
#profile_message .alert {
margin-top: 3px;
}
.alert {
margin-bottom: 0px;
padding-left: 35px;
}
.alert span.fa {
position: relative;
left: -23px;
margin-right: -1em;
}
/* /*
* DataTables * DataTables
*/ */
table.dataTable {
/* avoid getting centered and header misaligned with flex row (sidebar) */
margin: 0;
}
table.dataTable.mini thead th, table.dataTable.mini thead th,
table.dataTable.mini thead td { table.dataTable.mini thead td {
padding: 3px 13px 3px 2px; padding: 3px 13px 3px 2px;
@ -224,5 +381,147 @@ table.dataTable.hover tbody tr.even:hover,
table.dataTable.display tbody tr:hover, table.dataTable.display tbody tr:hover,
table.dataTable.display tbody tr.odd:hover, table.dataTable.display tbody tr.odd:hover,
table.dataTable.display tbody tr.even:hover { table.dataTable.display tbody tr.even:hover {
background-color: rgba(255,255,0,0.2); background-color: rgba(255, 255, 0, 0.2);
}
/*
* No-go areas
*/
.nogo-delete-marker {
text-align: center;
line-height: 15px;
font-size: 11px;
border-radius: 8px;
}
.leaflet-touch .nogo-delete-marker {
border-radius: 12px;
line-height: 24px;
}
/* tooltip */
.editing-tooltip,
.editing-tooltip-create {
color: #fff;
background-color: rgba(0, 0, 0, 0.7);
/* for direction arrows that inherit */
border-color: rgba(0, 0, 0, 0.7);
/* no border but still set a color for direction arrows */
border-width: 0px;
}
.editing-tooltip-create {
/* no (invisible) direction arrow for cursor tooltip */
border-color: transparent;
}
.leaflet-tooltip-bottom:before {
border-bottom-color: inherit;
}
.leaflet-tooltip-right:before {
border-right-color: inherit;
}
/*
* leaflet-sidebar-v2
*/
.leaflet-sidebar-pane#tab_profile,
.leaflet-sidebar-pane#tab_data,
.leaflet-sidebar-pane#tab_itinerary {
/* Full height for content with inner scrolling,
overrides scroll fix for long content in Firefox */
height: 100%;
}
.leaflet-sidebar-content {
/* for optional-layers-tree */
overflow-x: auto;
}
/* layers control as sidebar tab */
#tab_layers_control {
font-size: 0.9rem;
line-height: normal;
}
#layers-control-wrapper label {
/* override Bootstrap/Reboot label */
display: block;
margin-bottom: 0px;
/* normalize label height
| Firefox | Chrome |
Leaflet only | 21 | 20 |
Bootstrap | 23 | 22 |*/
height: 23px;
}
#layers-button-group {
display: flex;
justify-content: space-between;
}
#tree-button-group {
margin-right: 5px;
}
#optional-layers-tree {
margin-top: 5px;
}
.tree-code {
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console,
monospace;
margin-right: 7px;
color: #666; /* like root nodes, jstree-disabled */
}
/* hide currently unused bottom tabs container because of touch border artefacts */
.leaflet-sidebar-tabs > ul:last-child {
display: none;
}
/* layers svg icon not properly centered */
.leaflet-sidebar-tabs > ul > li > a[href='#tab_layers_control'] {
display: flex;
align-items: center;
justify-content: center;
}
/*
* CodeMirror
*/
.CodeMirror {
/* auto instead of 1 to avoid overflow to content height in Firefox */
flex: auto;
/* override default 300px, behaves like min-height with flex auto */
height: 0;
border: 1px solid #ddd;
font-family: Menlo, Consolas, Monaco, Liberation Mono, Lucida Console,
monospace;
line-height: 1.2em;
}
#loadNogos fieldset {
display: block;
margin-left: 2px;
margin-right: 2px;
padding-top: 0.35em;
padding-bottom: 0.625em;
padding-left: 0.75em;
padding-right: 0.75em;
border: 2px groove;
}
#loadNogos legend {
display: block;
padding-left: 2px;
padding-right: 2px;
border: none;
}
.nav-link.disabled {
/* by default, even if disabled, modals are opened by disabled nav-link
so we ignore pointer events in this situation to avoid that*/
pointer-events: none;
} }

View file

@ -1,13 +1,12 @@
var gulp = require('gulp'); var gulp = require('gulp');
var concat = require('gulp-concat'); var concat = require('gulp-concat');
var concatCss = require('gulp-concat-css'); var postcss = require('gulp-postcss');
var minifyCss = require('gulp-minify-css'); var autoprefixer = require('autoprefixer');
var uglify = require('gulp-uglify'); var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps'); var sourcemaps = require('gulp-sourcemaps');
var gulpDebug = require('gulp-debug'); var gulpDebug = require('gulp-debug');
var mainBowerFiles = require('main-bower-files'); var mainNpmFiles = require('npmfiles');
var del = require('del'); var del = require('del');
var tap = require('gulp-tap');
var path = require('path'); var path = require('path');
var cached = require('gulp-cached'); var cached = require('gulp-cached');
var remember = require('gulp-remember'); var remember = require('gulp-remember');
@ -20,219 +19,376 @@ var semver = require('semver');
var git = require('gulp-git'); var git = require('gulp-git');
var replace = require('gulp-replace'); var replace = require('gulp-replace');
var release = require('gulp-github-release'); var release = require('gulp-github-release');
var cleanCSS = require('gulp-clean-css');
var modifyCssUrls = require('gulp-modify-css-urls');
var sort = require('gulp-sort');
var scanner = require('i18next-scanner');
var jsonConcat = require('gulp-json-concat');
var rename = require('gulp-rename');
var debug = false; var debug = false;
var paths = { var paths = {
// see overrides in bower.json // see overrides in package.json
scriptsConfig: mainBowerFiles('**/url-search-params/**/*.js'), scriptsConfig: mainNpmFiles().filter(f =>
scripts: mainBowerFiles([ RegExp('url-search-params/.*\\.js', 'i').test(f)
'**/*.js', ),
'!**/*.min.js', scripts: [
'!**/url-search-params/**/*.js' 'node_modules/jquery/dist/jquery.js',
]).concat([ 'node_modules/tether/dist/js/tether.js',
'js/Browser.js', 'node_modules/async/lib/async.js',
'js/Util.js', 'node_modules/leaflet/dist/leaflet-src.js'
'js/Map.js', ]
'js/router/BRouter.js', .concat(
'js/plugin/*.js', mainNpmFiles().filter(
'js/control/*.js', f =>
'js/index.js' RegExp('.*\\.js', 'i').test(f) &&
]), !RegExp('.*\\.min\\.js', 'i').test(f) &&
styles: mainBowerFiles('**/*.css').concat('css/*.css'), !RegExp('url-search-params/.*\\.js', 'i').test(f)
images: mainBowerFiles('**/*.+(png|gif|svg)'), )
fonts: mainBowerFiles('**/font-awesome/fonts/*'), )
dest: 'dist', .concat([
destName: 'brouter-web' 'js/Browser.js',
'js/Util.js',
'js/Map.js',
'js/LayersConfig.js',
'js/router/BRouter.js',
'js/plugin/*.js',
'js/control/*.js',
'js/index.js'
]),
styles: mainNpmFiles()
.filter(
f =>
RegExp('.*\\.css', 'i').test(f) &&
!RegExp('.*\\.min\\.css', 'i').test(f)
)
.concat('css/*.css'),
images: mainNpmFiles().filter(f =>
RegExp('.*.+(png|gif|svg)', 'i').test(f)
),
fonts: mainNpmFiles().filter(f =>
RegExp('font-awesome/fonts/.*', 'i').test(f)
),
locales: 'locales/*.json',
layers: 'layers/**/*.geojson',
layersDestName: 'layers.js',
layersConfig: [
'layers/config/config.js',
'layers/config/tree.js',
'layers/config/overrides.js',
'layers/config/geometry.js'
],
layersConfigDestName: 'layersConf.js',
dest: 'dist',
destName: 'brouter-web'
}; };
gulp.task('clean', function(cb) {
del(paths.dest + '/**/*', cb);
});
// libs that require loading before config.js // libs that require loading before config.js
gulp.task('scripts_config', ['clean'], function() { gulp.task('scripts_config', function() {
// just copy for now // just copy for now
return gulp.src(paths.scriptsConfig) return gulp.src(paths.scriptsConfig).pipe(gulp.dest(paths.dest));
.pipe(gulp.dest(paths.dest));
}); });
gulp.task('scripts', function() { gulp.task('scripts', function() {
if (debug) if (debug) gutil.log(gutil.colors.yellow('Running in Debug mode'));
gutil.log( gutil.colors.yellow('Running in Debug mode') ); else gutil.log(gutil.colors.green('Running in Release mode'));
else
gutil.log( gutil.colors.green('Running in Release mode') );
return gulp.src(paths.scripts, { base: '.' }) return gulp
.pipe(sourcemaps.init()) .src(paths.scripts, { base: '.' })
.pipe(cached('scripts')) .pipe(sourcemaps.init())
.pipe(gulpif(!debug, uglify())) .pipe(cached('scripts'))
.pipe(remember('scripts')) .pipe(gulpif(!debug, uglify()))
.pipe(concat(paths.destName + '.js')) .pipe(remember('scripts'))
.pipe(sourcemaps.write('.')) .pipe(concat(paths.destName + '.js'))
.pipe(gulp.dest(paths.dest)); .pipe(sourcemaps.write('.'))
.pipe(gulp.dest(paths.dest));
}); });
// separate, fallback task for debugging (switch manually in index.html) // separate, fallback task for debugging (switch manually in index.html)
gulp.task('concat', function() { gulp.task('concat', function() {
return gulp.src(paths.scripts) return gulp
.pipe(concat(paths.destName + '.src.js')) .src(paths.scripts)
.pipe(gulp.dest(paths.dest)); .pipe(concat(paths.destName + '.src.js'))
.pipe(gulp.dest(paths.dest));
}); });
gulp.task('styles', function() { gulp.task('styles', function() {
return gulp.src(paths.styles) return gulp
// hack for rewriting relative URLs to images/fonts in gulp-concat-css .src(paths.styles)
// when src in css subfolder (remove '../') .pipe(
// see also (?) https://github.com/mariocasciaro/gulp-concat-css/pull/10 modifyCssUrls({
.pipe(tap(function (file) { modify(url, filePath) {
if (path.basename(file.base) === 'css') { var distUrl = url;
file.path = 'css/' + file.relative; var imageExt = ['.png', '.gif', '.svg'];
file.base = './css';
} else { if (imageExt.indexOf(path.extname(url)) !== -1) {
file.path = file.relative; distUrl = 'images/' + path.basename(url);
file.base = '.'; } else if (url.indexOf('font') !== -1) {
} distUrl = 'fonts/' + path.basename(url);
})) }
.pipe(concatCss(paths.destName + '.css'))
.pipe(minifyCss({ return distUrl;
rebase: false }
})) })
.pipe(gulp.dest(paths.dest)); )
.pipe(concat(paths.destName + '.css'))
.pipe(
cleanCSS({
rebase: false
})
)
.pipe(postcss([autoprefixer({ remove: false })]))
.pipe(gulp.dest(paths.dest));
}); });
gulp.task('images', ['clean'], function() { gulp.task('images', function() {
return gulp.src(paths.images) return gulp.src(paths.images).pipe(gulp.dest(paths.dest + '/images'));
.pipe(gulp.dest(paths.dest + '/images'));
}); });
gulp.task('fonts', ['clean'], function() { gulp.task('fonts', function() {
return gulp.src(paths.fonts) return gulp.src(paths.fonts).pipe(gulp.dest(paths.dest + '/fonts'));
.pipe(gulp.dest(paths.dest + '/fonts'));
}); });
gulp.task('clean', function(cb) { gulp.task('locales', function() {
del(paths.dest + '/**/*' , cb); return gulp.src(paths.locales).pipe(gulp.dest(paths.dest + '/locales'));
}); });
gulp.task('watch', function() { gulp.task('watch', function() {
debug = true; debug = true;
var watcher = gulp.watch(paths.scripts, ['scripts']); var watcher = gulp.watch(paths.scripts, gulp.series('scripts'));
watcher.on('change', function (event) { watcher.on('change', function(event) {
if (event.type === 'deleted') { if (event.type === 'deleted') {
delete cached.caches.scripts[event.path]; delete cached.caches.scripts[event.path];
remember.forget('scripts', event.path); remember.forget('scripts', event.path);
} }
}); });
gulp.watch(paths.styles, ['styles']); gulp.watch(paths.styles, gulp.series('styles'));
gulp.watch(paths.layersConfig, gulp.series('layers_config'));
}); });
// Print paths to console, for manually debugging the gulp build // Print paths to console, for manually debugging the gulp build
// (comment out corresponding line of paths to print) // (comment out corresponding line of paths to print)
gulp.task('log', function() { gulp.task('log', function() {
//return gulp.src(mainBowerFiles(['**/*.js', '!**/*.min.js'])) //return gulp.src(paths.scripts)
//return gulp.src(mainBowerFiles('**/*.css')) //return gulp.src(paths.styles)
return gulp.src(paths.scripts) //return gulp.src(paths.images)
//return gulp.src(paths.styles) // return gulp.src(paths.locales)
//return gulp.src(paths.images) return gulp
.pipe(gulpDebug()); .src(
paths.scripts
//return gulp.src(mainBowerFiles({debugging: true})); .concat(paths.styles)
.concat(paths.images)
.concat(paths.locales)
)
.pipe(gulpDebug());
}); });
gulp.task('inject', function () { gulp.task('inject', function() {
var target = gulp.src('index.html'); var target = gulp.src('index.html');
var sources = gulp.src(paths.scripts, { base: '.', read: false }); var sources = gulp.src(paths.scripts.concat(paths.styles), {
base: '.',
read: false
});
return target.pipe(inject(sources, { relative: true })) return target
.pipe(gulp.dest('.')); .pipe(inject(sources, { relative: true }))
}); .pipe(gulp.dest('.'));
gulp.task('default', ['clean', 'scripts_config', 'scripts', 'styles', 'images', 'fonts']);
gulp.task('debug', function() {
debug = true;
gulp.start('default');
}); });
var pkg = require('./package.json'); var pkg = require('./package.json');
var tags = {patch: 'patch', minor: 'minor', major: 'major'}; var tags = { patch: 'patch', minor: 'minor', major: 'major' };
var nextVersion; var nextVersion;
var ghToken; var ghToken;
gulp.task('release:init', function() { gulp.task('release:init', function(cb) {
var tag = gutil.env.tag; var tag = gutil.env.tag;
if (!tag) { if (!tag) {
gutil.log(gutil.colors.red('--tag is required')); return cb(new Error('--tag is required'));
process.exit(1);
}
if (['major', 'minor', 'patch'].indexOf(tag) < 0) {
gutil.log(gutil.colors.red('--tag must be major, minor or patch'));
process.exit(2);
}
ghToken = gutil.env.token;
if (!ghToken) {
gutil.log(gutil.colors.red('--token is required (github personnal access token)'));
process.exit(3);
}
if (ghToken.length != 40) {
gutil.log(gutil.colors.red('--token length must be 40'));
process.exit(4);
}
git.status({args: '--porcelain', quiet: true}, function(err, stdout) {
if (err) throw err;
if (stdout.length > 0) {
gutil.log(gutil.colors.red('Repository is not clean. Please commit or stash your pending modification'));
process.exit(5);
} }
}); if (['major', 'minor', 'patch'].indexOf(tag) < 0) {
nextVersion = semver.inc(pkg.version, tag); return cb(new Error('--tag must be major, minor or patch'));
return; }
ghToken = gutil.env.token;
if (!ghToken) {
return cb(
new Error('--token is required (github personnal access token')
);
}
if (ghToken.length != 40) {
return cb(new Error('--token length must be 40'));
}
nextVersion = semver.inc(pkg.version, tag);
git.status({ args: '--porcelain', quiet: true }, function(err, stdout) {
if (err) return cb(err);
if (stdout.length > 0) {
return cb(
new Error(
'Repository is not clean. Please commit or stash your pending modification'
)
);
}
cb();
});
}); });
gulp.task('bump', ['bump:json', 'bump:html']); gulp.task('bump:json', function() {
gutil.log(gutil.colors.green('Bump to ' + nextVersion));
gulp.task('bump:json', ['release:init'], function() { return gulp
gutil.log(gutil.colors.green('Bump to '+nextVersion)); .src(['./package.json'])
return(gulp.src(['./package.json', './bower.json']) .pipe(bump({ version: nextVersion }))
.pipe(bump({version: nextVersion})) .pipe(gulp.dest('./'));
.pipe(gulp.dest('./')));
}); });
gulp.task('bump:html', ['release:init'], function() { gulp.task('bump:html', function() {
return(gulp.src('./index.html') return gulp
.pipe(replace(/<sup class="version">(.*)<\/sup>/, '<sup class="version">'+nextVersion+'</sup>')) .src('./index.html')
.pipe(gulp.dest('.'))); .pipe(
replace(
/<sup class="version">(.*)<\/sup>/,
'<sup class="version">' + nextVersion + '</sup>'
)
)
.pipe(gulp.dest('.'));
}); });
gulp.task('release:commit', ['bump'], function() { gulp.task('bump', gulp.series('bump:json', 'bump:html'));
gulp.src(['./index.html', './package.json', './bower.json'])
.pipe(git.commit('release: '+nextVersion)); gulp.task('release:commit', function() {
return gulp
.src(['./index.html', './package.json'])
.pipe(git.commit('release: ' + nextVersion));
}); });
gulp.task('release:tag', ['release:commit'], function() { gulp.task('release:tag', function(cb) {
return(git.tag(nextVersion, '', function(err) { return git.tag(nextVersion, '', cb);
if (err) throw err;
}));
}); });
gulp.task('release:push', ['release:tag'], function() { gulp.task('release:push', function(cb) {
git.push('origin', 'master', {args: '--tags'}, function(err) { git.push('origin', 'master', { args: '--tags' }, cb);
if (err) throw err;
});
}); });
gulp.task('release:zip', ['release:tag', 'default'], function() { gulp.task('i18next', function() {
gutil.log(gutil.colors.green('Build brouter-web.'+nextVersion+'.zip')); return gulp
return(gulp.src(['dist/**', 'index.html', 'config.template.js', 'keys.template.js'], {'base': '.'}) .src([
.pipe(zip('brouter-web.'+nextVersion+'.zip')) 'index.html',
.pipe(gulp.dest('.'))); 'locales/keys.js',
'layers/config/overrides.js',
'js/**/*.js'
])
.pipe(sort())
.pipe(
scanner({
lngs: ['en'], // we only generate English version, other languages are handled by transifex via yarn transifex-pull/push
removeUnusedKeys: true,
sort: true,
resource: {
// the source path is relative to current working directory
loadPath: 'locales/{{lng}}.json',
// the destination path is relative to your `gulp.dest()` path
savePath: 'locales/{{lng}}.json'
}
})
)
.pipe(gulp.dest('.'));
}); });
gulp.task('release:publish', ['release:zip'], function() { gulp.task('layers_config', function() {
gulp.src('./brouter-web.'+nextVersion+'.zip') return gulp
.pipe(release({ .src(paths.layersConfig)
tag: nextVersion, .pipe(concat(paths.layersConfigDestName))
token: ghToken, .pipe(gulp.dest(paths.dest));
manifeste: pkg,
}))
}); });
gulp.task('release', ['release:init', 'bump', 'release:commit', 'release:tag', // Bundles layer files. To download and extract run "yarn layers"
'release:push', 'release:zip', 'release:publish']); gulp.task('layers', function() {
return (
gulp
.src(paths.layers)
// Workaround to get file extension removed from the dictionary key
.pipe(rename({ extname: '.json' }))
.pipe(
jsonConcat(paths.layersDestName, function(data) {
var header =
'// Licensed under the MIT License (https://github.com/nrenner/brouter-web#license + Credits and Licenses),\n' +
'// except JOSM imagery database (dataSource=JOSM) is licensed under Creative Commons (CC-BY-SA),\n' +
'// see https://josm.openstreetmap.de/wiki/Maps#Otherimportantinformation\n';
return Buffer.from(
header +
'BR.layerIndex = ' +
JSON.stringify(data, null, 2) +
';'
);
})
)
.pipe(gulp.dest(paths.dest))
);
});
gulp.task(
'default',
gulp.series(
'clean',
'scripts_config',
'layers_config',
'layers',
'scripts',
'styles',
'images',
'fonts',
'locales'
)
);
gulp.task(
'debug',
gulp.series(function(cb) {
debug = true;
cb();
}, 'default')
);
gulp.task('release:zip', function() {
gutil.log(gutil.colors.green('Build brouter-web.' + nextVersion + '.zip'));
return gulp
.src(
['dist/**', 'index.html', 'config.template.js', 'keys.template.js'],
{
base: '.'
}
)
.pipe(zip('brouter-web.' + nextVersion + '.zip'))
.pipe(gulp.dest('.'));
});
gulp.task('release:publish', function() {
return gulp.src('./brouter-web.' + nextVersion + '.zip').pipe(
release({
tag: nextVersion,
token: ghToken,
manifest: pkg
})
);
});
gulp.task(
'release',
gulp.series(
'release:init',
'bump',
'release:commit',
'release:tag',
'release:push',
'default',
'release:zip',
'release:publish'
)
);

1284
index.html

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,28 @@
(function () { (function() {
var touchScreen = (function() {
var touchScreen = (function () {
var result = null; var result = null;
if ('maxTouchPoints' in navigator) { if ('maxTouchPoints' in navigator) {
result = navigator.maxTouchPoints > 0; result = navigator.maxTouchPoints > 0;
} else if (window.matchMedia && window.matchMedia('(any-pointer:coarse),(any-pointer:fine),(any-pointer:none)').matches) { } else if (
result = window.matchMedia("(any-pointer:coarse)").matches; window.matchMedia &&
window.matchMedia(
'(any-pointer:coarse),(any-pointer:fine),(any-pointer:none)'
).matches
) {
result = window.matchMedia('(any-pointer:coarse)').matches;
} else if ('msMaxTouchPoints' in navigator) { } else if ('msMaxTouchPoints' in navigator) {
result = navigator.msMaxTouchPoints > 0; result = navigator.msMaxTouchPoints > 0;
}; }
return result; return result;
}()), })(),
touchScreenDetectable = touchScreen !== null; touchScreenDetectable = touchScreen !== null,
touch = touchScreenDetectable ? touchScreen : L.Browser.touch;
BR.Browser = { BR.Browser = {
touchScreen: touchScreen, touchScreen: touchScreen,
touchScreenDetectable: touchScreenDetectable touchScreenDetectable: touchScreenDetectable,
touch: touch
}; };
})();
}());

311
js/LayersConfig.js Normal file
View file

@ -0,0 +1,311 @@
BR.LayersConfig = L.Class.extend({
defaultBaseLayers: BR.confLayers.defaultBaseLayers,
defaultOverlays: BR.confLayers.defaultOverlays,
legacyNameToIdMap: BR.confLayers.legacyNameToIdMap,
initialize: function(map) {
this._map = map;
this._addLeafletProvidersLayers();
this._customizeLayers();
this.loadDefaultLayers();
this._addLanguageDefaultLayer();
},
loadDefaultLayers: function() {
if (BR.Util.localStorageAvailable()) {
var item = localStorage.getItem('map/defaultLayers');
if (item) {
var defaultLayers = JSON.parse(item);
this.defaultBaseLayers = defaultLayers.baseLayers;
this.defaultOverlays = defaultLayers.overlays;
}
}
},
storeDefaultLayers: function(baseLayers, overlays) {
if (BR.Util.localStorageAvailable()) {
var defaultLayers = {
baseLayers: baseLayers,
overlays: overlays
};
localStorage.setItem(
'map/defaultLayers',
JSON.stringify(defaultLayers)
);
}
},
_addLeafletProvidersLayers: function() {
var includeList = BR.confLayers.leafletProvidersIncludeList;
for (var i = 0; i < includeList.length; i++) {
var id = includeList[i];
var obj = {
geometry: null,
properties: {
id: id,
name: id.replace('.', ' '),
dataSource: 'leaflet-providers'
},
type: 'Feature'
};
BR.layerIndex[id] = obj;
}
},
_customizeLayers: function() {
var propertyOverrides = BR.confLayers.getPropertyOverrides();
for (id in propertyOverrides) {
var layer = BR.layerIndex[id];
if (layer) {
var properties = propertyOverrides[id];
for (key in properties) {
var value = properties[key];
layer.properties[key] = value;
}
} else {
console.error('Layer not found: ' + id);
}
}
BR.layerIndex['MtbMap'].geometry = BR.confLayers.europeGeofabrik;
BR.layerIndex['1069'].geometry = BR.confLayers.europeGeofabrik;
BR.layerIndex['OpenStreetMap.CH'].geometry =
BR.confLayers.switzerlandPadded;
BR.layerIndex['1017'].geometry = BR.confLayers.osmapaPl;
},
_addLanguageDefaultLayer: function() {
// language code -> layer id
var languageLayersMap = {};
var i;
for (i = 0; i < BR.confLayers.languageDefaultLayers.length; i++) {
var id = BR.confLayers.languageDefaultLayers[i];
var layer = BR.layerIndex[id];
if (layer) {
var layerLanguage = layer.properties['language_code'];
if (layerLanguage) {
languageLayersMap[layerLanguage] = id;
}
}
}
// iterate language code hierarchy, e.g ["de-DE", "de", "en"] (includes `i18next.options.fallbackLng`)
for (i = 0; i < i18next.languages.length; i++) {
var language = i18next.languages[i];
var layerId = languageLayersMap[language];
if (layerId) {
this.defaultBaseLayers.unshift(layerId);
break;
}
}
},
isDefaultLayer: function(id, overlay) {
var result = false;
if (overlay) {
result = this.defaultOverlays.indexOf(id) > -1;
} else {
result = this.defaultBaseLayers.indexOf(id) > -1;
}
return result;
},
getBaseLayers: function() {
return this._getLayers(this.defaultBaseLayers);
},
getOverlays: function() {
return this._getLayers(this.defaultOverlays);
},
_getLayers: function(ids) {
var layers = {};
for (var i = 0; i < ids.length; i++) {
var layerId = ids[i];
var layerData = BR.layerIndex[layerId];
if (layerData) {
// when key required only add if configured
var keyObj = this.getKeyName(layerData.properties.url);
if (!keyObj || (keyObj && BR.keys[keyObj.name])) {
layers[layerData.properties.name] = this.createLayer(
layerData
);
}
} else {
console.error('Layer not found: ' + layerId);
}
}
return layers;
},
// own convention: key placeholder with prefix
// e.g. ?api_key={keys_openrouteservice}
getKeyName: function(url) {
var result = null;
// L.Util.template only matches [\w_-]
var prefix = 'keys_';
var regex = new RegExp('{' + prefix + '([^}]*)}');
var found, name;
if (!url) return result;
found = url.match(regex);
if (found) {
name = found[1];
result = {
name: name,
urlVar: prefix + name
};
}
return result;
},
createLayer: function(layerData) {
var props = layerData.properties;
var url = props.url;
var layer;
// JOSM: https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png
// Leaflet: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
function convertUrlJosm(url) {
var rxSwitch = /{switch:[^}]*}/;
var rxZoom = /{zoom}/g;
var result = url.replace(rxSwitch, '{s}');
result = result.replace(rxZoom, '{z}');
return result;
}
// JOSM: https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png
// Leaflet: ['a','b','c']
function getSubdomains(url) {
var result = 'abc';
var regex = /{switch:([^}]*)}/;
var found = url.match(regex);
if (found) {
result = found[1].split(',');
}
return result;
}
function convertAttributionJosm(props) {
var result = '';
var attr = props.attribution;
if (attr) {
if (attr.html) {
result = attr.html;
} else if (attr.url && attr.text) {
result =
'<a href="' +
attr.url +
'" target="_blank" rel="noopener">' +
attr.text +
'</a>';
} else if (attr.text) {
result = attr.text;
}
}
if (!result) {
console.warn('No attribution: ' + props.id);
}
return result;
}
var options = {
maxZoom: this._map.getMaxZoom(),
bounds:
layerData.geometry && !props.worldTiles
? L.geoJson(layerData.geometry).getBounds()
: null
};
if (props.mapUrl) {
options.mapLink =
'<a target="_blank" href="' +
props.mapUrl +
'">' +
(props.nameShort || props.name) +
'</a>';
}
if (props.attribution) {
options.attribution = props.attribution;
}
var keyObj = this.getKeyName(url);
if (keyObj && BR.keys[keyObj.name]) {
options[keyObj.urlVar] = BR.keys[keyObj.name];
}
if (props.dataSource === 'leaflet-providers') {
layer = L.tileLayer.provider(props.id);
var layerOptions = L.Util.extend(options, {
maxNativeZoom: layer.options.maxZoom
});
L.setOptions(layer, layerOptions);
} else if (props.dataSource === 'LayersCollection') {
layer = L.tileLayer(
url,
L.Util.extend(options, {
minZoom: props.minZoom || 0,
maxNativeZoom: props.maxZoom
})
);
if (props.subdomains) {
layer.subdomains = props.subdomains;
}
} else {
// JOSM
var josmUrl = url;
var url = convertUrlJosm(josmUrl);
var josmOptions = L.Util.extend(options, {
minZoom: props.min_zoom || 0,
maxNativeZoom: props.max_zoom,
subdomains: getSubdomains(josmUrl),
attribution: convertAttributionJosm(props)
});
if (props.type && props.type === 'wms') {
layer = L.tileLayer.wms(
url,
L.Util.extend(josmOptions, {
layers: props.layers,
format: props.format
})
);
} else {
layer = L.tileLayer(url, josmOptions);
}
}
// Layer attribution here only as short link to original site,
// to keep current position use placeholders: {zoom}/{lat}/{lon}
// Copyright attribution in index.html #credits
var getAttribution = function() {
return this.options.mapLink;
};
layer.getAttribution = getAttribution;
layer.id = props.id;
return layer;
}
});
BR.layersConfig = function(map) {
return new BR.LayersConfig(map);
};

188
js/Map.js
View file

@ -1,94 +1,74 @@
BR.Map = { BR.Map = {
initMap: function() { initMap: function() {
var map, var map, layersControl;
layersControl;
BR.keys = BR.keys || {}; BR.keys = BR.keys || {};
var maxZoom = 19; var maxZoom = 19;
var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: maxZoom
});
var osmde = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
maxZoom: maxZoom
});
var topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
maxNativeZoom: 17,
maxZoom: maxZoom
});
var thunderforestAttribution = 'tiles &copy; <a target="_blank" href="https://www.thunderforest.com">Thunderforest</a> '
+ '(<a target="_blank" href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a>)';
var thunderforestAuth = BR.keys.thunderforest ? '?apikey=' + BR.keys.thunderforest : '';
var cycle = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png' + thunderforestAuth, {
maxNativeZoom: 18,
maxZoom: maxZoom
});
var outdoors = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png' + thunderforestAuth, {
maxNativeZoom: 18,
maxZoom: maxZoom
});
var esri = L.tileLayer('https://{s}.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
maxNativeZoom: 19,
maxZoom: maxZoom,
subdomains: ['server', 'services'],
attribution: '<a target="_blank" href="http://goto.arcgisonline.com/maps/World_Imagery">World Imagery</a> '
+ '&copy; <a target="_blank" href="https://www.esri.com/">Esri</a>, sources: '
+ 'Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP, and the GIS User Community'
});
var cycling = L.tileLayer('https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
opacity: 0.7,
maxZoom: maxZoom
});
var hiking = L.tileLayer('https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
opacity: 0.7,
maxZoom: maxZoom
});
map = new L.Map('map', { map = new L.Map('map', {
worldCopyJump: true zoomControl: false, // add it manually so that we can translate it
worldCopyJump: true,
minZoom: 0,
maxZoom: maxZoom
}); });
L.control
.zoom({
zoomInTitle: i18next.t('map.zoomInTitle'),
zoomOutTitle: i18next.t('map.zoomOutTitle')
})
.addTo(map);
if (!map.restoreView()) { if (!map.restoreView()) {
map.setView([50.99, 9.86], 6); map.setView([50.99, 9.86], 5);
} }
// two attribution lines by adding two controls, prevents ugly wrapping on
// small screens, better separates static from layer-specific attribution
var osmAttribution =
$(map.getContainer()).outerWidth() >= 400
? i18next.t('map.attribution-osm-long')
: i18next.t('map.attribution-osm-short');
map.attributionControl.setPrefix(
'&copy; <a target="_blank" href="https://www.openstreetmap.org/copyright">' +
osmAttribution +
'</a>' +
' &middot; <a href="" data-toggle="modal" data-target="#credits">' +
i18next.t('map.copyright') +
'</a>' +
' &middot; <a target="_blank" href="http://brouter.de/privacypolicy.html">' +
i18next.t('map.privacy') +
'</a>'
);
$('#credits').on('show.bs.modal', function(event) {
BR.Map._renderLayerCredits(layersControl._layers);
});
new L.Control.PermalinkAttribution().addTo(map);
map.attributionControl.setPrefix(false); map.attributionControl.setPrefix(false);
map.attributionControl.addAttribution('<a href="" data-toggle="modal" data-target="#credits">Copyright & credits</a>')
var layersConfig = BR.layersConfig(map);
var baseLayers = { var baseLayers = layersConfig.getBaseLayers();
'OpenStreetMap': osm, var overlays = layersConfig.getOverlays();
'OpenStreetMap.de': osmde,
'OpenTopoMap': topo,
'OpenCycleMap (Thunderf.)': cycle,
'Outdoors (Thunderforest)': outdoors,
'Esri World Imagery': esri
};
var overlays = {
'Cycling (Waymarked Trails)': cycling,
'Hiking (Waymarked Trails)': hiking
};
if (BR.keys.bing) { if (BR.keys.bing) {
baseLayers['Bing Aerial'] = new BR.BingLayer(BR.keys.bing); baseLayers[i18next.t('map.layer.bing')] = new BR.BingLayer(
BR.keys.bing
);
} }
if (BR.keys.digitalGlobe) { if (BR.keys.digitalGlobe) {
var recent = new L.tileLayer('https://{s}.tiles.mapbox.com/v4/digitalglobe.nal0g75k/{z}/{x}/{y}.png?access_token=' + BR.keys.digitalGlobe, { var recent = new L.tileLayer(
minZoom: 1, 'https://{s}.tiles.mapbox.com/v4/digitalglobe.nal0g75k/{z}/{x}/{y}.png?access_token=' +
maxZoom: 19, BR.keys.digitalGlobe,
attribution: '&copy; <a href="https://www.digitalglobe.com/platforms/mapsapi">DigitalGlobe</a> (' {
+ '<a href="https://bit.ly/mapsapiview">Terms of Use</a>)' minZoom: 1,
}); maxZoom: 19,
baseLayers['DigitalGlobe Recent Imagery'] = recent; attribution:
'&copy; <a href="https://www.digitalglobe.com/platforms/mapsapi">DigitalGlobe</a> (<a href="https://bit.ly/mapsapiview">Terms of Use</a>)'
}
);
baseLayers[i18next.t('map.layer.digitalglobe')] = recent;
} }
if (BR.conf.clearBaseLayers) { if (BR.conf.clearBaseLayers) {
@ -105,19 +85,27 @@ BR.Map = {
overlays[i] = L.tileLayer(BR.conf.overlays[i]); overlays[i] = L.tileLayer(BR.conf.overlays[i]);
} }
} }
// after applying custom base layer configurations, add first base layer to map
var firstLayer = baseLayers[Object.keys(baseLayers)[0]]; layersControl = BR.layersTab(layersConfig, baseLayers, overlays).addTo(
if (firstLayer) { map
map.addLayer(firstLayer); );
var secureContext =
'isSecureContext' in window
? isSecureContext
: location.protocol === 'https:';
if (secureContext) {
L.control
.locate({
strings: {
title: i18next.t('map.locate-me')
},
icon: 'fa fa-location-arrow',
iconLoading: 'fa fa-spinner fa-pulse'
})
.addTo(map);
} }
layersControl = L.control.layers(baseLayers, overlays).addTo(map);
L.control.locate({
icon: 'fa fa-location-arrow',
iconLoading: 'fa fa-spinner fa-pulse',
}).addTo(map);
L.control.scale().addTo(map); L.control.scale().addTo(map);
new BR.Layers().init(map, layersControl, baseLayers, overlays); new BR.Layers().init(map, layersControl, baseLayers, overlays);
@ -126,15 +114,31 @@ BR.Map = {
BR.debug = BR.debug || {}; BR.debug = BR.debug || {};
BR.debug.map = map; BR.debug.map = map;
var layersAndOverlays = baseLayers;
for (var o in overlays) {
layersAndOverlays[o] = overlays[o];
}
return { return {
map: map, map: map,
layersControl: layersControl, layersControl: layersControl
layers: layersAndOverlays
}; };
} },
}; _renderLayerCredits: function(layers) {
var dl = document.getElementById('credits-maps');
var i, obj, dt, dd, attribution;
L.DomUtil.empty(dl);
for (i = 0; i < layers.length; i++) {
obj = layers[i];
attribution = obj.layer.options.attribution;
if (attribution) {
dt = document.createElement('dt');
dt.innerHTML = obj.name;
dd = document.createElement('dd');
dd.innerHTML = obj.layer.options.attribution;
dl.appendChild(dt);
dl.appendChild(dd);
}
}
}
};

View file

@ -1,5 +1,4 @@
BR.Util = { BR.Util = {
get: function(url, cb) { get: function(url, cb) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
@ -16,17 +15,17 @@ BR.Util = {
}; };
try { try {
xhr.send(); xhr.send();
} catch(e) { } catch (e) {
cb(e); cb(e);
} }
}, },
getError: function(xhr) { getError: function(xhr) {
var msg = 'no response from server'; var msg = i18next.t('warning.no-response');
if (xhr.responseText) { if (xhr.responseText) {
msg = xhr.responseText; msg = xhr.responseText;
} else if (xhr.status || xhr.statusText) { } else if (xhr.status || xhr.statusText) {
msg = xhr.status + ': ' + xhr.statusText; msg = xhr.status + ': ' + xhr.statusText;
} }
return new Error(msg); return new Error(msg);
}, },
@ -46,9 +45,8 @@ BR.Util = {
storage.setItem(x, x); storage.setItem(x, x);
storage.removeItem(x); storage.removeItem(x);
return true; return true;
} } catch (e) {
catch(e) {
return false; return false;
} }
} }
}; };

View file

@ -0,0 +1,99 @@
BR.ControlLayers = L.Control.Layers.extend({
getActiveLayers: function() {
var result = [];
for (var i = 0; i < this._layers.length; i++) {
var obj = this._layers[i];
if (this._map.hasLayer(obj.layer)) {
if (obj.overlay) {
result.push(obj);
} else {
result.unshift(obj);
}
}
}
return result;
},
getActiveBaseLayer: function() {
var activeLayers = this.getActiveLayers();
for (var i = 0; i < activeLayers.length; i++) {
var obj = activeLayers[i];
if (!obj.overlay) {
return obj;
}
}
return null;
},
removeActiveLayers: function() {
var removed = [];
for (var i = 0; i < this._layers.length; i++) {
var obj = this._layers[i];
if (this._map.hasLayer(obj.layer)) {
this._map.removeLayer(obj.layer);
removed.push(obj);
}
}
return removed;
},
getLayer: function(name) {
for (var i = 0; i < this._layers.length; i++) {
var obj = this._layers[i];
if (obj.name === name) {
return obj;
}
}
return null;
},
getBaseLayers: function() {
return this._layers.filter(function(obj) {
return !obj.overlay;
});
},
activateLayer: function(obj) {
if (!this._map.hasLayer(obj.layer)) {
this._map.addLayer(obj.layer);
}
},
activateFirstLayer: function() {
for (var i = 0; i < this._layers.length; i++) {
var obj = this._layers[i];
if (!obj.overlay) {
this.activateLayer(obj);
break;
}
}
},
activateBaseLayerIndex: function(index) {
var baseLayers = this.getBaseLayers();
var obj = baseLayers[index];
this.activateLayer(obj);
},
_addLayer: function(layer, name, overlay) {
L.Control.Layers.prototype._addLayer.call(this, layer, name, overlay);
// override z-index assignment to fix that base layers added later
// are on top of overlays; set all base layers to 0
if (this.options.autoZIndex && layer.setZIndex) {
if (!overlay) {
// undo increase in super method
this._lastZIndex--;
layer.setZIndex(0);
}
}
}
});

View file

@ -1,39 +0,0 @@
BR.Control = L.Control.extend({
options: {
position: 'bottomleft'
},
onAdd: function (map) {
var container = L.DomUtil.create('div', 'info'),
heading,
div;
if (this.options.heading) {
heading = L.DomUtil.create('div', 'heading', container);
heading.innerHTML = this.options.heading;
this._content = L.DomUtil.create('div', 'content', container);
} else {
this._content = container;
}
if (this.options.divId) {
div = L.DomUtil.get(this.options.divId);
L.DomUtil.removeClass(div, 'hidden');
this._content.appendChild(div);
}
var stop = L.DomEvent.stopPropagation;
L.DomEvent
.on(container, 'click', stop)
.on(container, 'mousedown', stop)
.on(container, 'dblclick', stop)
.on(container, 'mousewheel', stop)
.on(container, 'MozMousePixelScroll', stop);
// disabled because links not working, remove?
//L.DomEvent.on(container, 'click', L.DomEvent.preventDefault);
return container;
}
});

View file

@ -1,16 +0,0 @@
BR.Download = L.Class.extend({
update: function (urls) {
if (urls) {
['gpx', 'kml', 'geojson', 'csv'].forEach(function(e, i, a) {
var a = L.DomUtil.get('dl-'+e);
a.setAttribute('href', urls[e]);
a.setAttribute('download', 'brouter.'+e);
a.removeAttribute('disabled');
})
}
}
});
BR.download = function() {
return new BR.Download();
};

167
js/control/Export.js Normal file
View file

@ -0,0 +1,167 @@
BR.Export = L.Class.extend({
latLngs: [],
initialize: function(router) {
this.router = router;
this.exportButton = $('#exportButton');
var trackname = (this.trackname = document.getElementById('trackname'));
this.tracknameAllowedChars = BR.conf.tracknameAllowedChars;
if (this.tracknameAllowedChars) {
this.tracknameMessage = document.getElementById(
'trackname-message'
);
var patternRegex = new RegExp(
'[' + this.tracknameAllowedChars + ']+'
);
// warn about special characters getting removed by server quick fix (#194)
trackname.pattern = patternRegex.toString().slice(1, -1);
trackname.addEventListener(
'input',
L.bind(this._validationMessage, this)
);
}
this.exportButton.on('click', L.bind(this._generateTrackname, this));
L.DomUtil.get('submitExport').onclick = L.bind(this._export, this);
this.update([]);
},
update: function(latLngs) {
this.latLngs = latLngs;
if (latLngs.length < 2) {
this.exportButton.addClass('disabled');
} else {
this.exportButton.removeClass('disabled');
}
},
_export: function() {
var exportForm = document.forms['export'];
var format =
exportForm['format'].value ||
$('#export-format input:radio:checked').val();
var name = encodeURIComponent(exportForm['trackname'].value);
var includeWaypoints = exportForm['include-waypoints'].checked;
var uri = this.router.getUrl(
this.latLngs,
format,
name,
includeWaypoints
);
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent(
'click',
true,
true,
window,
0,
0,
0,
0,
0,
false,
false,
false,
false,
0,
null
);
var link = document.createElement('a');
link.href = uri;
link.dispatchEvent(evt);
},
_validationMessage: function() {
var trackname = this.trackname;
var replaceRegex = new RegExp(
'[^' + this.tracknameAllowedChars + ']',
'g'
);
if (trackname.validity.patternMismatch) {
var replaced = trackname.value.replace(replaceRegex, '');
var patternStr = this.tracknameAllowedChars.replace(/\\/g, '');
this.tracknameMessage.textContent =
'[' + patternStr + '] --> ' + replaced;
} else {
this.tracknameMessage.textContent = '';
}
},
_generateTrackname: function() {
var trackname = this.trackname;
this._getCityAtPosition(
this.latLngs[0],
L.bind(function(from) {
this._getCityAtPosition(
this.latLngs[this.latLngs.length - 1],
L.bind(function(to) {
var distance = document.getElementById('distance')
.innerHTML;
if (this.tracknameAllowedChars) {
distance = distance.replace(',', '.'); // temp. fix (#202)
}
if (!from || !to) {
trackname.value = null;
} else if (from === to) {
trackname.value = i18next.t('export.route-loop', {
from: from,
distance: distance
});
} else {
trackname.value = i18next.t(
'export.route-from-to',
{ from: from, to: to, distance: distance }
);
}
if (this.tracknameAllowedChars) {
// temp. fix: replace and remove characters that will get removed by server quick fix (#194)
trackname.value = trackname.value
.replace(/[>)]/g, '')
.replace(/ \(/g, ' - ');
this._validationMessage();
}
}, this)
);
}, this)
);
},
_getCityAtPosition: function(lonlat, cb) {
var url = L.Util.template(
'https://nominatim.openstreetmap.org/reverse?lon={lng}&lat={lat}&format=json',
lonlat
);
BR.Util.get(
url,
L.bind(function(err, response) {
try {
var addr = JSON.parse(response).address;
cb(
addr.village ||
addr.town ||
addr.hamlet ||
addr.city_district ||
addr.city
);
} catch (err) {
BR.message.showError(
'Error getting position city "' + lonlat + '": ' + err
);
return cb(null);
}
})
);
}
});
BR.export = function() {
return new BR.Export();
};

View file

@ -1,24 +1,20 @@
BR.Itinerary = L.Class.extend({ BR.Itinerary = L.Class.extend({
options: { initialize: function() {
heading: 'Itinerary'
},
onAdd: function (map) {
this._content = document.getElementById('itinerary'); this._content = document.getElementById('itinerary');
L.DomUtil.removeClass(this._content.parentElement, 'hidden');
this.update(); this.update();
}, },
update: function (polyline, segments) { update: function(polyline, segments) {
var i, j, iter, html = ''; var i,
j,
iter,
html = '';
html += '<pre>'; html += '<pre class="flexgrow">';
for (i = 0; segments && i < segments.length; i++) for (i = 0; segments && i < segments.length; i++) {
{
iter = segments[i].feature.iternity; iter = segments[i].feature.iternity;
for (j = 0; iter && j < iter.length; j++) for (j = 0; iter && j < iter.length; j++) {
{ html += iter[j] + '\n';
html += iter[j] + '\n';
} }
} }
html += '</pre>'; html += '</pre>';

View file

@ -1,10 +1,9 @@
BR.Layers = L.Class.extend({ BR.Layers = L.Class.extend({
_loadLayers: function() { _loadLayers: function() {
this._customLayers = {}; this._customLayers = {};
if (BR.Util.localStorageAvailable()) { if (BR.Util.localStorageAvailable()) {
var layers = JSON.parse(localStorage.getItem("map/customLayers")); var layers = JSON.parse(localStorage.getItem('map/customLayers'));
for (a in layers) { for (a in layers) {
this._addLayer(a, layers[a].layer, layers[a].isOverlay); this._addLayer(a, layers[a].layer, layers[a].isOverlay);
} }
@ -14,21 +13,28 @@ BR.Layers = L.Class.extend({
_loadTable: function() { _loadTable: function() {
var layersData = []; var layersData = [];
for (layer in this._customLayers) { for (layer in this._customLayers) {
layersData.push([layer, this._customLayers[layer].layer._url, this._customLayers[layer].isOverlay ? "Overlay" : "Layer"]); layersData.push([
layer,
this._customLayers[layer].layer._url,
this._customLayers[layer].isOverlay ? 'Overlay' : 'Layer'
]);
} }
if (this._layersTable != null) { if (this._layersTable != null) {
this._layersTable.destroy(); this._layersTable.destroy();
} }
this._layersTable = $('#custom_layers_table').DataTable({ this._layersTable = $('#custom_layers_table').DataTable({
data: layersData, data: layersData,
info: false, info: false,
searching: false, searching: false,
paging: false, paging: false,
language: {
emptyTable: i18next.t('sidebar.layers.table.empty')
},
columns: [ columns: [
{ title: "Name" }, { title: i18next.t('sidebar.layers.table.name') },
{ title: "URL" }, { title: i18next.t('sidebar.layers.table.URL') },
{ title: "Type" } { title: i18next.t('sidebar.layers.table.type') }
] ]
}); });
}, },
@ -36,22 +42,29 @@ BR.Layers = L.Class.extend({
init: function(map, layersControl, baseLayers, overlays) { init: function(map, layersControl, baseLayers, overlays) {
this._layersControl = layersControl; this._layersControl = layersControl;
this._map = map; this._map = map;
this._layers = {} this._layers = {};
for (var l in overlays) for (var l in overlays) this._layers[l] = [overlays[l], true];
this._layers[l] = [overlays[l], true]; for (var l in baseLayers) this._layers[l] = [baseLayers[l], false];
for (var l in baseLayers)
this._layers[l] = [baseLayers[l], false];
L.DomUtil.get('custom_layers_add_base').onclick = L.bind(this._addBaseLayer, this); L.DomUtil.get('custom_layers_add_base').onclick = L.bind(
L.DomUtil.get('custom_layers_add_overlay').onclick = L.bind(this._addOverlay, this); this._addBaseLayer,
L.DomUtil.get('custom_layers_remove').onclick = L.bind(this._remove, this); this
);
L.DomUtil.get('custom_layers_add_overlay').onclick = L.bind(
this._addOverlay,
this
);
L.DomUtil.get('custom_layers_remove').onclick = L.bind(
this._remove,
this
);
this._loadLayers(); this._loadLayers();
this._loadTable(); this._loadTable();
var table = this._layersTable; var table = this._layersTable;
$('#custom_layers_table tbody').on( 'click', 'tr', function () { $('#custom_layers_table tbody').on('click', 'tr', function() {
if ( $(this).hasClass('selected') ) { if ($(this).hasClass('selected')) {
$(this).removeClass('selected'); $(this).removeClass('selected');
} else { } else {
table.$('tr.selected').removeClass('selected'); table.$('tr.selected').removeClass('selected');
@ -59,16 +72,9 @@ BR.Layers = L.Class.extend({
} }
}); });
addLayer = L.easyButton( L.DomUtil.get('custom_layers_button').onclick = function() {
'fa-plus-square', $('#custom_layers').modal();
function () { };
$('#custom_layers').modal();
},
'Add or remove custom layers',
{
position: 'topright'
}
).addTo(map);
}, },
_remove: function(evt) { _remove: function(evt) {
@ -78,7 +84,10 @@ BR.Layers = L.Class.extend({
this._layersControl.removeLayer(this._customLayers[name].layer); this._layersControl.removeLayer(this._customLayers[name].layer);
this._map.removeLayer(this._customLayers[name].layer); this._map.removeLayer(this._customLayers[name].layer);
delete this._customLayers[name]; delete this._customLayers[name];
this._layersTable.row('.selected').remove().draw( false ); this._layersTable
.row('.selected')
.remove()
.draw(false);
this._sync(); this._sync();
} }
}, },
@ -98,16 +107,17 @@ BR.Layers = L.Class.extend({
}, },
_addLayer: function(layerName, layerUrl, isOverlay) { _addLayer: function(layerName, layerUrl, isOverlay) {
if (layerName in this._layers) if (layerName in this._layers) return;
return
if (layerName in this._customLayers) if (layerName in this._customLayers) return;
return
try { try {
var layer = L.tileLayer(layerUrl); var layer = L.tileLayer(layerUrl);
this._customLayers[layerName] = {layer: layer, isOverlay: isOverlay}; this._customLayers[layerName] = {
layer: layer,
isOverlay: isOverlay
};
if (isOverlay) { if (isOverlay) {
this._layersControl.addOverlay(layer, layerName); this._layersControl.addOverlay(layer, layerName);
@ -118,17 +128,20 @@ BR.Layers = L.Class.extend({
this._sync(); this._sync();
return layer; return layer;
} catch (e) { } catch (e) {
console.warn("Oops:", e); console.warn('Oops:', e);
return return;
} }
}, },
_sync: function() { _sync: function() {
if (BR.Util.localStorageAvailable()) { if (BR.Util.localStorageAvailable()) {
localStorage.setItem("map/customLayers", JSON.stringify(this._customLayers, function(k, v) { localStorage.setItem(
// dont write Leaflet.Layer in localStorage; simply keep the URL 'map/customLayers',
return v._url || v; JSON.stringify(this._customLayers, function(k, v) {
})); // dont write Leaflet.Layer in localStorage; simply keep the URL
return v._url || v;
})
);
} }
} }
}); });

484
js/control/LayersTab.js Normal file
View file

@ -0,0 +1,484 @@
BR.LayersTab = BR.ControlLayers.extend({
previewLayer: null,
previewBounds: null,
saveLayers: [],
initialize: function(layersConfig, baseLayers, overlays, options) {
L.Control.Layers.prototype.initialize.call(
this,
baseLayers,
overlays,
options
);
this.layersConfig = layersConfig;
},
addTo: function(map) {
this._map = map;
this.onAdd(map);
L.DomUtil.get('layers-control-wrapper').appendChild(this._section);
this.initOpacitySlider(map);
this.initButtons();
this.initJsTree();
return this;
},
onAdd: function(map) {
BR.ControlLayers.prototype.onAdd.call(this, map);
map.on(
'baselayerchange overlayadd overlayremove',
this.storeActiveLayers,
this
);
},
onRemove: function(map) {
BR.ControlLayers.prototype.onRemove.call(this, map);
map.off(
'baselayerchange overlayadd overlayremove',
this.storeActiveLayers,
this
);
},
initOpacitySlider: function(map) {
var self = this;
var overlayOpacitySlider = new BR.OpacitySlider({
id: 'overlay',
reversed: false,
orientation: 'horizontal',
defaultValue: 1,
title: i18next.t('layers.opacity-slider'),
callback: function(opacity) {
for (var i = 0; i < self._layers.length; i++) {
if (
!self._layers[i].overlay ||
!map.hasLayer(self._layers[i].layer)
) {
continue;
}
self._layers[i].layer.setOpacity(opacity);
}
}
});
L.DomUtil.get(
'leaflet-control-layers-overlays-opacity-slider'
).appendChild(overlayOpacitySlider.getElement());
},
initButtons: function() {
var expandTree = function(e) {
this.jstree.open_all();
};
var collapseTree = function(e) {
this.jstree.close_all();
};
var toggleOptionalLayers = function(e) {
var button = L.DomUtil.get('optional_layers_button');
var treeButtons = L.DomUtil.get('tree-button-group');
var div = L.DomUtil.get('optional-layers-tree');
div.hidden = !div.hidden;
treeButtons.hidden = !treeButtons.hidden;
button.classList.toggle('active');
if (div.hidden) {
this.deselectNode();
}
};
L.DomUtil.get('expand_tree_button').onclick = L.bind(expandTree, this);
L.DomUtil.get('collapse_tree_button').onclick = L.bind(
collapseTree,
this
);
L.DomUtil.get('optional_layers_button').onclick = L.bind(
toggleOptionalLayers,
this
);
},
initJsTree: function() {
var layerIndex = BR.layerIndex;
var treeData = this.toJsTree(BR.confLayers.tree);
var oldSelected = null;
var onSelectNode = function(e, data) {
var layerData = layerIndex[data.node.id];
var selected = data.selected[0];
if (selected !== oldSelected) {
this.showPreview(layerData);
oldSelected = selected;
} else {
data.instance.deselect_node(data.node);
}
};
var onDeselectNode = function(e, data) {
this.hidePreview();
oldSelected = null;
};
var onCheckNode = function(e, data) {
var layerData = layerIndex[data.node.id];
var layer = this.createLayer(layerData);
var name = layerData.properties.name;
var overlay = layerData.properties.overlay;
if (overlay) {
this.addOverlay(layer, name);
} else {
this.addBaseLayer(layer, name);
}
this.storeDefaultLayers();
};
var onUncheckNode = function(e, data) {
var obj = this.getLayerById(data.node.id);
if (!obj) return;
this.removeLayer(obj.layer);
if (this._map.hasLayer(obj.layer)) {
this._map.removeLayer(obj.layer);
if (!obj.overlay) {
this.activateFirstLayer();
}
}
this.storeDefaultLayers();
};
$('#optional-layers-tree')
.on('select_node.jstree', L.bind(onSelectNode, this))
.on('deselect_node.jstree', L.bind(onDeselectNode, this))
.on('check_node.jstree', L.bind(onCheckNode, this))
.on('uncheck_node.jstree', L.bind(onUncheckNode, this))
.on('ready.jstree', function(e, data) {
data.instance.open_all();
})
.jstree({
plugins: ['checkbox'],
checkbox: {
whole_node: false,
tie_selection: false
},
core: {
multiple: false,
themes: {
icons: false,
dots: false
},
data: treeData
}
});
this.jstree = $('#optional-layers-tree').jstree(true);
},
toJsTree: function(layerTree) {
var data = {
children: []
};
var self = this;
function createRootNode(name) {
var rootNode = {
text: i18next.t('sidebar.layers.category.' + name, name),
state: {
disabled: true
},
children: []
};
return rootNode;
}
function getText(props, parent) {
var text = '';
var code = props.country_code || props.language_code;
if (code && parent.text !== code) {
text += '<span class="tree-code">' + code + '</span>';
}
text += props.name;
return text;
}
function createNode(id, layerData, parent) {
var props = layerData.properties;
var url = props.url;
var keyObj = self.layersConfig.getKeyName(url);
var childNode = null;
// when key required only add if configured
if (!keyObj || (keyObj && BR.keys[keyObj.name])) {
childNode = {
id: id,
text: getText(props, parent),
state: {
checked: self.layersConfig.isDefaultLayer(
id,
props.overlay
)
}
};
}
return childNode;
}
function walkTree(inTree, outTree) {
function walkObject(obj) {
for (name in obj) {
var value = obj[name];
var rootNode = createRootNode(name);
outTree.children.push(rootNode);
walkTree(value, rootNode);
}
}
if (Array.isArray(inTree)) {
for (var i = 0; i < inTree.length; i++) {
var entry = inTree[i];
if (typeof entry === 'object') {
walkObject(entry);
} else {
var layer = BR.layerIndex[entry];
if (layer) {
var childNode = createNode(entry, layer, outTree);
if (childNode) {
outTree.children.push(childNode);
}
} else {
console.error('Layer "' + entry + '" not found');
}
}
}
} else {
walkObject(inTree);
}
}
walkTree(layerTree, data);
return data.children;
},
storeDefaultLayers: function() {
var baseLayers = [];
var overlays = [];
for (var i = 0; i < this._layers.length; i++) {
var obj = this._layers[i];
// id set in LayersConfig.createLayer
var id = obj.layer.id;
if (id) {
if (obj.overlay) {
overlays.push(id);
} else {
baseLayers.push(id);
}
}
}
this.layersConfig.storeDefaultLayers(baseLayers, overlays);
},
createLayer: function(layerData) {
var layer = this.layersConfig.createLayer(layerData);
var overlay = layerData.properties.overlay;
// preview z-index, like in BR.ControlLayers._addLayer
layer.options.zIndex = overlay ? this._lastZIndex + 1 : 0;
return layer;
},
getLayerById: function(id) {
for (var i = 0; i < this._layers.length; i++) {
var obj = this._layers[i];
if (obj.layer.id === id) {
return obj;
}
}
return null;
},
getLayerByLegacyName: function(legacyName) {
var obj = null;
var id = this.layersConfig.legacyNameToIdMap[legacyName];
if (id) {
obj = this.getLayerById(id);
}
return obj;
},
activateDefaultBaseLayer: function() {
var index = BR.conf.defaultBaseLayerIndex || 0;
var activeBaseLayer = this.getActiveBaseLayer();
if (!activeBaseLayer) {
this.activateBaseLayerIndex(index);
}
},
saveRemoveActiveLayers: function() {
this.saveLayers = this.removeActiveLayers();
},
restoreActiveLayers: function(overlaysOnly) {
for (var i = 0; i < this.saveLayers.length; i++) {
var obj = this.saveLayers[i];
if (!overlaysOnly || (overlaysOnly && obj.overlay)) {
var hasLayer = !!this._getLayer(L.Util.stamp(obj.layer));
if (hasLayer) {
this.activateLayer(obj);
} else if (!obj.overlay) {
// saved base layer has been removed during preview, select first
this.activateFirstLayer();
}
}
}
this.saveLayers = [];
},
removePreviewLayer: function() {
if (this.previewLayer && this._map.hasLayer(this.previewLayer)) {
this._map.removeLayer(this.previewLayer);
this.previewLayer = null;
return true;
}
return false;
},
showPreviewBounds: function(layerData) {
if (layerData.geometry) {
this.previewBounds = L.geoJson(layerData.geometry, {
// fill/mask outside of bounds polygon with Leaflet.snogylop
invert: true,
// reduce unmasked areas appearing due to clipping while panning and zooming out
renderer: L.svg({ padding: 1 }),
color: '#333',
fillOpacity: 0.4,
weight: 2
}).addTo(this._map);
}
},
removePreviewBounds: function() {
if (this.previewBounds && this._map.hasLayer(this.previewBounds)) {
this._map.removeLayer(this.previewBounds);
this.previewBounds = null;
}
},
deselectNode: function() {
var selected = this.jstree.get_selected();
if (selected.length > 0) {
this.jstree.deselect_node(selected[0]);
}
},
onBaselayerchange: function() {
// execute after current input click handler,
// otherwise added overlay checkbox state doesn't update
setTimeout(
L.Util.bind(function() {
this.removePreviewBounds();
this.removePreviewLayer();
this.restoreActiveLayers(true);
this.deselectNode();
}, this),
0
);
},
showPreview: function(layerData) {
var layer = this.createLayer(layerData);
this._map.addLayer(layer);
this.removePreviewBounds();
if (!this.removePreviewLayer()) {
this.saveRemoveActiveLayers();
this._map.once('baselayerchange', this.onBaselayerchange, this);
}
this.previewLayer = layer;
this.showPreviewBounds(layerData);
},
hidePreview: function(layer) {
this._map.off('baselayerchange', this.onBaselayerchange, this);
this.removePreviewBounds();
this.removePreviewLayer();
this.restoreActiveLayers();
},
toLayerString: function(obj) {
return obj.layer.id ? obj.layer.id : obj.name;
},
getLayerFromString: function(layerString) {
var obj = this.getLayerById(layerString);
if (!obj) {
// fallback to name for custom and config layers
obj = this.getLayer(layerString);
if (!obj) {
// legacy layer name support
obj = this.getLayerByLegacyName(layerString);
}
}
return obj;
},
storeActiveLayers: function() {
if (BR.Util.localStorageAvailable()) {
var objList = this.getActiveLayers();
var idList = objList.map(
L.bind(function(obj) {
return this.toLayerString(obj);
}, this)
);
var str = JSON.stringify(idList);
localStorage.setItem('map/activeLayers', str);
}
},
loadActiveLayers: function() {
if (BR.Util.localStorageAvailable()) {
var item = localStorage.getItem('map/activeLayers');
if (item) {
var idList = JSON.parse(item);
for (var i = 0; i < idList.length; i++) {
var id = idList[i];
var obj = this.getLayerFromString(id);
if (obj) {
this.activateLayer(obj);
}
}
}
}
}
});
BR.layersTab = function(baseLayers, overlays, options) {
return new BR.LayersTab(baseLayers, overlays, options);
};

View file

@ -4,40 +4,52 @@ BR.Message = L.Class.extend({
// Bootstrap data-api's auto-initialization doesn't work in Controls because of stopPropagation // Bootstrap data-api's auto-initialization doesn't work in Controls because of stopPropagation
alert: false alert: false
}, },
initialize: function (id, options) { initialize: function(id, options) {
L.setOptions(this, options); L.setOptions(this, options);
this.id = id; this.id = id;
}, },
_show: function (msg, type) { _show: function(msg, type) {
var ele = L.DomUtil.get(this.id), var ele = L.DomUtil.get(this.id),
iconClass = (type === 'warning') ? 'fa-exclamation-triangle' : 'fa-times-circle', iconClass =
alertClass = (type === 'warning') ? 'alert-warning' : 'alert-danger'; type === 'warning'
? 'fa-exclamation-triangle'
: 'fa-times-circle',
alertClass = type === 'warning' ? 'alert-warning' : 'alert-danger';
L.DomEvent.disableClickPropagation(ele);
ele.innerHTML = ele.innerHTML =
'<div class="alert ' + alertClass + ' alert-dismissible fade in" role="alert">' '<div class="alert ' +
+ ' <button type="button" class="close" data-dismiss="alert" aria-label="Close">' alertClass +
+ ' <span aria-hidden="true">&times;</span>' ' alert-dismissible fade show" role="alert">' +
+ ' </button>' ' <button type="button" class="close" data-dismiss="alert" aria-label="Close">' +
+ ' <span class="fa ' + iconClass + '" aria-hidden="true"/></span>' ' <span aria-hidden="true">&times;</span>' +
+ msg ' </button>' +
+ '</div>'; ' <span class="fa ' +
iconClass +
'" aria-hidden="true"/></span>' +
msg +
'</div>';
if (this.options.alert) { if (this.options.alert) {
$('#' + this.id + ' .alert').alert(); $('#' + this.id + ' .alert').alert();
} }
}, },
hide: function () { hide: function() {
$('#' + this.id + ' .alert').alert('close'); $('#' + this.id + ' .alert').alert('close');
}, },
showError: function (err) { showError: function(err) {
if (err == 'Error: target island detected for section 0\n') {
err = i18next.t('warning.no-route-found');
}
this._show(err, 'error'); this._show(err, 'error');
}, },
showWarning: function (msg) { showWarning: function(msg) {
this._show(msg, 'warning'); this._show(msg, 'warning');
} }
}); });

View file

@ -1,67 +1,58 @@
BR.OpacitySlider = L.Control.extend({ BR.OpacitySlider = L.Class.extend({
options: { options: {
position: 'topleft', id: '',
reversed: true,
orientation: 'vertical',
defaultValue: BR.conf.defaultOpacity,
title: '',
callback: function(opacity) {} callback: function(opacity) {}
}, },
onAdd: function (map) { initialize: function(options) {
var container = L.DomUtil.create('div', 'leaflet-bar control-slider'), L.setOptions(this, options);
input = $('<input id="slider" type="text"/>'),
item = BR.Util.localStorageAvailable() ? localStorage.opacitySliderValue : null, var input = (this.input = $(
value = item ? parseInt(item) : BR.conf.defaultOpacity * 100, '<input id="slider-' + this.options.id + '" type="text"/>'
)),
item = BR.Util.localStorageAvailable()
? localStorage['opacitySliderValue' + this.options.id]
: null,
value = item ? parseInt(item) : this.options.defaultValue * 100,
minOpacity = (BR.conf.minOpacity || 0) * 100; minOpacity = (BR.conf.minOpacity || 0) * 100;
if (value < minOpacity) { if (value < minOpacity) {
value = minOpacity; value = minOpacity;
} }
// prevent also dragging map in Chrome input
L.DomEvent.disableClickPropagation(container); .slider({
id: this.options.id,
min: 0,
max: 100,
step: 1,
value: value,
orientation: this.options.orientation,
reversed: this.options.reversed,
selection: this.options.reversed ? 'before' : 'after', // inverted, serves as track style, see css
tooltip: 'hide'
})
.on('slide slideStop', { self: this }, function(evt) {
evt.data.self.options.callback(evt.value / 100);
})
.on('slideStop', { self: this }, function(evt) {
if (BR.Util.localStorageAvailable()) {
localStorage[
'opacitySliderValue' + evt.data.self.options.id
] = evt.value;
}
});
var stopClickAfterSlide = function(evt) { this.getElement().title = this.options.title;
L.DomEvent.stop(evt);
removeStopClickListeners();
};
var removeStopClickListeners = function() {
document.removeEventListener('click', stopClickAfterSlide, true);
document.removeEventListener('mousedown', removeStopClickListeners, true);
};
$(container).html(input);
$(container).attr('title', 'Set transparency of route track and markers');
input.slider({
min: 0,
max: 100,
step: 1,
value: value,
orientation: 'vertical',
reversed : true,
selection: 'before', // inverted, serves as track style, see css
tooltip: 'hide'
}).on('slideStart', function (evt) {
// dragging beyond slider control selects zoom control +/- text in Firefox
L.DomUtil.disableTextSelection();
}).on('slide slideStop', { self: this }, function (evt) {
evt.data.self.options.callback(evt.value / 100);
}).on('slideStop', function (evt) {
if (BR.Util.localStorageAvailable()) {
localStorage.opacitySliderValue = evt.value;
}
L.DomUtil.enableTextSelection();
// When dragging outside slider and over map, click event after mouseup
// adds marker when active on Chromium. So disable click (not needed)
// once after sliding.
document.addEventListener('click', stopClickAfterSlide, true);
// Firefox does not fire click event in this case, so make sure stop listener
// is always removed on next mousedown.
document.addEventListener('mousedown', removeStopClickListeners, true);
});
this.options.callback(value / 100); this.options.callback(value / 100);
},
return container; getElement: function() {
return this.input.slider('getElement');
} }
}); });

View file

@ -0,0 +1,64 @@
BR.OpacitySliderControl = L.Control.extend({
options: {
position: 'topleft'
},
onAdd: function(map) {
var container = L.DomUtil.create('div', 'leaflet-bar control-slider');
// prevent also dragging map in Chrome
L.DomEvent.disableClickPropagation(container);
// migrate legacy value
if (BR.Util.localStorageAvailable()) {
var value = localStorage.getItem('opacitySliderValue');
if (value !== null) {
localStorage.setItem(
'opacitySliderValue' + this.options.id,
value
);
localStorage.removeItem('opacitySliderValue');
}
}
var slider = new BR.OpacitySlider(this.options);
container.appendChild(slider.getElement());
var stopClickAfterSlide = function(evt) {
L.DomEvent.stop(evt);
removeStopClickListeners();
};
var removeStopClickListeners = function() {
document.removeEventListener('click', stopClickAfterSlide, true);
document.removeEventListener(
'mousedown',
removeStopClickListeners,
true
);
};
slider.input
.on('slideStart', function(evt) {
// dragging beyond slider control selects zoom control +/- text in Firefox
L.DomUtil.disableTextSelection();
})
.on('slideStop', { self: this }, function(evt) {
L.DomUtil.enableTextSelection();
// When dragging outside slider and over map, click event after mouseup
// adds marker when active on Chromium. So disable click (not needed)
// once after sliding.
document.addEventListener('click', stopClickAfterSlide, true);
// Firefox does not fire click event in this case, so make sure stop listener
// is always removed on next mousedown.
document.addEventListener(
'mousedown',
removeStopClickListeners,
true
);
});
return container;
}
});

View file

@ -1,11 +1,15 @@
BR.Profile = L.Class.extend({ BR.Profile = L.Evented.extend({
cache: {}, cache: {},
initialize: function () { initialize: function() {
var textArea = L.DomUtil.get('profile_upload');
this.editor = CodeMirror.fromTextArea(textArea, {
lineNumbers: true
});
L.DomUtil.get('upload').onclick = L.bind(this._upload, this); L.DomUtil.get('upload').onclick = L.bind(this._upload, this);
L.DomUtil.get('clear').onclick = L.bind(this.clear, this); L.DomUtil.get('clear').onclick = L.bind(this.clear, this);
this.ele = L.DomUtil.get('profile_upload');
autosize(this.ele);
this.message = new BR.Message('profile_message', { this.message = new BR.Message('profile_message', {
alert: true alert: true
}); });
@ -15,9 +19,7 @@ BR.Profile = L.Class.extend({
var button = evt.target || evt.srcElement; var button = evt.target || evt.srcElement;
evt.preventDefault(); evt.preventDefault();
this.ele.value = null; this._setValue('');
this.ele.defaultValue = null;
autosize.update(this.ele);
this.fire('clear'); this.fire('clear');
button.blur(); button.blur();
@ -26,39 +28,54 @@ BR.Profile = L.Class.extend({
update: function(options) { update: function(options) {
var profileName = options.profile, var profileName = options.profile,
profileUrl, profileUrl,
ele = this.ele, empty = !this.editor.getValue(),
dirty = ele.defaultValue !== ele.value; clean = this.editor.isClean();
this.profileName = profileName; this.profileName = profileName;
if (profileName && BR.conf.profilesUrl && (!ele.value || !dirty)) { if (profileName && BR.conf.profilesUrl && (empty || clean)) {
if (!(profileName in this.cache)) { if (!(profileName in this.cache)) {
profileUrl = BR.conf.profilesUrl + profileName + '.brf'; profileUrl = BR.conf.profilesUrl + profileName + '.brf';
BR.Util.get(profileUrl, L.bind(function(err, profileText) { BR.Util.get(
if (err) { profileUrl,
console.warn('Error getting profile from "' + profileUrl + '": ' + err); L.bind(function(err, profileText) {
return; if (err) {
} console.warn(
'Error getting profile from "' +
profileUrl +
'": ' +
err
);
return;
}
this.cache[profileName] = profileText; this.cache[profileName] = profileText;
// don't set when option has changed while loading // don't set when option has changed while loading
if (!this.profileName || this.profileName === profileName) { if (
ele.value = profileText; !this.profileName ||
ele.defaultValue = ele.value; this.profileName === profileName
autosize.update(this.ele); ) {
} this._setValue(profileText);
}, this)); }
}, this)
);
} else { } else {
ele.value = this.cache[profileName]; this._setValue(this.cache[profileName]);
ele.defaultValue = ele.value;
autosize.update(this.ele);
} }
} }
}, },
show: function() {
this.editor.refresh();
},
onResize: function() {
this.editor.refresh();
},
_upload: function(evt) { _upload: function(evt) {
var button = evt.target || evt.srcElement, var button = evt.target || evt.srcElement,
profile = this.ele.value; profile = this.editor.getValue();
this.message.hide(); this.message.hide();
$(button).button('uploading'); $(button).button('uploading');
@ -66,12 +83,15 @@ BR.Profile = L.Class.extend({
this.fire('update', { this.fire('update', {
profileText: profile, profileText: profile,
callback: function () { callback: function() {
$(button).button('reset'); $(button).button('reset');
$(button).blur(); $(button).blur();
} }
}); });
},
_setValue: function(profileText) {
this.editor.setValue(profileText);
this.editor.markClean();
} }
}); });
BR.Profile.include(L.Mixin.Events);

View file

@ -1,43 +1,45 @@
BR.RoutingOptions = BR.Control.extend({ BR.RoutingOptions = L.Evented.extend({
initialize: function() {
onAdd: function (map) { $('#profile-alternative').on(
$('#profile-alternative').on('changed.bs.select', this._getChangeHandler()); 'changed.bs.select',
this._getChangeHandler()
);
// build option list from config // build option list from config
var profiles = BR.conf.profiles; var profiles = BR.conf.profiles;
var profiles_list = L.DomUtil.get('profile'); var profiles_list = L.DomUtil.get('profile');
for (var i = 0; i < profiles.length; i++) { for (var i = 0; i < profiles.length; i++) {
var option = document.createElement("option"); var option = document.createElement('option');
option.value = profiles[i]; option.value = profiles[i];
option.text = profiles[i]; option.text = i18next.t('navbar.profile.' + profiles[i]);
profiles_list.appendChild(option); profiles_list.appendChild(option);
} }
// set default value, used as indicator for empty custom profile
profiles_list.children[0].value = 'Custom';
// <custom> profile is empty at start, select next one // <custom> profile is empty at start, select next one
profiles_list.children[1].selected = true; profiles_list.children[1].selected = true;
return BR.Control.prototype.onAdd.call(this, map);
}, },
refreshUI: function() { refreshUI: function() {
var profile = $('#profile option:selected'),
alternative = $('#alternative option:selected');
$('#stat-profile').html(profile.text() + ' (' + alternative.text() +')');
// we do not allow to select more than one profile and/or alternative at a time // we do not allow to select more than one profile and/or alternative at a time
// so we disable the current selected items // so we disable the current selected items
$('#profile-alternative').find('option:disabled').each(function(index) { $('#profile-alternative')
$(this).prop('disabled', false); .find('option:disabled')
}); .each(function(index) {
$('#profile-alternative').find('option:selected').each(function(index) { $(this).prop('disabled', false);
$(this).prop('disabled', true); });
}); $('#profile-alternative')
.find('option:selected')
.each(function(index) {
$(this).prop('disabled', true);
});
// disable custom option if it has no value yet (default value is "Custom") // disable custom option if it has no value yet (default value is "Custom")
var custom = L.DomUtil.get('profile').children[0]; var custom = L.DomUtil.get('profile').children[0];
if (custom.value === "Custom") { if (custom.value === 'Custom') {
custom.disabled = true; custom.disabled = true;
} }
$('.selectpicker').selectpicker('refresh') $('.selectpicker').selectpicker('refresh');
}, },
getOptions: function() { getOptions: function() {
@ -53,8 +55,12 @@ BR.RoutingOptions = BR.Control.extend({
setOptions: function(options) { setOptions: function(options) {
var values = [ var values = [
options.profile ? options.profile : $('#profile option:selected').val(), options.profile
options.alternative ? options.alternative : $('#alternative option:selected').val() ? options.profile
: $('#profile option:selected').val(),
options.alternative
? options.alternative
: $('#alternative option:selected').val()
]; ];
$('.selectpicker').selectpicker('val', values); $('.selectpicker').selectpicker('val', values);
this.refreshUI(); this.refreshUI();
@ -68,22 +74,28 @@ BR.RoutingOptions = BR.Control.extend({
}, },
setCustomProfile: function(profile, noUpdate) { setCustomProfile: function(profile, noUpdate) {
var profiles_grp, var profiles_grp, option;
option;
profiles_grp = L.DomUtil.get('profile'); profiles_grp = L.DomUtil.get('profile');
option = profiles_grp.children[0] option = profiles_grp.children[0];
option.value = profile; option.value = profile || 'Custom';
option.disabled = !profile; option.disabled = !profile;
$('#profile').find('option:selected').each(function(index) { if (profile) {
$(this).prop('selected', false); $('#profile')
}); .find('option:selected')
.each(function(index) {
$(this).prop('selected', false);
});
} else if (option.selected) {
// clear, select next in group when custom deselected
profiles_grp.children[1].selected = true;
}
option.selected = !!profile; option.selected = !!profile;
if (!noUpdate) { if (!noUpdate) {
this.fire('update', {options: this.getOptions()}); this.fire('update', { options: this.getOptions() });
} }
}, },
@ -92,7 +104,7 @@ BR.RoutingOptions = BR.Control.extend({
option = profiles_grp.children[0], option = profiles_grp.children[0],
profile = null; profile = null;
if (!option.disabled) { if (option.value !== 'Custom') {
profile = option.value; profile = option.value;
} }
return profile; return profile;
@ -100,9 +112,7 @@ BR.RoutingOptions = BR.Control.extend({
_getChangeHandler: function() { _getChangeHandler: function() {
return L.bind(function(evt) { return L.bind(function(evt) {
this.fire('update', {options: this.getOptions()}); this.fire('update', { options: this.getOptions() });
}, this); }, this);
} }
}); });
BR.RoutingOptions.include(L.Mixin.Events);

View file

@ -1,47 +0,0 @@
BR.Tabs = BR.Control.extend({
options: {
divId: 'sidebar',
// tab a.hash > instance
tabs: {}
},
initialize: function (options) {
L.setOptions(this, options);
},
onAdd: function (map) {
var tabs = this.options.tabs;
for (var key in tabs) {
$('<li><a href="' + key + '" role="tab">' + tabs[key].options.heading + '</a></li>').appendTo('#tab');
if (tabs[key].onAdd) {
tabs[key].onAdd(map);
}
}
$('#tab a').click(function (e) {
e.preventDefault();
$(this).tab('show');
});
// e.target = activated tab
// e.relatedTarget = previous tab
$('#tab a').on('shown.bs.tab', L.bind(function (e) {
var tab = this.options.tabs[e.target.hash],
prevTab = e.relatedTarget ? this.options.tabs[e.relatedTarget.hash] : null;
if (tab && tab.show) {
tab.show();
}
if (prevTab && prevTab.hide) {
prevTab.hide();
}
}, this));
// activate first tab (instead of setting 'active' class in html)
$('#tab li:not(.hidden) a:first').tab('show');
return BR.Control.prototype.onAdd.call(this, map);
}
});

View file

@ -1,7 +1,5 @@
BR.TrackMessages = L.Class.extend({ BR.TrackMessages = L.Class.extend({
options: { options: {
heading: 'Segment data',
edgeStyle: { edgeStyle: {
color: 'yellow', color: 'yellow',
opacity: 0.8, opacity: 0.8,
@ -13,31 +11,31 @@ BR.TrackMessages = L.Class.extend({
active: false, active: false,
columnOptions: { columnOptions: {
'Longitude': { visible: false }, Longitude: { visible: false },
'Latitude': { visible: false }, Latitude: { visible: false },
'Elevation': { title: 'elev.', className: 'dt-body-right' }, Elevation: { title: 'elev.', className: 'dt-body-right' },
'Distance': { title: 'dist.', className: 'dt-body-right' }, Distance: { title: 'dist.', className: 'dt-body-right' },
'CostPerKm': { title: '$/km', className: 'dt-body-right' }, CostPerKm: { title: '$/km', className: 'dt-body-right' },
'ElevCost': { title: 'elev$', className: 'dt-body-right' }, ElevCost: { title: 'elev$', className: 'dt-body-right' },
'TurnCost': { title: 'turn$', className: 'dt-body-right' }, TurnCost: { title: 'turn$', className: 'dt-body-right' },
'NodeCost': { title: 'node$', className: 'dt-body-right' }, NodeCost: { title: 'node$', className: 'dt-body-right' },
'InitialCost': { title: 'initial$', className: 'dt-body-right' } InitialCost: { title: 'initial$', className: 'dt-body-right' }
}, },
initialize: function (options) { initialize: function(map, options) {
L.setOptions(this, options); L.setOptions(this, options);
this._map = map;
var table = document.getElementById('datatable'); var table = document.getElementById('datatable');
this.tableClassName = table.className; this.tableClassName = table.className;
this.tableParent = table.parentElement; this.tableParent = table.parentElement;
}, },
onAdd: function (map) { update: function(polyline, segments) {
this._map = map; var i,
}, messages,
columns,
update: function (polyline, segments) { headings,
var i, messages, columns, headings,
data = []; data = [];
if (!this.active) { if (!this.active) {
@ -54,7 +52,7 @@ BR.TrackMessages = L.Class.extend({
this._destroyTable(); this._destroyTable();
if (data.length === 0) { if (data.length === 0) {
return; return;
} }
headings = messages[0]; headings = messages[0];
@ -67,13 +65,19 @@ BR.TrackMessages = L.Class.extend({
paging: false, paging: false,
searching: false, searching: false,
info: false, info: false,
// flexbox workaround: without scrollY height Firefox extends to content height
// (^= minimum height with flexbox?)
scrollY: 50,
scrollX: true, scrollX: true,
order: [] order: []
}); });
// highlight track segment (graph edge) on row hover // highlight track segment (graph edge) on row hover
this._setEdges(polyline, segments); this._setEdges(polyline, segments);
$('#datatable tbody tr').hover(L.bind(this._handleHover, this), L.bind(this._handleHoverOut, this)); $('#datatable tbody tr').hover(
L.bind(this._handleHover, this),
L.bind(this._handleHoverOut, this)
);
}, },
show: function() { show: function() {
@ -88,9 +92,11 @@ BR.TrackMessages = L.Class.extend({
_destroyTable: function() { _destroyTable: function() {
var ele; var ele;
if ($.fn.DataTable.isDataTable('#datatable') ) { if ($.fn.DataTable.isDataTable('#datatable')) {
// destroy option too slow on update, really remove elements with destroy method // destroy option too slow on update, really remove elements with destroy method
$('#datatable').DataTable().destroy(true); $('#datatable')
.DataTable()
.destroy(true);
// recreate original table element, destroy removes all // recreate original table element, destroy removes all
ele = document.createElement('table'); ele = document.createElement('table');
@ -144,7 +150,14 @@ BR.TrackMessages = L.Class.extend({
}, },
_setEdges: function(polyline, segments) { _setEdges: function(polyline, segments) {
var messages, segLatLngs, length, si, mi, latLng, i, segIndex, var messages,
segLatLngs,
length,
si,
mi,
latLng,
i,
segIndex,
baseIndex = 0; baseIndex = 0;
// track latLngs index for end node of edge // track latLngs index for end node of edge
@ -185,7 +198,10 @@ BR.TrackMessages = L.Class.extend({
endIndex = this._edges[row.index()], endIndex = this._edges[row.index()],
edgeLatLngs = trackLatLngs.slice(startIndex, endIndex + 1); edgeLatLngs = trackLatLngs.slice(startIndex, endIndex + 1);
this._selectedEdge = L.polyline(edgeLatLngs, this.options.edgeStyle).addTo(this._map); this._selectedEdge = L.polyline(
edgeLatLngs,
this.options.edgeStyle
).addTo(this._map);
}, },
_handleHoverOut: function(evt) { _handleHoverOut: function(evt) {
@ -193,5 +209,3 @@ BR.TrackMessages = L.Class.extend({
this._selectedEdge = null; this._selectedEdge = null;
} }
}); });
BR.TrackMessages.include(L.Mixin.Events);

View file

@ -1,13 +1,61 @@
BR.TrackStats = L.Class.extend({ BR.TrackStats = L.Class.extend({
update: function (polyline, segments) { update: function(polyline, segments) {
var stats = this.calcStats(polyline, segments), if (segments.length == 0) {
length1 = L.Util.formatNum(stats.trackLength/1000,1), $('#distance').html('-');
length3 = L.Util.formatNum(stats.trackLength/1000,3), $('#distance').attr('title', '');
meanCostFactor = stats.trackLength ? L.Util.formatNum(stats.cost / stats.trackLength, 2) : '' $('#ascend').html('-');
$('#plainascend').html('-');
$('#cost').html('-');
$('#meancostfactor').html('-');
$('#totaltime').html('-');
$('#totalenergy').html('-');
$('#meanenergy').html('-');
return;
}
$('#distance').html(length1 + ' <abbr title="kilometer">km</abbr>'); var stats = this.calcStats(polyline, segments),
$('#ascend').html(stats.filteredAscend + ' (' + stats.plainAscend +')' + ' <abbr title="meter">m</abbr>'); length1 = L.Util.formatNum(
$('#cost').html(stats.cost + ' (' + meanCostFactor + ')'); stats.trackLength / 1000,
1
).toLocaleString(),
length3 = L.Util.formatNum(
stats.trackLength / 1000,
3
).toLocaleString(),
formattedAscend = stats.filteredAscend.toLocaleString(),
formattedPlainAscend = stats.plainAscend.toLocaleString(),
formattedCost = stats.cost.toLocaleString(),
meanCostFactor = stats.trackLength
? L.Util.formatNum(
stats.cost / stats.trackLength,
2
).toLocaleString()
: '0',
formattedTime =
Math.trunc(stats.totalTime / 3600) +
':' +
('0' + Math.trunc((stats.totalTime % 3600) / 60)).slice(-2),
formattedEnergy = L.Util.formatNum(
stats.totalEnergy / 3600000,
2
).toLocaleString(),
meanEnergy = stats.trackLength
? L.Util.formatNum(
stats.totalEnergy / 36 / stats.trackLength,
2
).toLocaleString()
: '0';
$('#distance').html(length1);
// alternative 3-digit format down to meters as tooltip
$('#distance').attr('title', length3 + ' km');
$('#ascend').html(formattedAscend);
$('#plainascend').html(formattedPlainAscend);
$('#cost').html(formattedCost);
$('#meancostfactor').html(meanCostFactor);
$('#totaltime').html(formattedTime);
$('#totalenergy').html(formattedEnergy);
$('#meanenergy').html(meanEnergy);
}, },
calcStats: function(polyline, segments) { calcStats: function(polyline, segments) {
@ -15,6 +63,8 @@ BR.TrackStats = L.Class.extend({
trackLength: 0, trackLength: 0,
filteredAscend: 0, filteredAscend: 0,
plainAscend: 0, plainAscend: 0,
totalTime: 0,
totalEnergy: 0,
cost: 0 cost: 0
}; };
var i, props; var i, props;
@ -24,6 +74,8 @@ BR.TrackStats = L.Class.extend({
stats.trackLength += +props['track-length']; stats.trackLength += +props['track-length'];
stats.filteredAscend += +props['filtered ascend']; stats.filteredAscend += +props['filtered ascend'];
stats.plainAscend += +props['plain-ascend']; stats.plainAscend += +props['plain-ascend'];
stats.totalTime += +props['total-time'];
stats.totalEnergy += +props['total-energy'];
stats.cost += +props['cost']; stats.cost += +props['cost'];
} }

View file

@ -5,21 +5,26 @@
*/ */
(function() { (function() {
var mapContext; var mapContext;
function verifyTouchStyle(mapContext) { function verifyTouchStyle(mapContext) {
// revert touch style (large icons) when touch screen detection is available and negative // revert touch style (large icons) when touch screen detection is available and negative
// see https://github.com/nrenner/brouter-web/issues/69 // see https://github.com/nrenner/brouter-web/issues/69
if (L.Browser.touch && BR.Browser.touchScreenDetectable && !BR.Browser.touchScreen) { if (
L.DomUtil.removeClass(mapContext.map.getContainer(), 'leaflet-touch'); L.Browser.touch &&
BR.Browser.touchScreenDetectable &&
!BR.Browser.touchScreen
) {
L.DomUtil.removeClass(
mapContext.map.getContainer(),
'leaflet-touch'
);
} }
} }
function initApp(mapContext) { function initApp(mapContext) {
var map = mapContext.map, var map = mapContext.map,
layersControl = mapContext.layersControl, layersControl = mapContext.layersControl,
mapLayers = mapContext.layers,
search, search,
router, router,
routing, routing,
@ -28,20 +33,23 @@
stats, stats,
itinerary, itinerary,
elevation, elevation,
download, exportRoute,
tabs,
profile, profile,
trackMessages, trackMessages,
sidebar,
drawButton, drawButton,
deleteButton, deleteRouteButton,
drawToolbar, drawToolbar,
urlHash, urlHash,
reverseRoute,
saveWarningShown = false; saveWarningShown = false;
// By default bootstrap-select use glyphicons // By default bootstrap-select use glyphicons
$('.selectpicker').selectpicker({ $('.selectpicker').selectpicker({
iconBase: 'fa', iconBase: 'fa',
tickIcon: 'fa-check' tickIcon: 'fa-check',
// don't overlap with footer
windowPadding: [0, 0, 40, 0]
}); });
search = new BR.Search(); search = new BR.Search();
@ -50,45 +58,75 @@
router = L.bRouter(); //brouterCgi dummyRouter router = L.bRouter(); //brouterCgi dummyRouter
drawButton = L.easyButton({ drawButton = L.easyButton({
states: [{ states: [
stateName: 'deactivate-draw', {
icon: 'fa-pencil active', stateName: 'deactivate-draw',
onClick: function (control) { icon: 'fa-pencil active',
routing.draw(false); onClick: function(control) {
control.state('activate-draw'); routing.draw(false);
control.state('activate-draw');
},
title: i18next.t('map.draw-route-stop')
}, },
title: 'Stop drawing route (ESC key)' {
}, { stateName: 'activate-draw',
stateName: 'activate-draw', icon: 'fa-pencil',
icon: 'fa-pencil', onClick: function(control) {
onClick: function (control) { routing.draw(true);
routing.draw(true); control.state('deactivate-draw');
control.state('deactivate-draw'); },
}, title: i18next.t('map.draw-route-start')
title: 'Draw route (D key)' }
}] ]
}); });
deleteButton = L.easyButton( reverseRouteButton = L.easyButton(
'fa-random',
function() {
routing.reverse();
},
i18next.t('map.reverse-route')
);
deletePointButton = L.easyButton(
'<span><i class="fa fa-caret-left"></i><i class="fa fa-map-marker" style="margin-left: 1px; color: gray;"></i></span>',
function() {
routing.removeWaypoint(routing.getLast(), function(
err,
data
) {});
},
i18next.t('map.delete-last-point')
);
deleteRouteButton = L.easyButton(
'fa-trash-o', 'fa-trash-o',
function () { function() {
bootbox.confirm({ bootbox.prompt({
size: 'small', size: 'small',
message: "Delete route?", title: i18next.t('map.delete-route'),
inputType: 'checkbox',
inputOptions: [
{
text: i18next.t('map.delete-nogo-areas'),
value: 'nogo'
}
],
callback: function(result) { callback: function(result) {
if (result) { if (result !== null) {
routing.clear(); routing.clear();
if (result.length > 0 && result[0] === 'nogo') {
nogos.clear();
}
onUpdate(); onUpdate();
urlHash.onMapMove(); urlHash.onMapMove();
} }
} }
}); });
}, },
'Clear route' i18next.t('map.clear-route')
); );
drawToolbar = L.easyBar([drawButton, deleteButton]).addTo(map);
function updateRoute(evt) { function updateRoute(evt) {
router.setOptions(evt.options); router.setOptions(evt.options);
@ -112,6 +150,11 @@
profile.update(evt.options); profile.update(evt.options);
}); });
BR.NogoAreas.MSG_BUTTON = i18next.t('map.nogo.draw');
BR.NogoAreas.MSG_BUTTON_CANCEL = i18next.t('map.nogo.cancel');
BR.NogoAreas.MSG_CREATE = i18next.t('map.nogo.click-drag');
BR.NogoAreas.MSG_DISABLED = i18next.t('map.nogo.edit');
BR.NogoAreas.MSG_ENABLED = i18next.t('map.nogo.help');
nogos = new BR.NogoAreas(); nogos = new BR.NogoAreas();
nogos.on('update', updateRoute); nogos.on('update', updateRoute);
@ -121,22 +164,26 @@
} else { } else {
stats = new BR.TrackStats(); stats = new BR.TrackStats();
} }
download = new BR.Download(); exportRoute = new BR.Export(router);
elevation = new BR.Elevation(); elevation = new BR.Elevation();
profile = new BR.Profile(); profile = new BR.Profile();
profile.on('update', function(evt) { profile.on('update', function(evt) {
BR.message.hide(); BR.message.hide();
var profileId = routingOptions.getCustomProfile(); var profileId = routingOptions.getCustomProfile();
router.uploadProfile(profileId, evt.profileText, function(err, profileId) { router.uploadProfile(profileId, evt.profileText, function(
err,
profileId
) {
if (!err) { if (!err) {
routingOptions.setCustomProfile(profileId, true); routingOptions.setCustomProfile(profileId, true);
updateRoute({ updateRoute({
options: routingOptions.getOptions() options: routingOptions.getOptions()
}); });
if (!saveWarningShown) { if (!saveWarningShown) {
profile.message.showWarning('<strong>Note:</strong> Uploaded custom profiles are only cached temporarily on the server.' profile.message.showWarning(
+ '<br/>Please save your edits to your local PC.'); i18next.t('warning.temporary-profile')
);
saveWarningShown = true; saveWarningShown = true;
} }
} else { } else {
@ -156,7 +203,7 @@
profile.message.hide(); profile.message.hide();
routingOptions.setCustomProfile(null); routingOptions.setCustomProfile(null);
}); });
trackMessages = new BR.TrackMessages({ trackMessages = new BR.TrackMessages(map, {
requestUpdate: requestUpdate requestUpdate: requestUpdate
}); });
@ -167,7 +214,9 @@
styles: BR.conf.routingStyles styles: BR.conf.routingStyles
}); });
routing.on('routing:routeWaypointEnd routing:setWaypointsEnd', function(evt) { routing.on('routing:routeWaypointEnd routing:setWaypointsEnd', function(
evt
) {
search.clear(); search.clear();
onUpdate(evt && evt.err); onUpdate(evt && evt.err);
}); });
@ -191,8 +240,7 @@
var track = routing.toPolyline(), var track = routing.toPolyline(),
segments = routing.getSegments(), segments = routing.getSegments(),
latLngs = routing.getWaypoints(), latLngs = routing.getWaypoints(),
segmentsLayer = routing._segments, segmentsLayer = routing._segments;
urls = {};
elevation.update(track, segmentsLayer); elevation.update(track, segmentsLayer);
if (BR.conf.transit) { if (BR.conf.transit) {
@ -202,54 +250,62 @@
} }
trackMessages.update(track, segments); trackMessages.update(track, segments);
if (latLngs.length > 1) { exportRoute.update(latLngs);
urls.gpx = router.getUrl(latLngs, 'gpx'); }
urls.kml = router.getUrl(latLngs, 'kml');
urls.geojson = router.getUrl(latLngs, 'geojson');
urls.csv = router.getUrl(latLngs, 'csv');
}
download.update(urls);
};
routingOptions.addTo(map);
routing.addTo(map); routing.addTo(map);
elevation.addBelow(map); elevation.addBelow(map);
tabs = new BR.Tabs({ sidebar = BR.sidebar({
tabs: { defaultTabId: BR.conf.transit ? 'tab_itinerary' : 'tab_profile',
'#tab_itinerary': itinerary, listeningTabs: {
'#tab_data': trackMessages tab_profile: profile,
tab_data: trackMessages
} }
}); }).addTo(map);
if (!BR.conf.transit) { if (BR.conf.transit) {
delete tabs.options.tabs['#tab_itinerary']; sidebar.showPanel('tab_itinerary');
} }
map.addControl(tabs);
var sidebar = L.control.sidebar('sidebar', {
position: 'left'
});
sidebar.id = 'sidebar-control'; //required for persistence in local storage
map.addControl(sidebar);
nogos.addTo(map); nogos.addTo(map);
map.addControl(new BR.OpacitySlider({ drawToolbar = L.easyBar([
callback: L.bind(routing.setOpacity, routing) drawButton,
})); reverseRouteButton,
nogos.getButton(),
deletePointButton,
deleteRouteButton
]).addTo(map);
nogos.preventRoutePointOnCreate(routing);
if (BR.keys.strava) {
BR.stravaSegments(map, layersControl);
}
map.addControl(
new BR.OpacitySliderControl({
id: 'route',
title: i18next.t('map.opacity-slider'),
callback: L.bind(routing.setOpacity, routing)
})
);
// initial option settings (after controls are added and initialized with onAdd) // initial option settings (after controls are added and initialized with onAdd)
router.setOptions(nogos.getOptions()); router.setOptions(nogos.getOptions());
router.setOptions(routingOptions.getOptions()); router.setOptions(routingOptions.getOptions());
profile.update(routingOptions.getOptions()); profile.update(routingOptions.getOptions());
// restore active layers from local storage when called without hash
// (check before hash plugin init)
if (!location.hash) {
layersControl.loadActiveLayers();
}
var onHashChangeCb = function(url) { var onHashChangeCb = function(url) {
var url2params = function (s) { var url2params = function(s) {
s = s.replace(/;/g, '|');
var p = {}; var p = {};
var sep = '&'; var sep = '&';
if (s.search('&amp;') !== -1) if (s.search('&amp;') !== -1) sep = '&amp;';
sep = '&amp;';
var params = s.split(sep); var params = s.split(sep);
for (var i = 0; i < params.length; i++) { for (var i = 0; i < params.length; i++) {
var tmp = params[i].split('='); var tmp = params[i].split('=');
@ -257,7 +313,7 @@
p[tmp[0]] = decodeURIComponent(tmp[1]); p[tmp[0]] = decodeURIComponent(tmp[1]);
} }
return p; return p;
} };
if (url == null) return; if (url == null) return;
var opts = router.parseUrlParams(url2params(url)); var opts = router.parseUrlParams(url2params(url));
router.setOptions(opts); router.setOptions(opts);
@ -283,35 +339,48 @@
// do not initialize immediately // do not initialize immediately
urlHash = new L.Hash(null, null); urlHash = new L.Hash(null, null);
urlHash.additionalCb = function() { urlHash.additionalCb = function() {
var url = router.getUrl(routing.getWaypoints(), null).substr('brouter?'.length+1); var url = router
return url.length > 0 ? '&' + url : null; .getUrl(routing.getWaypoints(), null)
}; .substr('brouter?'.length + 1);
url = url.replace(/\|/g, ';');
return url.length > 0 ? '&' + url : null;
};
urlHash.onHashChangeCb = onHashChangeCb; urlHash.onHashChangeCb = onHashChangeCb;
urlHash.onInvalidHashChangeCb = onInvalidHashChangeCb; urlHash.onInvalidHashChangeCb = onInvalidHashChangeCb;
urlHash.layers = mapLayers; urlHash.init(map, {
urlHash.map = map; layersControl: layersControl
urlHash.init(map, mapLayers); });
// activate configured default base layer or first if no hash,
// only after hash init, by using the same delay
setTimeout(function() {
layersControl.activateDefaultBaseLayer();
}, urlHash.changeDefer);
routingOptions.on('update', urlHash.onMapMove, urlHash); routingOptions.on('update', urlHash.onMapMove, urlHash);
nogos.on('update', urlHash.onMapMove, urlHash); nogos.on('update', urlHash.onMapMove, urlHash);
// waypoint add, move, delete (but last) // waypoint add, move, delete (but last)
routing.on('routing:routeWaypointEnd', urlHash.onMapMove, urlHash); routing.on('routing:routeWaypointEnd', urlHash.onMapMove, urlHash);
// delete last waypoint // delete last waypoint
routing.on('waypoint:click', function (evt) { routing.on(
var r = evt.marker._routing; 'waypoint:click',
if (!r.prevMarker && !r.nextMarker) { function(evt) {
urlHash.onMapMove(); var r = evt.marker._routing;
} if (!r.prevMarker && !r.nextMarker) {
}, urlHash); urlHash.onMapMove();
}
},
urlHash
);
$(window).resize(function () { $(window).resize(function() {
elevation.addBelow(map); elevation.addBelow(map);
}); });
$('#elevation-chart').on('show.bs.collapse', function () { $('#elevation-chart').on('show.bs.collapse', function() {
$('#elevation-btn').addClass('active'); $('#elevation-btn').addClass('active');
}); });
$('#elevation-chart').on('hidden.bs.collapse', function () { $('#elevation-chart').on('hidden.bs.collapse', function() {
$('#elevation-btn').removeClass('active'); $('#elevation-btn').removeClass('active');
// we must fetch tiles that are located behind elevation-chart // we must fetch tiles that are located behind elevation-chart
map._onResize(); map._onResize();
@ -319,36 +388,149 @@
var onHide = function() { var onHide = function() {
if (this.id && BR.Util.localStorageAvailable()) { if (this.id && BR.Util.localStorageAvailable()) {
localStorage[this.id] = 'true'; localStorage.removeItem(this.id);
} }
}; };
var onShow = function() { var onShow = function() {
if (this.id && BR.Util.localStorageAvailable()) { if (this.id && BR.Util.localStorageAvailable()) {
localStorage.removeItem(this.id); localStorage[this.id] = 'true';
} }
}; };
var toggleSidebar = function (event) {
sidebar.toggle();
$('#sidebar-btn').toggleClass('active');
};
$('#sidebar-btn').on('click', toggleSidebar);
sidebar.on('shown', onShow);
sidebar.on('hidden', onHide);
// on page load, we want to restore collapsible elements from previous usage // on page load, we want to restore collapsible elements from previous usage
$('.collapse').on('hidden.bs.collapse', onHide) $('.collapse')
.on('shown.bs.collapse', onShow) .on('hidden.bs.collapse', onHide)
.each(function() { .on('shown.bs.collapse', onShow)
if (!(this.id && BR.Util.localStorageAvailable() && localStorage[this.id] === 'true' )) { .each(function() {
$(this).collapse('hide'); if (
} this.id &&
}); BR.Util.localStorageAvailable() &&
if (BR.Util.localStorageAvailable() && localStorage[sidebar.id] !== 'true') { localStorage[this.id] === 'true'
toggleSidebar(); ) {
} $(this).collapse('show');
}
});
$('#submitNogos').on('click', function() {
var geoJSONPromise;
var nogoURL = $('#nogoURL').val();
var nogoFile = $('#nogoFile')[0].files[0];
if (nogoURL) {
// TODO: Handle {{bbox}}
geoJSONPromise = fetch(nogoURL).then(function(response) {
response.json();
});
} else if (nogoFile) {
geoJSONPromise = new Promise(function(resolve, reject) {
var reader = new FileReader();
reader.onload = function() {
resolve(reader.result);
};
reader.readAsText(nogoFile);
}).then(function(response) {
return JSON.parse(response);
});
} else {
$('#nogoError').text('Error: Missing file or URL.');
$('#nogoError').css('display', 'block');
return false;
}
var nogoWeight = parseFloat($('#nogoWeight').val());
if (isNaN(nogoWeight)) {
$('#nogoError').text('Error: Missing default nogo weight.');
$('#nogoError').css('display', 'block');
return false;
}
var nogoRadius = parseFloat($('#nogoRadius').val());
if (isNaN(nogoRadius) || nogoRadius < 0) {
$('#nogoError').text('Error: Invalid default nogo radius.');
$('#nogoError').css('display', 'block');
return false;
}
var nogoBuffer = parseFloat($('#nogoBuffer').val());
if (isNaN(nogoBuffer)) {
$('#nogoError').text('Error: Invalid nogo buffering radius.');
$('#nogoError').css('display', 'block');
return false;
}
geoJSONPromise.then(function(response) {
// Iterate on features in order to discard features without geometry
var cleanedGeoJSONFeatures = [];
turf.featureEach(response, function(feature) {
if (turf.getGeom(feature)) {
var maybeBufferedFeature = feature;
// Eventually buffer GeoJSON
if (nogoBuffer != 0) {
maybeBufferedFeature = turf.buffer(
maybeBufferedFeature,
nogoBuffer,
{ units: 'meters' }
);
}
cleanedGeoJSONFeatures.push(maybeBufferedFeature);
}
});
var geoJSON = L.geoJson(
turf.featureCollection(cleanedGeoJSONFeatures),
{
onEachFeature: function(feature, layer) {
layer.options.nogoWeight =
feature.properties.nogoWeight || nogoWeight;
}
}
);
var nogosPoints = geoJSON.getLayers().filter(function(e) {
return e.feature.geometry.type === 'Point';
});
nogosPoints = nogosPoints.map(function(item) {
var radius = item.feature.properties.radius || nogoRadius;
if (radius > 0) {
return L.circle(item.getLatLng(), { radius: radius });
}
return null;
});
nogosPoints = nogosPoints.filter(function(e) {
return e;
});
nogos.setOptions({
nogos: nogosPoints,
polygons: geoJSON.getLayers().filter(function(e) {
return e.feature.geometry.type === 'Polygon';
}),
polylines: geoJSON.getLayers().filter(function(e) {
return e.feature.geometry.type === 'LineString';
})
});
updateRoute({
options: nogos.getOptions()
});
urlHash.onMapMove();
$('#nogoError').text('');
$('#nogoError').css('display', 'none');
$('#loadNogos').modal('hide');
});
return false;
});
} }
mapContext = BR.Map.initMap(); i18next
verifyTouchStyle(mapContext); .use(window.i18nextXHRBackend)
initApp(mapContext); .use(window.i18nextBrowserLanguageDetector)
.init(
{
fallbackLng: 'en',
backend: {
loadPath: 'dist/locales/{{lng}}.json'
}
},
function(err, t) {
jqueryI18next.init(i18next, $);
$('html').localize();
mapContext = BR.Map.initMap();
verifyTouchStyle(mapContext);
initApp(mapContext);
}
);
})(); })();

View file

@ -1,17 +1,19 @@
BR.BingLayer = L.BingLayer.extend({ BR.BingLayer = L.BingLayer.extend({
options: { options: {
maxZoom: 19, maxZoom: 19,
attribution: '<a target="_blank" href="https://www.bing.com/maps/">Bing Maps</a>' attribution:
+ ' (<a target="_blank" href="https://go.microsoft.com/?linkid=9710837">TOU</a>)' '<a target="_blank" href="https://www.bing.com/maps/">Bing Maps</a>' +
' (<a target="_blank" href="https://go.microsoft.com/?linkid=9710837">TOU</a>)'
}, },
initialize: function(key, options) { initialize: function(key, options) {
L.BingLayer.prototype.initialize.call(this, key, options); L.BingLayer.prototype.initialize.call(this, key, options);
this._logo = L.control({position: 'bottomleft'}); this._logo = L.control({ position: 'bottomleft' });
this._logo.onAdd = function (map) { this._logo.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'bing-logo'); this._div = L.DomUtil.create('div', 'bing-logo');
this._div.innerHTML = '<img src="https://www.microsoft.com/maps/images/branding/Bing%20logo%20white_50px-19px.png">'; this._div.innerHTML =
'<img src="https://www.microsoft.com/maps/images/branding/Bing%20logo%20white_50px-19px.png">';
return this._div; return this._div;
}; };
}, },

View file

@ -1,33 +1,47 @@
BR.Elevation = L.Control.Elevation.extend({ BR.Elevation = L.Control.Elevation.extend({
options: { options: {
width:$('#map').outerWidth(), width: $('#map').outerWidth(),
margins: { margins: {
top: 20, top: 20,
right: 30, right: 30,
bottom: 30, bottom: 30,
left: 60 left: 60
}, },
theme: "steelblue-theme" theme: 'steelblue-theme'
}, },
onAdd: function (map) { onAdd: function(map) {
var container = L.Control.Elevation.prototype.onAdd.call(this, map); var container = L.Control.Elevation.prototype.onAdd.call(this, map);
// revert registering touch events when touch screen detection is available and negative // revert registering touch events when touch screen detection is available and negative
// see https://github.com/MrMufflon/Leaflet.Elevation/issues/67 // see https://github.com/MrMufflon/Leaflet.Elevation/issues/67
if (L.Browser.touch && BR.Browser.touchScreenDetectable && !BR.Browser.touchScreen) { if (
L.Browser.touch &&
this._background.on("touchmove.drag", null). BR.Browser.touchScreenDetectable &&
on("touchstart.drag", null). !BR.Browser.touchScreen
on("touchstart.focus", null); ) {
L.DomEvent.off(this._container, 'touchend', this._dragEndHandler, this); this._background
.on('touchmove.drag', null)
this._background.on("mousemove.focus", this._mousemoveHandler.bind(this)). .on('touchstart.drag', null)
on("mouseout.focus", this._mouseoutHandler.bind(this)). .on('touchstart.focus', null);
on("mousedown.drag", this._dragStartHandler.bind(this)). L.DomEvent.off(
on("mousemove.drag", this._dragHandler.bind(this)); this._container,
L.DomEvent.on(this._container, 'mouseup', this._dragEndHandler, this); 'touchend',
this._dragEndHandler,
this
);
this._background
.on('mousemove.focus', this._mousemoveHandler.bind(this))
.on('mouseout.focus', this._mouseoutHandler.bind(this))
.on('mousedown.drag', this._dragStartHandler.bind(this))
.on('mousemove.drag', this._dragHandler.bind(this));
L.DomEvent.on(
this._container,
'mouseup',
this._dragEndHandler,
this
);
} }
return container; return container;
@ -36,7 +50,7 @@ BR.Elevation = L.Control.Elevation.extend({
addBelow: function(map) { addBelow: function(map) {
// waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66 // waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66
// this.width($('#map').outerWidth()); // this.width($('#map').outerWidth());
this.options.width = $('#map').outerWidth(); this.options.width = $('#content').outerWidth();
if (this.getContainer() != null) { if (this.getContainer() != null) {
this.remove(map); this.remove(map);
@ -44,10 +58,13 @@ BR.Elevation = L.Control.Elevation.extend({
function setParent(el, newParent) { function setParent(el, newParent) {
newParent.appendChild(el); newParent.appendChild(el);
} }
this.addTo(map); this.addTo(map);
// move elevation graph outside of the map // move elevation graph outside of the map
setParent(this.getContainer(), document.getElementById('elevation-chart')); setParent(
this.getContainer(),
document.getElementById('elevation-chart')
);
}, },
update: function(track, layer) { update: function(track, layer) {
@ -62,7 +79,7 @@ BR.Elevation = L.Control.Elevation.extend({
if (track && track.getLatLngs().length > 0) { if (track && track.getLatLngs().length > 0) {
this.addData(track.toGeoJSON(), layer); this.addData(track.toGeoJSON(), layer);
layer.on("mouseout", this._hidePositionMarker.bind(this)); layer.on('mouseout', this._hidePositionMarker.bind(this));
} }
} }
}); });

View file

@ -1,76 +1,504 @@
L.drawLocal.draw.toolbar.buttons.circle = 'Draw no-go area (circle)'; BR.NogoAreas = L.Control.extend({
L.drawLocal.edit.toolbar.buttons.edit = 'Edit no-go areas'; statics: {
L.drawLocal.edit.toolbar.buttons.remove = 'Delete no-go areas'; MSG_BUTTON: 'Draw no-go area (circle)',
MSG_BUTTON_CANCEL: 'Cancel drawing no-go area',
BR.NogoAreas = L.Control.Draw.extend({ MSG_CREATE: 'Click and drag to draw circle',
initialize: function () { MSG_DISABLED: 'Click to edit',
this.drawnItems = new L.FeatureGroup(); MSG_ENABLED:
'&square; = move / resize, <span class="fa fa-trash-o"></span> = delete,<br>click nogo to quit editing',
L.Control.Draw.prototype.initialize.call(this, { STATE_CREATE: 'no-go-create',
draw: { STATE_CANCEL: 'cancel-no-go-create'
position: 'topleft',
polyline: false,
polygon: false,
circle: true,
rectangle: false,
marker: false
},
edit: {
featureGroup: this.drawnItems,
//edit: false,
edit: {
selectedPathOptions: {
//opacity: 0.8
}
},
remove: true
}
});
}, },
onAdd: function (map) { style: {
map.addLayer(this.drawnItems); color: '#f06eaa',
weight: 4,
opacity: 0.5,
fillColor: null, //same as color by default
fillOpacity: 0.2,
dashArray: null
},
map.on('draw:created', function (e) { editStyle: {
var layer = e.layer; color: '#fe57a1',
this.drawnItems.addLayer(layer); opacity: 0.6,
this._fireUpdate(); dashArray: '10, 10',
}, this); fillOpacity: 0.1
},
map.on('draw:editstart', function (e) { initialize: function() {
this.drawnItems.eachLayer(function (layer) { this._wasRouteDrawing = false;
layer.on('edit', function(e) { },
this._fireUpdate();
}, this);
}, this);
}, this);
map.on('draw:deleted', function (e) { onAdd: function(map) {
this._fireUpdate(); var self = this;
}, this);
return L.Control.Draw.prototype.onAdd.call(this, map); this.drawnItems = new L.FeatureGroup().addTo(map);
this.drawnItems.on('click', function(e) {
L.DomEvent.stop(e);
e.layer.toggleEdit();
});
var editTools = (this.editTools = map.editTools = new L.Editable(map, {
circleEditorClass: BR.DeletableCircleEditor,
// FeatureGroup instead of LayerGroup to propagate events to members
editLayer: new L.FeatureGroup().addTo(map),
featuresLayer: this.drawnItems
}));
this.button = L.easyButton({
states: [
{
stateName: BR.NogoAreas.STATE_CREATE,
icon: 'fa-ban',
title: BR.NogoAreas.MSG_BUTTON,
onClick: function(control) {
// initial radius of 0 to detect click, see DeletableCircleEditor.onDrawingMouseUp
var opts = L.extend({ radius: 0 }, self.style);
editTools.startCircle(null, opts);
control.state('cancel-no-go-create');
}
},
{
stateName: BR.NogoAreas.STATE_CANCEL,
icon: 'fa-ban active',
title: BR.NogoAreas.MSG_BUTTON_CANCEL,
onClick: function(control) {
editTools.stopDrawing();
control.state('no-go-create');
}
}
]
});
this.editTools.on(
'editable:drawing:end',
function(e) {
self.button.state(BR.NogoAreas.STATE_CREATE);
setTimeout(
L.bind(function() {
// turn editing off after create; async to still fire 'editable:vertex:dragend'
e.layer.disableEdit();
}, this),
0
);
},
this
);
this.editTools.on(
'editable:vertex:dragend editable:deleted',
function(e) {
this._fireUpdate();
},
this
);
this.editTools.on(
'editable:enable',
function(e) {
e.layer.setStyle(this.editStyle);
},
this
);
this.editTools.on(
'editable:disable',
function(e) {
e.layer.setStyle(this.style);
},
this
);
this.tooltip = new BR.EditingTooltip(map, editTools, this.button);
this.tooltip.enable();
// dummy, no own representation, delegating to EasyButton
return L.DomUtil.create('div');
},
// prevent route waypoint added after circle create (map click after up)
preventRoutePointOnCreate: function(routing) {
this.editTools.on(
'editable:drawing:start',
function(e) {
this._wasRouteDrawing = routing.isDrawing();
routing.draw(false);
},
this
);
// after create
this.editTools.on(
'editable:drawing:end',
function(e) {
if (this._wasRouteDrawing) {
setTimeout(function() {
routing.draw(true);
}, 0);
}
},
this
);
}, },
getOptions: function() { getOptions: function() {
return { return {
nogos: this.drawnItems.getLayers() nogos: this.drawnItems.getLayers().filter(function(e) {
return e instanceof L.Circle;
}),
polygons: this.drawnItems.getLayers().filter(function(e) {
return e instanceof L.Polygon;
}),
polylines: this.drawnItems.getLayers().filter(function(e) {
return e instanceof L.Polyline && !(e instanceof L.Polygon);
})
}; };
}, },
setOptions: function(options) { setOptions: function(options) {
var nogos = options.nogos; var nogos = options.nogos;
this.drawnItems.clearLayers(); var polylines = options.polylines;
var polygons = options.polygons;
this._clear();
if (nogos) { if (nogos) {
for (var i = 0; i < nogos.length; i++) { for (var i = 0; i < nogos.length; i++) {
nogos[i].setStyle(this.style);
this.drawnItems.addLayer(nogos[i]); this.drawnItems.addLayer(nogos[i]);
} }
} }
if (polylines) {
for (var i = 0; i < polylines.length; i++) {
polylines[i].setStyle(this.style);
this.drawnItems.addLayer(polylines[i]);
}
}
if (polygons) {
for (var i = 0; i < polygons.length; i++) {
polygons[i].setStyle(this.style);
this.drawnItems.addLayer(polygons[i]);
}
}
}, },
_fireUpdate: function () { _clear: function() {
this.fire('update', {options: this.getOptions()}); this.drawnItems.clearLayers();
},
clear: function() {
this._clear();
this._fireUpdate();
},
_fireUpdate: function() {
this.fire('update', { options: this.getOptions() });
},
getFeatureGroup: function() {
return this.drawnItems;
},
getEditGroup: function() {
return this.editTools.editLayer;
},
getButton: function() {
return this.button;
} }
}); });
BR.NogoAreas.include(L.Mixin.Events); BR.NogoAreas.include(L.Evented.prototype);
L.Editable.prototype.createVertexIcon = function(options) {
return BR.Browser.touch
? new L.Editable.TouchVertexIcon(options)
: new L.Editable.VertexIcon(options);
};
BR.EditingTooltip = L.Handler.extend({
options: {
closeTimeout: 2000
},
initialize: function(map, editTools, button) {
this.map = map;
this.editTools = editTools;
this.button = button;
},
addHooks: function() {
// hack: listen to EasyButton click (instead of editable:drawing:start),
// to get mouse position from event for initial tooltip location
L.DomEvent.addListener(
this.button.button,
'click',
this._addCreate,
this
);
this.editTools.featuresLayer.on('layeradd', this._bind, this);
this.editTools.on('editable:drawing:end', this._postCreate, this);
this.editTools.on('editable:enable', this._enable, this);
this.editTools.on('editable:disable', this._disable, this);
},
removeHooks: function() {
L.DomEvent.removeListener(
this.button.button,
'click',
this._addCreate,
this
);
this.editTools.featuresLayer.off('layeradd', this._bind, this);
this.editTools.off('editable:drawing:end', this._postCreate, this);
this.editTools.off('editable:enable', this._enable, this);
this.editTools.off('editable:disable', this._disable, this);
},
_bind: function(e) {
// Position tooltip at bottom of circle, less distracting than
// sticky with cursor or at center.
var layer = e.layer;
layer.bindTooltip(BR.NogoAreas.MSG_DISABLED, {
direction: 'bottom',
className: 'editing-tooltip'
});
// Override to set position to south instead of center (circle latlng);
// works better with zooming than updating offset to match radius
layer.openTooltip = function(layer, latlng) {
if (!latlng && layer instanceof L.Layer) {
latlng = L.latLng(
layer.getBounds().getSouth(),
0.5 *
(layer.getBounds().getWest() +
layer.getBounds().getEast())
);
}
L.Layer.prototype.openTooltip.call(this, layer, latlng);
};
},
_addCreate: function(e) {
// button cancel
if (!this.editTools.drawing()) return;
var initialLatLng = this.map.mouseEventToLatLng(e);
var tooltip = L.tooltip({
// no effect with map tooltip
sticky: true,
// offset wrong with 'auto' when switching direction
direction: 'right',
offset: L.point(5, 28),
className: 'editing-tooltip-create'
});
// self-reference hack for _moveTooltip, as tooltip is not bound to layer
tooltip._tooltip = tooltip;
// simulate sticky feature (follow mouse) for map tooltip without layer
var onOffMove = function(e) {
var onOff = e.type === 'tooltipclose' ? 'off' : 'on';
this._map[onOff]('mousemove', this._moveTooltip, this);
};
this.map.on('tooltipopen', onOffMove, tooltip);
this.map.on('tooltipclose', onOffMove, tooltip);
var onTooltipRemove = function(e) {
this.map.off('tooltipopen', onOffMove, e.tooltip);
this.map.off('tooltipclose', onOffMove, e.tooltip);
this.map.off('tooltipclose', onTooltipRemove, this);
e.tooltip._tooltip = null;
};
this.map.on('tooltipclose', onTooltipRemove, this);
tooltip.setTooltipContent(BR.NogoAreas.MSG_CREATE);
this.map.openTooltip(tooltip, initialLatLng);
var closeTooltip = function() {
this.map.closeTooltip(tooltip);
};
this.editTools.once(
'editable:editing editable:drawing:cancel',
closeTooltip,
this
);
if (BR.Browser.touch) {
// can't move with cursor on touch devices, so show at start pos for a few seconds
setTimeout(L.bind(closeTooltip, this), this.options.closeTimeout);
}
},
_setCloseTimeout: function(layer) {
var timeoutId = setTimeout(function() {
layer.closeTooltip();
}, this.options.closeTimeout);
// prevent timer to close tooltip that changed in the meantime
layer.once('tooltipopen', function(e) {
clearTimeout(timeoutId);
});
},
_postCreate: function() {
// editing is disabled by another handler, tooltip won't stay open before
this.editTools.once(
'editable:disable',
function(e) {
// show for a few seconds, as mouse often not hovering circle after create
e.layer.openTooltip(e.layer);
this._setCloseTimeout(e.layer);
},
this
);
},
_enable: function(e) {
e.layer.setTooltipContent(BR.NogoAreas.MSG_ENABLED);
this.editTools.once(
'editable:editing',
function(e) {
e.layer.closeTooltip();
},
this
);
},
_disable: function(e) {
e.layer.setTooltipContent(BR.NogoAreas.MSG_DISABLED);
this._setCloseTimeout(e.layer);
}
});
BR.DeletableCircleEditor = L.Editable.CircleEditor.extend({
_computeDeleteLatLng: function() {
// While circle is not added to the map, _radius is not set.
var delta =
(this.feature._radius || this.feature._mRadius) *
Math.cos(Math.PI / 4),
point = this.map.project(this.feature._latlng);
return this.map.unproject([point.x - delta, point.y - delta]);
},
_updateDeleteLatLng: function() {
this._deleteLatLng.update(this._computeDeleteLatLng());
this._deleteLatLng.__vertex.update();
},
_addDeleteMarker: function() {
if (!this.enabled()) return;
this._deleteLatLng = this._computeDeleteLatLng();
return new BR.DeleteMarker(this._deleteLatLng, this);
},
_delete: function() {
this.disable();
this.tools.featuresLayer.removeLayer(this.feature);
},
delete: function() {
this._delete();
this.fireAndForward('editable:deleted');
},
initialize: function(map, feature, options) {
L.Editable.CircleEditor.prototype.initialize.call(
this,
map,
feature,
options
);
this._deleteLatLng = this._computeDeleteLatLng();
// FeatureGroup instead of LayerGroup to propagate events to members
this.editLayer = new L.FeatureGroup();
},
addHooks: function() {
L.Editable.CircleEditor.prototype.addHooks.call(this);
if (this.feature) {
this._addDeleteMarker();
}
return this;
},
reset: function() {
L.Editable.CircleEditor.prototype.reset.call(this);
this._addDeleteMarker();
},
onDrawingMouseDown: function(e) {
this._deleteLatLng.update(e.latlng);
L.Editable.CircleEditor.prototype.onDrawingMouseDown.call(this, e);
},
// override to cancel/remove created circle when added by click instead of drag, because:
// - without resize, edit handles stacked on top of each other
// - makes event handling more complicated (editable:vertex:dragend not called)
onDrawingMouseUp: function(e) {
if (this.feature.getRadius() > 0) {
this.commitDrawing(e);
} else {
this.cancelDrawing(e);
this._delete();
}
e.originalEvent._simulated = false;
L.Editable.PathEditor.prototype.onDrawingMouseUp.call(this, e);
},
onVertexMarkerDrag: function(e) {
this._updateDeleteLatLng();
L.Editable.CircleEditor.prototype.onVertexMarkerDrag.call(this, e);
}
});
BR.DeleteMarker = L.Marker.extend({
options: {
draggable: false,
icon: L.divIcon({
iconSize: BR.Browser.touch
? new L.Point(24, 24)
: new L.Point(16, 16),
className: 'leaflet-div-icon fa fa-trash-o nogo-delete-marker'
})
},
initialize: function(latlng, editor, options) {
// derived from L.Editable.VertexMarker.initialize
// We don't use this._latlng, because on drag Leaflet replace it while
// we want to keep reference.
this.latlng = latlng;
this.editor = editor;
L.Marker.prototype.initialize.call(this, latlng, options);
this.latlng.__vertex = this;
this.editor.editLayer.addLayer(this);
// to keep small circles editable, make sure delete button is below drag handle
// (not using "+ 1" to place at bottom of other vertex markers)
this.setZIndexOffset(editor.tools._lastZIndex);
},
onAdd: function(map) {
L.Marker.prototype.onAdd.call(this, map);
this.on('click', this.onClick);
},
onRemove: function(map) {
delete this.latlng.__vertex;
this.off('click', this.onClick);
L.Marker.prototype.onRemove.call(this, map);
},
onClick: function(e) {
this.editor.delete();
}
});

View file

@ -1,7 +1,7 @@
L.Routing.Draw.prototype._hideTrailer = function() { L.Routing.Draw.prototype._hideTrailer = function() {
if (this._trailer.options.opacity !== 0.0) { if (this._trailer.options.opacity !== 0.0) {
this._trailer.setStyle({opacity: 0.0}); this._trailer.setStyle({ opacity: 0.0 });
} }
}; };
BR.Routing = L.Routing.extend({ BR.Routing = L.Routing.extend({
@ -20,7 +20,7 @@ BR.Routing = L.Routing.extend({
zIndexOffset: -2000 zIndexOffset: -2000
}, },
onAdd: function (map) { onAdd: function(map) {
this._segmentsCasing = new L.FeatureGroup().addTo(map); this._segmentsCasing = new L.FeatureGroup().addTo(map);
var container = L.Routing.prototype.onAdd.call(this, map); var container = L.Routing.prototype.onAdd.call(this, map);
@ -31,35 +31,50 @@ BR.Routing = L.Routing.extend({
this._waypoints.on('layeradd', this._setMarkerOpacity, this); this._waypoints.on('layeradd', this._setMarkerOpacity, this);
// turn line mouse marker off while over waypoint marker // turn line mouse marker off while over waypoint marker
this.on('waypoint:mouseover', function(e) { this.on(
// L.Routing.Edit._segmentOnMouseout without firing 'segment:mouseout' (enables draw) 'waypoint:mouseover',
if (this._dragging) { return; } function(e) {
// L.Routing.Edit._segmentOnMouseout without firing 'segment:mouseout' (enables draw)
if (this._dragging) {
return;
}
this._mouseMarker.setOpacity(0.0); this._mouseMarker.setOpacity(0.0);
this._map.off('mousemove', this._segmentOnMousemove, this); this._map.off('mousemove', this._segmentOnMousemove, this);
this._suspended = true; this._suspended = true;
}, this._edit); },
this._edit
);
this.on('waypoint:mouseout', function(e) { this.on(
this._segmentOnMouseover(e); 'waypoint:mouseout',
this._suspended = false; function(e) {
}, this._edit); this._segmentOnMouseover(e);
this._suspended = false;
},
this._edit
);
this._edit._mouseMarker.setIcon(L.divIcon({ this._edit._mouseMarker.setIcon(
className: 'line-mouse-marker' L.divIcon({
,iconAnchor: [8, 8] // size/2 + border/2 className: 'line-mouse-marker',
,iconSize: [16, 16] iconAnchor: [8, 8], // size/2 + border/2
})); iconSize: [16, 16]
})
);
// Forward mousemove event to snapped feature (for Leaflet.Elevation to // Forward mousemove event to snapped feature (for Leaflet.Elevation to
// update indicator), see also L.Routing.Edit._segmentOnMousemove // update indicator), see also L.Routing.Edit._segmentOnMousemove
this._edit._mouseMarker.on('move', L.bind(function(e) { this._edit._mouseMarker.on(
var latLng = e.latlng; 'move',
if (latLng._feature) { L.bind(function(e) {
this._mouseMarker._feature = latLng._feature; var latLng = e.latlng;
latLng._feature.fire('mousemove', e, true); if (latLng._feature) {
} this._mouseMarker._feature = latLng._feature;
}, this._edit)); latLng._feature.fire('mousemove', e, true);
}
}, this._edit)
);
var mouseoutHandler = function(e) { var mouseoutHandler = function(e) {
if (this._mouseMarker._feature) { if (this._mouseMarker._feature) {
this._mouseMarker._feature.fire('mouseout', e, true); this._mouseMarker._feature.fire('mouseout', e, true);
@ -79,12 +94,20 @@ BR.Routing = L.Routing.extend({
// intercept listener: only re-show draw trailer after marker hover // intercept listener: only re-show draw trailer after marker hover
// when edit is not active (i.e. wasn't also supended) // when edit is not active (i.e. wasn't also supended)
this._parent.off('waypoint:mouseout' , this._catchWaypointEvent, this); this._parent.off(
this.on('waypoint:mouseout' , function(e) { 'waypoint:mouseout',
if (!this._parent._edit._suspended) { this._catchWaypointEvent,
this._catchWaypointEvent(e); this
} );
}, this); this.on(
'waypoint:mouseout',
function(e) {
if (!this._parent._edit._suspended) {
this._catchWaypointEvent(e);
}
},
this
);
}); });
this._draw.on('disabled', function() { this._draw.on('disabled', function() {
L.DomUtil.removeClass(map.getContainer(), 'routing-draw-enabled'); L.DomUtil.removeClass(map.getContainer(), 'routing-draw-enabled');
@ -111,7 +134,12 @@ BR.Routing = L.Routing.extend({
this._map.off('mouseout', hide, this); this._map.off('mouseout', hide, this);
this._map.off('mouseover', show, this); this._map.off('mouseover', show, this);
L.DomEvent.off(this._map._controlContainer, 'mouseout', show, this); L.DomEvent.off(this._map._controlContainer, 'mouseout', show, this);
L.DomEvent.off(this._map._controlContainer, 'mouseover', hide, this); L.DomEvent.off(
this._map._controlContainer,
'mouseover',
hide,
this
);
}); });
// Call show after deleting last waypoint, but hide trailer. // Call show after deleting last waypoint, but hide trailer.
@ -119,180 +147,216 @@ BR.Routing = L.Routing.extend({
// mouseout to show again never fires when deleted. Click handler // mouseout to show again never fires when deleted. Click handler
// _onMouseClick aborts when hidden, so no waypoint can be added // _onMouseClick aborts when hidden, so no waypoint can be added
// although enabled. // although enabled.
this.on('waypoint:click', function() { this.on(
if (this._hidden && !this._parent._waypoints._first) { 'waypoint:click',
this._show(); function() {
this._hideTrailer(); if (this._hidden && !this._parent._waypoints._first) {
} this._show();
}, this._draw); this._hideTrailer();
}
},
this._draw
);
// keys not working when map container does not have focus, use document instead // keys not working when map container does not have focus, use document instead
L.DomEvent.removeListener(this._container, 'keyup', this._keyupListener); L.DomEvent.removeListener(
this._container,
'keyup',
this._keyupListener
);
L.DomEvent.addListener(document, 'keyup', this._keyupListener, this); L.DomEvent.addListener(document, 'keyup', this._keyupListener, this);
// enable drawing mode // enable drawing mode
this.draw(true); this.draw(true);
return container; return container;
} },
,_addSegmentCasing: function(e) { _addSegmentCasing: function(e) {
var casing = L.polyline(e.layer.getLatLngs(), this.options.styles.trackCasing); var casing = L.polyline(
this._segmentsCasing.addLayer(casing); e.layer.getLatLngs(),
e.layer._casing = casing; this.options.styles.trackCasing
this._segments.bringToFront(); );
} this._segmentsCasing.addLayer(casing);
e.layer._casing = casing;
this._segments.bringToFront();
},
,_removeSegmentCasing: function(e) { _removeSegmentCasing: function(e) {
this._segmentsCasing.removeLayer(e.layer._casing); this._segmentsCasing.removeLayer(e.layer._casing);
} },
,setOpacity: function(opacity) { setOpacity: function(opacity) {
// Due to the second Polyline layer for casing, the combined opacity is less // Due to the second Polyline layer for casing, the combined opacity is less
// transparent than with a single layer and the slider is non-linear. The // transparent than with a single layer and the slider is non-linear. The
// inverted formula is used to get the same result as with a single layer. // inverted formula is used to get the same result as with a single layer.
// SVG simple alpha compositing: Ca' = 1 - (1 - Ea) * (1 - Ca) // SVG simple alpha compositing: Ca' = 1 - (1 - Ea) * (1 - Ca)
// https://www.w3.org/TR/SVG11/masking.html#SimpleAlphaBlending // https://www.w3.org/TR/SVG11/masking.html#SimpleAlphaBlending
var sourceOpacity = 1 - Math.sqrt(1 - opacity); var sourceOpacity = 1 - Math.sqrt(1 - opacity);
this.options.styles.track.opacity = sourceOpacity; this.options.styles.track.opacity = sourceOpacity;
this.options.styles.trackCasing.opacity = sourceOpacity; this.options.styles.trackCasing.opacity = sourceOpacity;
this.options.icons.opacity = opacity; this.options.icons.opacity = opacity;
this._segments.setStyle({ this._segments.setStyle({
opacity: sourceOpacity opacity: sourceOpacity
});
this._segmentsCasing.setStyle({
opacity: sourceOpacity
});
this._waypoints.eachLayer(function(marker) {
marker.setOpacity(opacity);
});
}
,_setMarkerOpacity: function(e) {
e.layer.setOpacity(this.options.icons.opacity);
}
,_removeMarkerEvents: function(marker) {
marker.off('mouseover', this._fireWaypointEvent, this);
marker.off('mouseout' , this._fireWaypointEvent, this);
marker.off('dragstart', this._fireWaypointEvent, this);
marker.off('dragend' , this._fireWaypointEvent, this);
marker.off('drag' , this._fireWaypointEvent, this);
marker.off('click' , this._fireWaypointEvent, this);
}
,clear: function() {
var drawEnabled = this._draw._enabled;
var current = this._waypoints._first;
this.draw(false);
if (current === null) { return; }
this._removeMarkerEvents(current);
while (current._routing.nextMarker) {
var marker = current._routing.nextMarker;
this._removeMarkerEvents(marker);
current = marker;
};
this._waypoints._first = null;
this._waypoints._last = null;
this._waypoints.clearLayers();
this._segments.clearLayers();
if (drawEnabled) {
this.draw(true);
}
}
,setWaypoints: function(latLngs, cb) {
var i;
var callbackCount = 0;
var firstErr;
var $this = this;
var callback = function(err, data) {
callbackCount++;
firstErr = firstErr || err;
if (callbackCount >= latLngs.length) {
$this.fire('routing:setWaypointsEnd', { err: firstErr });
if (cb) {
cb(firstErr);
}
}
};
this.fire('routing:setWaypointsStart');
for (i = 0; latLngs && i < latLngs.length; i++) {
this.addWaypoint(latLngs[i], this._waypoints._last, null, callback);
}
}
// patch to fix error when line is null or error line
// (when called while still segments to calculate, e.g. permalink or fast drawing)
,toPolyline: function() {
var latLngs = [];
this._eachSegment(function(m1, m2, line) {
// omit if null (still calculating) or error
// NOTE: feature check specific to BRouter GeoJSON response, workaround to detect error line
if (line && line.feature) {
latLngs = latLngs.concat(line.getLatLngs());
}
});
return L.polyline(latLngs);
}
,_routeSegment: function(m1, m2, cb) {
var loadingTrailer;
// change segment color before request to indicate recalculation (mark old)
if (m1 && m1._routing.nextLine !== null) {
m1._routing.nextLine.setStyle({color: 'dimgray' });
}
// animate dashed trailer as loading indicator
if (m1 && m2) {
loadingTrailer = new L.Polyline([m1.getLatLng(), m2.getLatLng()], {
color: this.options.styles.track.color,
opacity: this.options.styles.trailer.opacity,
dashArray: [10, 10],
className: 'loading-trailer'
}); });
loadingTrailer.addTo(this._map); this._segmentsCasing.setStyle({
} opacity: sourceOpacity
});
this._waypoints.eachLayer(function(marker) {
marker.setOpacity(opacity);
});
},
L.Routing.prototype._routeSegment.call(this, m1, m2, L.bind(function(err, data) { _setMarkerOpacity: function(e) {
if (loadingTrailer) { e.layer.setOpacity(this.options.icons.opacity);
this._map.removeLayer(loadingTrailer); },
_removeMarkerEvents: function(marker) {
marker.off('mouseover', this._fireWaypointEvent, this);
marker.off('mouseout', this._fireWaypointEvent, this);
marker.off('dragstart', this._fireWaypointEvent, this);
marker.off('dragend', this._fireWaypointEvent, this);
marker.off('drag', this._fireWaypointEvent, this);
marker.off('click', this._fireWaypointEvent, this);
},
clear: function() {
var drawEnabled = this._draw._enabled;
var current = this._waypoints._first;
this.draw(false);
if (current === null) {
return;
}
this._removeMarkerEvents(current);
while (current._routing.nextMarker) {
var marker = current._routing.nextMarker;
this._removeMarkerEvents(marker);
current = marker;
} }
cb(err, data);
}, this));
}
,getSegments: function() { this._waypoints._first = null;
var segments = []; this._waypoints._last = null;
this._waypoints.clearLayers();
this._segments.clearLayers();
this._eachSegment(function(m1, m2, line) { if (drawEnabled) {
// omit if null (still calculating) or error this.draw(true);
// NOTE: feature check specific to BRouter GeoJSON response, workaround to detect error line }
if (line && line.feature) { },
segments.push(line);
}
});
return segments; setWaypoints: function(latLngs, cb) {
} var i;
var callbackCount = 0;
var firstErr;
var $this = this;
// add 'esc' to disable drawing var callback = function(err, data) {
,_keyupListener: function (e) { callbackCount++;
if (e.keyCode === 27) { firstErr = firstErr || err;
this._draw.disable(); if (callbackCount >= latLngs.length) {
} else { $this.fire('routing:setWaypointsEnd', { err: firstErr });
L.Routing.prototype._keyupListener.call(this, e); if (cb) {
cb(firstErr);
}
}
};
this.fire('routing:setWaypointsStart');
for (i = 0; latLngs && i < latLngs.length; i++) {
this.addWaypoint(latLngs[i], this._waypoints._last, null, callback);
}
},
// patch to fix error when line is null or error line
// (when called while still segments to calculate, e.g. permalink or fast drawing)
toPolyline: function() {
var latLngs = [];
this._eachSegment(function(m1, m2, line) {
// omit if null (still calculating) or error
// NOTE: feature check specific to BRouter GeoJSON response, workaround to detect error line
if (line && line.feature) {
latLngs = latLngs.concat(line.getLatLngs());
}
});
return L.polyline(latLngs);
},
_routeSegment: function(m1, m2, cb) {
var loadingTrailer;
// change segment color before request to indicate recalculation (mark old)
if (m1 && m1._routing.nextLine !== null) {
m1._routing.nextLine.setStyle({ color: 'dimgray' });
}
// animate dashed trailer as loading indicator
if (m1 && m2) {
loadingTrailer = new L.Polyline([m1.getLatLng(), m2.getLatLng()], {
color: this.options.styles.track.color,
opacity: this.options.styles.trailer.opacity,
dashArray: [10, 10],
className: 'loading-trailer'
});
loadingTrailer.addTo(this._map);
}
L.Routing.prototype._routeSegment.call(
this,
m1,
m2,
L.bind(function(err, data) {
if (loadingTrailer) {
this._map.removeLayer(loadingTrailer);
}
cb(err, data);
}, this)
);
},
getSegments: function() {
var segments = [];
this._eachSegment(function(m1, m2, line) {
// omit if null (still calculating) or error
// NOTE: feature check specific to BRouter GeoJSON response, workaround to detect error line
if (line && line.feature) {
segments.push(line);
}
});
return segments;
},
_keyupListener: function(e) {
// Suppress shortcut handling when a text input field is focussed
if (
document.activeElement.type == 'text' ||
document.activeElement.type == 'textarea'
) {
return;
}
// add 'esc' to disable drawing
if (e.keyCode === 27) {
this._draw.disable();
} else {
L.Routing.prototype._keyupListener.call(this, e);
}
},
isDrawing: function() {
return this._draw._enabled;
},
reverse: function() {
var waypoints = this.getWaypoints();
waypoints.reverse();
this.clear();
this.setWaypoints(waypoints);
} }
}
}); });

View file

@ -1,18 +1,14 @@
BR.Search = L.Control.Geocoder.extend({ BR.Search = L.Control.Geocoder.extend({
options: { options: {
geocoder: new L.Control.Geocoder.Nominatim({ geocoder: new L.Control.Geocoder.LatLng({
serviceUrl: 'https://nominatim.openstreetmap.org/' next: new L.Control.Geocoder.Nominatim({
serviceUrl: 'https://nominatim.openstreetmap.org/'
}),
sizeInMeters: 800
}), }),
position: 'topleft' position: 'topleft'
}, },
onAdd: function (map) {
map.attributionControl.addAttribution(
'search by <a href="https://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Nominatim</a>');
return L.Control.Geocoder.prototype.onAdd.call(this, map);
},
markGeocode: function(result) { markGeocode: function(result) {
this._map.fitBounds(result.geocode.bbox, { this._map.fitBounds(result.geocode.bbox, {
maxZoom: 17 maxZoom: 17

115
js/plugin/Sidebar.js Normal file
View file

@ -0,0 +1,115 @@
BR.Sidebar = L.Control.Sidebar.extend({
storageId: 'sidebar-control',
options: {
position: 'right',
container: 'sidebar',
tabContainer: 'sidebarTabs',
autopan: false,
defaultTabId: '',
// Tabs to be notified when shown or hidden
// (tab div id -> object implementing show/hide methods)
listeningTabs: {}
},
initialize: function(id, options) {
L.Control.Sidebar.prototype.initialize.call(this, id, options);
this.oldTab = null;
},
addTo: function(map) {
L.Control.Sidebar.prototype.addTo.call(this, map);
this.on('content', this._notifyOnContent, this);
this.on('closing', this._notifyOnClose, this);
this.on('toggleExpand', this._notifyOnResize, this);
this.on(
'closing',
function() {
this._map.getContainer().focus();
},
this
);
this._rememberTabState();
if (
L.Browser.touch &&
BR.Browser.touchScreenDetectable &&
!BR.Browser.touchScreen
) {
L.DomUtil.removeClass(this._container, 'leaflet-touch');
L.DomUtil.removeClass(this._tabContainer, 'leaflet-touch');
}
return this;
},
showPanel: function(id) {
var tab = this._getTab(id);
tab.hidden = false;
return this;
},
_rememberTabState: function() {
if (BR.Util.localStorageAvailable()) {
this.on('content closing', this._storeActiveTab, this);
var tabId = localStorage.getItem(this.storageId);
// 'true': legacy value for toggling old sidebar
if (tabId === 'true') {
tabId = this.options.defaultTabId;
} else if (tabId === null) {
// not set: closed by default for new users
tabId = '';
}
if (tabId !== '') {
this.open(tabId);
}
}
},
_notifyShow: function(tab) {
if (tab && tab.show) {
tab.show();
}
},
_notifyHide: function(tab) {
if (tab && tab.hide) {
tab.hide();
}
},
_notifyOnContent: function(e) {
var tab = this.options.listeningTabs[e.id];
this._notifyHide(this.oldTab);
this._notifyShow(tab);
this.oldTab = tab;
},
_notifyOnClose: function(e) {
this._notifyHide(this.oldTab);
this.oldTab = null;
},
_notifyOnResize: function(e) {
var tab = this.oldTab;
if (tab && tab.onResize) {
tab.onResize();
}
},
_storeActiveTab: function(e) {
localStorage.setItem(this.storageId, e.id || '');
}
});
BR.sidebar = function(divId, options) {
return new BR.Sidebar(divId, options);
};

View file

@ -1,8 +1,9 @@
(function(window) { (function(window) {
var HAS_HASHCHANGE = (function() { var HAS_HASHCHANGE = (function() {
var doc_mode = window.documentMode; var doc_mode = window.documentMode;
return ('onhashchange' in window) && return (
(doc_mode === undefined || doc_mode > 7); 'onhashchange' in window && (doc_mode === undefined || doc_mode > 7)
);
})(); })();
L.Hash = function(map, options) { L.Hash = function(map, options) {
@ -14,17 +15,17 @@
}; };
L.Hash.parseHash = function(hash) { L.Hash.parseHash = function(hash) {
if(hash.indexOf('#map=') === 0) { if (hash.indexOf('#map=') === 0) {
hash = hash.substr(5); hash = hash.substr(5);
} }
var args = hash.split(/\&(.+)/); var args = hash.split(/\&(.+)/);
var mapsArgs = args[0].split("/"); var mapsArgs = args[0].split('/');
if (mapsArgs.length == 4) { if (mapsArgs.length == 4) {
var zoom = parseInt(mapsArgs[0], 10), var zoom = parseInt(mapsArgs[0], 10),
lat = parseFloat(mapsArgs[1]), lat = parseFloat(mapsArgs[1]),
lon = parseFloat(mapsArgs[2]), lon = parseFloat(mapsArgs[2]),
layers = decodeURIComponent(mapsArgs[3]).split('-'), layers = this.parseLayers(mapsArgs[3]),
additional = args[1]; additional = args[1];
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) { if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
return false; return false;
} else { } else {
@ -40,32 +41,19 @@
} }
}; };
L.Hash.formatHash = function(map) { (L.Hash.formatHash = function(map) {
var center = map.getCenter(), var center = map.getCenter(),
zoom = map.getZoom(), zoom = map.getZoom(),
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)), precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
layers = []; layers = this.formatLayers();
//console.log(this.options);
var options = this.options;
//Check active layers
for(var key in options) {
if (options.hasOwnProperty(key)) {
if (map.hasLayer(options[key])) {
layers.push(key);
};
};
};
if (layers.length == 0) {
layers.push(Object.keys(options)[0]);
}
var params = [ var params = [
zoom, zoom,
center.lat.toFixed(precision), center.lat.toFixed(precision),
center.lng.toFixed(precision), center.lng.toFixed(precision),
encodeURIComponent(layers.join("-")) layers
]; ];
url = "#map=" + params.join("/"); url = '#map=' + params.join('/');
if (this.additionalCb != null) { if (this.additionalCb != null) {
var additional = this.additionalCb(); var additional = this.additionalCb();
if (additional != null) { if (additional != null) {
@ -73,160 +61,244 @@
} }
} }
return url; return url;
}, }),
(L.Hash.prototype = {
options: {
layerSeparator: ','
},
map: null,
lastHash: null,
L.Hash.prototype = { parseHash: L.Hash.parseHash,
map: null, formatHash: L.Hash.formatHash,
lastHash: null,
parseHash: L.Hash.parseHash, init: function(map, options) {
formatHash: L.Hash.formatHash, this.map = map;
L.Util.setOptions(this, options);
init: function(map, options) { // reset the hash
this.map = map; this.lastHash = null;
L.Util.setOptions(this, options); this.onHashChange();
// reset the hash if (!this.isListening) {
this.lastHash = null; this.startListening();
this.onHashChange(); }
},
if (!this.isListening) { _parseLayers: function(layersParam, layerSeparator) {
this.startListening(); var layers = layersParam.split(layerSeparator).map(
} L.bind(function(layerEncoded) {
}, var obj = null;
var layerString = decodeURIComponent(layerEncoded);
removeFrom: function(map) { if (layerString) {
if (this.changeTimeout) { obj = this.options.layersControl.getLayerFromString(
clearTimeout(this.changeTimeout); layerString
} );
}
if (this.isListening) { return obj;
this.stopListening(); }, this)
} );
this.map = null; return layers;
}, },
onMapMove: function() { parseLayers: function(layersParam) {
// bail if we're moving the map (updating from a hash), var countFoundLayers = function(count, obj) {
// or if the map is not yet loaded if (obj) {
count++;
}
return count;
};
if (this.movingMap || !this.map._loaded) { var layers = this._parseLayers(
return false; layersParam,
} this.options.layerSeparator
);
var found = layers.reduce(countFoundLayers, 0);
var hash = this.formatHash(this.map); if (found < layers.length) {
if (this.lastHash != hash) { // legacy support for name instead of id and '-' layer separator
location.replace(hash); var layersLegacy = this._parseLayers(layersParam, '-');
this.lastHash = hash; var foundLegacy = layersLegacy.reduce(countFoundLayers, 0);
}
},
movingMap: false, if (foundLegacy > found) {
update: function() { layers = layersLegacy;
var hash = location.hash;
if (hash === this.lastHash) {
return;
}
var parsed = this.parseHash(hash);
if (!parsed) {
// migration from old hash style to new one
if (this.onInvalidHashChangeCb != null) {
var newHash = this.onInvalidHashChangeCb(hash);
if (newHash != null && newHash != hash) {
parsed = this.parseHash(newHash);
} }
} }
}
if (parsed) { return layers;
this.movingMap = true; },
this.map.setView(parsed.center, parsed.zoom); activateLayers: function(layers) {
var layers = parsed.layers, var layersControl = this.options.layersControl;
options = this.options, var added = false;
that = this;
//Add/remove layer layersControl.removeActiveLayers();
this.map.eachLayer(function(layer) {
for (alayer in that.layers) { layers.forEach(
if (that.layers[alayer] == layer) { L.bind(function(obj, index, array) {
that.map.removeLayer(layer); if (obj) {
break; layersControl.activateLayer(obj);
if (obj && !obj.overlay) {
added = true;
}
}
}, this)
);
if (!added) {
// if we couldn't add layers (removed or invalid name), add the default one
layersControl.activateDefaultBaseLayer();
}
},
formatLayers: function() {
var objList = this.options.layersControl.getActiveLayers();
var layerList = objList.map(
L.bind(function(obj) {
return encodeURIComponent(
this.options.layersControl.toLayerString(obj)
);
}, this)
);
return layerList.join(this.options.layerSeparator);
},
removeFrom: function(map) {
if (this.changeTimeout) {
clearTimeout(this.changeTimeout);
}
if (this.isListening) {
this.stopListening();
}
this.map = null;
},
onMapMove: function() {
// bail if we're moving the map (updating from a hash),
// or if the map is not yet loaded
if (this.movingMap || !this.map._loaded) {
return false;
}
var hash = this.formatHash(this.map);
if (this.lastHash != hash) {
location.replace(hash);
this.lastHash = hash;
}
},
movingMap: false,
update: function() {
var hash = location.hash;
if (hash === this.lastHash) {
return;
}
var parsed = this.parseHash(hash);
if (!parsed) {
// migration from old hash style to new one
if (this.onInvalidHashChangeCb != null) {
var newHash = this.onInvalidHashChangeCb(hash);
if (newHash != null && newHash != hash) {
parsed = this.parseHash(newHash);
} }
} }
}); }
var added = false;
layers.forEach(function(element, index, array) { if (parsed) {
if (element in options) { this.movingMap = true;
added = true;
that.map.addLayer(options[element]); this.map.setView(parsed.center, parsed.zoom);
this.activateLayers(parsed.layers);
if (this.onHashChangeCb != null) {
this.onHashChangeCb(parsed.additional);
} }
});
if (!added) { this.movingMap = false;
// if we couldn't add layers (custom ones or invalid name), add the default one } else {
this.map.addLayer(options[Object.keys(options)[0]]); this.onMapMove(this.map);
} }
},
if (this.onHashChangeCb != null) { // defer hash change updates every 100ms
this.onHashChangeCb(parsed.additional); changeDefer: 100,
changeTimeout: null,
onHashChange: function() {
// throttle calls to update() so that they only happen every
// `changeDefer` ms
if (!this.changeTimeout) {
var that = this;
this.changeTimeout = setTimeout(function() {
that.update();
that.changeTimeout = null;
}, this.changeDefer);
} }
},
this.movingMap = false; isListening: false,
} else { hashChangeInterval: null,
this.onMapMove(this.map); startListening: function() {
this.map.on(
'moveend layeradd layerremove',
this.onMapMove,
this
);
if (HAS_HASHCHANGE) {
L.DomEvent.addListener(
window,
'hashchange',
this.onHashChange
);
} else {
clearInterval(this.hashChangeInterval);
this.hashChangeInterval = setInterval(
this.onHashChange,
50
);
}
this.isListening = true;
},
stopListening: function() {
this.map.off(
'moveend layeradd layerremove',
this.onMapMove,
this
);
if (HAS_HASHCHANGE) {
L.DomEvent.removeListener(
window,
'hashchange',
this.onHashChange
);
} else {
clearInterval(this.hashChangeInterval);
}
this.isListening = false;
},
_keyByValue: function(obj, value) {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] === value) {
return key;
} else {
return null;
}
}
}
} }
}, });
// defer hash change updates every 100ms
changeDefer: 100,
changeTimeout: null,
onHashChange: function() {
// throttle calls to update() so that they only happen every
// `changeDefer` ms
if (!this.changeTimeout) {
var that = this;
this.changeTimeout = setTimeout(function() {
that.update();
that.changeTimeout = null;
}, this.changeDefer);
}
},
isListening: false,
hashChangeInterval: null,
startListening: function() {
this.map.on("moveend layeradd layerremove", this.onMapMove, this);
if (HAS_HASHCHANGE) {
L.DomEvent.addListener(window, "hashchange", this.onHashChange);
} else {
clearInterval(this.hashChangeInterval);
this.hashChangeInterval = setInterval(this.onHashChange, 50);
}
this.isListening = true;
},
stopListening: function() {
this.map.off("moveend layeradd layerremove", this.onMapMove, this);
if (HAS_HASHCHANGE) {
L.DomEvent.removeListener(window, "hashchange", this.onHashChange);
} else {
clearInterval(this.hashChangeInterval);
}
this.isListening = false;
},
_keyByValue: function(obj, value) {
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] === value) {
return key;
} else { return null; };
};
};
}
};
L.hash = function(map, options) { L.hash = function(map, options) {
return new L.Hash(map, options); return new L.Hash(map, options);
}; };

View file

@ -0,0 +1,31 @@
BR.stravaSegments = function(map, layersControl) {
const stravaControl = L.control
.stravaSegments({
runningTitle: i18next.t('map.strava-running'),
bikingTitle: i18next.t('map.strava-biking'),
loadingTitle: i18next.t('map.loading'),
stravaToken: BR.keys.strava
})
.addTo(map);
layersControl.addOverlay(
stravaControl.stravaLayer,
i18next.t('map.layer.strava-segments')
);
stravaControl.onError = function(err) {
BR.message.showError(
i18next.t('warning.strava-error', {
error: err && err.message ? err.message : err
})
);
};
// hide strava buttons when layer is inactive
var toggleStravaControl = function() {
var stravaBar = stravaControl.runningButton.button.parentElement;
stravaBar.hidden = !stravaBar.hidden;
};
toggleStravaControl();
stravaControl.stravaLayer.on('add remove', toggleStravaControl);
return stravaControl;
};

View file

@ -2,7 +2,8 @@ L.BRouter = L.Class.extend({
statics: { statics: {
// NOTE: the routing API used here is not public! // NOTE: the routing API used here is not public!
// /brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml // /brouter?lonlats=1.1,1.2|2.1,2.2|3.1,3.2|4.1,4.2&nogos=-1.1,-1.2,1|-2.1,-2.2,2&profile=shortest&alternativeidx=1&format=kml
URL_TEMPLATE: '/brouter?lonlats={lonlats}&nogos={nogos}&profile={profile}&alternativeidx={alternativeidx}&format={format}', URL_TEMPLATE:
'/brouter?lonlats={lonlats}&nogos={nogos}&polylines={polylines}&polygons={polygons}&profile={profile}&alternativeidx={alternativeidx}&format={format}',
URL_PROFILE_UPLOAD: BR.conf.host + '/brouter/profile', URL_PROFILE_UPLOAD: BR.conf.host + '/brouter/profile',
PRECISION: 6, PRECISION: 6,
NUMBER_SEPARATOR: ',', NUMBER_SEPARATOR: ',',
@ -10,18 +11,20 @@ L.BRouter = L.Class.extend({
ABORTED_ERROR: 'aborted' ABORTED_ERROR: 'aborted'
}, },
options: { options: {},
},
initialize: function (options) { initialize: function(options) {
L.setOptions(this, options); L.setOptions(this, options);
this.queue = async.queue(L.bind(function (task, callback) { this.queue = async.queue(
this.getRoute(task.segment, callback); L.bind(function(task, callback) {
}, this), 1); this.getRoute(task.segment, callback);
}, this),
1
);
// patch to call callbacks on kill for cleanup (loadingTrailer) // patch to call callbacks on kill for cleanup (loadingTrailer)
this.queue.kill = function () { this.queue.kill = function() {
var aborted = this.tasks; var aborted = this.tasks;
this.drain = null; this.drain = null;
this.tasks = []; this.tasks = [];
@ -37,15 +40,32 @@ L.BRouter = L.Class.extend({
getUrlParams: function(latLngs, format) { getUrlParams: function(latLngs, format) {
params = {}; params = {};
if (this._getLonLatsString(latLngs) != null) if (this._getLonLatsString(latLngs) != null)
params.lonlats = this._getLonLatsString(latLngs); params.lonlats = this._getLonLatsString(latLngs);
if (this._getNogosString(this.options.nogos).length > 0) if (
this.options.nogos &&
this._getNogosString(this.options.nogos).length > 0
)
params.nogos = this._getNogosString(this.options.nogos); params.nogos = this._getNogosString(this.options.nogos);
if (this.options.profile != null) if (
params.profile = this.options.profile; this.options.polylines &&
this._getNogosPolylinesString(this.options.polylines).length > 0
)
params.polylines = this._getNogosPolylinesString(
this.options.polylines
);
if (
this.options.polygons &&
this._getNogosPolygonsString(this.options.polygons).length > 0
)
params.polygons = this._getNogosPolygonsString(
this.options.polygons
);
if (this.options.profile != null) params.profile = this.options.profile;
params.alternativeidx = this.options.alternative; params.alternativeidx = this.options.alternative;
@ -53,10 +73,16 @@ L.BRouter = L.Class.extend({
params.format = format; params.format = format;
} else { } else {
// do not put values in URL if this is the default value (format===null) // do not put values in URL if this is the default value (format===null)
if (params.profile === BR.conf.profiles[0]) if (params.profile === BR.conf.profiles[0]) delete params.profile;
if (params.alternativeidx == 0) delete params.alternativeidx;
// don't add custom profile, as these are only stored temporarily
if (
params.profile &&
params.profile.substring(0, 7) === 'custom_'
) {
delete params.profile; delete params.profile;
if (params.alternativeidx == 0) }
delete params.alternativeidx;
} }
return params; return params;
@ -70,6 +96,12 @@ L.BRouter = L.Class.extend({
if (params.nogos) { if (params.nogos) {
opts.nogos = this._parseNogos(params.nogos); opts.nogos = this._parseNogos(params.nogos);
} }
if (params.polylines) {
opts.polylines = this._parseNogosPolylines(params.polylines);
}
if (params.polygons) {
opts.polygons = this._parseNogosPolygons(params.polygons);
}
if (params.alternativeidx) { if (params.alternativeidx) {
opts.alternative = params.alternativeidx; opts.alternative = params.alternativeidx;
} }
@ -79,24 +111,39 @@ L.BRouter = L.Class.extend({
return opts; return opts;
}, },
getUrl: function(latLngs, format) { getUrl: function(latLngs, format, trackname, exportWaypoints) {
var urlParams = this.getUrlParams(latLngs, format); var urlParams = this.getUrlParams(latLngs, format);
var args = [] var args = [];
if (urlParams.lonlats != null && urlParams.lonlats.length > 0) if (urlParams.lonlats != null && urlParams.lonlats.length > 0)
args.push(L.Util.template('lonlats={lonlats}', urlParams)); args.push(L.Util.template('lonlats={lonlats}', urlParams));
if (urlParams.nogos != null) if (urlParams.nogos != null)
args.push(L.Util.template('nogos={nogos}', urlParams)); args.push(L.Util.template('nogos={nogos}', urlParams));
if (urlParams.polylines != null)
args.push(L.Util.template('polylines={polylines}', urlParams));
if (urlParams.polygons != null)
args.push(L.Util.template('polygons={polygons}', urlParams));
if (urlParams.profile != null) if (urlParams.profile != null)
args.push(L.Util.template('profile={profile}', urlParams)); args.push(L.Util.template('profile={profile}', urlParams));
if (urlParams.alternativeidx != null) if (urlParams.alternativeidx != null)
args.push(L.Util.template('alternativeidx={alternativeidx}', urlParams)); args.push(
L.Util.template('alternativeidx={alternativeidx}', urlParams)
);
if (urlParams.format != null) if (urlParams.format != null)
args.push(L.Util.template('format={format}', urlParams)); args.push(L.Util.template('format={format}', urlParams));
if (trackname)
args.push(
L.Util.template('trackname={trackname}', {
trackname: trackname
})
);
if (exportWaypoints) args.push('exportWaypoints=1');
var prepend_host = (format != null); var prepend_host = format != null;
return (prepend_host ? BR.conf.host : '') + '/brouter?' + args.join('&'); return (
(prepend_host ? BR.conf.host : '') + '/brouter?' + args.join('&')
);
}, },
getRoute: function(latLngs, cb) { getRoute: function(latLngs, cb) {
@ -104,26 +151,32 @@ L.BRouter = L.Class.extend({
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
if (!url) { if (!url) {
return cb(new Error('Error getting route URL')); return cb(new Error(i18next.t('warning.cannot-get-route')));
} }
xhr.open('GET', url, true); xhr.open('GET', url, true);
xhr.onload = L.bind(this._handleRouteResponse, this, xhr, cb); xhr.onload = L.bind(this._handleRouteResponse, this, xhr, cb);
xhr.onerror = L.bind(function(xhr, cb) { xhr.onerror = L.bind(
cb(BR.Util.getError(xhr)); function(xhr, cb) {
}, this, xhr, cb); cb(BR.Util.getError(xhr));
},
this,
xhr,
cb
);
xhr.send(); xhr.send();
}, },
_handleRouteResponse: function(xhr, cb) { _handleRouteResponse: function(xhr, cb) {
var layer, var layer, geojson;
geojson;
if (xhr.status === 200
&& xhr.responseText
// application error when not GeoJSON format (text/plain for errors)
&& xhr.getResponseHeader('Content-Type').split(';')[0] === 'application/vnd.geo+json') {
if (
xhr.status === 200 &&
xhr.responseText &&
// application error when not GeoJSON format (text/plain for errors)
xhr.getResponseHeader('Content-Type').split(';')[0] ===
'application/vnd.geo+json'
) {
// leaflet.spin // leaflet.spin
//gpxLayer.fire('data:loaded'); //gpxLayer.fire('data:loaded');
@ -132,7 +185,7 @@ L.BRouter = L.Class.extend({
layer = L.geoJSON(geojson).getLayers()[0]; layer = L.geoJSON(geojson).getLayers()[0];
return cb(null, layer); return cb(null, layer);
} catch(e) { } catch (e) {
console.error(e, xhr.responseText); console.error(e, xhr.responseText);
return cb(e); return cb(e);
} }
@ -147,7 +200,7 @@ L.BRouter = L.Class.extend({
uploadProfile: function(profileId, profileText, cb) { uploadProfile: function(profileId, profileText, cb) {
var url = L.BRouter.URL_PROFILE_UPLOAD; var url = L.BRouter.URL_PROFILE_UPLOAD;
xhr = new XMLHttpRequest(); xhr = new XMLHttpRequest();
// reuse existing profile file // reuse existing profile file
if (profileId) { if (profileId) {
@ -158,7 +211,7 @@ L.BRouter = L.Class.extend({
xhr.onload = L.bind(this._handleProfileResponse, this, xhr, cb); xhr.onload = L.bind(this._handleProfileResponse, this, xhr, cb);
xhr.onerror = function(evt) { xhr.onerror = function(evt) {
var xhr = this; var xhr = this;
cb('Upload error: ' + xhr.statusText); cb(i18next.t('warning.upload-error', { error: xhr.statusText }));
}; };
// send profile text only, as text/plain;charset=UTF-8 // send profile text only, as text/plain;charset=UTF-8
@ -168,11 +221,15 @@ L.BRouter = L.Class.extend({
_handleProfileResponse: function(xhr, cb) { _handleProfileResponse: function(xhr, cb) {
var response; var response;
if (xhr.status === 200 && xhr.responseText && xhr.responseText.length > 0) { if (
xhr.status === 200 &&
xhr.responseText &&
xhr.responseText.length > 0
) {
response = JSON.parse(xhr.responseText); response = JSON.parse(xhr.responseText);
cb(response.error, response.profileid); cb(response.error, response.profileid);
} else { } else {
cb('Profile error: no or empty response from server'); cb(i18next.t('warning.profile-error'));
} }
}, },
@ -180,7 +237,7 @@ L.BRouter = L.Class.extend({
var s = ''; var s = '';
for (var i = 0; i < latLngs.length; i++) { for (var i = 0; i < latLngs.length; i++) {
s += this._formatLatLng(latLngs[i]); s += this._formatLatLng(latLngs[i]);
if (i < (latLngs.length - 1)) { if (i < latLngs.length - 1) {
s += L.BRouter.GROUP_SEPARATOR; s += L.BRouter.GROUP_SEPARATOR;
} }
} }
@ -213,7 +270,16 @@ L.BRouter = L.Class.extend({
s += this._formatLatLng(circle.getLatLng()); s += this._formatLatLng(circle.getLatLng());
s += L.BRouter.NUMBER_SEPARATOR; s += L.BRouter.NUMBER_SEPARATOR;
s += Math.round(circle.getRadius()); s += Math.round(circle.getRadius());
if (i < (nogos.length - 1)) { // -1 is default nogo exclusion, it should not be passed as a URL parameter.
if (
circle.options.nogoWeight !== undefined &&
circle.options.nogoWeight !== null &&
circle.options.nogoWeight !== -1
) {
s += L.BRouter.NUMBER_SEPARATOR;
s += circle.options.nogoWeight;
}
if (i < nogos.length - 1) {
s += L.BRouter.GROUP_SEPARATOR; s += L.BRouter.GROUP_SEPARATOR;
} }
} }
@ -231,26 +297,137 @@ L.BRouter = L.Class.extend({
groups = s.split(L.BRouter.GROUP_SEPARATOR); groups = s.split(L.BRouter.GROUP_SEPARATOR);
for (var i = 0; i < groups.length; i++) { for (var i = 0; i < groups.length; i++) {
// lng,lat,radius // lng,lat,radius(,weight)
numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR); numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
// TODO refactor: pass simple obj, create circle in NogoAreas; use shapeOptions of instance // TODO refactor: pass simple obj, create circle in NogoAreas; use shapeOptions of instance
// [lat,lng],radius // [lat,lng],radius
nogos.push(L.circle([numbers[1], numbers[0]], {radius: numbers[2]})); // Parse as a nogo circle
var nogoOptions = { radius: numbers[2] };
if (numbers.length > 3) {
nogoOptions.nogoWeight = numbers[3];
}
nogos.push(L.circle([numbers[1], numbers[0]], nogoOptions));
} }
return nogos; return nogos;
}, },
_getNogosPolylinesString: function(nogos) {
var s = '';
for (var i = 0, polyline, vertices; i < nogos.length; i++) {
polyline = nogos[i];
vertices = polyline.getLatLngs();
for (var j = 0; j < vertices.length; j++) {
if (j > 0) {
s += L.BRouter.NUMBER_SEPARATOR;
}
s += this._formatLatLng(vertices[j]);
}
// -1 is default nogo exclusion, it should not be passed as a URL parameter.
if (
polyline.options.nogoWeight !== undefined &&
polyline.options.nogoWeight !== null &&
polyline.options.nogoWeight !== -1
) {
s += L.BRouter.NUMBER_SEPARATOR;
s += polyline.options.nogoWeight;
}
if (i < nogos.length - 1) {
s += L.BRouter.GROUP_SEPARATOR;
}
}
return s;
},
_parseNogosPolylines: function(s) {
var groups,
numbers,
latlngs,
nogos = [];
groups = s.split(L.BRouter.GROUP_SEPARATOR);
for (var i = 0; i < groups.length; i++) {
numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
if (numbers.length > 1) {
latlngs = [];
for (var j = 0; j < numbers.length - 1; ) {
var lng = Number.parseFloat(numbers[j++]);
var lat = Number.parseFloat(numbers[j++]);
latlngs.push([lat, lng]);
}
var nogoWeight;
if (j < numbers.length) {
nogoWeight = Number.parseFloat(numbers[j++]);
}
nogos.push(L.polyline(latlngs, { nogoWeight: nogoWeight }));
}
}
return nogos;
},
_getNogosPolygonsString: function(nogos) {
var s = '';
for (var i = 0, polygon, vertices; i < nogos.length; i++) {
polygon = nogos[i];
vertices = polygon.getLatLngs()[0];
for (var j = 0; j < vertices.length; j++) {
if (j > 0) {
s += L.BRouter.NUMBER_SEPARATOR;
}
s += this._formatLatLng(vertices[j]);
}
// -1 is default nogo exclusion, it should not be passed as a URL parameter.
if (
polygon.options.nogoWeight !== undefined &&
polygon.options.nogoWeight !== null &&
polygon.options.nogoWeight !== -1
) {
s += L.BRouter.NUMBER_SEPARATOR;
s += polygon.options.nogoWeight;
}
if (i < nogos.length - 1) {
s += L.BRouter.GROUP_SEPARATOR;
}
}
return s;
},
_parseNogosPolygons: function(s) {
var groups,
numbers,
latlngs,
nogos = [];
groups = s.split(L.BRouter.GROUP_SEPARATOR);
for (var i = 0; i < groups.length; i++) {
numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
if (numbers.length > 1) {
latlngs = [];
for (var j = 0; j < numbers.length - 1; ) {
var lng = Number.parseFloat(numbers[j++]);
var lat = Number.parseFloat(numbers[j++]);
latlngs.push([lat, lng]);
}
var nogoWeight;
if (j < numbers.length) {
nogoWeight = Number.parseFloat(numbers[j++]);
}
nogos.push(L.polygon(latlngs, { nogoWeight: nogoWeight }));
}
}
return nogos;
},
// formats L.LatLng object as lng,lat string // formats L.LatLng object as lng,lat string
_formatLatLng: function(latLng) { _formatLatLng: function(latLng) {
var s = ''; var s = '';
s += L.Util.formatNum(latLng.lng, L.BRouter.PRECISION); s += L.Util.formatNum(latLng.lng || latLng[1], L.BRouter.PRECISION);
s += L.BRouter.NUMBER_SEPARATOR; s += L.BRouter.NUMBER_SEPARATOR;
s += L.Util.formatNum(latLng.lat, L.BRouter.PRECISION); s += L.Util.formatNum(latLng.lat || latLng[0], L.BRouter.PRECISION);
return s; return s;
} }
}); });
L.bRouter = function (options) { L.bRouter = function(options) {
return new L.BRouter(options); return new L.BRouter(options);
}; };

View file

@ -21,9 +21,8 @@ var brouterCgi = (function() {
//return 'test/test.gpx'; //return 'test/test.gpx';
return url; return url;
} }
return { return {
getUrl: getUrl getUrl: getUrl
} };
})();
})();

View file

@ -1,5 +1,4 @@
(function() { (function() {
// COPYING: Please get your own API keys from the sites listed below // COPYING: Please get your own API keys from the sites listed below
BR.keys = { BR.keys = {
@ -10,7 +9,12 @@
digitalGlobe: '', digitalGlobe: '',
// Thunderforest, https://thunderforest.com/pricing/ // Thunderforest, https://thunderforest.com/pricing/
thunderforest: '' thunderforest: '',
};
// Strava API token in case you want to display Strava segments
strava: '',
// OpenMapSurfer (OpenRouteService API), https://openrouteservice.org/plans/
openrouteservice: ''
};
})(); })();

View file

@ -0,0 +1,20 @@
{
"geometry": null,
"properties": {
"name": "OpenStreetMap.se",
"maxZoom": 18,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://openstreetmap.se/\" target=\"_blank\">OpenStreetMap Sweden</a>",
"id": "1010",
"subdomains": [
"a",
"b",
"c",
"d",
"e",
"f"
],
"url": "http://{s}.tile.openstreetmap.se/hydda/full/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,12 @@
{
"geometry": null,
"properties": {
"name": "4UMaps",
"maxZoom": 15,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://www.4umaps.eu/\">4UMaps</a>",
"id": "1016",
"url": "http://4umaps.eu/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,14 @@
{
"geometry": null,
"properties": {
"name": "Osmapa.pl",
"maxZoom": 20,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://osmapa.pl/\" target=\"_blank\">Osmapa.pl</a>",
"id": "1017",
"threed": "true",
"language": "pl",
"url": "http://{s}.tile.openstreetmap.pl/osmapa.pl/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,14 @@
{
"geometry": null,
"properties": {
"name": "Спутник",
"maxZoom": 19,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://maps.sputnik.ru/\" target=\"_blank\">Спутник</a>",
"id": "1021",
"threed": "true",
"language": "ru",
"url": "http://{s}.tiles.maps.sputnik.ru/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,19 @@
{
"geometry": null,
"properties": {
"name": "Космоснимки",
"maxZoom": 18,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://osm.kosmosnimki.ru/\" target=\"_blank\">ScanEx</a>",
"id": "1023",
"subdomains": [
"a",
"b",
"c",
"d"
],
"language": "ru",
"url": "http://{s}.tile.osm.kosmosnimki.ru/kosmo/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,12 @@
{
"geometry": null,
"properties": {
"name": "ÖPNV Karte",
"maxZoom": 18,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://öpnvkarte.de/\" target=\"_blank\">ÖPNV Karte</a>",
"id": "1059",
"url": "http://tileserver.memomaps.de/tilegen/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,12 @@
{
"geometry": null,
"properties": {
"name": "Thunderforest Outdoors",
"maxZoom": 22,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://www.thunderforest.com/\" target=\"_blank\">Andy Allan</a>",
"id": "1061",
"url": "http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,12 @@
{
"geometry": null,
"properties": {
"name": "Hike & Bike",
"maxZoom": 19,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://hikebikemap.de/\" target=\"_blank\">Colin Marquardt</a>",
"id": "1065",
"url": "http://toolserver.org/tiles/hikebike/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,13 @@
{
"geometry": null,
"properties": {
"name": "Refuges.info hiking",
"maxZoom": 18,
"attribution": "Map data: &copy; <a href=\"http://www.openstreetmap.org/copyright\" target=\"_blank\">OpenStreetMap</a> contributors, under ODbL | Tiles: &copy; <a href=\"http://maps.refuges.info/\" target=\"_blank\">sly</a>",
"id": "1069",
"old": "true",
"url": "http://maps.refuges.info/hiking/{z}/{x}/{y}.png",
"dataSource": "LayersCollection"
},
"type": "Feature"
}

View file

@ -0,0 +1,53 @@
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const outDir = __dirname;
var includeList = [
"1016", // 4UMaps
"1065", // Hike & Bike Map
"1061", // Thunderforest Outdoors
"1021", // kosmosnimki.ru
"1017", // sputnik.ru
"1023", // Osmapa.pl - Mapa OpenStreetMap Polska
"1010", // OpenStreetMap.se (Hydda.Full)
"1069", // MRI (maps.refuges.info),
"1059" // ÖPNV Karte
];
function extract(constantsJs) {
eval(constantsJs);
for (let i = 0; i < includeList.length; i++) {
let id = includeList[i];
let layer = getLayerDataByID(id);
if (!layer) {
console.warn('Layer not found: ' + id);
continue;
}
//console.log(`${layer.id}, ${layer.name}, ${layer.address}`);
layer.url = layer.address;
delete layer.address;
let geoJson = {
geometry: null,
properties: layer,
type: "Feature"
};
geoJson.properties.dataSource = 'LayersCollection';
const outFileName = path.join(outDir, layer.id + '.geojson');
const data = JSON.stringify(geoJson, null, 2);
fs.writeFileSync(outFileName, data);
}
}
// https://github.com/Edward17/LayersCollection/blob/gh-pages/constants.js
fetch('http://edward17.github.io/LayersCollection/constants.js')
.then(res => res.text())
.then(text => extract(text))
.catch(err => console.error(err));

38
layers/config/config.js Normal file
View file

@ -0,0 +1,38 @@
BR.confLayers = {};
BR.confLayers.defaultBaseLayers = [
'standard',
'OpenTopoMap',
'Stamen.Terrain',
'Esri.WorldImagery'
];
// worldwide monolingual layers to add as default when browser language matches
BR.confLayers.languageDefaultLayers = [
'osm-mapnik-german_style',
'osmfr',
'1021' // sputnik.ru
];
BR.confLayers.defaultOverlays = [
'HikeBike.HillShading',
'Waymarked_Trails-Cycling',
'Waymarked_Trails-Hiking'
];
BR.confLayers.legacyNameToIdMap = {
'OpenStreetMap': 'standard',
'OpenStreetMap.de': 'osm-mapnik-german_style',
'OpenTopoMap': 'OpenTopoMap',
'Esri World Imagery': 'Esri.WorldImagery',
'Cycling (Waymarked Trails)': 'Waymarked_Trails-Cycling',
'Hiking (Waymarked Trails)': 'Waymarked_Trails-Hiking'
};
BR.confLayers.leafletProvidersIncludeList = [
'Stamen.Terrain',
'MtbMap',
'OpenStreetMap.CH',
'HikeBike.HillShading',
'Esri.WorldImagery'
];

1475
layers/config/geometry.js Normal file

File diff suppressed because it is too large Load diff

232
layers/config/overrides.js Normal file
View file

@ -0,0 +1,232 @@
BR.confLayers.getPropertyOverrides = function() {
return {
'standard': {
'name': i18next.t('map.layer.osm'),
'attribution': {
'html': '&copy; <a target="_blank" href="https://www.openstreetmap.org/copyright">openstreetmap.org</a>, <a target="_blank" href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a>'
},
'mapUrl': 'https://www.openstreetmap.org/#map={zoom}/{lat}/{lon}'
},
'OpenTopoMap': {
'name': i18next.t('map.layer.topo'),
'attribution': {
'html': '&copy; <a target="_blank" href="https://opentopomap.org/about#verwendung">OpenTopoMap</a>, <a target="_blank" href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA 3.0</a>; <a target="_blank" href="http://viewfinderpanoramas.org">SRTM</a>'
},
'mapUrl': 'https://opentopomap.org/#map={zoom}/{lat}/{lon}'
},
'Stamen.Terrain': {
'name': i18next.t('map.layer.stamen-terrain'),
'attribution': '&copy; <a href="http://stamen.com">Stamen Design</a>, <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>',
'mapUrl': 'http://maps.stamen.com/#terrain/{zoom}/{lat}/{lon}'
},
'Esri.WorldImagery': {
'name': i18next.t('map.layer.esri'),
'nameShort': i18next.t('credits.esri-tiles'),
'attribution': i18next.t('credits.esri-license'),
'mapUrl': 'http://www.arcgis.com/home/item.html?id=10df2279f9684e4a9f6a7f08febac2a9'
},
'wikimedia-map': {
'mapUrl': 'https://maps.wikimedia.org/#{zoom}/{lat}/{lon}'
},
'HDM_HOT': {
'nameShort': 'HOT',
'mapUrl': 'http://map.hotosm.org/#{zoom}/{lat}/{lon}'
},
// OpenStreetMap.se (Hydda.Full)
'1010': {
'mapUrl': 'https://maps.openstreetmap.se/#{zoom}/{lat}/{lon}'
},
'opencylemap': {
'name': i18next.t('map.layer.cycle'),
'nameShort': 'OpenCycleMap',
// add Thunderforest API key variable
'url': 'https://{switch:a,b,c}.tile.thunderforest.com/cycle/{zoom}/{x}/{y}.png?apikey={keys_thunderforest}',
'mapUrl': 'https://www.opencyclemap.org/?zoom={zoom}&lat={lat}&lon={lon}&layers=B0000'
},
'1061': {
'name': i18next.t('map.layer.outdoors'),
'nameShort': 'Outdoors',
'url': 'http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png?apikey={keys_thunderforest}',
'mapUrl': 'https://www.opencyclemap.org/?zoom={zoom}&lat={lat}&lon={lon}&layers=000B0'
},
// Hike & Bike Map
'1065': {
'mapUrl': 'http://hikebikemap.org/?zoom={zoom}&lat={lat}&lon={lon}&layer=HikeBikeMap'
},
// 4UMaps
'1016': {
'mapUrl': 'https://www.4umaps.com/map.htm?zoom={zoom}&lat={lat}&lon={lon}&layers=B00'
},
'openmapsurfer': {
'mapUrl': 'https://maps.openrouteservice.org/directions?n1={lat}&n2={lon}&n3={zoom}&b=0&c=0&k1=en-US&k2=km'
},
// ÖPNV Karte
'1059': {
'name': 'Public transport (ÖPNVKarte)',
'nameShort': 'ÖPNVKarte',
'mapUrl': 'http://öpnvkarte.de/#{lon};{lat};{zoom}'
},
'osm-mapnik-german_style': {
'name': i18next.t('map.layer.osmde'),
'language_code': 'de',
'attribution': {
'html': '&copy; <a target="_blank" href="https://openstreetmap.de/karte.html">openstreetmap.de</a>'
},
'mapUrl': 'https://www.openstreetmap.de/karte.html?zoom={zoom}&lat={lat}&lon={lon}&layers=B000TF'
},
'osmfr': {
'language_code': 'fr',
'nameShort': 'OSM French',
'mapUrl': 'http://tile.openstreetmap.fr/?layers=B00000000FFFFFF&zoom={zoom}&lat={lat}&lon={lon}'
},
// kosmosnimki.ru
'1023': {
'language_code': 'ru',
'mapUrl': 'http://kosmosnimki.ru/'
},
// sputnik.ru
'1021': {
'language_code': 'ru',
'mapUrl': 'http://maps.sputnik.ru/?lat={lat}&lng={lon}&zoom={zoom}'
},
'MtbMap': {
'mapUrl': 'http://mtbmap.cz/#zoom={zoom}&lat={lat}&lon={lon}',
'worldTiles': true // -z12
},
// MRI (maps.refuges.info)
'1069': {
'nameShort': 'Refuges.info',
'mapUrl': 'http://maps.refuges.info/?zoom={zoom}&lat={lat}&lon={lon}&layers=B',
'worldTiles': true
},
'osmfr-basque': {
'language_code': 'eu',
'nameShort': 'OSM Basque',
'mapUrl': 'http://tile.openstreetmap.fr/?layers=00000000BFFFFFF&zoom={zoom}&lat={lat}&lon={lon}',
'worldTiles': true
},
'osmfr-breton': {
'language_code': 'br',
'nameShort': 'OSM Breton',
'mapUrl': 'https://kartenn.openstreetmap.bzh/#map={zoom}/{lat}/{lon}',
'worldTiles': true
},
'osmfr-occitan': {
'language_code': 'oc',
'nameShort': 'OSM Occitan',
'mapUrl': 'http://tile.openstreetmap.fr/?layers=0000000B0FFFFFF&zoom={zoom}&lat={lat}&lon={lon}',
'worldTiles': true
},
'osmbe': {
'nameShort': 'OSM Belgium',
'mapUrl': 'https://tile.osm.be/#map={zoom}/{lat}/{lon}',
'worldTiles': true // -z7
},
'osmbe-fr': {
'nameShort': 'OSM Belgium (fr)',
'mapUrl': 'https://tile.osm.be/#map={zoom}/{lat}/{lon}',
'worldTiles': true // -z7
},
'osmbe-nl': {
'nameShort': 'OSM Belgium (nl)',
'mapUrl': 'https://tile.osm.be/#map={zoom}/{lat}/{lon}',
'worldTiles': true // -z7
},
'OpenStreetMap.CH': {
'country_code': 'CH',
'mapUrl': 'https://osm.ch/#{zoom}/{lat}/{lon}',
'worldTiles': true
},
'topplus-open': {
'country_code': 'DE',
'mapUrl': 'http://www.geodatenzentrum.de/geodaten/gdz_rahmen.gdz_div?gdz_spr=deu&gdz_user_id=0&gdz_akt_zeile=5&gdz_anz_zeile=1&gdz_unt_zeile=41',
'worldTiles': true // World -z9, Europe -z14
},
'OpenStreetMap-turistautak': {
'nameShort': 'OSM Turistautak',
'mapUrl': 'https://turistautak.openstreetmap.hu/?zoom={zoom}&lat={lat}&lon={lon}&layers=0B00F'
},
'Israel_Hiking': {
'mapUrl': 'https://israelhiking.osm.org.il/map/{zoom}/{lat}/{lon}'
},
'Israel_MTB': {
'mapUrl': 'https://israelhiking.osm.org.il/map/{zoom}/{lat}/{lon}'
},
'mtbmap-no': {
'mapUrl': 'https://mtbmap.no/#{zoom}/{lat}/{lon}'
},
// Osmapa.pl - Mapa OpenStreetMap Polska
'1017': {
"country_code": "PL",
'language_code': 'pl',
'mapUrl': 'http://osmapa.pl/#lat={lat}&lon={lon}&z={zoom}&m=os',
'worldTiles': true // -z13
},
'Freemap.sk-Car': {
'mapUrl': 'https://www.freemap.sk/?map={zoom}/{lat}/{lon}&layers=A'
},
'Freemap.sk-Hiking': {
'mapUrl': 'https://www.freemap.sk/?map={zoom}/{lat}/{lon}&layers=T'
},
'Freemap.sk-Cyclo': {
'mapUrl': 'https://www.freemap.sk/?map={zoom}/{lat}/{lon}&layers=C'
},
'osm-cambodia_laos_thailand_vietnam-bilingual': {
'country_code': 'TH+',
'nameShort': 'Thaimap',
'mapUrl': 'http://thaimap.osm-tools.org/?zoom={zoom}&lat={lat}&lon={lon}&layers=BT',
'worldTiles': true
},
'HikeBike.HillShading': {
'name': i18next.t('map.layer.hikebike-hillshading'),
'nameShort': i18next.t('map.hikebike-hillshading'),
'attribution': '&copy; <a target="_blank" href="http://hikebikemap.org">hikebikemap.org</a>; SRTM3 v2 (<a target="_blank" href="http://www2.jpl.nasa.gov/srtm/">NASA</a>)',
'mapUrl': 'http://hikebikemap.org/?zoom={zoom}&lat={lat}&lon={lon}&layer=HikeBikeMap',
'overlay': true
},
'Waymarked_Trails-Cycling': {
'name': i18next.t('map.layer.cycling'),
'nameShort': i18next.t('map.cycling'),
'attribution': {
'html': '&copy; <a target="_blank" href="https://cycling.waymarkedtrails.org/en/help/legal">waymarkedtrails.org</a>, <a target="_blank" href="https://creativecommons.org/licenses/by-sa/3.0/de/deed.en">CC-BY-SA 3.0 DE</a>'
},
'mapUrl': 'http://cycling.waymarkedtrails.org/#?map={zoom}!{lat}!{lon}'
},
'Waymarked_Trails-Hiking': {
'name': i18next.t('map.layer.hiking'),
'nameShort': i18next.t('map.hiking'),
'attribution': {
'html': '&copy; <a target="_blank" href="https://hiking.waymarkedtrails.org/en/help/legal">waymarkedtrails.org</a>, <a target="_blank" href="https://creativecommons.org/licenses/by-sa/3.0/de/deed.en">CC-BY-SA 3.0 DE</a>'
},
'mapUrl': 'http://hiking.waymarkedtrails.org/#?map={zoom}!{lat}!{lon}'
},
'Waymarked_Trails-MTB': {
'nameShort': 'MTB',
'mapUrl': 'http://mtb.waymarkedtrails.org/#?map={zoom}!{lat}!{lon}'
},
'mapillary-coverage-raster': {
'nameShort': 'Mapillary',
'mapUrl': 'https://www.mapillary.com/app/?lat={lat}&lng={lon}&z={zoom}&menu=false'
},
'openpt_map': {
'nameShort': 'openptmap',
'mapUrl': 'http://openptmap.org/?zoom={zoom}&lat={lat}&lon={lon}&layers=B0000TFT'
},
'historic-place-contours': {
'mapUrl': 'http://gk.historic.place/historische_objekte/?zoom={zoom}&lat={lat}&lon={lon}&pid=GhHaSaHe'
},
'hu-hillshade': {
'nameShort': 'Hillshade HU',
'mapUrl': 'http://map.turistautak.hu/?zoom={zoom}&lat={lat}&lon={lon}&layers=0B000FTF'
},
'mapaszlakow-cycle': {
'nameShort': 'Cycleways PL',
'mapUrl': 'http://mapaszlakow.eu/#{zoom}/{lat}/{lon}'
},
'mapaszlakow-routes': {
'nameShort': 'Routes PL',
'mapUrl': 'http://mapaszlakow.eu/#{zoom}/{lat}/{lon}'
}
};
};

82
layers/config/tree.js Normal file
View file

@ -0,0 +1,82 @@
BR.confLayers.tree = {
'base-layers': {
'worldwide-international': [
'standard',
'OpenTopoMap',
'Stamen.Terrain',
'Esri.WorldImagery',
'wikimedia-map',
'HDM_HOT',
'1010', // OpenStreetMap.se (Hydda.Full)
'opencylemap',
'1061', // Thunderforest Outdoors
'1065', // Hike & Bike Map
'1016', // 4UMaps,
'openmapsurfer',
'1059' // ÖPNV Karte
],
'worldwide-monolingual': [
'osm-mapnik-german_style',
'osmfr',
'1023', // kosmosnimki.ru
'1021' // sputnik.ru
],
'europe': [
'MtbMap',
'1069' // MRI (maps.refuges.info)
],
'europe-monolingual': [
'osmfr-basque',
'osmfr-breton',
'osmfr-occitan'
],
'country': [
{
'BE': [
'osmbe',
'osmbe-fr',
'osmbe-nl',
]
},
'OpenStreetMap.CH',
'topplus-open',
'OpenStreetMap-turistautak',
{
'IL': [
'Israel_Hiking',
'Israel_MTB',
]
},
'mtbmap-no',
'1017', // Osmapa.pl - Mapa OpenStreetMap Polska
{
'SK': [
'Freemap.sk-Car',
'Freemap.sk-Hiking',
'Freemap.sk-Cyclo',
]
},
'osm-cambodia_laos_thailand_vietnam-bilingual'
]
},
'overlays': {
'worldwide': [
'HikeBike.HillShading',
'Waymarked_Trails-Cycling',
'Waymarked_Trails-Hiking',
'Waymarked_Trails-MTB',
'mapillary-coverage-raster',
'openpt_map'
],
'country': [
'historic-place-contours',
'hu-hillshade',
{
'PL': [
'mapaszlakow-cycle',
'mapaszlakow-routes'
]
}
]
}
};

View file

@ -0,0 +1,45 @@
{
"geometry": {
"coordinates": [
[
[
5.45,
45
],
[
17,
45
],
[
17,
56.27
],
[
5.45,
56.27
],
[
5.45,
45
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"html": "<a href=\"https://www.eea.europa.eu/data-and-maps/data/eu-dem\" target=\"_blank\" rel=\"noopener\">Produced using Copernicus data and information funded by the European Union - EU-DEM layers</a>, download from <a href=\"http://opendem.info/index.html\" target=\"_blank\">OpenDEM</a>, Data sources <a href=\"https://www2.jpl.nasa.gov/srtm/\" target=\"_blank\">SRTM</a>, <a href=\"https://asterweb.jpl.nasa.gov/gdem.asp\" target=\"_blank\">ASTER GDEM</a>, Tiles from <a href=\"http://www.historic.place/\" target=\"_blank\" rel=\"noopener\">Historic.Place</a>"
},
"country_code": "DACH",
"id": "historic-place-contours",
"min_zoom": 12,
"max_zoom": 15,
"name": "Contours DACH",
"overlay": true,
"type": "tms",
"url": "http://tiles.historic.place/ele/de/{zoom}/{x}/{y}.png",
"valid-georeference": true
},
"type": "Feature"
}

View file

@ -0,0 +1,412 @@
{
"geometry": {
"coordinates": [
[
[
15.9751041,
54.3709213
],
[
16.311164,
54.5561775
],
[
17.1391878,
54.7845723
],
[
18.3448458,
54.9022727
],
[
19.6613689,
54.4737213
],
[
20.2815206,
54.4213456
],
[
21.4663914,
54.3406369
],
[
22.7759855,
54.3769755
],
[
22.8625989,
54.4233613
],
[
23.2956657,
54.2678633
],
[
23.5347186,
54.0955258
],
[
23.5208604,
53.9775182
],
[
23.7183389,
53.4629603
],
[
23.9296755,
53.1856735
],
[
23.9296755,
52.6887269
],
[
23.732197,
52.6067497
],
[
23.5658994,
52.5878101
],
[
23.2090523,
52.3302642
],
[
23.1951942,
52.2370089
],
[
23.5035377,
52.1860596
],
[
23.6906226,
52.0030113
],
[
23.5970802,
51.739903
],
[
23.6629063,
51.3888562
],
[
23.9366046,
50.9827781
],
[
24.1687284,
50.8604752
],
[
24.0197534,
50.8035823
],
[
24.1098313,
50.6610467
],
[
24.0578633,
50.4188439
],
[
23.6178674,
50.3083403
],
[
22.6824431,
49.5163532
],
[
22.7378756,
49.2094935
],
[
22.9041733,
49.0780441
],
[
22.8625989,
48.9940062
],
[
22.6096878,
49.0371785
],
[
22.0761495,
49.2004392
],
[
21.8474902,
49.3721872
],
[
21.3763135,
49.4488281
],
[
21.1026153,
49.3721872
],
[
20.9120659,
49.3022043
],
[
20.6452967,
49.3902311
],
[
20.1845136,
49.3315641
],
[
20.1186875,
49.2004392
],
[
19.9419962,
49.1302123
],
[
19.765305,
49.2117568
],
[
19.7479823,
49.3992506
],
[
19.6024718,
49.4150307
],
[
19.5089294,
49.5815389
],
[
19.4292451,
49.5905232
],
[
19.2317666,
49.4150307
],
[
18.9961783,
49.387976
],
[
18.9338167,
49.4916048
],
[
18.8368097,
49.4938552
],
[
18.8021643,
49.6623381
],
[
18.6427958,
49.7094091
],
[
18.521537,
49.8994693
],
[
18.0815412,
50.0109209
],
[
17.8875272,
49.9886512
],
[
17.7385522,
50.0687739
],
[
17.6068999,
50.1709584
],
[
17.7454813,
50.2153184
],
[
17.710836,
50.3017019
],
[
17.4163505,
50.2640668
],
[
16.9486384,
50.4453265
],
[
16.8932058,
50.4033889
],
[
17.0006064,
50.3105529
],
[
17.017929,
50.2241854
],
[
16.8135215,
50.186489
],
[
16.6402948,
50.0976742
],
[
16.4324227,
50.2862087
],
[
16.1968344,
50.4276731
],
[
16.4220291,
50.5885165
],
[
16.3388803,
50.6632429
],
[
16.2280152,
50.6368824
],
[
16.0547884,
50.6127057
],
[
15.5732181,
50.7641544
],
[
15.2683391,
50.8976368
],
[
15.2440873,
50.980597
],
[
15.0292862,
51.0133036
],
[
15.0015699,
50.8582883
],
[
14.8110205,
50.8735944
],
[
14.956531,
51.0721176
],
[
15.0188926,
51.2914636
],
[
14.9392083,
51.4601459
],
[
14.7209426,
51.5571799
],
[
14.7521234,
51.6260562
],
[
14.5996839,
51.8427626
],
[
14.70362,
52.0733396
],
[
14.5581095,
52.2497371
],
[
14.5165351,
52.425436
],
[
14.6031485,
52.5878101
],
[
14.1146491,
52.8208272
],
[
14.152759,
52.9733951
],
[
14.3502374,
53.0734212
],
[
14.4229927,
53.2665624
],
[
14.1977979,
53.8734759
],
[
14.2220497,
53.9958517
],
[
15.9751041,
54.3709213
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"text": "Tiles © OSM Mapa Szlaków",
"url": "http://mapaszlakow.eu/"
},
"country_code": "PL",
"id": "mapaszlakow-cycle",
"max_zoom": 16,
"min_zoom": 6,
"name": "Cycleways (Mapa Szlaków)",
"overlay": true,
"type": "tms",
"url": "http://mapaszlakow.eu/c{zoom}/{zoom}/{x}/{y}.png"
},
"type": "Feature"
}

View file

@ -0,0 +1,412 @@
{
"geometry": {
"coordinates": [
[
[
15.9751041,
54.3709213
],
[
16.311164,
54.5561775
],
[
17.1391878,
54.7845723
],
[
18.3448458,
54.9022727
],
[
19.6613689,
54.4737213
],
[
20.2815206,
54.4213456
],
[
21.4663914,
54.3406369
],
[
22.7759855,
54.3769755
],
[
22.8625989,
54.4233613
],
[
23.2956657,
54.2678633
],
[
23.5347186,
54.0955258
],
[
23.5208604,
53.9775182
],
[
23.7183389,
53.4629603
],
[
23.9296755,
53.1856735
],
[
23.9296755,
52.6887269
],
[
23.732197,
52.6067497
],
[
23.5658994,
52.5878101
],
[
23.2090523,
52.3302642
],
[
23.1951942,
52.2370089
],
[
23.5035377,
52.1860596
],
[
23.6906226,
52.0030113
],
[
23.5970802,
51.739903
],
[
23.6629063,
51.3888562
],
[
23.9366046,
50.9827781
],
[
24.1687284,
50.8604752
],
[
24.0197534,
50.8035823
],
[
24.1098313,
50.6610467
],
[
24.0578633,
50.4188439
],
[
23.6178674,
50.3083403
],
[
22.6824431,
49.5163532
],
[
22.7378756,
49.2094935
],
[
22.9041733,
49.0780441
],
[
22.8625989,
48.9940062
],
[
22.6096878,
49.0371785
],
[
22.0761495,
49.2004392
],
[
21.8474902,
49.3721872
],
[
21.3763135,
49.4488281
],
[
21.1026153,
49.3721872
],
[
20.9120659,
49.3022043
],
[
20.6452967,
49.3902311
],
[
20.1845136,
49.3315641
],
[
20.1186875,
49.2004392
],
[
19.9419962,
49.1302123
],
[
19.765305,
49.2117568
],
[
19.7479823,
49.3992506
],
[
19.6024718,
49.4150307
],
[
19.5089294,
49.5815389
],
[
19.4292451,
49.5905232
],
[
19.2317666,
49.4150307
],
[
18.9961783,
49.387976
],
[
18.9338167,
49.4916048
],
[
18.8368097,
49.4938552
],
[
18.8021643,
49.6623381
],
[
18.6427958,
49.7094091
],
[
18.521537,
49.8994693
],
[
18.0815412,
50.0109209
],
[
17.8875272,
49.9886512
],
[
17.7385522,
50.0687739
],
[
17.6068999,
50.1709584
],
[
17.7454813,
50.2153184
],
[
17.710836,
50.3017019
],
[
17.4163505,
50.2640668
],
[
16.9486384,
50.4453265
],
[
16.8932058,
50.4033889
],
[
17.0006064,
50.3105529
],
[
17.017929,
50.2241854
],
[
16.8135215,
50.186489
],
[
16.6402948,
50.0976742
],
[
16.4324227,
50.2862087
],
[
16.1968344,
50.4276731
],
[
16.4220291,
50.5885165
],
[
16.3388803,
50.6632429
],
[
16.2280152,
50.6368824
],
[
16.0547884,
50.6127057
],
[
15.5732181,
50.7641544
],
[
15.2683391,
50.8976368
],
[
15.2440873,
50.980597
],
[
15.0292862,
51.0133036
],
[
15.0015699,
50.8582883
],
[
14.8110205,
50.8735944
],
[
14.956531,
51.0721176
],
[
15.0188926,
51.2914636
],
[
14.9392083,
51.4601459
],
[
14.7209426,
51.5571799
],
[
14.7521234,
51.6260562
],
[
14.5996839,
51.8427626
],
[
14.70362,
52.0733396
],
[
14.5581095,
52.2497371
],
[
14.5165351,
52.425436
],
[
14.6031485,
52.5878101
],
[
14.1146491,
52.8208272
],
[
14.152759,
52.9733951
],
[
14.3502374,
53.0734212
],
[
14.4229927,
53.2665624
],
[
14.1977979,
53.8734759
],
[
14.2220497,
53.9958517
],
[
15.9751041,
54.3709213
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"text": "Tiles © OSM Mapa Szlaków",
"url": "http://mapaszlakow.eu/"
},
"country_code": "PL",
"id": "mapaszlakow-routes",
"max_zoom": 16,
"min_zoom": 6,
"name": "Routes (Mapa Szlaków)",
"overlay": true,
"type": "tms",
"url": "http://mapaszlakow.eu/b{zoom}/{zoom}/{x}/{y}.png"
},
"type": "Feature"
}

View file

@ -0,0 +1,17 @@
{
"geometry": null,
"properties": {
"attribution": {
"text": "Mapillary, CC BY",
"url": "https://www.mapillary.com"
},
"id": "mapillary-coverage-raster",
"max_zoom": 17,
"name": "Mapillary Coverage",
"overlay": true,
"type": "tms",
"url": "https://d6a1v2w10ny40.cloudfront.net/v0.1/{z}/{x}/{y}.png"
},
"type": "Feature"
}

View file

@ -0,0 +1,15 @@
{
"geometry": null,
"properties": {
"attribution": {
"html": "<a href=\"https://giscience.uni-hd.de\" target=\"_blank\" rel=\"noopener\">GIScience Research Group</a> @ University of Heidelberg (<a href=\"https://openrouteservice.org/\" target=\"_blank\" rel=\"noopener\">info</a>)"
},
"id": "openmapsurfer",
"max_zoom": 19,
"name": "OpenMapSurfer",
"type": "tms",
"url": "https://api.openrouteservice.org/mapsurfer/{zoom}/{x}/{y}.png?api_key={keys_openrouteservice}"
},
"type": "Feature"
}

View file

@ -0,0 +1,42 @@
{
"geometry": {
"coordinates": [
[
[
4.219,
46.317
],
[
16.875,
46.317
],
[
16.875,
55.776
],
[
4.219,
55.776
],
[
4.219,
46.317
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"html": "© <a href=\"http://www.bkg.bund.de\" target=\"_blank\" rel=\"noopener\">Bundesamt für Kartographie und Geodäsie</a> 2018, <a href=\"http://sg.geodatenzentrum.de/web_public/Datenquellen_TopPlus_Open.pdf\" target=\"_blank\" rel=\"noopener\">Datenquellen</a>"
},
"id": "topplus-open",
"name": "TopPlusOpen",
"type": "wms",
"url": "http://sgx.geodatenzentrum.de/wms_topplus_web_open",
"layers": "web",
"format": "image/png"
},
"type": "Feature"
}

View file

@ -0,0 +1,210 @@
{
"geometry": {
"coordinates": [
[
[
19.83682,
49.25529
],
[
19.80075,
49.42385
],
[
19.60437,
49.48058
],
[
19.49179,
49.63961
],
[
19.21831,
49.52604
],
[
19.16778,
49.42521
],
[
19.00308,
49.42236
],
[
18.97611,
49.5308
],
[
18.54685,
49.51425
],
[
18.31432,
49.33818
],
[
18.15913,
49.2961
],
[
18.05564,
49.11134
],
[
17.56396,
48.84938
],
[
17.17929,
48.88816
],
[
17.058,
48.81105
],
[
16.90426,
48.61947
],
[
16.79685,
48.38561
],
[
17.06762,
48.01116
],
[
17.32787,
47.97749
],
[
17.51699,
47.82535
],
[
17.74776,
47.73093
],
[
18.29515,
47.72075
],
[
18.67959,
47.75541
],
[
18.89755,
47.81203
],
[
18.79463,
47.88245
],
[
18.84318,
48.04046
],
[
19.46212,
48.05333
],
[
19.62064,
48.22938
],
[
19.89585,
48.09387
],
[
20.33766,
48.2643
],
[
20.55395,
48.52358
],
[
20.82335,
48.55714
],
[
21.10271,
48.47096
],
[
21.45863,
48.55513
],
[
21.74536,
48.31435
],
[
22.15293,
48.37179
],
[
22.61255,
49.08914
],
[
22.09997,
49.23814
],
[
21.9686,
49.36363
],
[
21.6244,
49.46989
],
[
21.06873,
49.46402
],
[
20.94336,
49.31088
],
[
20.73052,
49.44006
],
[
20.22804,
49.41714
],
[
20.05234,
49.23052
],
[
19.83682,
49.25529
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"logo-image": "https://raw.githubusercontent.com/FreemapSlovakia/freemap-v3-react/master/src/images/freemap-logo-small.png",
"required": true,
"text": "Copyright ©2007-2012 Freemap Slovakia (www.freemap.sk). Some rights reserved."
},
"country_code": "SK",
"icon": "https://raw.githubusercontent.com/FreemapSlovakia/freemap-v3-react/master/src/images/freemap-logo-small.png",
"id": "Freemap.sk-Car",
"max_zoom": 16,
"min_zoom": 8,
"name": "Freemap.sk Car",
"type": "tms",
"url": "https://{switch:a,b,c,d}.freemap.sk/A/{zoom}/{x}/{y}.jpeg",
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,210 @@
{
"geometry": {
"coordinates": [
[
[
19.83682,
49.25529
],
[
19.80075,
49.42385
],
[
19.60437,
49.48058
],
[
19.49179,
49.63961
],
[
19.21831,
49.52604
],
[
19.16778,
49.42521
],
[
19.00308,
49.42236
],
[
18.97611,
49.5308
],
[
18.54685,
49.51425
],
[
18.31432,
49.33818
],
[
18.15913,
49.2961
],
[
18.05564,
49.11134
],
[
17.56396,
48.84938
],
[
17.17929,
48.88816
],
[
17.058,
48.81105
],
[
16.90426,
48.61947
],
[
16.79685,
48.38561
],
[
17.06762,
48.01116
],
[
17.32787,
47.97749
],
[
17.51699,
47.82535
],
[
17.74776,
47.73093
],
[
18.29515,
47.72075
],
[
18.67959,
47.75541
],
[
18.89755,
47.81203
],
[
18.79463,
47.88245
],
[
18.84318,
48.04046
],
[
19.46212,
48.05333
],
[
19.62064,
48.22938
],
[
19.89585,
48.09387
],
[
20.33766,
48.2643
],
[
20.55395,
48.52358
],
[
20.82335,
48.55714
],
[
21.10271,
48.47096
],
[
21.45863,
48.55513
],
[
21.74536,
48.31435
],
[
22.15293,
48.37179
],
[
22.61255,
49.08914
],
[
22.09997,
49.23814
],
[
21.9686,
49.36363
],
[
21.6244,
49.46989
],
[
21.06873,
49.46402
],
[
20.94336,
49.31088
],
[
20.73052,
49.44006
],
[
20.22804,
49.41714
],
[
20.05234,
49.23052
],
[
19.83682,
49.25529
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"logo-image": "https://raw.githubusercontent.com/FreemapSlovakia/freemap-v3-react/master/src/images/freemap-logo-small.png",
"required": true,
"text": "Copyright ©2007-2012 Freemap Slovakia (www.freemap.sk). Some rights reserved."
},
"country_code": "SK",
"icon": "https://raw.githubusercontent.com/FreemapSlovakia/freemap-v3-react/master/src/images/freemap-logo-small.png",
"id": "Freemap.sk-Cyclo",
"max_zoom": 16,
"min_zoom": 8,
"name": "Freemap.sk Cyclo",
"type": "tms",
"url": "https://{switch:a,b,c,d}.freemap.sk/C/{zoom}/{x}/{y}.jpeg",
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,210 @@
{
"geometry": {
"coordinates": [
[
[
19.83682,
49.25529
],
[
19.80075,
49.42385
],
[
19.60437,
49.48058
],
[
19.49179,
49.63961
],
[
19.21831,
49.52604
],
[
19.16778,
49.42521
],
[
19.00308,
49.42236
],
[
18.97611,
49.5308
],
[
18.54685,
49.51425
],
[
18.31432,
49.33818
],
[
18.15913,
49.2961
],
[
18.05564,
49.11134
],
[
17.56396,
48.84938
],
[
17.17929,
48.88816
],
[
17.058,
48.81105
],
[
16.90426,
48.61947
],
[
16.79685,
48.38561
],
[
17.06762,
48.01116
],
[
17.32787,
47.97749
],
[
17.51699,
47.82535
],
[
17.74776,
47.73093
],
[
18.29515,
47.72075
],
[
18.67959,
47.75541
],
[
18.89755,
47.81203
],
[
18.79463,
47.88245
],
[
18.84318,
48.04046
],
[
19.46212,
48.05333
],
[
19.62064,
48.22938
],
[
19.89585,
48.09387
],
[
20.33766,
48.2643
],
[
20.55395,
48.52358
],
[
20.82335,
48.55714
],
[
21.10271,
48.47096
],
[
21.45863,
48.55513
],
[
21.74536,
48.31435
],
[
22.15293,
48.37179
],
[
22.61255,
49.08914
],
[
22.09997,
49.23814
],
[
21.9686,
49.36363
],
[
21.6244,
49.46989
],
[
21.06873,
49.46402
],
[
20.94336,
49.31088
],
[
20.73052,
49.44006
],
[
20.22804,
49.41714
],
[
20.05234,
49.23052
],
[
19.83682,
49.25529
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"logo-image": "https://raw.githubusercontent.com/FreemapSlovakia/freemap-v3-react/master/src/images/freemap-logo-small.png",
"required": true,
"text": "Copyright ©2007-2012 Freemap Slovakia (www.freemap.sk). Some rights reserved."
},
"country_code": "SK",
"icon": "https://raw.githubusercontent.com/FreemapSlovakia/freemap-v3-react/master/src/images/freemap-logo-small.png",
"id": "Freemap.sk-Hiking",
"max_zoom": 16,
"min_zoom": 8,
"name": "Freemap.sk Hiking",
"type": "tms",
"url": "https://{switch:a,b,c,d}.freemap.sk/T/{zoom}/{x}/{y}.jpeg",
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,19 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© OpenStreetMap contributors, tiles courtesy of Humanitarian OpenStreetMap Team",
"url": "https://www.hotosm.org/"
},
"icon": "https://wiki.openstreetmap.org/w/images/thumb/c/c9/Hot_logo.svg/300px-Hot_logo.svg.png",
"id": "HDM_HOT",
"max_zoom": 20,
"name": "HDM (Humanitarian OpenStreetMap Team)",
"type": "tms",
"url": "https://{switch:a,b,c}.tile.openstreetmap.fr/hot/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,187 @@
{
"geometry": {
"coordinates": [
[
[
34.64563,
32.92073
],
[
34.98374,
33.13352
],
[
35.15662,
33.09994
],
[
35.31781,
33.11463
],
[
35.36541,
33.06285
],
[
35.46229,
33.09994
],
[
35.51741,
33.12652
],
[
35.5266,
33.21531
],
[
35.53893,
33.25442
],
[
35.56446,
33.2969
],
[
35.61264,
33.27918
],
[
35.67429,
33.30627
],
[
35.70785,
33.34269
],
[
35.75363,
33.35091
],
[
35.81509,
33.3392
],
[
35.91531,
32.9406
],
[
35.80834,
32.772
],
[
35.77835,
32.72446
],
[
35.59491,
32.62828
],
[
35.5729,
32.36541
],
[
35.59461,
32.21856
],
[
35.55452,
32.02901
],
[
35.57225,
31.75415
],
[
35.48771,
31.41951
],
[
35.4209,
31.25116
],
[
35.47936,
31.1783
],
[
35.42771,
30.95172
],
[
35.3321,
30.77107
],
[
35.20709,
30.53307
],
[
35.17202,
30.11204
],
[
35.07514,
29.83713
],
[
35.02336,
29.64569
],
[
34.93992,
29.39946
],
[
34.89517,
29.37711
],
[
34.84785,
29.59084
],
[
34.69667,
30.10714
],
[
34.52423,
30.40912
],
[
34.48879,
30.64515
],
[
34.07929,
31.52265
],
[
34.64563,
32.92073
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "Tiles © IsraelHiking, CC BY-SA-NC 3.0. Data by OpenStreetMap under ODbL.",
"url": "https://israelhiking.osm.org.il/"
},
"country_code": "IL",
"description": "Israel Hiking map",
"icon": "https://israelhiking.osm.org.il/content/favicons/favicon.ico",
"id": "Israel_Hiking",
"max_zoom": 16,
"min_zoom": 7,
"name": "Israel Hiking",
"type": "tms",
"url": "https://israelhiking.osm.org.il/Tiles/{zoom}/{x}/{y}.png",
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,187 @@
{
"geometry": {
"coordinates": [
[
[
34.64563,
32.92073
],
[
34.98374,
33.13352
],
[
35.15662,
33.09994
],
[
35.31781,
33.11463
],
[
35.36541,
33.06285
],
[
35.46229,
33.09994
],
[
35.51741,
33.12652
],
[
35.5266,
33.21531
],
[
35.53893,
33.25442
],
[
35.56446,
33.2969
],
[
35.61264,
33.27918
],
[
35.67429,
33.30627
],
[
35.70785,
33.34269
],
[
35.75363,
33.35091
],
[
35.81509,
33.3392
],
[
35.91531,
32.9406
],
[
35.80834,
32.772
],
[
35.77835,
32.72446
],
[
35.59491,
32.62828
],
[
35.5729,
32.36541
],
[
35.59461,
32.21856
],
[
35.55452,
32.02901
],
[
35.57225,
31.75415
],
[
35.48771,
31.41951
],
[
35.4209,
31.25116
],
[
35.47936,
31.1783
],
[
35.42771,
30.95172
],
[
35.3321,
30.77107
],
[
35.20709,
30.53307
],
[
35.17202,
30.11204
],
[
35.07514,
29.83713
],
[
35.02336,
29.64569
],
[
34.93992,
29.39946
],
[
34.89517,
29.37711
],
[
34.84785,
29.59084
],
[
34.69667,
30.10714
],
[
34.52423,
30.40912
],
[
34.48879,
30.64515
],
[
34.07929,
31.52265
],
[
34.64563,
32.92073
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "Tiles © IsraelHiking, CC BY-SA-NC 3.0. Data by OpenStreetMap under ODbL.",
"url": "https://israelhiking.osm.org.il/"
},
"country_code": "IL",
"description": "Israel MTB map",
"icon": "https://israelhiking.osm.org.il/content/favicons/favicon.ico",
"id": "Israel_MTB",
"max_zoom": 16,
"min_zoom": 7,
"name": "Israel MTB",
"type": "tms",
"url": "https://israelhiking.osm.org.il/MTBTiles/{zoom}/{x}/{y}.png",
"dataSource": "JOSM"
},
"type": "Feature"
}

7
layers/josm/LICENSE Normal file
View file

@ -0,0 +1,7 @@
JOSM imagery database is licensed under Creative Commons (CC-BY-SA).
Content since April 2014 is dual licensed with LGPL (except contents copied from ELI, which is only CC-BY-SA).
https://creativecommons.org/licenses/by-sa/3.0/
Source: https://josm.openstreetmap.de/wiki/Maps#Otherimportantinformation

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,20 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© waymarkedtrails.org, OpenStreetMap contributors, CC by-SA 3.0",
"url": "https://cycling.waymarkedtrails.org/en/help/legal"
},
"icon": "https://static.waymarkedtrails.org/img/map_cycling.png",
"id": "Waymarked_Trails-Cycling",
"max_zoom": 17,
"name": "Waymarked Trails: Cycling",
"overlay": true,
"type": "tms",
"url": "https://tile.waymarkedtrails.org/cycling/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,20 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© waymarkedtrails.org, OpenStreetMap contributors, CC by-SA 3.0",
"url": "https://hiking.waymarkedtrails.org/en/help/legal"
},
"icon": "https://static.waymarkedtrails.org/img/map_hiking.png",
"id": "Waymarked_Trails-Hiking",
"max_zoom": 17,
"name": "Waymarked Trails: Hiking",
"overlay": true,
"type": "tms",
"url": "https://tile.waymarkedtrails.org/hiking/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,20 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© waymarkedtrails.org, OpenStreetMap contributors, CC by-SA 3.0",
"url": "https://mtb.waymarkedtrails.org/en/help/legal"
},
"icon": "https://static.waymarkedtrails.org/img/map_mtb.png",
"id": "Waymarked_Trails-MTB",
"max_zoom": 17,
"name": "Waymarked Trails: MTB",
"overlay": true,
"type": "tms",
"url": "https://tile.waymarkedtrails.org/mtb/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

63
layers/josm/extract.js Normal file
View file

@ -0,0 +1,63 @@
const fs = require('fs');
const path = require('path');
const fetch = require('node-fetch');
const outDir = __dirname;
const includeList = [
"osmbe",
"osmbe-fr",
"osmbe-nl",
"osmfr-basque",
"osmfr-breton",
"osmfr-occitan",
"OpenStreetMap-turistautak",
"hu-hillshade",
"Israel_Hiking",
"Israel_MTB",
"mtbmap-no",
"Freemap.sk-Car",
"Freemap.sk-Hiking",
"Freemap.sk-Cyclo",
"opencylemap",
"standard",
"HDM_HOT",
"osmfr",
"osm-mapnik-german_style",
"OpenTopoMap",
"osm-cambodia_laos_thailand_vietnam-bilingual",
"Waymarked_Trails-Hiking",
"Waymarked_Trails-Cycling",
"Waymarked_Trails-MTB",
"wikimedia-map",
"openpt_map"
];
function extract(layersJosm) {
for (let i = 0; i < layersJosm.features.length; i++) {
let layer = layersJosm.features[i];
let props = layer.properties;
let id = props.id;
if (includeList.includes(id)) {
//console.log(`${id}, ${props.name}, ${props.url}`);
props.dataSource = 'JOSM';
const outFileName = path.join(outDir, id + '.geojson');
const data = JSON.stringify(layer, null, 2);
fs.writeFileSync(outFileName, data);
includeList.splice(includeList.indexOf(id), 1);
}
}
if (includeList.length > 0) {
console.warn('Layers not found: ', includeList);
}
}
fetch('https://josm.openstreetmap.de/maps?format=geojson')
.then(res => res.json())
.then(json => extract(json))
.catch(err => console.error(err));

View file

@ -0,0 +1,46 @@
{
"geometry": {
"coordinates": [
[
[
15,
45
],
[
24,
45
],
[
24,
49
],
[
15,
49
],
[
15,
45
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "SRTM"
},
"country_code": "HU",
"id": "hu-hillshade",
"max_zoom": 18,
"min_zoom": 0,
"name": "Hillshade Hungary",
"overlay": true,
"type": "tms",
"url": "https://{switch:a,b,c}.map.turistautak.hu/tiles/shading/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,134 @@
{
"geometry": {
"coordinates": [
[
[
31.904253,
70.4368136
],
[
28.4765186,
71.3289643
],
[
23.6865015,
71.2514263
],
[
16.8090601,
70.0730823
],
[
11.1620655,
67.5253903
],
[
9.975542,
64.811576
],
[
4.2187061,
62.1449966
],
[
4.3725367,
59.1871966
],
[
6.1743055,
57.8915032
],
[
7.932118,
57.7393554
],
[
10.777577,
58.8649103
],
[
11.7224012,
58.762509
],
[
12.722157,
60.1141506
],
[
13.0517469,
61.3493518
],
[
12.5243921,
63.6169922
],
[
14.2382593,
63.9856094
],
[
15.1171656,
65.9016624
],
[
18.6987085,
68.3749083
],
[
20.0610132,
68.2612583
],
[
21.0058375,
68.7841518
],
[
25.2465601,
68.3506025
],
[
26.9384546,
69.8472011
],
[
28.7621851,
69.6112133
],
[
28.5864039,
68.8556004
],
[
31.069314,
69.5191547
],
[
31.904253,
70.4368136
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "© MTBmap.no",
"url": "https://www.mtbmap.no/"
},
"country_code": "NO",
"description": "Norwegian mountain biking map from OSM (max zoom 14-16, varies per region)",
"icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAMAAADXqc3KAAAAyVBMVEUAAAAAAAAAAAAAAABHcEwAAAAAAAAAAAAAAAAAAAAAAAAGBgYAAAAAAAD///8DAwMKCgrX19d3d3fe3t4fHx/4+PikpKQGBgbb29vIyMizs7MlJSXExMQQEBA2NjYNDQ2pqam6urr+/v4UFBS8vLzg4OCMjIxvb297e3vLy8taWlqXl5fs7Oz7+/vn5+c7OzvQ0NDT09MxMTFFRUWTk5O/v7/k5OReXl6urq5iYmJLS0uGhoZOTk7y8vJpaWmcnJyDg4N/f3+bm5thW7HyAAAADXRSTlM9D3VaADJrd4BEKIEG/oGXoQAAAP1JREFUKM9tkIdywyAMhmmb0WVhjLcd2/HK3jvdff+HKjQ1kNr/HYdOH5J+hG463QbdtdCtJtXDImyje5nP5oaIkQLiKcwCxzH+A30PAAXpg30NdPJmMcLPWAXxINLwMAUuy7yewfT6YtgWJJ6ugmPoXDxH2BxiCTKAd1GJzQo4+bS/7ClN/wAeARB1WAV8loelXwczKHAZ+rgGtim4Zq1VSSkZr+Ar21Ga6xJsw1hbz9fwSdj6DCJBwR+d2O5CnhgtKvBE+R2sUv+DB/ZAVFA+dTLZRC7/kBsIcN6fFt8u65d4m7OXKK6CY1j+mjns8oO029Ea9Yie0UOD2q0frM8mZOHCEDcAAAAASUVORK5CYII=",
"id": "mtbmap-no",
"max_zoom": 14,
"min_zoom": 3,
"name": "MTBmap.no",
"tile-size": "512",
"tile_size": 512,
"type": "tms",
"url": "https://mtbmap.no/tiles/osm/mtbmap/{zoom}/{x}/{y}.jpg",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,21 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"terms-of-use-text": "Maps © Thunderforest",
"terms-of-use-url": "https://thunderforest.com/terms/",
"text": "Data © OpenStreetMap contributors",
"url": "https://www.openstreetmap.org/copyright/"
},
"icon": "data:image/png;base64,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",
"id": "opencylemap",
"max_zoom": 22,
"name": "OpenCycleMap",
"type": "tms",
"url": "https://{switch:a,b,c}.tile.thunderforest.com/cycle/{zoom}/{x}/{y}.png?apikey=7bd5ed2197cf4da29fa26de0ba6530cc",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,21 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© OpenStreetMap contributors, CC-BY-SA",
"url": "http://openptmap.de/"
},
"icon": "http://openptmap.de/favicon_pt.png",
"id": "openpt_map",
"max_zoom": 17,
"min_zoom": 4,
"name": "OpenPT Map (overlay)",
"overlay": true,
"type": "tms",
"url": "http://openptmap.de/tiles/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,168 @@
{
"geometry": {
"coordinates": [
[
[
92.1023798,
20.8135629
],
[
93.5690546,
24.0975527
],
[
94.1733026,
23.9269484
],
[
95.1950312,
26.707274
],
[
96.7550898,
27.5286657
],
[
97.5845575,
28.5805966
],
[
98.738122,
27.514051
],
[
98.7436151,
25.8799151
],
[
97.6779413,
24.7577376
],
[
97.9635858,
24.042382
],
[
98.8205194,
24.1627239
],
[
99.5236444,
22.9593356
],
[
100.3695917,
21.5051376
],
[
101.7923212,
22.4830518
],
[
105.3628778,
23.3331079
],
[
106.8185663,
22.8480137
],
[
108.1973505,
21.3619661
],
[
107.4389505,
18.8539792
],
[
117.1453714,
7.4656173
],
[
119.6172953,
5.2875389
],
[
118.1231546,
4.0502277
],
[
117.2552347,
4.3624942
],
[
115.8654642,
4.3460623
],
[
115.5084085,
3.0249771
],
[
114.552598,
1.5100953
],
[
113.5418558,
1.2574836
],
[
112.9650736,
1.5704982
],
[
112.2454691,
1.5100953
],
[
111.67418,
1.0158321
],
[
110.4546976,
0.9004918
],
[
109.4988871,
1.9218969
],
[
103.2256937,
1.1256762
],
[
100.4626322,
3.2388904
],
[
97.6721048,
8.0588831
],
[
93.892808,
15.9398659
],
[
92.1023798,
20.8135629
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "© osm-tools.org & OpenStreetMap contributors, CC-BY-SA",
"url": "https://www.osm-tools.org/"
},
"id": "osm-cambodia_laos_thailand_vietnam-bilingual",
"max_zoom": 20,
"name": "Cambodia, Laos, Thailand, Vietnam, Malaysia, Myanmar bilingual",
"type": "tms",
"url": "https://{switch:a,b,c,d}.tile.osm-tools.org/osm/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,20 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© OpenStreetMap contributors, CC-BY-SA",
"url": "https://www.openstreetmap.org/"
},
"icon": "data:image/png;base64,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",
"id": "osm-mapnik-german_style",
"max_zoom": 18,
"mod-tile-features": true,
"name": "OpenStreetMap (German Style)",
"type": "tms",
"url": "https://{switch:a,b,c,d}.tile.openstreetmap.de/tiles/osmde/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

2023
layers/josm/osmbe-fr.geojson Normal file

File diff suppressed because it is too large Load diff

2023
layers/josm/osmbe-nl.geojson Normal file

File diff suppressed because it is too large Load diff

2023
layers/josm/osmbe.geojson Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
{
"geometry": {
"coordinates": [
[
[
16.2075922,
62.7408449
],
[
16.2900797,
33.8453727
],
[
-10.7364447,
33.8043768
],
[
-10.8189321,
62.7182339
],
[
16.2075922,
62.7408449
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL",
"url": "https://www.openstreetmap.org/"
},
"icon": "data:image/png;base64,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",
"id": "osmfr-basque",
"max_zoom": 20,
"mod-tile-features": true,
"name": "OpenStreetMap (Basque Style)",
"type": "tms",
"url": "https://tile.openstreetmap.bzh/eu/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,46 @@
{
"geometry": {
"coordinates": [
[
[
16.2075922,
62.7408449
],
[
16.2900797,
33.8453727
],
[
-10.7364447,
33.8043768
],
[
-10.8189321,
62.7182339
],
[
16.2075922,
62.7408449
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL",
"url": "https://www.openstreetmap.org/"
},
"icon": "data:image/png;base64,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",
"id": "osmfr-breton",
"max_zoom": 20,
"mod-tile-features": true,
"name": "OpenStreetMap (Breton Style)",
"type": "tms",
"url": "https://tile.openstreetmap.bzh/br/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,46 @@
{
"geometry": {
"coordinates": [
[
[
16.2075922,
62.7408449
],
[
16.2900797,
33.8453727
],
[
-10.7364447,
33.8043768
],
[
-10.8189321,
62.7182339
],
[
16.2075922,
62.7408449
]
]
],
"type": "Polygon"
},
"properties": {
"attribution": {
"required": true,
"text": "Tiles © OpenStreetMap France, data © OpenStreetMap contributors, ODbL",
"url": "https://www.openstreetmap.org/"
},
"icon": "data:image/png;base64,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",
"id": "osmfr-occitan",
"max_zoom": 20,
"mod-tile-features": true,
"name": "OpenStreetMap (Occitan Style)",
"type": "tms",
"url": "https://tile.openstreetmap.bzh/oc/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

20
layers/josm/osmfr.geojson Normal file
View file

@ -0,0 +1,20 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "Tiles © cquest@Openstreetmap France, data © OpenStreetMap contributors, ODBL",
"url": "https://www.openstreetmap.org/"
},
"icon": "data:image/png;base64,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",
"id": "osmfr",
"max_zoom": 20,
"mod-tile-features": true,
"name": "OpenStreetMap (French Style)",
"type": "tms",
"url": "https://{switch:a,b,c}.tile.openstreetmap.fr/osmfr/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,21 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© OpenStreetMap contributors, CC-BY-SA",
"url": "https://www.openstreetmap.org/"
},
"default": true,
"icon": "data:image/png;base64,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",
"id": "standard",
"max_zoom": 19,
"mod-tile-features": true,
"name": "OpenStreetMap Carto (Standard)",
"type": "tms",
"url": "https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

View file

@ -0,0 +1,18 @@
{
"geometry": null,
"properties": {
"attribution": {
"required": true,
"text": "© OpenStreetMap contributors, CC-BY-SA",
"url": "https://www.openstreetmap.org/"
},
"id": "wikimedia-map",
"max_zoom": 18,
"name": "Wikimedia Map",
"type": "tms",
"url": "https://maps.wikimedia.org/osm-intl/{zoom}/{x}/{y}.png",
"valid-georeference": true,
"dataSource": "JOSM"
},
"type": "Feature"
}

209
locales/de.json Normal file
View file

@ -0,0 +1,209 @@
{
"about": {
"bug-reports": "Fehlerberichte und Funktionsanfragen:",
"bug-reports-back": "Server / Backend, Routing Engine, Android-App, Profile, brouter.de Website",
"bug-reports-front": "Web-Client / Frontend.",
"chat": "Chatte mit Benutzern und Entwicklern",
"contact": "Kontakt:",
"data": "Daten:",
"data-description": "Dies basiert auf <a href=\"https://www.openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>. Es wird normalerweise einmal pro Woche aktualisiert, wenn ein neues Planet-File verfügbar ist. Siehe Datum der <a href=\"http://brouter.de/brouter/segments4/\" target=\"_blank\">Datendateien</a>.",
"description": "Online-Service der BRouter Routing Engine. Für die Offline-Android-App und weitere Informationen siehe <a href=\"http://brouter.de/\" target=\"_blank\">brouter.de</a>.",
"details": "<i><a href=\"http://brouter.de/privacypolicy.html\" target=\"_blank\">Datenschutz-Bestimmungen</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web#credits-and-licenses\" target=\"_blank\">Credits</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web/blob/master/CHANGELOG.md\" target=\"_blank\">Änderungsprotokoll</a></i> und\n<i><a href=\"https://github.com/nrenner/brouter-web#readme\" target=\"_blank\">weitere Infos</a></i> zum Client.",
"support": "Generelle Diskussionen/Fragen, Support",
"title": "Über"
},
"credits": {
"brouter": "BRouter",
"brouter-license": "<a target=\"_blank\" href=\"http://brouter.de/brouter\">BRouter</a> &copy; Arndt Brenschede",
"esri-license": "<a target=\"_blank\" href=\"http://goto.arcgisonline.com/maps/World_Imagery\">World Imagery</a> &copy; <a target=\"_blank\" href=\"https://www.esri.com/\">Esri</a>, Quellen: Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP und die GIS Benutzergemeinschaft",
"esri-tiles": "Esri World Imagery",
"map-data": "Kartendaten",
"map-tiles": "Kartendarstellung",
"nominatim": "Suchen mit <a href=\"https://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\" data-i18n=\"credits.nominatim\">Nominatim</a>",
"openstreetmap": "&copy; <a target=\"_blank\" href=\"https://www.openstreetmap.org/copyright\" >OpenStreetMap contributors</a> unter <a target=\"_blank\" href=\"https://opendatacommons.org/licenses/odbl/\" >ODbL</a>"
},
"export": {
"format": "Format",
"format_csv": "CSV",
"format_geojson": "GeoJSON",
"format_gpx": "GPX",
"format_kml": "KML",
"route-from-to": "{{from}} -> {{to}} ({{distance}} km)",
"route-loop": "{{from}} ({{distance}} km)",
"title": "Route exportieren",
"trackname": "Name"
},
"footer": {
"ascend": "Aufsteigend",
"cost": "Kosten",
"distance": "Entfernung",
"energy-per-100km": "Energie pro 100 km",
"hours": "Stunden",
"hours-abbrev": "h",
"kilometer": "Kilometer",
"kilometer-abbrev": "km",
"kilowatthour": "Kilowattstunden",
"kilowatthour-abbrev": "kWh",
"mean-cost-factor": "durchschnittlicher Kostenfaktor",
"meter": "Meter",
"meter-abbrev": "m",
"plain-ascend": "einfach aufsteigend",
"total-energy": "Gesamtenergie",
"travel-time": "Reisezeit"
},
"layers": {
"add-base": "Basisebene hinzufügen",
"add-overlay": "Overlay hinzufügen",
"customize": "Ebenen anpassen",
"placeholder-layer-name": "angepasster Ebenenname (Bsp: OpenStreetMap)",
"placeholder-layer-url": "angepasste Ebenen-URL (Bsp: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png)",
"remove-selection": "Auswahl entfernen"
},
"loadNogos": {
"defaultProperties": "Standardeigenschaften",
"file": "Datei:",
"load": "Laden",
"nogoBuffer": "Puffer um No-go-Areas (in Metern):",
"nogoRadius": "No-go Radius (für Punkte):",
"nogoWeight": "No-go Gewicht:",
"source": "Quelle",
"title": "No-go-Areas laden",
"url": "URL:"
},
"map": {
"attribution-osm-long": "OpenStreetMap-Mitwirkende",
"attribution-osm-short": "OpenStreetMap",
"clear-route": "Route löschen",
"copyright": "Copyright",
"cycling": "Radfahren",
"delete-last-point": "Letzten Punkt löschen",
"delete-nogo-areas": "&nbsp;&nbsp;auch alle no-go areas löschen",
"delete-route": "Route löschen?",
"draw-route-start": "Route anzeigen (D Taste)",
"draw-route-stop": "Route entfernen (ESC Taste)",
"hikebike-hillshading": "Höhenschummerung",
"hiking": "Wandern",
"layer": {
"bing": "Bing Luftbild",
"cycle": "OpenCycleMap (Thunderf.)",
"cycling": "Radfahren (markierte Routen)",
"digitalglobe": "DigitalGlobe neueste Bilder",
"esri": "Esri Weltbilder",
"hikebike-hillshading": "Höhenschummerung (Hike & Bike Map)",
"hiking": "Wandern (markierte Routen)",
"osm": "OpenStreetMap",
"osmde": "OpenStreetMap.de",
"outdoors": "Outdoor (Thunderforest)",
"stamen-terrain": "Terrain (Stamen)",
"strava-segments": "Strava Segmente",
"topo": "OpenTopoMap"
},
"loading": "Lade...",
"locate-me": "Zeige meinen Standort",
"nogo": {
"cancel": "No-Go-Areas löschen",
"click-drag": "klicken und ziehen zum Zeichnen eines Kreises",
"draw": "No-Go-Area zeichnen (Kreis)",
"edit": "Klicken zum Bearbeiten",
"help": "&square; = Bewegen / Größe anpassen, <span class=\"fa fa-trash-o\"></span> = Löschen,<br>Kreis anklicken um Bearbeiten zu beenden"
},
"opacity-slider": "Transparenz von Route und Markern anpassen",
"privacy": "Datenschutz",
"reverse-route": "Route umkehren",
"strava-biking": "Zeige Strava Radfahrsegmente",
"strava-running": "Zeige Strava Läufersegmente",
"zoomInTitle": "Hineinzoomen",
"zoomOutTitle": "Herauszoomen"
},
"modal": {
"close": "Schließen"
},
"navbar": {
"about": "Über",
"alternative": {
"first": "1. Alternative",
"original": "Original",
"second": "2. Alternative",
"third": "3. Alternative"
},
"export": "Export",
"load": {
"nogos": "No-go-Areas",
"title": "Laden"
},
"profile": {
"car-eco": "Auto (effizient)",
"car-fast": "Auto (schnell)",
"car-test": "Auto (Test)",
"custom": "Benutzerdefiniert",
"fastbike": "Fahrrad (schnell)",
"fastbike-asia-pacific": "Fahrrad (schnell, Asien Pazifik)",
"fastbike-lowtraffic": "Fahrrad (schnell, Routen mit wenig Verkehr)",
"hiking-beta": "Wandern (beta)",
"moped": "Moped",
"rail": "Eisenbahn",
"river": "Fluss",
"safety": "Sicherste Route",
"shortest": "Kürzeste Route",
"trekking": "Trekkingrad",
"trekking-ignore-cr": "Trekkingrad (ignoriere Radrouten)",
"trekking-noferries": "Trekkingrad (keine Fähren)",
"trekking-nosteps": "Trekkingrad (keine Treppen)",
"trekking-steep": "Trekkingrad (steil)",
"vm-forum-liegerad-schnell": "Liegerad (schnell)",
"vm-forum-velomobil-schnell": "Velomobil (schnell)"
}
},
"sidebar": {
"custom-profile": {
"title": "Benutzerdefiniertes Profil"
},
"data": {
"title": "Daten"
},
"itinerary": {
"title": "Reiseroute"
},
"layers": {
"category": {
"base-layers": "Grundkarten",
"country": "Land",
"europe": "Europa",
"europe-monolingual": "Europa einsprachig",
"overlays": "Überlagerungen",
"worldwide": "Weltweit",
"worldwide-international": "Weltweit international",
"worldwide-monolingual": "Weltweit einsprachig"
},
"collapse": "Alle zusammenklappen",
"custom-layers": "Benutzerdefinierte Ebenen",
"customize": "Benutzerdefinierte Ebenen hinzufügen oder entfernen",
"expand": "Alle ausklappen",
"optional": "Optionale Ebenen hinzufügen oder entfernen",
"optional-layers": "Mehr",
"table": {
"URL": "URL",
"empty": "Noch keine benutzerdefinierte Ebene definiert.",
"name": "Name",
"type": "Type"
},
"title": "Ebenen"
},
"profile": {
"clear": "Löschen",
"help": "Hilfe",
"placeholder": "Erstelle benutzerdefiniertes Profil hier.",
"upload": "Hochladen"
}
},
"title": "BRouter Web Client",
"warning": {
"cannot-get-route": "Fehler beim Abrufen der Routen-URL",
"no-response": "Keine Antwort vom Server",
"no-route-found": "Fehler: kann für angegebene Punkte keine Route finden. Vielleicht die Punkte näher an Straßen verschieben?",
"profile-error": "Profil-Fehler: keine oder leere Antwort vom Server",
"strava-error": "Fehler beim Laden der Strava Segmente: {{error}}",
"temporary-profile": "<strong>Note:</strong> Hochgeladene benutzerdefinierte Profile nur verübergehend auf dem Server zwischengespeichert. <br/>Bitte Bearbeitungen auf dem lokalen PC speichern.",
"upload-error": "Fehler beim Hochladen: {{error}}"
}
}

209
locales/en.json Normal file
View file

@ -0,0 +1,209 @@
{
"about": {
"bug-reports": "Bug reports and feature requests:",
"bug-reports-back": "server / backend, routing engine, Android app, profiles, brouter.de site",
"bug-reports-front": "web client / frontend.",
"chat": "Chat with users and developers",
"contact": "Contact:",
"data": "Data:",
"data-description": "This is based on <a href=\"https://www.openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>. It is usually updated once a week when a new Planet file is available, see dates of <a href=\"http://brouter.de/brouter/segments4/\" target=\"_blank\">data files</a>.",
"description": "Online service of the BRouter routing engine. For the offline Android app and more information see <a href=\"http://brouter.de/\" target=\"_blank\">brouter.de</a>",
"details": "<i><a href=\"http://brouter.de/privacypolicy.html\" target=\"_blank\">Privacy Policy</a></i>, \n<i><a href=\"https://github.com/nrenner/brouter-web#credits-and-licenses\" target=\"_blank\">Credits</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web/blob/master/CHANGELOG.md\" target=\"_blank\">Changelog</a></i> and\n<i><a href=\"https://github.com/nrenner/brouter-web#readme\" target=\"_blank\">more info</a></i> on the client.",
"support": "General discussions/questions, support",
"title": "About"
},
"credits": {
"brouter": "BRouter",
"brouter-license": "<a target=\"_blank\" href=\"http://brouter.de/brouter\">BRouter</a> &copy; Arndt Brenschede",
"esri-license": "<a target=\"_blank\" href=\"http://goto.arcgisonline.com/maps/World_Imagery\">World Imagery</a> &copy; <a target=\"_blank\" href=\"https://www.esri.com/\">Esri</a>, sources: Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP, and the GIS User Community",
"esri-tiles": "Esri World Imagery",
"map-data": "Map data",
"map-tiles": "Map tiles",
"nominatim": "Search by <a href=\"https://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\" data-i18n=\"credits.nominatim\">Nominatim</a>",
"openstreetmap": "&copy; <a target=\"_blank\" href=\"https://www.openstreetmap.org/copyright\" >OpenStreetMap contributors</a> under <a target=\"_blank\" href=\"https://opendatacommons.org/licenses/odbl/\" >ODbL</a>"
},
"export": {
"format": "Format",
"format_csv": "CSV",
"format_geojson": "GeoJSON",
"format_gpx": "GPX",
"format_kml": "KML",
"route-from-to": "{{from}} -> {{to}} ({{distance}}km)",
"route-loop": "{{from}} ({{distance}}km)",
"title": "Export route",
"trackname": "Name"
},
"footer": {
"ascend": "Ascend",
"cost": "Cost",
"distance": "Distance",
"energy-per-100km": "Energy per 100 km",
"hours": "hours",
"hours-abbrev": "h",
"kilometer": "kilometers",
"kilometer-abbrev": "km",
"kilowatthour": "kilowatt hours",
"kilowatthour-abbrev": "kWh",
"mean-cost-factor": "Mean cost factor",
"meter": "meters",
"meter-abbrev": "m",
"plain-ascend": "Plain ascend",
"total-energy": "Total Energy",
"travel-time": "Travel time"
},
"layers": {
"add-base": "Add base layer",
"add-overlay": "Add overlay",
"customize": "Customize layers",
"placeholder-layer-name": "Custom layer name. (ex: OpenStreetMap)",
"placeholder-layer-url": "Custom layer URL. (ex: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png)",
"remove-selection": "Remove selection"
},
"loadNogos": {
"defaultProperties": "Default properties",
"file": "File: ",
"load": "Load",
"nogoBuffer": "Buffer no-go areas (in meters): ",
"nogoRadius": "No-go radius (for points): ",
"nogoWeight": "No-go weight: ",
"source": "Source",
"title": "Load no-go areas",
"url": "URL: "
},
"map": {
"attribution-osm-long": "OpenStreetMap contributors",
"attribution-osm-short": "OpenStreetMap",
"clear-route": "Clear route",
"copyright": "Copyright",
"cycling": "Cycling",
"delete-last-point": "Delete last point",
"delete-nogo-areas": "&nbsp;&nbsp;also delete all no-go areas",
"delete-route": "Delete route?",
"draw-route-start": "Draw route (D key)",
"draw-route-stop": "Stop drawing route (ESC key)",
"hikebike-hillshading": "Hillshading",
"hiking": "Hiking",
"layer": {
"bing": "Bing Aerial",
"cycle": "OpenCycleMap (Thunderf.)",
"cycling": "Cycling (Waymarked Trails)",
"digitalglobe": "DigitalGlobe Recent Imagery",
"esri": "Esri World Imagery",
"hikebike-hillshading": "Hillshading (Hike & Bike Map)",
"hiking": "Hiking (Waymarked Trails)",
"osm": "OpenStreetMap",
"osmde": "OpenStreetMap.de",
"outdoors": "Outdoors (Thunderforest)",
"stamen-terrain": "Terrain (Stamen)",
"strava-segments": "Strava segments",
"topo": "OpenTopoMap"
},
"loading": "Loading…",
"locate-me": "Show me where I am",
"nogo": {
"cancel": "Cancel drawing no-go area",
"click-drag": "Click and drag to draw circle",
"draw": "Draw no-go area (circle)",
"edit": "Click to edit",
"help": "&square; = move / resize, <span class=\"fa fa-trash-o\"></span> = delete,<br>click circle to quit editing"
},
"opacity-slider": "Set transparency of route track and markers",
"privacy": "Privacy",
"reverse-route": "Reverse route",
"strava-biking": "Show Strava biking segments",
"strava-running": "Show Strava running segments",
"zoomInTitle": "Zoom in",
"zoomOutTitle": "Zoom out"
},
"modal": {
"close": "Close"
},
"navbar": {
"about": "About",
"alternative": {
"first": "1st alternative",
"original": "Original",
"second": "2nd alternative",
"third": "3rd alternative"
},
"export": "Export",
"load": {
"nogos": "No-go areas",
"title": "Load"
},
"profile": {
"car-eco": "Car (economic)",
"car-fast": "Car (fast)",
"car-test": "Car (test)",
"custom": "Custom",
"fastbike": "Fastbike",
"fastbike-asia-pacific": "Fastbike (Asia Pacific)",
"fastbike-lowtraffic": "Fastbike (low traffic)",
"hiking-beta": "Hiking (beta)",
"moped": "Moped",
"rail": "Rail",
"river": "River",
"safety": "Safety",
"shortest": "Shortest",
"trekking": "Trekking bike",
"trekking-ignore-cr": "Trekking bike (ignore cycle routes)",
"trekking-noferries": "Trekking bike (no ferries)",
"trekking-nosteps": "Trekking bike (no steps)",
"trekking-steep": "Trekking bike (steep)",
"vm-forum-liegerad-schnell": "Recumbent bike (fast)",
"vm-forum-velomobil-schnell": "Velomobile (fast)"
}
},
"sidebar": {
"custom-profile": {
"title": "Custom profile"
},
"data": {
"title": "Data"
},
"itinerary": {
"title": "Itinerary"
},
"layers": {
"category": {
"base-layers": "Base layers",
"country": "Country",
"europe": "Europe",
"europe-monolingual": "Europe monolingual",
"overlays": "Overlays",
"worldwide": "Worldwide",
"worldwide-international": "Worldwide international",
"worldwide-monolingual": "Worldwide monolingual"
},
"collapse": "Collapse all",
"custom-layers": "Custom layers",
"customize": "Add or remove custom layers",
"expand": "Expand all",
"optional": "Add or remove optional layers",
"optional-layers": "More",
"table": {
"URL": "URL",
"empty": "No custom layer configured yet.",
"name": "Name",
"type": "Type"
},
"title": "Layers"
},
"profile": {
"clear": "Clear",
"help": "Help",
"placeholder": "Write your custom profile here.",
"upload": "Upload"
}
},
"title": "BRouter web client",
"warning": {
"cannot-get-route": "Error getting route URL",
"no-response": "no response from server",
"no-route-found": "Error: cannot find a route for given points. Maybe try to move them closer to roads?",
"profile-error": "Profile error: no or empty response from server",
"strava-error": "Error getting Strava segments: {{error}}",
"temporary-profile": "<strong>Note:</strong> Uploaded custom profiles are only cached temporarily on the server.<br/>Please save your edits to your local PC.",
"upload-error": "Upload error: {{error}}"
}
}

209
locales/fr.json Normal file
View file

@ -0,0 +1,209 @@
{
"about": {
"bug-reports": "Rapporter un problème ou une amélioration:",
"bug-reports-back": "serveur, moteur de routage, appli Android, profils, site brouter.de;",
"bug-reports-front": "client web.",
"chat": "Discuter avec d'autres utilisateurs et développeurs",
"contact": "Contact:",
"data": "Données:",
"data-description": "Les données reposent sur <a href=\"https://www.openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>. La mise à jour est généralement hebdomadaire, voir <a href=\"http://brouter.de/brouter/segments4/\" target=\"_blank\">les fichiers de données</a>. ",
"description": "Service web pour le moteur de routage BRouter. Pour l'appli Android et de plus amples informations, voir <a href=\"http://brouter.de/\" target=\"_blank\">brouter.de</a>.",
"details": "<i><a href=\"http://brouter.de/privacypolicy.html\" target=\"_blank\">Respect de la vie privée</a></i>, \n<i><a href=\"https://github.com/nrenner/brouter-web#credits-and-licenses\" target=\"_blank\">Crédits</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web/blob/master/CHANGELOG.md\" target=\"_blank\">Changements</a></i> et\n<i><a href=\"https://github.com/nrenner/brouter-web#readme\" target=\"_blank\">plus d'infos</a></i> sur le client web.",
"support": "Discussions/Questions générales, support;",
"title": "À propos"
},
"credits": {
"brouter": "BRouter",
"brouter-license": "<a target=\"_blank\" href=\"http://brouter.de/brouter\">BRouter</a> &copy; Arndt Brenschede",
"esri-license": "<a target=\"_blank\" href=\"http://goto.arcgisonline.com/maps/World_Imagery\">World Imagery</a> &copy; <a target=\"_blank\" href=\"https://www.esri.com/\">Esri</a>, sources : Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP, et la communauté d'utilisateurs GIS",
"esri-tiles": "Imagerie mondiale Esri",
"map-data": "Données de carte",
"map-tiles": "Tuiles de carte",
"nominatim": "Recherche par <a href=\"https://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\" data-i18n=\"credits.nominatim\">Nominatim</a>",
"openstreetmap": "&copy; <a target=\"_blank\" href=\"https://www.openstreetmap.org/copyright\" >contributeurs OpenStreetMap</a> sous <a target=\"_blank\" href=\"https://opendatacommons.org/licenses/odbl/\" >ODbL</a>"
},
"export": {
"format": "Format",
"format_csv": "CSV",
"format_geojson": "GeoJSON",
"format_gpx": "GPX",
"format_kml": "KML",
"route-from-to": "{{from}} -> {{to}} ({{distance}} km)",
"route-loop": "{{from}} ({{distance}} km)",
"title": "Exporter l'itinéraire",
"trackname": "Nom"
},
"footer": {
"ascend": "Dénivelé cumulé",
"cost": "Coût",
"distance": "Distance",
"energy-per-100km": "pour 100km",
"hours": "heures",
"hours-abbrev": "h",
"kilometer": "kilomètres",
"kilometer-abbrev": "km",
"kilowatthour": "kilowattheures",
"kilowatthour-abbrev": "kWh",
"mean-cost-factor": "Facteur coût moyen",
"meter": "mètres",
"meter-abbrev": "m",
"plain-ascend": "global",
"total-energy": "Énergie totale",
"travel-time": "Temps de trajet"
},
"layers": {
"add-base": "Ajouter un fond de carte ",
"add-overlay": "Ajouter un calque superposable",
"customize": "Personnaliser les calques",
"placeholder-layer-name": "Nom du calque (p. ex. OpenStreetMap).",
"placeholder-layer-url": "URL du calque (p. ex. https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png).",
"remove-selection": "Supprimer la sélection"
},
"loadNogos": {
"defaultProperties": "Propriétés par défaut",
"file": "Fichier:",
"load": "Charger",
"nogoBuffer": "Tampon des zones interdites (en mètres):",
"nogoRadius": "Rayon des zones interdites (pour les points):",
"nogoWeight": "Poids des zones interdites:",
"source": "Source",
"title": "Charger les zones interdites",
"url": "URL:"
},
"map": {
"attribution-osm-long": "Contributeurs OpenStreetMap",
"attribution-osm-short": "OpenStreetMap",
"clear-route": "Supprimer l'itinéraire",
"copyright": "Copyright",
"cycling": "Vélo",
"delete-last-point": "Supprimer le dernier point",
"delete-nogo-areas": "&nbsp;&nbsp;supprimer aussi toutes les zones interdites",
"delete-route": "Supprimer l'itinéraire?",
"draw-route-start": "Dessiner l'itinéraire (touche D)",
"draw-route-stop": "Arrêter de dessiner l'itinéraire (touche ECH)",
"hikebike-hillshading": "Relief avec ombre",
"hiking": "Randonnée",
"layer": {
"bing": "Imagerie aérienne BING",
"cycle": "OpenCycleMap (Thunderf.)",
"cycling": "Cycling (Sentiers balisés)",
"digitalglobe": "Imagerie récente DigitalGlobe",
"esri": "Imagerie mondiale Esri",
"hikebike-hillshading": "Relief avec ombre (carte randonnée et vélo)",
"hiking": "Randonnée (Sentiers balisés)",
"osm": "OpenStreetMap",
"osmde": "OpenStreetMap.de",
"outdoors": "Extérieur (Thunderforest)",
"stamen-terrain": "Terrain (Stamen)",
"strava-segments": "Segments Strava",
"topo": "OpenTopoMap"
},
"loading": "Chargement…",
"locate-me": "Où suis-je ?",
"nogo": {
"cancel": "Annuler la zone interdite",
"click-drag": "Cliquer et faire glisser pour dessiner un cercle",
"draw": "Dessiner une zone interdite (cercle)",
"edit": "Cliquer pour éditer",
"help": "&square; = déplacer / redimensionner, <span class=\"fa fa-trash-o\"></span> = supprimer,<br>cliquer sur le cercle pour arrêter l'édition"
},
"opacity-slider": "Définie l'opacité de l'itinéraire et des marqueurs",
"privacy": "Vie privée",
"reverse-route": "Inverse l'itinéraire",
"strava-biking": "Afficher les segments Strava vélo",
"strava-running": "Afficher les segments Strava à pied",
"zoomInTitle": "Zoomer",
"zoomOutTitle": "Dézoomer"
},
"modal": {
"close": "Fermer"
},
"navbar": {
"about": "À propos",
"alternative": {
"first": "1ère alternative",
"original": "Originale",
"second": "2nde alternative",
"third": "3ème alternative"
},
"export": "Exporter",
"load": {
"nogos": "Zones interdites",
"title": "Charger"
},
"profile": {
"car-eco": "Voiture (économique)",
"car-fast": "Voiture (rapide)",
"car-test": "Voiture (test)",
"custom": "Personnalisé",
"fastbike": "Vélo de route",
"fastbike-asia-pacific": "Vélo de route (Asie Pacifique)",
"fastbike-lowtraffic": "Vélo de route (faible traffic)",
"hiking-beta": "Randonnée (beta)",
"moped": "Mobylette",
"rail": "Train",
"river": "Fluvial",
"safety": "Sécurité",
"shortest": "Le plus court",
"trekking": "Cyclotourisme",
"trekking-ignore-cr": "Cycloutourisme (ignore pistes cyclables)",
"trekking-noferries": "Cyclotourisme (pas de ferries)",
"trekking-nosteps": "Cyclotourisme (pas d'escaliers)",
"trekking-steep": "Cyclotourisme (escarpé)",
"vm-forum-liegerad-schnell": "Vélo couché (rapide)",
"vm-forum-velomobil-schnell": "Vélomobile (rapide)"
}
},
"sidebar": {
"custom-profile": {
"title": "Profil personnalisé"
},
"data": {
"title": "Données"
},
"itinerary": {
"title": "Itinéraire"
},
"layers": {
"category": {
"base-layers": "Fonds de carte",
"country": "Pays",
"europe": "Europe",
"europe-monolingual": "Europe monolingue",
"overlays": "Calques superposables",
"worldwide": "Mondial",
"worldwide-international": "Mondial international",
"worldwide-monolingual": "Mondial monolingue"
},
"collapse": "Tout réduire",
"custom-layers": "Calques personels",
"customize": "Ajouter ou supprimer des calques",
"expand": "Tout ouvrir",
"optional": "Ajouter ou supprimer des calques optionnels",
"optional-layers": "Plus",
"table": {
"URL": "URL",
"empty": "Aucun calque personnel trouvé.",
"name": "Nom",
"type": "Type"
},
"title": "Calques"
},
"profile": {
"clear": "Nettoyer",
"help": "Aide",
"placeholder": "Saisissez votre profil personnalisé ici.",
"upload": "Envoyer"
}
},
"title": "Client web BRouter",
"warning": {
"cannot-get-route": "Erreur lors de la réception de l'itinéraire",
"no-response": "aucune réponse du serveur",
"no-route-found": "Erreur: impossible de trouver un itinéraire correspondant aux points donnés. Essayez peut-être de rapprocher les points des routes?",
"profile-error": "Erreur de profil: pas de réponse ou réponse invalide du serveur",
"strava-error": "Impossible d'afficher les segments Strava: {{error}}",
"temporary-profile": "<strong>Note:</strong> Les profils personnalisés téléversés ne sont disponibles que temporairement sur le serveur.<br/>Merci de sauvegarder votre profil sur votre ordinateur.",
"upload-error": "Erreur d'envoi : {{error}}"
}
}

209
locales/gl.json Normal file
View file

@ -0,0 +1,209 @@
{
"about": {
"bug-reports": "Bug reports and feature requests:",
"bug-reports-back": "server / backend, routing engine, Android app, profiles, brouter.de site",
"bug-reports-front": "web client / frontend.",
"chat": "Chat with users and developers",
"contact": "Contacto:",
"data": "Datos:",
"data-description": "This is based on <a href=\"https://www.openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>. It is usually updated once a week when a new Planet file is available, see dates of <a href=\"http://brouter.de/brouter/segments4/\" target=\"_blank\">data files</a>.",
"description": "Online service of the BRouter routing engine. For the offline Android app and more information see <a href=\"http://brouter.de/\" target=\"_blank\">brouter.de</a>",
"details": "<i><a href=\"http://brouter.de/privacypolicy.html\" target=\"_blank\">Privacy Policy</a></i>, \n<i><a href=\"https://github.com/nrenner/brouter-web#credits-and-licenses\" target=\"_blank\">Credits</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web/blob/master/CHANGELOG.md\" target=\"_blank\">Changelog</a></i> and\n<i><a href=\"https://github.com/nrenner/brouter-web#readme\" target=\"_blank\">more info</a></i> on the client.",
"support": "General discussions/questions, support",
"title": "Sobre nós"
},
"credits": {
"brouter": "BRouter",
"brouter-license": "<a target=\"_blank\" href=\"http://brouter.de/brouter\">BRouter</a> &copy; Arndt Brenschede",
"esri-license": "<a target=\"_blank\" href=\"http://goto.arcgisonline.com/maps/World_Imagery\">World Imagery</a> &copy; <a target=\"_blank\" href=\"https://www.esri.com/\">Esri</a>, sources: Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP, and the GIS User Community",
"esri-tiles": "Esri World Imagery",
"map-data": "Map data",
"map-tiles": "Map tiles",
"nominatim": "Search by <a href=\"https://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\" data-i18n=\"credits.nominatim\">Nominatim</a>",
"openstreetmap": "&copy; <a target=\"_blank\" href=\"https://www.openstreetmap.org/copyright\" >OpenStreetMap contributors</a> under <a target=\"_blank\" href=\"https://opendatacommons.org/licenses/odbl/\" >ODbL</a>"
},
"export": {
"format": "Format",
"format_csv": "CSV",
"format_geojson": "GeoJSON",
"format_gpx": "GPX",
"format_kml": "KML",
"route-from-to": "{{from}} -> {{to}} ({{distance}}km)",
"route-loop": "{{from}} ({{distance}}km)",
"title": "Export route",
"trackname": "Name"
},
"footer": {
"ascend": "Ascend",
"cost": "Cost",
"distance": "Distance",
"energy-per-100km": "Energy per 100 km",
"hours": "hours",
"hours-abbrev": "h",
"kilometer": "kilometers",
"kilometer-abbrev": "km",
"kilowatthour": "kilowatt hours",
"kilowatthour-abbrev": "kWh",
"mean-cost-factor": "Mean cost factor",
"meter": "meters",
"meter-abbrev": "m",
"plain-ascend": "Plain ascend",
"total-energy": "Total Energy",
"travel-time": "Travel time"
},
"layers": {
"add-base": "Add base layer",
"add-overlay": "Add overlay",
"customize": "Customize layers",
"placeholder-layer-name": "Custom layer name. (ex: OpenStreetMap)",
"placeholder-layer-url": "Custom layer URL. (ex: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png)",
"remove-selection": "Remove selection"
},
"loadNogos": {
"defaultProperties": "Default properties",
"file": "File: ",
"load": "Load",
"nogoBuffer": "Buffer no-go areas (in meters): ",
"nogoRadius": "No-go radius (for points): ",
"nogoWeight": "No-go weight: ",
"source": "Source",
"title": "Load no-go areas",
"url": "URL: "
},
"map": {
"attribution-osm-long": "OpenStreetMap contributors",
"attribution-osm-short": "OpenStreetMap",
"clear-route": "Clear route",
"copyright": "Copyright",
"cycling": "Cycling",
"delete-last-point": "Delete last point",
"delete-nogo-areas": "&nbsp;&nbsp;also delete all no-go areas",
"delete-route": "Delete route?",
"draw-route-start": "Draw route (D key)",
"draw-route-stop": "Stop drawing route (ESC key)",
"hikebike-hillshading": "Hillshading",
"hiking": "Hiking",
"layer": {
"bing": "Bing Aerial",
"cycle": "OpenCycleMap (Thunderf.)",
"cycling": "Cycling (Waymarked Trails)",
"digitalglobe": "DigitalGlobe Recent Imagery",
"esri": "Esri World Imagery",
"hikebike-hillshading": "Hillshading (Hike & Bike Map)",
"hiking": "Hiking (Waymarked Trails)",
"osm": "OpenStreetMap",
"osmde": "OpenStreetMap.de",
"outdoors": "Outdoors (Thunderforest)",
"stamen-terrain": "Terrain (Stamen)",
"strava-segments": "Strava segments",
"topo": "OpenTopoMap"
},
"loading": "Loading…",
"locate-me": "Show me where I am",
"nogo": {
"cancel": "Cancel drawing no-go area",
"click-drag": "Click and drag to draw circle",
"draw": "Draw no-go area (circle)",
"edit": "Click to edit",
"help": "&square; = move / resize, <span class=\"fa fa-trash-o\"></span> = delete,<br>click circle to quit editing"
},
"opacity-slider": "Set transparency of route track and markers",
"privacy": "Privacy",
"reverse-route": "Reverse route",
"strava-biking": "Show Strava biking segments",
"strava-running": "Show Strava running segments",
"zoomInTitle": "Zoom in",
"zoomOutTitle": "Zoom out"
},
"modal": {
"close": "Close"
},
"navbar": {
"about": "About",
"alternative": {
"first": "1st alternative",
"original": "Original",
"second": "2nd alternative",
"third": "3rd alternative"
},
"export": "Export",
"load": {
"nogos": "No-go areas",
"title": "Load"
},
"profile": {
"car-eco": "Car (economic)",
"car-fast": "Car (fast)",
"car-test": "Car (test)",
"custom": "Custom",
"fastbike": "Fastbike",
"fastbike-asia-pacific": "Fastbike (Asia Pacific)",
"fastbike-lowtraffic": "Fastbike (low traffic)",
"hiking-beta": "Hiking (beta)",
"moped": "Moped",
"rail": "Rail",
"river": "River",
"safety": "Safety",
"shortest": "Shortest",
"trekking": "Trekking bike",
"trekking-ignore-cr": "Trekking bike (ignore cycle routes)",
"trekking-noferries": "Trekking bike (no ferries)",
"trekking-nosteps": "Trekking bike (no steps)",
"trekking-steep": "Trekking bike (steep)",
"vm-forum-liegerad-schnell": "Recumbent bike (fast)",
"vm-forum-velomobil-schnell": "Velomobile (fast)"
}
},
"sidebar": {
"custom-profile": {
"title": "Custom profile"
},
"data": {
"title": "Data"
},
"itinerary": {
"title": "Itinerary"
},
"layers": {
"category": {
"base-layers": "Base layers",
"country": "Country",
"europe": "Europe",
"europe-monolingual": "Europe monolingual",
"overlays": "Overlays",
"worldwide": "Worldwide",
"worldwide-international": "Worldwide international",
"worldwide-monolingual": "Worldwide monolingual"
},
"collapse": "Collapse all",
"custom-layers": "Custom layers",
"customize": "Add or remove custom layers",
"expand": "Expand all",
"optional": "Add or remove optional layers",
"optional-layers": "More",
"table": {
"URL": "URL",
"empty": "No custom layer configured yet.",
"name": "Name",
"type": "Type"
},
"title": "Layers"
},
"profile": {
"clear": "Clear",
"help": "Help",
"placeholder": "Write your custom profile here.",
"upload": "Upload"
}
},
"title": "BRouter web client",
"warning": {
"cannot-get-route": "Error getting route URL",
"no-response": "no response from server",
"no-route-found": "Error: cannot find a route for given points. Maybe try to move them closer to roads?",
"profile-error": "Profile error: no or empty response from server",
"strava-error": "Error getting Strava segments: {{error}}",
"temporary-profile": "<strong>Note:</strong> Uploaded custom profiles are only cached temporarily on the server.<br/>Please save your edits to your local PC.",
"upload-error": "Upload error: {{error}}"
}
}

209
locales/hu.json Normal file
View file

@ -0,0 +1,209 @@
{
"about": {
"bug-reports": "Hibák és kérések bejelentése:",
"bug-reports-back": "kiszolgálóoldal / back end, útvonaltervező motor, Android alkalmazás, profilok, brouter.de weboldal",
"bug-reports-front": "weboldali kliens / frontend.",
"chat": "Chat with users and developers",
"contact": "Kapcsolat:",
"data": "Adatok:",
"data-description": "Az adatok alapja az <a href=\"https://www.openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>. Az oldal általában hetente frissül, amikor az új bolygófájl (Planet fájl) elérhetővé válik. A dátumok megtalálhatók az <a href=\"http://brouter.de/brouter/segments4/\" target=\"_blank\">adatfájlokban</a>.",
"description": "Ez itt a BRouter útvonaltervező motor online szolgáltatása. Az Android alkalmazásról és a további információkról a <a href=\"http://brouter.de/\" target=\"_blank\">brouter.de</a> oldalon olvashat.",
"details": "<i><a href=\"http://brouter.de/privacypolicy.html\" target=\"_blank\">Adatvédelmi irányelvek</a></i> \n<i><a href=\"https://github.com/nrenner/brouter-web#credits-and-licenses\" target=\"_blank\">stáblista</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web/blob/master/CHANGELOG.md\" target=\"_blank\">változásnapló</a></i> és\n<i><a href=\"https://github.com/nrenner/brouter-web#readme\" target=\"_blank\">további tájékoztatás</a></i> a kliensen.",
"support": "Általános viták/kérdések, támogatás",
"title": "Névjegy"
},
"credits": {
"brouter": "BRouter",
"brouter-license": "<a target=\"_blank\" href=\"http://brouter.de/brouter\">BRouter</a> &copy; Arndt Brenschede",
"esri-license": "<a target=\"_blank\" href=\"http://goto.arcgisonline.com/maps/World_Imagery\">Globális légi felvételek</a> &copy; <a target=\"_blank\" href=\"https://www.esri.com/\">Esri</a>, források: Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP valamint a GIS felhasználói közösség",
"esri-tiles": "Esri World Imagery",
"map-data": "Térképadatok",
"map-tiles": "Map tiles",
"nominatim": "Keresés: <a href=\"https://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\" data-i18n=\"credits.nominatim\">Nominatim</a>",
"openstreetmap": "&copy; <a target=\"_blank\" href=\"https://www.openstreetmap.org/copyright\" >OpenStreetMap közreműködők</a>, <a target=\"_blank\" href=\"https://opendatacommons.org/licenses/odbl/\" >ODbL</a> licenc szerint"
},
"export": {
"format": "Formátum",
"format_csv": "CSV",
"format_geojson": "GeoJSON",
"format_gpx": "GPX",
"format_kml": "KML",
"route-from-to": "{{from}} -> {{to}} ({{distance}} km)",
"route-loop": "{{from}} ({{distance}} km)",
"title": "Útvonal exportálása",
"trackname": "Név"
},
"footer": {
"ascend": "Összes szintkülönbség",
"cost": "Ráfordítás",
"distance": "Távolság",
"energy-per-100km": "Energia / 100 km",
"hours": "óra",
"hours-abbrev": "h",
"kilometer": "kilométer",
"kilometer-abbrev": "km",
"kilowatthour": "kilowattóra",
"kilowatthour-abbrev": "kWh",
"mean-cost-factor": "Közepes ráfordítástényező",
"meter": "méter",
"meter-abbrev": "m",
"plain-ascend": "Végpontok közötti szintkülönbség",
"total-energy": "Teljes energiafelhasználás",
"travel-time": "Utazási idő"
},
"layers": {
"add-base": "Alapréteg hozzáadása",
"add-overlay": "Rátétréteg hozzáadása",
"customize": "Rétegek személyre szabása",
"placeholder-layer-name": "Egyedi rétegnév (pl. OpenStreetMap)",
"placeholder-layer-url": "Egyedi réteg URL-je (pl. https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png)",
"remove-selection": "Kijelölés eltávolítása"
},
"loadNogos": {
"defaultProperties": "Alapértelmezett tulajdonságok",
"file": "Fájl:",
"load": "Betöltés",
"nogoBuffer": "Elkerülendő területek körüli távolság (méter)",
"nogoRadius": "Elkerülendő pontok körüli sugár:",
"nogoWeight": "Elkerülés súlyozása:",
"source": "Forrás",
"title": "Elkerülendő területek betöltése",
"url": "URL:"
},
"map": {
"attribution-osm-long": "OpenStreetMap közreműködők",
"attribution-osm-short": "OpenStreetMap",
"clear-route": "Útvonal törlése",
"copyright": "Szerzői jogok",
"cycling": "Kerékpározás",
"delete-last-point": "Utolsó pont törlése",
"delete-nogo-areas": "&nbsp;&nbsp;törlődjenek az elkerülendő területek is",
"delete-route": "Útvonal törlése?",
"draw-route-start": "Útvonal rajzolása (D billentyű)",
"draw-route-stop": "Útvonal rajzolásának megszakítása (ESC billentyű)",
"hikebike-hillshading": "Domborzatárnyékolás",
"hiking": "Túrázás",
"layer": {
"bing": "Bing Aerial",
"cycle": "OpenCycleMap (Thunderfores)",
"cycling": "Kerékpározás (Waymarked Trails)",
"digitalglobe": "DigitalGlobe Recent Imagery",
"esri": "Esri World Imagery",
"hikebike-hillshading": "Domborzatárnyékolás (Hike & Bike Map)",
"hiking": "Túrázás (Waymarked Trails)",
"osm": "OpenStreetMap",
"osmde": "OpenStreetMap.de",
"outdoors": "Outdoors (Thunderforest)",
"stamen-terrain": "Terrain (Stamen)",
"strava-segments": "Strava útvonalszakaszok",
"topo": "OpenTopoMap"
},
"loading": "Betöltés…",
"locate-me": "Mutassa a térképen, hol vagyok",
"nogo": {
"cancel": "Elkerülendő terület rajzolásának megszakítása",
"click-drag": "Kör rajzolása kattintással és húzással",
"draw": "Elkerülendő terület rajzolása (kör)",
"edit": "Szerkesztés kattintással",
"help": "&square; = áthelyezés / átméretezés, <span class=\"fa fa-trash-o\"></span> = törlés,<br>a szerkesztés befejezéséhez kattintson a körre"
},
"opacity-slider": "Nyomvonal és jelölők átlátszóságának beállítása",
"privacy": "Adatvédelem",
"reverse-route": "Útvonal megfordítása",
"strava-biking": "Strava motoros útvonalszakaszok megjelenítése",
"strava-running": "Strava futó útvonalszakaszok megjelenítése",
"zoomInTitle": "Nagyítás",
"zoomOutTitle": "Kicsinyítés"
},
"modal": {
"close": "Bezárás"
},
"navbar": {
"about": "Névjegy",
"alternative": {
"first": "1. alternatíva",
"original": "Eredeti",
"second": "2. alternatíva",
"third": "3. alternatíva"
},
"export": "Exportálás",
"load": {
"nogos": "Elkerülendő területek",
"title": "Betöltés"
},
"profile": {
"car-eco": "Személyautó (gazdaságos)",
"car-fast": "Személyautó (gyors)",
"car-test": "Személyautó (teszt)",
"custom": "Egyedi",
"fastbike": "Versenykerékpár",
"fastbike-asia-pacific": "Versenykerékpár (ázsiai-csendes-óceáni térség)",
"fastbike-lowtraffic": "Versenykerékpár (kis forgalom)",
"hiking-beta": "Túrázás (béta)",
"moped": "Robogó",
"rail": "Vasút",
"river": "Hajózás",
"safety": "Biztonság",
"shortest": "Legrövidebb",
"trekking": "Túrakerékpár",
"trekking-ignore-cr": "Túrakerékpár (kerékpárutak figyelmen kívül hagyása)",
"trekking-noferries": "Túrakerékpár (komp nélkül)",
"trekking-nosteps": "Túrakerékpár (lépcső nélkül)",
"trekking-steep": "Túrakerékpár (meredek)",
"vm-forum-liegerad-schnell": "Rekumbens kerékpár (gyors)",
"vm-forum-velomobil-schnell": "Velomobil (aerodinamikus kerékpár, gyors)"
}
},
"sidebar": {
"custom-profile": {
"title": "Egyedi profil"
},
"data": {
"title": "Adatok"
},
"itinerary": {
"title": "Útiterv"
},
"layers": {
"category": {
"base-layers": "Alaprétegek",
"country": "Ország",
"europe": "Európa",
"europe-monolingual": "Európa (egynyelvű)",
"overlays": "Rátétrétegek",
"worldwide": "Világ",
"worldwide-international": "Világ (nemzetközi)",
"worldwide-monolingual": "Világ (egynyelvű)"
},
"collapse": "Minden összecsukása",
"custom-layers": "Egyedi rétegek",
"customize": "Egyedi rétegek hozzáadása vagy eltávolítása",
"expand": "Minden kibontása",
"optional": "Rétegek hozzáadása vagy eltávolítása",
"optional-layers": "Több",
"table": {
"URL": "URL",
"empty": "Még nincs beállítva egyedi réteg",
"name": "Név",
"type": "Típus"
},
"title": "Rétegek"
},
"profile": {
"clear": "Törlés",
"help": "Súgó",
"placeholder": "Ide írhatja egyedi profilját",
"upload": "Feltöltés"
}
},
"title": "BRouter webkliens",
"warning": {
"cannot-get-route": "Hiba történt az útvonal URL-jének beolvasásakor",
"no-response": "a kiszolgáló nem válaszol",
"no-route-found": "Hiba: az adott pontokhoz nem sikerül útvonalat találni. Esetleg próbálja meg közelebb tenni őket az utakhoz.",
"profile-error": "Profilhiba: a kiszolgáló nem válaszol vagy üres választ küld",
"strava-error": "Hiba történt a Strava útvonalszakaszok lekérésénél: {{error}}",
"temporary-profile": "<strong>Figyelem:</strong> A kiszolgáló csak ideiglenesen gyorsítótárazza a feltöltött egyedi profilokat.<br/>Kérjük, szerkesztéseit mentse el saját számítógépére.",
"upload-error": "Feltöltési hiba: {{error}}"
}
}

36
locales/keys.js Normal file
View file

@ -0,0 +1,36 @@
// this file contains translatable keys that are dynamic / not visible by i18n extractor tool
i18next.t('navbar.profile.car-eco');
i18next.t('navbar.profile.car-fast');
i18next.t('navbar.profile.car-test');
i18next.t('navbar.profile.fastbike');
i18next.t('navbar.profile.fastbike-asia-pacific');
i18next.t('navbar.profile.fastbike-asia-pacific');
i18next.t('navbar.profile.fastbike-lowtraffic');
i18next.t('navbar.profile.hiking-beta');
i18next.t('navbar.profile.moped');
i18next.t('navbar.profile.rail');
i18next.t('navbar.profile.river');
i18next.t('navbar.profile.safety');
i18next.t('navbar.profile.shortest');
i18next.t('navbar.profile.trekking');
i18next.t('navbar.profile.trekking-ignore-cr');
i18next.t('navbar.profile.trekking-noferries');
i18next.t('navbar.profile.trekking-nosteps');
i18next.t('navbar.profile.trekking-steep');
i18next.t('navbar.profile.vm-forum-liegerad-schnell');
i18next.t('navbar.profile.vm-forum-velomobil-schnell');
i18next.t('sidebar.layers.category.base-layers', 'Base layers');
i18next.t(
'sidebar.layers.category.worldwide-international',
'Worldwide international'
);
i18next.t(
'sidebar.layers.category.worldwide-monolingual',
'Worldwide monolingual'
);
i18next.t('sidebar.layers.category.europe', 'Europe');
i18next.t('sidebar.layers.category.europe-monolingual', 'Europe monolingual');
i18next.t('sidebar.layers.category.country', 'Country');
i18next.t('sidebar.layers.category.overlays', 'Overlays');
i18next.t('sidebar.layers.category.worldwide', 'Worldwide');

209
locales/pl.json Normal file
View file

@ -0,0 +1,209 @@
{
"about": {
"bug-reports": "Zgłaszanie błędów i nowych funkcji:",
"bug-reports-back": "serwer / aplikacja, silnik wyznaczania tras, aplikacja na Androida, profile, strona brouter.de",
"bug-reports-front": "klient przeglądarkowy / interfejs.",
"chat": "Czat z użytkownikami i twórcami",
"contact": "Kontakt:",
"data": "Dane:",
"data-description": "Bazujemy na <a href=\"https://www.openstreetmap.org\" target=\"_blank\">OpenStreetMap</a>. Aktualizacje są zazwyczaj raz w tygodniu, kiedy są dostępne nowe pliki Planet, więc sprawdź daty <a href=\"http://brouter.de/brouter/segments4/\" target=\"_blank\">plików z danymi</a>.",
"description": "Dostęp online do silnika wyznaczania tras BRouter. Aplikację offline na Androida i więcej informacji znajdziesz na <a href=\"http://brouter.de/\" target=\"_blank\">brouter.de</a>",
"details": "<i><a href=\"http://brouter.de/privacypolicy.html\" target=\"_blank\">Polityka prywatności</a></i>, \n<i><a href=\"https://github.com/nrenner/brouter-web#credits-and-licenses\" target=\"_blank\">Autorzy</a></i>,\n<i><a href=\"https://github.com/nrenner/brouter-web/blob/master/CHANGELOG.md\" target=\"_blank\">Histroia zmian</a></i> i\n<i><a href=\"https://github.com/nrenner/brouter-web#readme\" target=\"_blank\">więcej informacji</a></i> na temat klienta.",
"support": "Ogólna dyskusja/pytania, wsparcie",
"title": "Informacje"
},
"credits": {
"brouter": "BRouter",
"brouter-license": "<a target=\"_blank\" href=\"http://brouter.de/brouter\">BRouter</a> &copy; Arndt Brenschede",
"esri-license": "<a target=\"_blank\" href=\"http://goto.arcgisonline.com/maps/World_Imagery\">World Imagery</a> &copy; <a target=\"_blank\" href=\"https://www.esri.com/\">Esri</a>, źródła: Esri, DigitalGlobe, Earthstar Geographics, CNES/Airbus DS, GeoEye, USDA FSA, USGS, Getmapping, Aerogrid, IGN, IGP oraz Społeczność Użytkowników GIS",
"esri-tiles": "Obrazowanie świata Esri",
"map-data": "Dane mapy",
"map-tiles": "Kafelki mapy",
"nominatim": "Wyszukaj przez <a href=\"https://wiki.openstreetmap.org/wiki/Nominatim\" target=\"_blank\" data-i18n=\"credits.nominatim\">Nominatim</a>",
"openstreetmap": "&copy; <a target=\"_blank\" href=\"https://www.openstreetmap.org/copyright\" >Współtwórcy OpenStreetMap</a> na licencji <a target=\"_blank\" href=\"https://opendatacommons.org/licenses/odbl/\" >ODbL</a>"
},
"export": {
"format": "Format",
"format_csv": "CSV",
"format_geojson": "GeoJSON",
"format_gpx": "GPX",
"format_kml": "KML",
"route-from-to": "{{from}} -> {{to}} ({{distance}}km)",
"route-loop": "{{from}} ({{distance}}km)",
"title": "Eksportuj trasę",
"trackname": "Nazwa"
},
"footer": {
"ascend": "Przewyższenie",
"cost": "Koszt",
"distance": "Odległość",
"energy-per-100km": "Energia na 100 km",
"hours": "godzin",
"hours-abbrev": "godz.",
"kilometer": "kilometrów",
"kilometer-abbrev": "km",
"kilowatthour": "kilowatogodziny",
"kilowatthour-abbrev": "kWh",
"mean-cost-factor": "Średni współczynnik kosztów",
"meter": "metrów",
"meter-abbrev": "m",
"plain-ascend": "Różnica wysokości",
"total-energy": "Całkowita energia",
"travel-time": "Czas podróży"
},
"layers": {
"add-base": "Dodaj warstwę bazową",
"add-overlay": "Dodaj nakładkę",
"customize": "Dostosuj warstwy",
"placeholder-layer-name": "Własna nazwa warstwy. (np. OpenStreetMap)",
"placeholder-layer-url": "URL niestandardowej warstwy. (np: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png)",
"remove-selection": "Usuń zaznaczenie"
},
"loadNogos": {
"defaultProperties": "Domyślne właściwości",
"file": "Plik:",
"load": "Wczytaj",
"nogoBuffer": "Bufor stref no-go (w metrach):",
"nogoRadius": "Promień no-go (dla punktów):",
"nogoWeight": "Waga no-go:",
"source": "Źródło",
"title": "Wczytaj strefy no-go",
"url": "URL: "
},
"map": {
"attribution-osm-long": "Współtwórcy OpenStreetMap",
"attribution-osm-short": "OpenStreetMap",
"clear-route": "Wyczyść trasę",
"copyright": "Prawa autorskie",
"cycling": "Kolarstwo",
"delete-last-point": "Usuń ostatni punkt",
"delete-nogo-areas": "&nbsp;&nbsp;usunie również wszystkie strefy no-go",
"delete-route": "Usunąć trasę?",
"draw-route-start": "Rysuj trasę (klawisz D)",
"draw-route-stop": "Przestań rysować trasę (klawisz Esc)",
"hikebike-hillshading": "Cieniowanie",
"hiking": "Wspinaczka",
"layer": {
"bing": "Zdjęcia lotnicze Bing",
"cycle": "OpenCycleMap (Thunderf.)",
"cycling": "Jazda na rowerze (oznaczone szlaki)",
"digitalglobe": "Najnowsze obrazowanie DigitalGlobe",
"esri": "Obrazowanie świata Esri",
"hikebike-hillshading": "Cieniowanie (Mapa wspinaczkowo-rowerowa)",
"hiking": "Wspinaczka (oznaczone szlaki)",
"osm": "OpenStreetMap",
"osmde": "OpenStreetMap.de",
"outdoors": "Na zewnątrz (Thunderforest)",
"stamen-terrain": "Teren (Stamen)",
"strava-segments": "Segmenty Strava",
"topo": "OpenTopoMap"
},
"loading": "Wczytywanie…",
"locate-me": "Pokaż mi gdzie jestem",
"nogo": {
"cancel": "Anuluj rysowanie strefy no-go",
"click-drag": "Kliknij i przeciągnij, aby narysować okrąg",
"draw": "Rysuj strefę no-go (okrąg)",
"edit": "Kliknij, aby edytować",
"help": "&square; = przesuń / zmień rozmiar, <span class=\"fa fa-trash-o\"></span> = usuń,<br>kliknij okrąg, aby wyjść z edycji"
},
"opacity-slider": "Ustaw przezroczystość trasy i znaczników",
"privacy": "Prywatność",
"reverse-route": "Odwróć trasę",
"strava-biking": "Pokaż rowerowe segmenty Strava ",
"strava-running": "Pokaż biegowe segmenty Strava",
"zoomInTitle": "Przybliż",
"zoomOutTitle": "Oddal"
},
"modal": {
"close": "Zamknij"
},
"navbar": {
"about": "Informacje",
"alternative": {
"first": "Alternatywa nr 1",
"original": "Pierwotna",
"second": "Alternatywa nr 2",
"third": "Alternatywa nr 3"
},
"export": "Eksportuj",
"load": {
"nogos": "Stefy no-go",
"title": "Wczytaj"
},
"profile": {
"car-eco": "Samochód (ekonomicznie)",
"car-fast": "Samochód (szybko)",
"car-test": "Samochód (testowo)",
"custom": "Własne",
"fastbike": "Rower szosowy",
"fastbike-asia-pacific": "Rower szosowy (Azja Pacyfik)",
"fastbike-lowtraffic": "Rower szosowy (mały ruch)",
"hiking-beta": "Wspinaczka (beta)",
"moped": "Motorower",
"rail": "Kolej",
"river": "Rzeka",
"safety": "Bezpieczeństwo",
"shortest": "Najkrótsza",
"trekking": "Rower trekkingowy",
"trekking-ignore-cr": "Rower trekkingowy (ignoruj trasy rowerowe)",
"trekking-noferries": "Rower trekkingowy (bez promów)",
"trekking-nosteps": "Rower trekkingowy (bez schodów)",
"trekking-steep": "Rower trekkingowy (stromo)",
"vm-forum-liegerad-schnell": "Rower poziomy (szosowy)",
"vm-forum-velomobil-schnell": "Velomobile (szosowy)"
}
},
"sidebar": {
"custom-profile": {
"title": "Własny profil"
},
"data": {
"title": "Dane"
},
"itinerary": {
"title": "Plan podróży"
},
"layers": {
"category": {
"base-layers": "Warstwy bazowe",
"country": "Kraj",
"europe": "Europa",
"europe-monolingual": "Europa jeden język",
"overlays": "Nakładki",
"worldwide": "Cały świat",
"worldwide-international": "Świat wielojęzyczny",
"worldwide-monolingual": "Świat jeden język"
},
"collapse": "Zwiń wszystkie",
"custom-layers": "Własne warstwy",
"customize": "Dodaj lub usuń niestandardowe warstwy",
"expand": "Rozwiń wszystkie",
"optional": "Dodaj lub usuń opcjonalne warstwy",
"optional-layers": "Więcej",
"table": {
"URL": "URL",
"empty": "Nie skonfigurowano jeszcze niestandardowych warstw.",
"name": "Nazwa",
"type": "Rodzaj"
},
"title": "Warstwy"
},
"profile": {
"clear": "Wyczyść",
"help": "Pomoc",
"placeholder": "Zapisz tutaj swój własny profil.",
"upload": "Prześlij"
}
},
"title": "Klient przeglądarkowy BRouter",
"warning": {
"cannot-get-route": "Błąd pobierania URL trasy",
"no-response": "brak odpowiedzi z serwera",
"no-route-found": "Błąd: nie można znaleźć trasy dla zadanych punktów. Spróbuj przesunąć je bliżej dróg.",
"profile-error": "Błąd profilu: brak lub pusta odpowiedź z serwera",
"strava-error": "Błąd pobierania segmentów Strava: {{error}}",
"temporary-profile": "<strong>Uwaga:</strong> Przesłane własne profile są zapisywane na serwerze tylko tymczasowo.<br/>Zapisz zmiany na swoim komputerze.",
"upload-error": "Błąd przesyłania: {{error}}"
}
}

Some files were not shown because too many files have changed in this diff Show more