diff --git a/js/Map.js b/js/Map.js
index ad35450..55a5767 100644
--- a/js/Map.js
+++ b/js/Map.js
@@ -126,7 +126,8 @@ BR.Map = {
return {
map: map,
- layersControl: layersControl
+ layersControl: layersControl,
+ layers: baseLayers
};
}
diff --git a/js/index.js b/js/index.js
index ea174fe..8041867 100644
--- a/js/index.js
+++ b/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.updateHash();
}
}
});
@@ -229,29 +230,60 @@
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);
+ urlHash = new L.Hash(map, mapLayers, function() {
+ var latLngs = routing.getWaypoints();
+ if (latLngs.length > 1) {
+ return router.getUrl(latLngs, null);
+ }
+ return null;
+ });
+ routingOptions.on('update', urlHash.updateHash, urlHash);
+ nogos.on('update', urlHash.updateHash, urlHash);
+ // waypoint add, move, delete (but last)
+ routing.on('routing:routeWaypointEnd', urlHash.updateHash, urlHash);
+ // delete last waypoint
+ routing.on('waypoint:click', function (evt) {
+ var r = evt.marker._routing;
+ if (!r.prevMarker && !r.nextMarker) {
+ urlHash.updateHash();
+ }
+ }, urlHash);
+
+ 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;
+ }
+
+ urlHash.onHashChangeCb = function(url) {
+ if (url == null) return;
+ url = url.substr('brouter?'.length);
+ var opts = router.parseUrlParams(url2params(url));
+ router.setOptions(opts);
+ routingOptions.setOptions(opts);
+ nogos.setOptions(opts);
+ profile.update(opts);
+
+ if (opts.lonlats) {
+ routing.draw(false);
+ routing.clear();
+ routing.setWaypoints(opts.lonlats);
+ }
+ };
- // FIXME permalink temporary hack
- $('#permalink').on('click', function() {
- $('#permalink-input').val($('.leaflet-control-permalink a')[0].href)
- })
- $('#permalink-input').on('click', function() {
- $(this).select()
- })
$(window).resize(function () {
elevation.addBelow(map);
diff --git a/js/plugin/NogoAreas.js b/js/plugin/NogoAreas.js
index 4570ab3..d7ff4a3 100644
--- a/js/plugin/NogoAreas.js
+++ b/js/plugin/NogoAreas.js
@@ -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]);
}
diff --git a/js/plugin/Permalink.Routing.js b/js/plugin/Permalink.Routing.js
deleted file mode 100644
index 240ddf5..0000000
--- a/js/plugin/Permalink.Routing.js
+++ /dev/null
@@ -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);
- }
- }
-});
diff --git a/js/plugin/leaflet-fullHash.js b/js/plugin/leaflet-fullHash.js
new file mode 100644
index 0000000..9a68404
--- /dev/null
+++ b/js/plugin/leaflet-fullHash.js
@@ -0,0 +1,212 @@
+(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, layers, additionalCb, onHashChangeCb) {
+ this.onHashChange = L.Util.bind(this.onHashChange, this);
+ this.additionalCb = additionalCb;
+ this.onHashChangeCb = onHashChangeCb;
+ if (map) {
+ this.init(map, layers);
+ }
+ };
+
+ L.Hash.parseHash = function(hash) {
+ if(hash.indexOf('#map=') === 0) {
+ hash = hash.substr(5);
+ }
+ var args = hash.split("/");
+ if (args.length >= 4) {
+ var zoom = parseInt(args[0], 10),
+ lat = parseFloat(args[1]),
+ lon = parseFloat(args[2]),
+ layer = args[3];
+ additional = args[4];
+ if (isNaN(zoom) || isNaN(lat) || isNaN(lon)) {
+ return false;
+ } else {
+ return {
+ center: new L.LatLng(lat, lon),
+ zoom: zoom,
+ layer: layer,
+ 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)),
+ layer = null;
+
+ var options = this.options;
+ //Check active layer
+ for(var key in options) {
+ if (options.hasOwnProperty(key)) {
+ if (map.hasLayer(options[key])) {
+ layer = key;
+ break;
+ };
+ };
+ };
+ var params = [
+ zoom,
+ center.lat.toFixed(precision),
+ center.lng.toFixed(precision),
+ layer
+ ];
+ 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;
+ },
+
+ updateHash: function() {
+ // bail if we're moving the map (updating from a hash),
+ // or if the map is not yet loaded
+
+ if (this.isUpdatingHash || !this.map._loaded) {
+ return false;
+ }
+
+ var hash = this.formatHash(this.map);
+ if (this.lastHash != hash) {
+ location.replace(hash);
+ this.lastHash = hash;
+ }
+ },
+
+ isUpdatingHash: false,
+ update: function() {
+ var hash = location.hash;
+ if (hash === this.lastHash) {
+ return;
+ }
+ var parsed = this.parseHash(hash);
+ if (parsed) {
+ this.isUpdatingHash = true;
+
+ this.map.setView(parsed.center, parsed.zoom);
+
+ if (this.onHashChangeCb != null) {
+ this.onHashChangeCb(parsed.additional);
+ }
+
+ var options = this.options,
+ layer = parsed.layer in options ? parsed.layer : Object.keys(options)[0],
+ that = this;
+ //Add/remove layer
+ this.map.eachLayer(function(layer) {
+ that.map.removeLayer(layer);
+ });
+ that.map.addLayer(options[layer]);
+
+ this.isUpdatingHash = false;
+ } else {
+ this.updateHash(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.updateHash, 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.updateHash, 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);
diff --git a/js/router/BRouter.js b/js/router/BRouter.js
index 2ddbf39..6e44699 100644
--- a/js/router/BRouter.js
+++ b/js/router/BRouter.js
@@ -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,26 @@ 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;
+
+ // do not put alternative in URL if it has its default value,
+ // but always set it if we want to generate route because Brouter API requires it.
+ if (this.options.alternative != 0 || format != null)
+ params.alternativeidx = this.options.alternative;
+
+ if (format != null)
+ params.format = format;
+
+ return params;
},
parseUrlParams: function(params) {
@@ -66,12 +77,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)
+ 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 +224,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;