Merge branch 'master' into feature/profile-sidebar
This commit is contained in:
commit
68538378fe
21 changed files with 707 additions and 231 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,3 +6,4 @@ bingkey.txt
|
|||
/.project
|
||||
/keys.js
|
||||
/dist
|
||||
brouter-web.*.zip
|
||||
|
|
|
|||
13
README.md
13
README.md
|
|
@ -3,6 +3,9 @@ brouter-web
|
|||
|
||||
Web client (by [@nrenner](https://github.com/nrenner)) 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).
|
||||
Feedbacks are appreciated, do not hesitate to create issues about it!**
|
||||
|
||||
BRouter online service (provided by [@abrensch](https://github.com/abrensch)):
|
||||
http://brouter.de/brouter-web/
|
||||
|
||||
|
|
@ -13,7 +16,7 @@ More information:
|
|||
http://brouter.de
|
||||
|
||||
General BRouter discussions/questions, support:
|
||||
http://groups.google.com/group/osm-android-bikerouting
|
||||
https://groups.google.com/group/osm-android-bikerouting
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -72,7 +75,7 @@ This is needed for pre-loading the selected profile (unless you allowed local fi
|
|||
|
||||
### Dependencies
|
||||
|
||||
Requires [Node and npm](http://nodejs.org/) (or [io.js](https://iojs.org)), [Bower](http://bower.io/) and [Gulp](http://gulpjs.com/):
|
||||
Requires [Node and npm](https://nodejs.org/) (or [io.js](https://iojs.org)), [Bower](https://bower.io/) and [Gulp](http://gulpjs.com/):
|
||||
|
||||
npm install -g bower
|
||||
npm install -g gulp
|
||||
|
|
@ -115,12 +118,12 @@ Copyright (c) 2012 [sa3m](https://github.com/sa3m), Copyright (c) 2013 Per Liedm
|
|||
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)
|
||||
Copyright (c) 2010-2014 Caolan McMahon; [MIT License](https://github.com/caolan/async/blob/master/LICENSE)
|
||||
* [Bootstrap](http://getbootstrap.com/)
|
||||
* [Bootstrap](https://getbootstrap.com/)
|
||||
Copyright (c) 2011-2014 Twitter, Inc; [MIT License](https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* [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)
|
||||
* [DataTables](https://github.com/DataTables/DataTables)
|
||||
Copyright (C) 2008-2014, SpryMedia Ltd.; [MIT License](http://www.datatables.net/license/mit)
|
||||
Copyright (C) 2008-2014, SpryMedia Ltd.; [MIT License](https://www.datatables.net/license/MIT-LICENCE)
|
||||
* [Leaflet.EasyButton](https://github.com/CliffCloud/Leaflet.EasyButton)
|
||||
Copyright (C) 2014 Daniel Montague; [MIT License](https://github.com/CliffCloud/Leaflet.EasyButton/blob/master/LICENSE)
|
||||
* [Bootbox](https://github.com/makeusabrew/bootbox)
|
||||
|
|
@ -132,4 +135,4 @@ Copyright (c) 2012 Makina Corpus, [MIT License](https://github.com/makinacorpus/
|
|||
* [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)
|
||||
* [Font Awesome](http://fontawesome.io/license/)
|
||||
by Dave Gandy; [SIL OFL 1.1](http://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)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "brouter-web",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.3",
|
||||
"main": "dist/**/*",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
|
|
@ -38,8 +38,6 @@
|
|||
},
|
||||
"leaflet-plugins": {
|
||||
"main": [
|
||||
"control/Permalink.js",
|
||||
"control/Permalink.Layer.js",
|
||||
"layer/tile/Bing.js"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@ html, body, #map {
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
/* 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
|
||||
stacking context on their common ancestor */
|
||||
body, #content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.flexcolumn {
|
||||
display: -webkit-box;
|
||||
display: -moz-box;
|
||||
|
|
@ -88,17 +96,12 @@ footer {
|
|||
cursor: crosshair;
|
||||
}
|
||||
|
||||
/* FIXME permalink temporary hack */
|
||||
.leaflet-control-permalink {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#message {
|
||||
position: absolute;
|
||||
left: 446px; /* 400 + 10 + 26 + 10 */
|
||||
top: 0px;
|
||||
margin-top: 10px;
|
||||
z-index: 1000;
|
||||
z-index: 3000;
|
||||
}
|
||||
|
||||
/* track messages (data tab) */
|
||||
|
|
@ -107,7 +110,7 @@ footer {
|
|||
}
|
||||
|
||||
/* dashed line animation, derived from Chris Coyier and others
|
||||
http://css-tricks.com/svg-line-animation-works/
|
||||
https://css-tricks.com/svg-line-animation-works/
|
||||
*/
|
||||
.loading-trailer {
|
||||
-webkit-animation: dash 0.4s linear infinite;
|
||||
|
|
|
|||
94
gulpfile.js
94
gulpfile.js
|
|
@ -14,6 +14,12 @@ var remember = require('gulp-remember');
|
|||
var inject = require('gulp-inject');
|
||||
var gulpif = require('gulp-if');
|
||||
var gutil = require('gulp-util');
|
||||
var zip = require('gulp-zip');
|
||||
var bump = require('gulp-bump');
|
||||
var semver = require('semver');
|
||||
var git = require('gulp-git');
|
||||
var replace = require('gulp-replace');
|
||||
var release = require('gulp-github-release');
|
||||
|
||||
var debug = false;
|
||||
|
||||
|
|
@ -142,4 +148,90 @@ gulp.task('default', ['clean', 'scripts_config', 'scripts', 'styles', 'images',
|
|||
gulp.task('debug', function() {
|
||||
debug = true;
|
||||
gulp.start('default');
|
||||
});
|
||||
});
|
||||
|
||||
var pkg = require('./package.json');
|
||||
var tags = {patch: 'patch', minor: 'minor', major: 'major'};
|
||||
var nextVersion;
|
||||
var ghToken;
|
||||
|
||||
gulp.task('release:init', function() {
|
||||
var tag = gutil.env.tag;
|
||||
if (!tag) {
|
||||
gutil.log(gutil.colors.red('--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);
|
||||
}
|
||||
});
|
||||
nextVersion = semver.inc(pkg.version, tag);
|
||||
return;
|
||||
});
|
||||
|
||||
gulp.task('bump', ['bump:json', 'bump:html']);
|
||||
|
||||
gulp.task('bump:json', ['release:init'], function() {
|
||||
gutil.log(gutil.colors.green('Bump to '+nextVersion));
|
||||
return(gulp.src(['./package.json', './bower.json'])
|
||||
.pipe(bump({version: nextVersion}))
|
||||
.pipe(gulp.dest('./')));
|
||||
});
|
||||
|
||||
gulp.task('bump:html', ['release:init'], function() {
|
||||
return(gulp.src('./index.html')
|
||||
.pipe(replace(/<sup class="version">(.*)<\/sup>/, '<sup class="version">'+nextVersion+'</sup>'))
|
||||
.pipe(gulp.dest('.')));
|
||||
});
|
||||
|
||||
gulp.task('release:commit', ['bump'], function() {
|
||||
gulp.src(['./index.html', './package.json', './bower.json'])
|
||||
.pipe(git.commit('release: '+nextVersion));
|
||||
});
|
||||
|
||||
gulp.task('release:tag', ['release:commit'], function() {
|
||||
return(git.tag(nextVersion, '', function(err) {
|
||||
if (err) throw err;
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('release:push', ['release:tag'], function() {
|
||||
git.push('origin', 'master', {args: '--tags'}, function(err) {
|
||||
if (err) throw err;
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('release:zip', ['release:tag', 'default'], 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', ['release:zip'], function() {
|
||||
gulp.src('./brouter-web.'+nextVersion+'.zip')
|
||||
.pipe(release({
|
||||
tag: nextVersion,
|
||||
token: ghToken,
|
||||
manifeste: pkg,
|
||||
}))
|
||||
});
|
||||
|
||||
gulp.task('release', ['release:init', 'bump', 'release:commit', 'release:tag',
|
||||
'release:push', 'release:zip', 'release:publish']);
|
||||
60
index.html
60
index.html
|
|
@ -29,9 +29,6 @@
|
|||
<a class="dropdown-item" id="dl-csv" href="#" disabled>data CSV</a>
|
||||
</div>
|
||||
</div>
|
||||
<a class="nav-item nav-link" href="" data-toggle="modal" data-target="#permalink-win" id="permalink">
|
||||
<span class="fa fa-lg fa-share-alt"></span> Share URL</a>
|
||||
|
||||
<form class="navbar-form">
|
||||
<div class="form-group">
|
||||
<select class="selectpicker show-tick" id="profile-alternative" multiple>
|
||||
|
|
@ -51,23 +48,6 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Permalink -->
|
||||
<div class="modal fade" id="permalink-win" tabindex="-1" role="dialog" aria-labelledby="Permalink window" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h4 class="modal-title">Permalink</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input class="form-control" type="text" id="permalink-input" >
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Credits modal window -->
|
||||
<div class="modal fade" id="credits" tabindex="-1" role="dialog" aria-labelledby="Credits window" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
|
|
@ -82,11 +62,11 @@
|
|||
<dl>
|
||||
<dt>Map data</dt>
|
||||
<dd>
|
||||
© <a target="_blank" href="http://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>
|
||||
under <a target="_blank" href="http://opendatacommons.org/licenses/odbl/">ODbL</a>
|
||||
© <a target="_blank" href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>
|
||||
under <a target="_blank" href="https://opendatacommons.org/licenses/odbl/">ODbL</a>
|
||||
</dd>
|
||||
<dt>OpenstreetMap.de tiles</dt>
|
||||
<dd><a target="_blank" href="http://openstreetmap.de/karte.html">openstreetmap.de</a></dd>
|
||||
<dd><a target="_blank" href="https://openstreetmap.de/karte.html">openstreetmap.de</a></dd>
|
||||
<dt>OpenTopoMap tiles</dt>
|
||||
<dd>
|
||||
© <a target="_blank" href="https://opentopomap.org">OpenTopoMap</a>
|
||||
|
|
@ -95,13 +75,13 @@
|
|||
</dd>
|
||||
<dt>Thunderforest tiles</dt>
|
||||
<dd>
|
||||
© <a target="_blank" href="http://www.thunderforest.com">Thunderforest</a>
|
||||
under <a target="_blank" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a>
|
||||
© <a target="_blank" href="https://www.thunderforest.com">Thunderforest</a>
|
||||
under <a target="_blank" href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a>
|
||||
</dd>
|
||||
<dt>Waymarked Trails tiles</dt>
|
||||
<dd>
|
||||
© <a target="_blank" href="http://cycling.waymarkedtrails.org">Waymarked Trails</a>
|
||||
under <a target="_blank" href="http://creativecommons.org/licenses/by-sa/3.0/de/deed.en">CC-BY-SA 3.0 DE</a>
|
||||
© <a target="_blank" href="https://cycling.waymarkedtrails.org">Waymarked Trails</a>
|
||||
under <a target="_blank" href="https://creativecommons.org/licenses/by-sa/3.0/de/deed.en">CC-BY-SA 3.0 DE</a>
|
||||
</dd>
|
||||
<dt>BRouter</dt>
|
||||
<dd>
|
||||
|
|
@ -113,6 +93,24 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Layers modal window -->
|
||||
<div class="modal fade" id="custom_layers" tabindex="-1" role="dialog" aria-labelledby="Layers window" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<h4 class="modal-title">Customize layers</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input class="form-control" type="text" id="layer_name" spellcheck="true" wrap="off" placeholder="Custom layer name. (ex: OpenStreetMap)"></input>
|
||||
<input class="form-control" type="text" id="layer_url" spellcheck="false" wrap="off" placeholder="Custom layer URL. (ex: https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png)"></input>
|
||||
|
||||
<button type="button" id="custom_layers_add_base" class="btn btn-success">Add base layer</button>
|
||||
<button type="button" id="custom_layers_add_overlay" class="btn btn-success">Add overlay</button>
|
||||
<button type="button" id="custom_layers_remove" class="btn btn-danger">Remove selection</button>
|
||||
<table id="custom_layers_table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- About modal window -->
|
||||
<div class="modal fade" id="about" tabindex="-1" role="dialog" aria-labelledby="About window" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
|
|
@ -130,7 +128,7 @@
|
|||
<i>Contact:</i><br>
|
||||
<ul>
|
||||
<li>General discussions/questions, support:<br>
|
||||
<a href="http://groups.google.com/group/osm-android-bikerouting" target="_blank">http://groups.google.com/group/osm-android-bikerouting</a>
|
||||
<a href="https://groups.google.com/group/osm-android-bikerouting" target="_blank">https://groups.google.com/group/osm-android-bikerouting</a>
|
||||
</li>
|
||||
<li>Bug reports and feature requests:
|
||||
<ul>
|
||||
|
|
@ -148,7 +146,7 @@
|
|||
</p>
|
||||
<p>
|
||||
<i>Data:</i><br>
|
||||
This is based on <a href="http://www.openstreetmap.org" target="_blank">OpenStreetMap</a>. It is usually updated once a week when a new Planet file is available,
|
||||
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>.
|
||||
</p>
|
||||
<p>
|
||||
|
|
@ -193,7 +191,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapse in" id="elevation-chart"></div>
|
||||
<div class="collapse" id="elevation-chart"></div>
|
||||
|
||||
<footer>
|
||||
<div class="flexrow">
|
||||
|
|
@ -219,7 +217,7 @@
|
|||
<span class="fa fa-compress"></span>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-secondary btn-sm active" type="button" data-toggle="collapse" data-target="#elevation-chart" aria-controls="elevation-chart" id="elevation-btn" aria-expanded="false" aria-label="Toggle elevation chart">
|
||||
<button class="btn btn-secondary btn-sm" type="button" data-toggle="collapse" data-target="#elevation-chart" aria-controls="elevation-chart" id="elevation-btn" aria-expanded="false" aria-label="Toggle elevation chart">
|
||||
<span class="fa fa-area-chart"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
33
js/Map.js
33
js/Map.js
|
|
@ -8,28 +8,28 @@ BR.Map = {
|
|||
|
||||
var maxZoom = 19;
|
||||
|
||||
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: maxZoom
|
||||
});
|
||||
|
||||
var osmde = L.tileLayer('http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
|
||||
var osmde = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
|
||||
maxNativeZoom: 18,
|
||||
maxZoom: maxZoom
|
||||
});
|
||||
|
||||
var topo = L.tileLayer('http://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
|
||||
var topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
|
||||
maxNativeZoom: 17,
|
||||
maxZoom: maxZoom
|
||||
});
|
||||
|
||||
var thunderforestAttribution = 'tiles © <a target="_blank" href="http://www.thunderforest.com">Thunderforest</a> '
|
||||
+ '(<a target="_blank" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA 2.0</a>)';
|
||||
var thunderforestAttribution = 'tiles © <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('http://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png' + thunderforestAuth, {
|
||||
var cycle = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png' + thunderforestAuth, {
|
||||
maxNativeZoom: 18,
|
||||
maxZoom: maxZoom
|
||||
});
|
||||
var outdoors = L.tileLayer('http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png' + thunderforestAuth, {
|
||||
var outdoors = L.tileLayer('https://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png' + thunderforestAuth, {
|
||||
maxNativeZoom: 18,
|
||||
maxZoom: maxZoom
|
||||
});
|
||||
|
|
@ -39,16 +39,16 @@ BR.Map = {
|
|||
maxZoom: maxZoom,
|
||||
subdomains: ['server', 'services'],
|
||||
attribution: '<a target="_blank" href="http://goto.arcgisonline.com/maps/World_Imagery">World Imagery</a> '
|
||||
+ '© <a target="_blank" href="http://www.esri.com/">Esri</a>, sources: '
|
||||
+ '© <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('http://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png', {
|
||||
var cycling = L.tileLayer('https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png', {
|
||||
maxNativeZoom: 18,
|
||||
opacity: 0.7,
|
||||
maxZoom: maxZoom
|
||||
});
|
||||
var hiking = L.tileLayer('http://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png', {
|
||||
var hiking = L.tileLayer('https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png', {
|
||||
maxNativeZoom: 18,
|
||||
opacity: 0.7,
|
||||
maxZoom: maxZoom
|
||||
|
|
@ -86,7 +86,7 @@ BR.Map = {
|
|||
minZoom: 1,
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="https://www.digitalglobe.com/platforms/mapsapi">DigitalGlobe</a> ('
|
||||
+ '<a href="http://bit.ly/mapsapiview">Terms of Use</a>)'
|
||||
+ '<a href="https://bit.ly/mapsapiview">Terms of Use</a>)'
|
||||
});
|
||||
baseLayers['DigitalGlobe Recent Imagery'] = recent;
|
||||
}
|
||||
|
|
@ -120,13 +120,20 @@ BR.Map = {
|
|||
|
||||
L.control.scale().addTo(map);
|
||||
|
||||
new BR.Layers().init(map, layersControl, baseLayers, overlays);
|
||||
|
||||
// expose map instance for console debugging
|
||||
BR.debug = BR.debug || {};
|
||||
BR.debug.map = map;
|
||||
|
||||
var layersAndOverlays = baseLayers;
|
||||
for (var o in overlays) {
|
||||
layersAndOverlays[o] = overlays[o];
|
||||
}
|
||||
return {
|
||||
map: map,
|
||||
layersControl: layersControl
|
||||
layersControl: layersControl,
|
||||
layers: layersAndOverlays
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@ BR.Util = {
|
|||
|
||||
// check if localStorage is available, especially for catching SecurityError
|
||||
// when cookie settings are blocking access (Chrome, Pale Moon, older Firefox)
|
||||
//
|
||||
//
|
||||
// see also https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
|
||||
//
|
||||
//
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API#Testing_for_support_vs_availability
|
||||
// by Mozilla Contributors, with modifications;
|
||||
// Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/
|
||||
// by Mozilla Contributors, with modifications;
|
||||
// Any copyright is dedicated to the Public Domain. https://creativecommons.org/publicdomain/zero/1.0/
|
||||
localStorageAvailable: function() {
|
||||
try {
|
||||
var storage = window.localStorage,
|
||||
|
|
|
|||
134
js/control/Layers.js
Normal file
134
js/control/Layers.js
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
BR.Layers = L.Class.extend({
|
||||
|
||||
_loadLayers: function() {
|
||||
this._customLayers = {};
|
||||
|
||||
if (BR.Util.localStorageAvailable()) {
|
||||
var layers = JSON.parse(localStorage.getItem("map/customLayers"));
|
||||
for (a in layers) {
|
||||
this._addLayer(a, layers[a].layer, layers[a].isOverlay);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_loadTable: function() {
|
||||
var layersData = [];
|
||||
for (layer in this._customLayers) {
|
||||
layersData.push([layer, this._customLayers[layer].layer._url, this._customLayers[layer].isOverlay ? "Overlay" : "Layer"]);
|
||||
}
|
||||
if (this._layersTable != null) {
|
||||
this._layersTable.destroy();
|
||||
}
|
||||
|
||||
this._layersTable = $('#custom_layers_table').DataTable({
|
||||
data: layersData,
|
||||
info: false,
|
||||
searching: false,
|
||||
paging: false,
|
||||
columns: [
|
||||
{ title: "Name" },
|
||||
{ title: "URL" },
|
||||
{ title: "Type" }
|
||||
]
|
||||
});
|
||||
},
|
||||
|
||||
init: function(map, layersControl, baseLayers, overlays) {
|
||||
this._layersControl = layersControl;
|
||||
this._map = map;
|
||||
this._layers = {}
|
||||
for (var l in overlays)
|
||||
this._layers[l] = [overlays[l], true];
|
||||
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_overlay').onclick = L.bind(this._addOverlay, this);
|
||||
L.DomUtil.get('custom_layers_remove').onclick = L.bind(this._remove, this);
|
||||
|
||||
this._loadLayers();
|
||||
this._loadTable();
|
||||
|
||||
var table = this._layersTable;
|
||||
$('#custom_layers_table tbody').on( 'click', 'tr', function () {
|
||||
if ( $(this).hasClass('selected') ) {
|
||||
$(this).removeClass('selected');
|
||||
} else {
|
||||
table.$('tr.selected').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
}
|
||||
});
|
||||
|
||||
addLayer = L.easyButton(
|
||||
'fa-plus-square',
|
||||
function () {
|
||||
$('#custom_layers').modal();
|
||||
},
|
||||
'Add or remove custom layers',
|
||||
{
|
||||
position: 'topright'
|
||||
}
|
||||
).addTo(map);
|
||||
},
|
||||
|
||||
_remove: function(evt) {
|
||||
var row = this._layersTable.row('.selected').data();
|
||||
if (row != null) {
|
||||
var name = row[0];
|
||||
this._layersControl.removeLayer(this._customLayers[name].layer);
|
||||
this._map.removeLayer(this._customLayers[name].layer);
|
||||
delete this._customLayers[name];
|
||||
this._layersTable.row('.selected').remove().draw( false );
|
||||
this._sync();
|
||||
}
|
||||
},
|
||||
|
||||
_addFromInput: function(isOverlay) {
|
||||
var layer_name = L.DomUtil.get('layer_name').value;
|
||||
var layer_url = L.DomUtil.get('layer_url').value;
|
||||
if (layer_name.length > 0 && layer_url.length > 0)
|
||||
this._addLayer(layer_name, layer_url, isOverlay);
|
||||
},
|
||||
|
||||
_addBaseLayer: function(evt) {
|
||||
this._addFromInput(false);
|
||||
},
|
||||
_addOverlay: function(evt) {
|
||||
this._addFromInput(true);
|
||||
},
|
||||
|
||||
_addLayer: function(layerName, layerUrl, isOverlay) {
|
||||
if (layerName in this._layers)
|
||||
return
|
||||
|
||||
if (layerName in this._customLayers)
|
||||
return
|
||||
|
||||
try {
|
||||
var layer = L.tileLayer(layerUrl);
|
||||
|
||||
this._customLayers[layerName] = {layer: layer, isOverlay: isOverlay};
|
||||
|
||||
if (isOverlay) {
|
||||
this._layersControl.addOverlay(layer, layerName);
|
||||
} else {
|
||||
this._layersControl.addBaseLayer(layer, layerName);
|
||||
}
|
||||
this._loadTable();
|
||||
this._sync();
|
||||
return layer;
|
||||
} catch (e) {
|
||||
console.warn("Oops:", e);
|
||||
return
|
||||
}
|
||||
},
|
||||
|
||||
_sync: function() {
|
||||
if (BR.Util.localStorageAvailable()) {
|
||||
localStorage.setItem("map/customLayers", JSON.stringify(this._customLayers, function(k, v) {
|
||||
// dont write Leaflet.Layer in localStorage; simply keep the URL
|
||||
return v._url || v;
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -17,9 +17,10 @@ BR.RoutingOptions = BR.Control.extend({
|
|||
return BR.Control.prototype.onAdd.call(this, map);
|
||||
},
|
||||
|
||||
getOptions: 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
|
||||
|
|
@ -36,8 +37,13 @@ BR.RoutingOptions = BR.Control.extend({
|
|||
if (custom.value === "Custom") {
|
||||
custom.disabled = true;
|
||||
}
|
||||
|
||||
$('.selectpicker').selectpicker('refresh')
|
||||
},
|
||||
|
||||
getOptions: function() {
|
||||
var profile = $('#profile option:selected'),
|
||||
alternative = $('#alternative option:selected');
|
||||
this.refreshUI();
|
||||
|
||||
return {
|
||||
profile: profile.val(),
|
||||
|
|
@ -46,21 +52,19 @@ BR.RoutingOptions = BR.Control.extend({
|
|||
},
|
||||
|
||||
setOptions: function(options) {
|
||||
var profiles_grp,
|
||||
profile = options.profile;
|
||||
|
||||
if (profile) {
|
||||
profiles_grp = L.DomUtil.get('profile');
|
||||
profiles_grp.value = profile;
|
||||
var values = [
|
||||
options.profile ? options.profile : $('#profile option:selected').val(),
|
||||
options.alternative ? options.alternative : $('#alternative option:selected').val()
|
||||
];
|
||||
$('.selectpicker').selectpicker('val', values);
|
||||
this.refreshUI();
|
||||
|
||||
if (options.profile) {
|
||||
// profile got not selected = not in option values -> custom profile passed with permalink
|
||||
if (profiles_grp.value != profile) {
|
||||
this.setCustomProfile(profile, true);
|
||||
if (L.DomUtil.get('profile').value != options.profile) {
|
||||
this.setCustomProfile(options.profile, true);
|
||||
}
|
||||
}
|
||||
if (options.alternative) {
|
||||
L.DomUtil.get('alternative').value = options.alternative;
|
||||
}
|
||||
},
|
||||
|
||||
setCustomProfile: function(profile, noUpdate) {
|
||||
|
|
|
|||
111
js/index.js
111
js/index.js
|
|
@ -11,6 +11,7 @@
|
|||
function initApp(mapContext) {
|
||||
var map = mapContext.map,
|
||||
layersControl = mapContext.layersControl,
|
||||
mapLayers = mapContext.layers,
|
||||
search,
|
||||
router,
|
||||
routing,
|
||||
|
|
@ -26,7 +27,7 @@
|
|||
drawButton,
|
||||
deleteButton,
|
||||
drawToolbar,
|
||||
permalink,
|
||||
urlHash,
|
||||
saveWarningShown = false;
|
||||
|
||||
// By default bootstrap-select use glyphicons
|
||||
|
|
@ -70,7 +71,7 @@
|
|||
if (result) {
|
||||
routing.clear();
|
||||
onUpdate();
|
||||
permalink._update_routing();
|
||||
urlHash.onMapMove();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -222,6 +223,7 @@
|
|||
var sidebar = L.control.sidebar('sidebar', {
|
||||
position: 'left'
|
||||
});
|
||||
sidebar.id = 'sidebar-control'; //required for persistence in local storage
|
||||
map.addControl(sidebar);
|
||||
|
||||
nogos.addTo(map);
|
||||
|
|
@ -229,29 +231,70 @@
|
|||
callback: L.bind(routing.setOpacity, routing)
|
||||
}));
|
||||
|
||||
// initial option settings (after controls are added and initialized with onAdd, before permalink)
|
||||
// initial option settings (after controls are added and initialized with onAdd)
|
||||
router.setOptions(nogos.getOptions());
|
||||
router.setOptions(routingOptions.getOptions());
|
||||
profile.update(routingOptions.getOptions());
|
||||
|
||||
permalink = new L.Control.Permalink({
|
||||
text: 'Permalink',
|
||||
position: 'bottomright',
|
||||
layers: layersControl,
|
||||
routingOptions: routingOptions,
|
||||
nogos: nogos,
|
||||
router: router,
|
||||
routing: routing,
|
||||
profile: profile
|
||||
}).addTo(map);
|
||||
var onHashChangeCb = function(url) {
|
||||
var url2params = function (s) {
|
||||
var p = {};
|
||||
var sep = '&';
|
||||
if (s.search('&') !== -1)
|
||||
sep = '&';
|
||||
var params = s.split(sep);
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var tmp = params[i].split('=');
|
||||
if (tmp.length !== 2) continue;
|
||||
p[tmp[0]] = decodeURIComponent(tmp[1]);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
if (url == null) return;
|
||||
var opts = router.parseUrlParams(url2params(url));
|
||||
router.setOptions(opts);
|
||||
routingOptions.setOptions(opts);
|
||||
nogos.setOptions(opts);
|
||||
profile.update(opts);
|
||||
|
||||
// FIXME permalink temporary hack
|
||||
$('#permalink').on('click', function() {
|
||||
$('#permalink-input').val($('.leaflet-control-permalink a')[0].href)
|
||||
})
|
||||
$('#permalink-input').on('click', function() {
|
||||
$(this).select()
|
||||
})
|
||||
if (opts.lonlats) {
|
||||
routing.draw(false);
|
||||
routing.clear();
|
||||
routing.setWaypoints(opts.lonlats);
|
||||
}
|
||||
};
|
||||
|
||||
var onInvalidHashChangeCb = function(params) {
|
||||
params = params.replace('zoom=', 'map=');
|
||||
params = params.replace('&lat=', '/');
|
||||
params = params.replace('&lon=', '/');
|
||||
params = params.replace('&layer=', '/');
|
||||
return params;
|
||||
};
|
||||
|
||||
// do not initialize immediately
|
||||
urlHash = new L.Hash(null, null);
|
||||
urlHash.additionalCb = function() {
|
||||
var url = router.getUrl(routing.getWaypoints(), null).substr('brouter?'.length+1);
|
||||
return url.length > 0 ? '&' + url : null;
|
||||
};
|
||||
urlHash.onHashChangeCb = onHashChangeCb;
|
||||
urlHash.onInvalidHashChangeCb = onInvalidHashChangeCb;
|
||||
urlHash.layers = mapLayers;
|
||||
urlHash.map = map;
|
||||
urlHash.init(map, mapLayers);
|
||||
|
||||
routingOptions.on('update', urlHash.onMapMove, urlHash);
|
||||
nogos.on('update', urlHash.onMapMove, urlHash);
|
||||
// waypoint add, move, delete (but last)
|
||||
routing.on('routing:routeWaypointEnd', urlHash.onMapMove, urlHash);
|
||||
// delete last waypoint
|
||||
routing.on('waypoint:click', function (evt) {
|
||||
var r = evt.marker._routing;
|
||||
if (!r.prevMarker && !r.nextMarker) {
|
||||
urlHash.onMapMove();
|
||||
}
|
||||
}, urlHash);
|
||||
|
||||
$(window).resize(function () {
|
||||
elevation.addBelow(map);
|
||||
|
|
@ -266,10 +309,34 @@
|
|||
map._onResize();
|
||||
});
|
||||
|
||||
$('#sidebar-btn').on('click', function (event) {
|
||||
var onHide = function() {
|
||||
if (this.id && BR.Util.localStorageAvailable()) {
|
||||
localStorage[this.id] = 'true';
|
||||
}
|
||||
};
|
||||
var onShow = function() {
|
||||
if (this.id && BR.Util.localStorageAvailable()) {
|
||||
localStorage.removeItem(this.id);
|
||||
}
|
||||
};
|
||||
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
|
||||
$('.collapse').on('hidden.bs.collapse', onHide)
|
||||
.on('shown.bs.collapse', onShow)
|
||||
.each(function() {
|
||||
if (!(this.id && BR.Util.localStorageAvailable() && localStorage[this.id] === 'true' )) {
|
||||
$(this).collapse('hide');
|
||||
}
|
||||
});
|
||||
if (BR.Util.localStorageAvailable() && localStorage[sidebar.id] !== 'true') {
|
||||
toggleSidebar();
|
||||
}
|
||||
}
|
||||
|
||||
mapContext = BR.Map.initMap();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
BR.BingLayer = L.BingLayer.extend({
|
||||
options: {
|
||||
maxZoom: 19,
|
||||
attribution: '<a target="_blank" href="http://www.bing.com/maps/">Bing Maps</a>'
|
||||
+ ' (<a target="_blank" href="http://go.microsoft.com/?linkid=9710837">TOU</a>)'
|
||||
attribution: '<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) {
|
||||
|
|
@ -11,7 +11,7 @@ BR.BingLayer = L.BingLayer.extend({
|
|||
this._logo = L.control({position: 'bottomleft'});
|
||||
this._logo.onAdd = function (map) {
|
||||
this._div = L.DomUtil.create('div', 'bing-logo');
|
||||
this._div.innerHTML = '<img src="http://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;
|
||||
};
|
||||
},
|
||||
|
|
|
|||
|
|
@ -60,8 +60,8 @@ BR.NogoAreas = L.Control.Draw.extend({
|
|||
|
||||
setOptions: function(options) {
|
||||
var nogos = options.nogos;
|
||||
this.drawnItems.clearLayers();
|
||||
if (nogos) {
|
||||
this.drawnItems.clearLayers();
|
||||
for (var i = 0; i < nogos.length; i++) {
|
||||
this.drawnItems.addLayer(nogos[i]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
//#include "Permalink.js
|
||||
|
||||
// patch to not encode URL (beside 'layer', better readable/hackable, Browser can handle)
|
||||
L.Control.Permalink.include({
|
||||
_update_href: function () {
|
||||
//var params = L.Util.getParamString(this._params);
|
||||
var params = this.getParamString(this._params);
|
||||
|
||||
var sep = '?';
|
||||
if (this.options.useAnchor) sep = '#';
|
||||
var url = this._url_base + sep + params.slice(1);
|
||||
if (this._href) this._href.setAttribute('href', url);
|
||||
if (this.options.useLocation)
|
||||
location.replace('#' + params.slice(1));
|
||||
return url;
|
||||
},
|
||||
|
||||
getParamString: function (obj, existingUrl, uppercase) {
|
||||
var params = [];
|
||||
for (var i in obj) {
|
||||
// do encode layer (e.g. spaces)
|
||||
if (i === 'layer') {
|
||||
params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i]));
|
||||
} else {
|
||||
params.push(uppercase ? i.toUpperCase() : i + '=' + obj[i]);
|
||||
}
|
||||
}
|
||||
return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&');
|
||||
}
|
||||
});
|
||||
|
||||
// patch: no animation when setting the map view, strange effects with nogo circles
|
||||
L.Control.Permalink.include({
|
||||
_set_center: function(e)
|
||||
{
|
||||
//console.info('Update center', e);
|
||||
var params = e.params;
|
||||
if (params.zoom === undefined ||
|
||||
params.lat === undefined ||
|
||||
params.lon === undefined) return;
|
||||
this._map.setView(new L.LatLng(params.lat, params.lon), params.zoom, { reset: true });
|
||||
}
|
||||
});
|
||||
|
||||
L.Control.Permalink.include({
|
||||
|
||||
initialize_routing: function () {
|
||||
this.on('update', this._set_routing, this);
|
||||
this.on('add', this._onadd_routing, this);
|
||||
},
|
||||
|
||||
_onadd_routing: function (e) {
|
||||
this.options.routingOptions.on('update', this._update_routing, this);
|
||||
this.options.nogos.on('update', this._update_routing, this);
|
||||
// waypoint add, move, delete (but last)
|
||||
this.options.routing.on('routing:routeWaypointEnd', this._update_routing, this);
|
||||
// delete last waypoint
|
||||
this.options.routing.on('waypoint:click', function (evt) {
|
||||
var r = evt.marker._routing;
|
||||
if (!r.prevMarker && !r.nextMarker) {
|
||||
this._update_routing(evt);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
_update_routing: function (evt) {
|
||||
var router = this.options.router,
|
||||
routing = this.options.routing,
|
||||
routingOptions = this.options.routingOptions,
|
||||
latLngs = routing.getWaypoints(),
|
||||
params = router.getUrlParams(latLngs);
|
||||
|
||||
if (evt && evt.options) {
|
||||
router.setOptions(evt.options);
|
||||
}
|
||||
|
||||
// don't permalink to custom profile, as these are only stored temporarily
|
||||
if (params.profile && params.profile === routingOptions.getCustomProfile()) {
|
||||
params.profile = null;
|
||||
}
|
||||
|
||||
this._update(params);
|
||||
//console.log('permalink: ' + this._href.href);
|
||||
},
|
||||
|
||||
_set_routing: function (e) {
|
||||
var router = this.options.router,
|
||||
routing = this.options.routing,
|
||||
routingOptions = this.options.routingOptions,
|
||||
nogos = this.options.nogos,
|
||||
profile = this.options.profile;
|
||||
|
||||
var opts = router.parseUrlParams(e.params);
|
||||
router.setOptions(opts);
|
||||
routingOptions.setOptions(opts);
|
||||
nogos.setOptions(opts);
|
||||
profile.update(opts);
|
||||
|
||||
if (opts.lonlats) {
|
||||
routing.draw(false);
|
||||
routing.clear();
|
||||
routing.setWaypoints(opts.lonlats);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -152,7 +152,7 @@ BR.Routing = L.Routing.extend({
|
|||
// 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.
|
||||
// SVG simple alpha compositing: Ca' = 1 - (1 - Ea) * (1 - Ca)
|
||||
// http://www.w3.org/TR/SVG11/masking.html#SimpleAlphaBlending
|
||||
// https://www.w3.org/TR/SVG11/masking.html#SimpleAlphaBlending
|
||||
var sourceOpacity = 1 - Math.sqrt(1 - opacity);
|
||||
|
||||
this.options.styles.track.opacity = sourceOpacity;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ BR.Search = L.Control.Geocoder.extend({
|
|||
|
||||
onAdd: function (map) {
|
||||
map.attributionControl.addAttribution(
|
||||
'search by <a href="http://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Nominatim</a>');
|
||||
'search by <a href="https://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Nominatim</a>');
|
||||
|
||||
return L.Control.Geocoder.prototype.onAdd.call(this, map);
|
||||
},
|
||||
|
|
|
|||
239
js/plugin/leaflet-fullHash.js
Normal file
239
js/plugin/leaflet-fullHash.js
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
(function(window) {
|
||||
var HAS_HASHCHANGE = (function() {
|
||||
var doc_mode = window.documentMode;
|
||||
return ('onhashchange' in window) &&
|
||||
(doc_mode === undefined || doc_mode > 7);
|
||||
})();
|
||||
|
||||
L.Hash = function(map, options) {
|
||||
this.onHashChange = L.Util.bind(this.onHashChange, this);
|
||||
|
||||
if (map) {
|
||||
this.init(map, options);
|
||||
}
|
||||
};
|
||||
|
||||
L.Hash.parseHash = function(hash) {
|
||||
if(hash.indexOf('#map=') === 0) {
|
||||
hash = hash.substr(5);
|
||||
}
|
||||
var args = hash.split(/\&(.+)/);
|
||||
var mapsArgs = args[0].split("/");
|
||||
if (mapsArgs.length == 4) {
|
||||
var zoom = parseInt(mapsArgs[0], 10),
|
||||
lat = parseFloat(mapsArgs[1]),
|
||||
lon = parseFloat(mapsArgs[2]),
|
||||
layers = decodeURIComponent(mapsArgs[3]).split('-'),
|
||||
additional = args[1];
|
||||
if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
|
||||
return false;
|
||||
} else {
|
||||
return {
|
||||
center: new L.LatLng(lat, lon),
|
||||
zoom: zoom,
|
||||
layers: layers,
|
||||
additional: additional
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
L.Hash.formatHash = function(map) {
|
||||
var center = map.getCenter(),
|
||||
zoom = map.getZoom(),
|
||||
precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2)),
|
||||
layers = [];
|
||||
|
||||
//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 = [
|
||||
zoom,
|
||||
center.lat.toFixed(precision),
|
||||
center.lng.toFixed(precision),
|
||||
encodeURIComponent(layers.join("-"))
|
||||
];
|
||||
url = "#map=" + params.join("/");
|
||||
if (this.additionalCb != null) {
|
||||
var additional = this.additionalCb();
|
||||
if (additional != null) {
|
||||
return url + additional;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
|
||||
L.Hash.prototype = {
|
||||
map: null,
|
||||
lastHash: null,
|
||||
|
||||
parseHash: L.Hash.parseHash,
|
||||
formatHash: L.Hash.formatHash,
|
||||
|
||||
init: function(map, options) {
|
||||
this.map = map;
|
||||
L.Util.setOptions(this, options);
|
||||
|
||||
// reset the hash
|
||||
this.lastHash = null;
|
||||
this.onHashChange();
|
||||
|
||||
if (!this.isListening) {
|
||||
this.startListening();
|
||||
}
|
||||
},
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parsed) {
|
||||
this.movingMap = true;
|
||||
|
||||
this.map.setView(parsed.center, parsed.zoom);
|
||||
var layers = parsed.layers,
|
||||
options = this.options,
|
||||
that = this;
|
||||
//Add/remove layer
|
||||
this.map.eachLayer(function(layer) {
|
||||
for (alayer in that.layers) {
|
||||
if (that.layers[alayer] == layer) {
|
||||
that.map.removeLayer(layer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
var added = false;
|
||||
layers.forEach(function(element, index, array) {
|
||||
if (element in options) {
|
||||
added = true;
|
||||
that.map.addLayer(options[element]);
|
||||
}
|
||||
});
|
||||
if (!added) {
|
||||
// if we couldn't add layers (custom ones or invalid name), add the default one
|
||||
this.map.addLayer(options[Object.keys(options)[0]]);
|
||||
}
|
||||
|
||||
if (this.onHashChangeCb != null) {
|
||||
this.onHashChangeCb(parsed.additional);
|
||||
}
|
||||
|
||||
this.movingMap = false;
|
||||
} else {
|
||||
this.onMapMove(this.map);
|
||||
}
|
||||
},
|
||||
|
||||
// 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) {
|
||||
return new L.Hash(map, options);
|
||||
};
|
||||
L.Map.prototype.addHash = function() {
|
||||
this._hash = L.hash(this, this.options);
|
||||
};
|
||||
L.Map.prototype.removeHash = function() {
|
||||
this._hash.removeFrom();
|
||||
};
|
||||
})(window);
|
||||
|
|
@ -2,19 +2,17 @@ L.BRouter = L.Class.extend({
|
|||
statics: {
|
||||
// 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
|
||||
URL_TEMPLATE: BR.conf.host + '/brouter?lonlats={lonlats}&nogos={nogos}&profile={profile}&alternativeidx={alternativeidx}&format={format}',
|
||||
URL_TEMPLATE: '/brouter?lonlats={lonlats}&nogos={nogos}&profile={profile}&alternativeidx={alternativeidx}&format={format}',
|
||||
URL_PROFILE_UPLOAD: BR.conf.host + '/brouter/profile',
|
||||
PRECISION: 6,
|
||||
NUMBER_SEPARATOR: ',',
|
||||
GROUP_SEPARATOR: '|',
|
||||
ABORTED_ERROR: 'aborted'
|
||||
},
|
||||
|
||||
|
||||
options: {
|
||||
},
|
||||
|
||||
format: 'geojson',
|
||||
|
||||
initialize: function (options) {
|
||||
L.setOptions(this, options);
|
||||
|
||||
|
|
@ -38,13 +36,30 @@ L.BRouter = L.Class.extend({
|
|||
},
|
||||
|
||||
getUrlParams: function(latLngs, format) {
|
||||
return {
|
||||
lonlats: this._getLonLatsString(latLngs),
|
||||
nogos: this._getNogosString(this.options.nogos),
|
||||
profile: this.options.profile,
|
||||
alternativeidx: this.options.alternative,
|
||||
format: format || this.format
|
||||
};
|
||||
params = {};
|
||||
|
||||
if (this._getLonLatsString(latLngs) != null)
|
||||
params.lonlats = this._getLonLatsString(latLngs);
|
||||
|
||||
if (this._getNogosString(this.options.nogos).length > 0)
|
||||
params.nogos = this._getNogosString(this.options.nogos);
|
||||
|
||||
if (this.options.profile != null)
|
||||
params.profile = this.options.profile;
|
||||
|
||||
params.alternativeidx = this.options.alternative;
|
||||
|
||||
if (format != null) {
|
||||
params.format = format;
|
||||
} else {
|
||||
// do not put values in URL if this is the default value (format===null)
|
||||
if (params.profile === BR.conf.profiles[0])
|
||||
delete params.profile;
|
||||
if (params.alternativeidx == 0)
|
||||
delete params.alternativeidx;
|
||||
}
|
||||
|
||||
return params;
|
||||
},
|
||||
|
||||
parseUrlParams: function(params) {
|
||||
|
|
@ -66,12 +81,26 @@ L.BRouter = L.Class.extend({
|
|||
|
||||
getUrl: function(latLngs, format) {
|
||||
var urlParams = this.getUrlParams(latLngs, format);
|
||||
var url = L.Util.template(L.BRouter.URL_TEMPLATE, urlParams);
|
||||
return url;
|
||||
|
||||
var args = []
|
||||
if (urlParams.lonlats != null && urlParams.lonlats.length > 0)
|
||||
args.push(L.Util.template('lonlats={lonlats}', urlParams));
|
||||
if (urlParams.nogos != null)
|
||||
args.push(L.Util.template('nogos={nogos}', urlParams));
|
||||
if (urlParams.profile != null)
|
||||
args.push(L.Util.template('profile={profile}', urlParams));
|
||||
if (urlParams.alternativeidx != null)
|
||||
args.push(L.Util.template('alternativeidx={alternativeidx}', urlParams));
|
||||
if (urlParams.format != null)
|
||||
args.push(L.Util.template('format={format}', urlParams));
|
||||
|
||||
var prepend_host = (format != null);
|
||||
|
||||
return (prepend_host ? BR.conf.host : '') + '/brouter?' + args.join('&');
|
||||
},
|
||||
|
||||
getRoute: function(latLngs, cb) {
|
||||
var url = this.getUrl(latLngs),
|
||||
var url = this.getUrl(latLngs, 'geojson'),
|
||||
xhr = new XMLHttpRequest();
|
||||
|
||||
if (!url) {
|
||||
|
|
@ -199,14 +228,14 @@ L.BRouter = L.Class.extend({
|
|||
if (!s) {
|
||||
return nogos;
|
||||
}
|
||||
|
||||
|
||||
groups = s.split(L.BRouter.GROUP_SEPARATOR);
|
||||
for (var i = 0; i < groups.length; i++) {
|
||||
// lng,lat,radius
|
||||
numbers = groups[i].split(L.BRouter.NUMBER_SEPARATOR);
|
||||
// TODO refactor: pass simple obj, create circle in NogoAreas; use shapeOptions of instance
|
||||
// [lat,lng],radius
|
||||
nogos.push(L.circle([numbers[1], numbers[0]], numbers[2], L.Draw.Circle.prototype.options.shapeOptions));
|
||||
nogos.push(L.circle([numbers[1], numbers[0]], {radius: numbers[2]}));
|
||||
}
|
||||
|
||||
return nogos;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
// COPYING: Please get your own API keys from the sites listed below
|
||||
|
||||
BR.keys = {
|
||||
// Bing maps, http://www.microsoft.com/maps/default.aspx
|
||||
// Bing maps, https://www.microsoft.com/maps/default.aspx
|
||||
bing: '',
|
||||
|
||||
|
||||
// DigitalGlobe, https://developer.digitalglobe.com/maps-api/#plans
|
||||
digitalGlobe: '',
|
||||
|
||||
// Thunderforest, http://thunderforest.com/pricing/
|
||||
// Thunderforest, https://thunderforest.com/pricing/
|
||||
thunderforest: ''
|
||||
};
|
||||
|
||||
10
package.json
10
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "brouter-web",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.3",
|
||||
"description": "Web client for BRouter",
|
||||
"main": "js/index.js",
|
||||
"scripts": {
|
||||
|
|
@ -14,19 +14,25 @@
|
|||
"devDependencies": {
|
||||
"del": "^1.1.1",
|
||||
"gulp": "^3.8.11",
|
||||
"gulp-bump": "^2.7.0",
|
||||
"gulp-cached": "^1.0.4",
|
||||
"gulp-concat": "^2.5.2",
|
||||
"gulp-concat-css": "^2.1.2",
|
||||
"gulp-confirm": "^1.0.6",
|
||||
"gulp-debug": "^2.0.1",
|
||||
"gulp-git": "^2.2.0",
|
||||
"gulp-github-release": "^1.2.1",
|
||||
"gulp-if": "^2.0.0",
|
||||
"gulp-inject": "^1.2.0",
|
||||
"gulp-minify-css": "^1.0.0",
|
||||
"gulp-remember": "^0.3.0",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-replace": "^0.5.4",
|
||||
"gulp-sourcemaps": "^1.5.1",
|
||||
"gulp-tap": "^0.1.3",
|
||||
"gulp-uglify": "^1.1.0",
|
||||
"gulp-if": "^2.0.0",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-zip": "^4.0.0",
|
||||
"main-bower-files": "^2.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue