Updating UI to also fit on mobile devices. Fix #34

This commit is contained in:
Gautier Pelloux-Prayer 2016-11-26 20:54:59 +01:00
parent 1e26cb1027
commit d7e476db82
44 changed files with 555 additions and 1305 deletions

View file

@ -4,42 +4,33 @@ BR.Map = {
var map,
layersControl;
L.Icon.Default.imagePath = 'dist/images';
BR.keys = BR.keys || {};
var osmAttribution = '&copy; <a target="_blank" href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors';
var maxZoom = 19;
var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: maxZoom,
attribution: 'tiles ' + osmAttribution
maxZoom: maxZoom
});
var osmde = L.tileLayer('http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
maxZoom: maxZoom,
attribution: 'tiles by <a target="_blank" href="http://openstreetmap.de/karte.html">openstreetmap.de</a> ' + osmAttribution
maxZoom: maxZoom
});
var topo = L.tileLayer('http://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {
maxNativeZoom: 17,
maxZoom: maxZoom,
attribution: 'tiles &copy; <a target="_blank" href="https://opentopomap.org">OpenTopoMap</a>, <a target="_blank" href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>'
+ ', <a target="_blank" href="http://viewfinderpanoramas.org">SRTM</a>'
maxZoom: maxZoom
});
var thunderforestAttribution = 'tiles &copy; <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 cycle = L.tileLayer('http://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
maxZoom: maxZoom,
attribution: thunderforestAttribution
maxZoom: maxZoom
});
var outdoors = L.tileLayer('http://{s}.tile.thunderforest.com/outdoors/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
maxZoom: maxZoom,
attribution: thunderforestAttribution
maxZoom: maxZoom
});
var esri = L.tileLayer('https://{s}.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
@ -53,17 +44,13 @@ BR.Map = {
var cycling = L.tileLayer('http://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
maxZoom: maxZoom,
opacity: 0.7,
attribution: 'Cycling &copy; <a target="_blank" href="http://cycling.waymarkedtrails.org">Waymarked Trails</a> '
+ '(<a target="_blank" href="http://creativecommons.org/licenses/by-sa/3.0/de/deed.en">CC-BY-SA 3.0 DE</a>)'
maxZoom: maxZoom
});
var hiking = L.tileLayer('http://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png', {
maxNativeZoom: 18,
maxZoom: maxZoom,
opacity: 0.7,
attribution: 'Hiking &copy; <a target="_blank" href="http://hiking.waymarkedtrails.org">Waymarked Trails</a> '
+ '(<a target="_blank" href="http://creativecommons.org/licenses/by-sa/3.0/de/deed.en">CC-BY-SA 3.0 DE</a>)'
maxZoom: maxZoom
});
map = new L.Map('map', {
@ -72,10 +59,9 @@ BR.Map = {
if (!map.restoreView()) {
map.setView([50.99, 9.86], 6);
}
map.attributionControl.addAttribution(
'<a href="http://brouter.de/brouter" target="_blank">BRouter</a> &copy; Arndt Brenschede, '
+ 'routing + map data &copy; <a target="_blank" href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors '
+ '(<a target="_blank" href="http://opendatacommons.org/licenses/odbl/">ODbL</a>)');
map.attributionControl.setPrefix(false);
map.attributionControl.addAttribution('<a href="" data-toggle="modal" data-target="#credits">Copyright & credits</a>')
var baseLayers = {
'OpenStreetMap': osm,

View file

@ -1,6 +1,6 @@
BR.Control = L.Control.extend({
options: {
position: 'leftpane'
position: 'bottomleft'
},
onAdd: function (map) {

View file

@ -1,22 +1,16 @@
BR.Download = BR.Control.extend({
options: {
heading: 'Download'
},
onAdd: function (map) {
var container = BR.Control.prototype.onAdd.call(this, map);
return container;
},
BR.Download = L.Class.extend({
update: function (urls) {
var html = '<div class="value">';
if (urls.gpx) {
html += '<a href="' + urls.gpx + '" download="brouter.gpx">GPX</a> &middot; ';
html += '<a href="' + urls.kml + '" download="brouter.kml">KML</a> &middot; ';
html += '<a href="' + urls.geojson + '" download="brouter.geojson">GeoJSON</a> &middot; ';
html += '<a href="' + urls.csv + '" download="brouter.tsv">data CSV</a>';
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');
})
}
html += '</div>';
this._content.innerHTML = html;
}
});
BR.download = function() {
return new BR.Download();
};

View file

@ -1,15 +1,10 @@
BR.Profile = L.Class.extend({
options: {
heading: 'Profile'
},
cache: {},
initialize: function () {
L.DomUtil.get('upload').onclick = L.bind(this._upload, this);
L.DomUtil.get('clear').onclick = L.bind(this.clear, this);
this.ele = document.profile_upload.profile;
this.ele = L.DomUtil.get('profile_upload');
this.message = new BR.Message('profile_message', {
alert: true
});
@ -65,8 +60,8 @@ BR.Profile = L.Class.extend({
$(button).button('uploading');
evt.preventDefault();
this.fire('update', {
profileText: profile,
this.fire('update', {
profileText: profile,
callback: function () {
$(button).button('reset');
$(button).blur();

View file

@ -1,51 +1,62 @@
BR.RoutingOptions = BR.Control.extend({
options: {
heading: 'Options',
divId: 'route_options'
},
onAdd: function (map) {
var select = L.DomUtil.get('profile'),
i,
option;
select.onchange = this._getChangeHandler();
L.DomUtil.get('alternative').onchange = this._getChangeHandler();
$('#profile-alternative').on('changed.bs.select', this._getChangeHandler());
// build option list from config
var profiles = BR.conf.profiles;
for (i = 0; i < profiles.length; i++) {
option = document.createElement("option");
var profiles_list = L.DomUtil.get('profile');
for (var i = 0; i < profiles.length; i++) {
var option = document.createElement("option");
option.value = profiles[i];
option.text = profiles[i];
select.add(option, null);
profiles_list.appendChild(option);
}
// <custom> option disabled, select next
select.options[1].selected = true;
// <custom> profile is empty at start, select next one
profiles_list.children[1].selected = true;
return BR.Control.prototype.onAdd.call(this, map);
},
getOptions: 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
// so we disable the current selected items
$('#profile-alternative').find('option:disabled').each(function(index) {
$(this).prop('disabled', false);
});
$('#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")
var custom = L.DomUtil.get('profile').children[0];
if (custom.value === "Custom") {
custom.disabled = true;
}
$('.selectpicker').selectpicker('refresh')
return {
profile: L.DomUtil.get('profile').value,
alternative: L.DomUtil.get('alternative').value
profile: profile.val(),
alternative: alternative.val()
};
},
setOptions: function(options) {
var select,
var profiles_grp,
profile = options.profile;
if (profile) {
select = L.DomUtil.get('profile');
select.value = profile;
profiles_grp = L.DomUtil.get('profile');
profiles_grp.value = profile;
// profile got not selected = not in option values -> custom profile passed with permalink
if (select.value != profile) {
if (profiles_grp.value != profile) {
this.setCustomProfile(profile, true);
}
}
if (options.alternative) {
L.DomUtil.get('alternative').value = options.alternative;
@ -53,13 +64,18 @@ BR.RoutingOptions = BR.Control.extend({
},
setCustomProfile: function(profile, noUpdate) {
var select,
var profiles_grp,
option;
select = L.DomUtil.get('profile');
option = select.options[0]
profiles_grp = L.DomUtil.get('profile');
option = profiles_grp.children[0]
option.value = profile;
option.disabled = !profile;
$('#profile').find('option:selected').each(function(index) {
$(this).prop('selected', false);
});
option.selected = !!profile;
if (!noUpdate) {
@ -68,8 +84,8 @@ BR.RoutingOptions = BR.Control.extend({
},
getCustomProfile: function() {
var select = L.DomUtil.get('profile'),
option = select.options[0],
var profiles_grp = L.DomUtil.get('profile'),
option = profiles_grp.children[0],
profile = null;
if (!option.disabled) {

View file

@ -1,6 +1,6 @@
BR.Tabs = BR.Control.extend({
options: {
divId: 'tabs_div',
divId: 'sidebar',
// tab a.hash > instance
tabs: {}
},

View file

@ -1,7 +1,7 @@
BR.TrackMessages = L.Class.extend({
options: {
heading: 'Data',
heading: 'Segment data',
edgeStyle: {
color: 'yellow',
opacity: 0.8,
@ -60,7 +60,6 @@ BR.TrackMessages = L.Class.extend({
headings = messages[0];
columns = this._getColumns(headings, data);
console.time('datatable');
this._table = $('#datatable').DataTable({
destroy: true,
data: data,
@ -68,9 +67,6 @@ BR.TrackMessages = L.Class.extend({
paging: false,
searching: false,
info: false,
// flexbox workaround: without scrollY height Firefox extends to content height
// (^= minimum height with flexbox?)
scrollY: 50,
scrollX: true,
scrollCollapse: true,
order: []
@ -79,8 +75,6 @@ BR.TrackMessages = L.Class.extend({
// highlight track segment (graph edge) on row hover
this._setEdges(polyline, segments);
$('#datatable tbody tr').hover(L.bind(this._handleHover, this), L.bind(this._handleHoverOut, this));
console.timeEnd('datatable');
},
show: function() {
@ -129,7 +123,7 @@ BR.TrackMessages = L.Class.extend({
_getEmptyColumns: function(data) {
var empty = new Array(data[0].length),
i;
for (i = 0; i < empty.length; i++) {
empty[i] = true;
}
@ -146,7 +140,7 @@ BR.TrackMessages = L.Class.extend({
_getMessageLatLng: function(message) {
var lon = message[0] / 1000000,
lat = message[1] / 1000000;
return L.latLng(lat, lon);
},

View file

@ -1,30 +1,13 @@
BR.TrackStats = BR.Control.extend({
options: {
heading: 'Route'
},
onAdd: function (map) {
var container = BR.Control.prototype.onAdd.call(this, map);
this.update();
return container;
},
BR.TrackStats = L.Class.extend({
update: function (polyline, segments) {
var stats = this.calcStats(polyline, segments),
length1 = L.Util.formatNum(stats.trackLength/1000,1),
length3 = L.Util.formatNum(stats.trackLength/1000,3),
meanCostFactor = stats.trackLength ? L.Util.formatNum(stats.cost / stats.trackLength, 2) : '',
html = '';
meanCostFactor = stats.trackLength ? L.Util.formatNum(stats.cost / stats.trackLength, 2) : ''
html += '<table id="stats">';
html += '<tr><td>Length: </td><td title="' + length3 + ' km">' + length1 + '</td><td>km</td></tr>';
html += '<tr><td>Ascent filtered:</td><td>' + stats.filteredAscend + '</td><td>m</td></tr>';
html += '<tr><td>Ascent plain:</td><td>' + stats.plainAscend + '</td><td>m</td></tr>';
html += '<tr><td>Cost: </td><td>' + stats.cost + '</td><td></td></tr>';
html += '<tr><td>Mean cost:</td><td>' + meanCostFactor + '</td><td></td></tr>';
html += '</table>';
this._content.innerHTML = html;
$('#distance').html(length1 + ' <abbr title="kilometer">km</abbr>');
$('#ascend').html(stats.filteredAscend + ' (' + stats.plainAscend +')' + ' <abbr title="meter">m</abbr>');
$('#cost').html(stats.cost + ' (' + meanCostFactor + ')');
},
calcStats: function(polyline, segments) {

View file

@ -27,18 +27,13 @@
deleteButton,
drawToolbar,
permalink,
leftPaneId = 'leftpane',
saveWarningShown = false;
// left sidebar as additional control position
map._controlCorners[leftPaneId] = L.DomUtil.create('div', 'leaflet-' + leftPaneId, map._controlContainer);
document.getElementById('about_link').onclick = function() {
bootbox.alert({
title: 'About',
message: document.getElementById('about').innerHTML
});
};
// By default bootstrap-select use glyphicons
$('.selectpicker').selectpicker({
iconBase: 'fa',
tickIcon: 'fa-check'
});
search = new BR.Search();
map.addControl(search);
@ -53,7 +48,7 @@
routing.draw(false);
control.state('activate-draw');
},
title: 'Stop drawing route'
title: 'Stop drawing route (ESC key)'
}, {
stateName: 'activate-draw',
icon: 'fa-pencil',
@ -61,7 +56,7 @@
routing.draw(true);
control.state('deactivate-draw');
},
title: 'Draw route'
title: 'Draw route (D key)'
}]
});
@ -119,6 +114,7 @@
}
download = new BR.Download();
elevation = new BR.Elevation();
profile = new BR.Profile();
profile.on('update', function(evt) {
BR.message.hide();
@ -207,23 +203,14 @@
download.update(urls);
};
if (!BR.conf.transit) {
map.addControl(new BR.Control({
heading: '',
divId: 'header'
}));
}
routingOptions.addTo(map);
if (!BR.conf.transit) {
stats.addTo(map);
}
download.addTo(map);
elevation.addTo(map);
routing.addTo(map);
elevation.addBelow(map);
tabs = new BR.Tabs({
tabs: {
'#tab_itinerary': itinerary,
'#tab_profile': profile,
'#tab_data': trackMessages
}
});
@ -232,8 +219,12 @@
}
map.addControl(tabs);
var sidebar = L.control.sidebar('sidebar', {
position: 'left'
});
map.addControl(sidebar);
nogos.addTo(map);
routing.addTo(map);
map.addControl(new BR.OpacitySlider({
callback: L.bind(routing.setOpacity, routing)
}));
@ -253,6 +244,32 @@
routing: routing,
profile: profile
}).addTo(map);
// 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);
});
$('#elevation-chart').on('show.bs.collapse', function () {
$('#elevation-btn').addClass('active');
});
$('#elevation-chart').on('hidden.bs.collapse', function () {
$('#elevation-btn').removeClass('active');
// we must fetch tiles that are located behind elevation-chart
map._onResize();
});
$('#sidebar-btn').on('click', function (event) {
sidebar.toggle();
$('#sidebar-btn').toggleClass('active');
});
}
mapContext = BR.Map.initMap();

View file

@ -1,7 +1,6 @@
BR.Elevation = L.Control.Elevation.extend({
options: {
position: "leftpane",
width: 385,
width:$('#map').outerWidth(),
margins: {
top: 20,
right: 30,
@ -11,6 +10,23 @@ BR.Elevation = L.Control.Elevation.extend({
theme: "steelblue-theme" //purple
},
addBelow: function(map) {
// waiting for https://github.com/MrMufflon/Leaflet.Elevation/pull/66
// this.width($('#map').outerWidth());
this.options.width = $('#map').outerWidth();
if (this.getContainer() != null) {
this.remove(map);
}
function setParent(el, newParent) {
newParent.appendChild(el);
}
this.addTo(map);
// move elevation graph outside of the map
setParent(this.getContainer(), document.getElementById('elevation-chart'));
},
update: function(track, layer) {
this.clear();

View file

@ -44,7 +44,7 @@ BR.Routing = L.Routing.extend({
this._segmentOnMouseover(e);
this._suspended = false;
}, this._edit);
this._edit._mouseMarker.setIcon(L.divIcon({
className: 'line-mouse-marker'
,iconAnchor: [8, 8] // size/2 + border/2
@ -77,7 +77,7 @@ BR.Routing = L.Routing.extend({
// prevent cursor marker from consuming mouse events (invisible with draw:false)
this._marker._icon.style.pointerEvents = 'none';
// 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)
this._parent.off('waypoint:mouseout' , this._catchWaypointEvent, this);
this.on('waypoint:mouseout' , function(e) {
@ -115,9 +115,9 @@ BR.Routing = L.Routing.extend({
});
// Call show after deleting last waypoint, but hide trailer.
// Gets hidden in _catchWaypointEvent on waypoint mouseover, but
// mouseout to show again never fires when deleted. Click handler
// _onMouseClick aborts when hidden, so no waypoint can be added
// Gets hidden in _catchWaypointEvent on waypoint mouseover, but
// mouseout to show again never fires when deleted. Click handler
// _onMouseClick aborts when hidden, so no waypoint can be added
// although enabled.
this.on('waypoint:click', function() {
if (this._hidden && !this._parent._waypoints._first) {
@ -196,7 +196,7 @@ BR.Routing = L.Routing.extend({
this._removeMarkerEvents(marker);
current = marker;
};
this._waypoints._first = null;
this._waypoints._last = null;
this._waypoints.clearLayers();
@ -251,8 +251,7 @@ BR.Routing = L.Routing.extend({
// change segment color before request to indicate recalculation (mark old)
if (m1 && m1._routing.nextLine !== null) {
m1._routing.nextLine.options.color = 'dimgray';
m1._routing.nextLine._updateStyle();
m1._routing.nextLine.setStyle({color: 'dimgray' });
}
// animate dashed trailer as loading indicator

View file

@ -6,7 +6,7 @@ BR.Search = L.Control.Geocoder.extend({
position: 'topleft'
},
onAdd: function (map) {
onAdd: function (map) {
map.attributionControl.addAttribution(
'search by <a href="http://wiki.openstreetmap.org/wiki/Nominatim" target="_blank">Nominatim</a>');
@ -14,19 +14,19 @@ BR.Search = L.Control.Geocoder.extend({
},
markGeocode: function(result) {
this._map.fitBounds(result.bbox, {
this._map.fitBounds(result.geocode.bbox, {
maxZoom: 17
});
this.clear();
this._geocodeMarker = new L.CircleMarker(result.center, {
this._geocodeMarker = new L.CircleMarker(result.geocode.center, {
clickable: false,
color: 'red',
opacity: 1,
weight: 3
}).addTo(this._map);
return this;
return this;
},
clear: function() {