From 5b48b6172c9d88d4d3290c37def92efda0a60059 Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Mon, 4 Aug 2014 18:59:32 +0100 Subject: Improve bus route example Also extract the route related classes from leaflet-soton.js, in preparation to make a leaflet-route plugin. The new bus route example uses the leaflet-sidebar plugin, as this works better on multiple screen sizes. --- examples/bus.html | 5 + resources/leaflet-route/leaflet-route.js | 318 ++++++++++++++++++++++++++ src/leaflet-soton.js | 373 ++++--------------------------- 3 files changed, 364 insertions(+), 332 deletions(-) create mode 100644 resources/leaflet-route/leaflet-route.js diff --git a/examples/bus.html b/examples/bus.html index a936262..17daae9 100644 --- a/examples/bus.html +++ b/examples/bus.html @@ -5,6 +5,7 @@ + @@ -23,12 +24,16 @@
+ + + + diff --git a/resources/leaflet-route/leaflet-route.js b/resources/leaflet-route/leaflet-route.js new file mode 100644 index 0000000..501a620 --- /dev/null +++ b/resources/leaflet-route/leaflet-route.js @@ -0,0 +1,318 @@ + +L.Route = L.FeatureGroup.extend({ + initialize: function (routes, stops, options) { + L.setOptions(this, options); + + this._layers = {}; + + this._routeMasters = {}; + + for (var i in routes.features) { + var route = routes.features[i]; + + if ("routeMaster" in route.properties) { + var routeMaster = route.properties.routeMaster; + + if (routeMaster in this._routeMasters) { + this._routeMasters[routeMaster].routes.push(route); + } else { + this._routeMasters[routeMaster] = { + name: routeMaster, + routes: [ route ] + }; + } + } else { + this._routeMasters[route.properties.ref] = { + name: route.properties.ref, + routes: [ route ] + }; + } + } + + this._routes = routes; + this._busStops = {}; + + for (i in stops.features) { + var busStop = stops.features[i]; + + this._busStops[busStop.properties.uri] = busStop; + } + + var routeLayers = this._routeLayers = {} + this._routeLayer = new L.GeoJSON(routes, { + onEachFeature: function(feature, layer) { + routeLayers[feature.properties.name] = layer; + + if (options.routeOptions.onEachFeature) { + options.routeOptions.onEachFeature(feature, layer); + } + }, + style: options.routeOptions.style + }); + this.addLayer(this._routeLayer); + + var stopLayers = this._stopLayers = {}; + this._stopLayer = new L.GeoJSON(stops, { + onEachFeature: function(feature, layer) { + stopLayers[feature.properties.uri] = layer; + + if (options.stopOptions.onEachFeature) { + options.stopOptions.onEachFeature(feature, layer); + } + }, + pointToLayer: function (feature, latlng) { + return L.circleMarker(latlng, { + radius: 8, + opacity: 0, + }); + }, + style: function(feature) { + return { + radius: 8, + opacity: 0, + fillOpacity: 0 + }; + } + }); + this.addLayer(this._stopLayer); + }, + getRouteMasters: function() { + return this._routeMasters; + }, + getRoutesForRouteMaster: function(id) { + return this._routeMasters[id]; + }, + getRoutes: function() { + return this._routes; + }, + getStops: function() { + return this._busStops; + }, + highlightRoute: function(id) { + var layer = this._routeLayers[id]; + + layer.setStyle({ + weight: 10, + opacity: 1, + }); + + layer.setText('\u25BA', { + repeat: true, + offset: 3, + attributes: { + fill: 'black', + fontSize: 24 + } + }); + + layer.bringToFront(); + }, + resetRoute: function(id) { + var layer = this._routeLayers[id]; + + this._routeLayer.resetStyle(layer); + + layer.setText(null); + }, + resetRoutes: function() { + Object.keys(this._routeLayers).forEach(this.resetRoute, this); + }, + highlightStop: function(id) { + var layer = this._stopLayers[id]; + + layer.setStyle({ + color: '#ff0000', + opacity: 1, + fillOpacity: 0.3, + radius: 16, + stroke: true, + fill: true + }); + }, + resetStop: function(id) { + var layer = this._stopLayers[id]; + + this._stopLayer.resetStyle(layer); + }, + panToStop: function(id, panOptions) { + var layer = this._stopLayers[id]; + + this._map.panTo(layer._latlng, panOptions); + } +}); + +L.Control.Route = L.Control.Sidebar.extend({ + initialize: function (routeLayer, placeholder, options) { + L.Control.Sidebar.prototype.initialize.call(this, placeholder, options); + L.setOptions(this, options); + + this._routeLayer = routeLayer; + + var title = L.DomUtil.create('div', 'ls-routes-title', this._contentContainer); + title.textContent = "University Bus Routes"; + + var routeMasters = this._routeLayer.getRouteMasters(); + + var routeMasterRouteLists = {}; + + var ul = L.DomUtil.create('ul', 'ls-route-master-list', this._contentContainer); + + var routeMasterNames = Object.keys(routeMasters); + if (this.options.routeMasterSort) { + routeMasterNames.sort(this.options.routeMasterSort); + } + + for (var i in routeMasterNames) { + var routeMasterName = routeMasterNames[i]; + var routeMaster = routeMasters[routeMasterName]; + + var li = L.DomUtil.create('li', '', ul); + var a = L.DomUtil.create('a', '', li); + + a.style.background = routeMaster.routes[0].properties.colour; + + a.textContent = routeMaster.name; + a.onclick = (function(routeMasterName) { + return function() { + routeMasterRouteLists[activeRouteMaster].style.display = "none"; + routeMasterRouteLists[routeMasterName].style.display = "block"; + activeRouteMaster = routeMasterName; + }; + })(routeMasterName); + + // The route lists + + var routeList = this._createRouteList(routeMaster); + routeMasterRouteLists[routeMasterName] = routeList; + + routeList.style.display = "none"; + + this._contentContainer.appendChild(routeList); + } + + var activeRouteMaster = "U1"; // TODO: Dont hardcode like this + + routeMasterRouteLists[activeRouteMaster].style.display = "block"; + }, + _createRouteList: function(routeMaster) { + var div = L.DomUtil.create('div', null); + + var ul = L.DomUtil.create('ul', 'ls-route-list', div); + + var routeLayer = this._routeLayer; + + var stopLists = {}; + for (var i in routeMaster.routes) { + var route = routeMaster.routes[i]; + + var li = L.DomUtil.create('li', '', ul); + var a = L.DomUtil.create('a', null, li); + + //a.style.borderColor = route.properties.colour; + a.style.background = route.properties.colour; + + a.textContent = route.properties.ref; + a.onclick = (function(routeName) { + return function() { + routeLayer.resetRoutes(); + stopLists[activeStopList].style.display = "none"; + stopLists[routeName].style.display = "block"; + activeStopList = routeName; + routeLayer.highlightRoute(routeName); + }; + })(route.properties.name); + + // The stops + + var stopList = this._createStopList(route); + stopLists[route.properties.name] = stopList; + + stopList.style.display = "none"; + + div.appendChild(stopList); + } + + var activeStopList = routeMaster.routes[0].properties.name; + stopLists[activeStopList].style.display = "block"; + //routeLayer.highlightRoute(activeStopList); + + return div; + }, + _createStopList: function(route) { + var div = L.DomUtil.create('div', null, this._div); + + var description = L.DomUtil.create('div', 'ls-route-description', div); + description.textContent = route.properties.name.split(": ")[1].replace("=>", "\u2192"); + + var ul = L.DomUtil.create('ul', 'ls-stop-list', div); + + ul.style.listStyleImage = "url(" + LS.imagePath + "bus_stop.png)"; + + var routeLayer = this._routeLayer; + var busStops = routeLayer.getStops(); + + for (var i in route.properties.stops) { + var stop = route.properties.stops[i]; + + var li = L.DomUtil.create('li', '', ul); + + var busStop = busStops[stop]; + + var a = L.DomUtil.create('a', '', li); + + if (typeof(busStop) !== "undefined") { + a.textContent = busStop.properties.name; + a.onclick = (function(uri) { + return function() { + routeLayer.panToStop(uri, { + animate: true + }); + }; + })(busStop.properties.uri); + a.onmouseover = (function(uri) { + return function() { + routeLayer.highlightStop(uri); + }; + })(busStop.properties.uri); + a.onmouseout = (function(uri) { + return function() { + routeLayer.resetStop(uri); + }; + })(busStop.properties.uri); + } else { + a.textContent = "Name Unknown"; + } + } + + return div; + } +}); + +L.Control.ShowRouteSidebar = L.Control.extend({ + options: { + position: 'topleft', + }, + + initialize: function(sidebar, options) { + this._sidebar = sidebar; + }, + + onAdd: function (map) { + var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control'); + + var link = L.DomUtil.create('a', 'leaflet-bar-part leaflet-bar-part-single', container); + link.href = '#'; + + var sidebar = this._sidebar; + + L.DomEvent + .on(link, 'click', L.DomEvent.stopPropagation) + .on(link, 'click', L.DomEvent.preventDefault) + .on(link, 'click', function() { + sidebar.toggle(); + }) + .on(link, 'dblclick', L.DomEvent.stopPropagation); + + return container; + } +}); diff --git a/src/leaflet-soton.js b/src/leaflet-soton.js index 9e4b72f..b2d2a43 100644 --- a/src/leaflet-soton.js +++ b/src/leaflet-soton.js @@ -470,44 +470,51 @@ SELECT * WHERE {\ layer.addData(data[layerName]); } - var routeLayer = new LS.RouteLayer(options.busRoutes ? data.busRoutes : emptyFeatureCollection, data.busStops, { - routeOptions: { - onEachFeature: function(feature, layer) { - layer.on('click', function(e) { - var content = busRouteTemplate(feature.properties); - - showPopup(map, content, e.latlng); - }); - }, - style: busRouteStyle - }, - stopOptions: { - onEachFeature: function(feature, layer) { - layer.on('click', function(e) { - var content = busStopTemplate(feature.properties); + if (options.busRoutes) { + var routeLayer = new L.Route(options.busRoutes ? data.busRoutes : emptyFeatureCollection, data.busStops, { + routeOptions: { + onEachFeature: function(feature, layer) { + layer.on('click', function(e) { + var content = busRouteTemplate(feature.properties); - showPopup(map, content, e.latlng); - }); - } - } - }); - routeLayer.addTo(map); - - if (options.busRouteControl) { - var routeControl = new LS.RouteControl(routeLayer, { - routeMasterSort: function(a, b) { - var refs = { - "U1": 1, - "U2": 2, - "U6": 6, - "U9": 9, - "U1N": 10 - }; + map.showInfo(content, e.latlng); + }); + }, + style: busRouteStyle + }, + stopOptions: { + onEachFeature: function(feature, layer) { + layer.on('click', function(e) { + var content = busStopTemplate(feature.properties); - return refs[a] - refs[b]; + map.showInfo(content, e.latlng); + }); + } } }); - routeControl.addTo(map); + routeLayer.addTo(map); + + if (options.busRouteControl) { + var routeControl = new L.Control.Route(routeLayer, "sidebar", { + routeMasterSort: function(a, b) { + var refs = { + "U1": 1, + "U2": 2, + "U6": 6, + "U9": 9, + "U1N": 10 + }; + + return refs[a] - refs[b]; + }, + position: "right" + }); + routeControl.addTo(map); + routeControl.show(); + + var routeSidebarControl = new L.Control.ShowRouteSidebar(routeControl); + routeSidebarControl.addTo(map); + } } LS.getWorkstationData(function(workstationData) { @@ -1650,304 +1657,6 @@ SELECT * WHERE {\ return window.innerWidth < 500; } - LS.RouteLayer = L.FeatureGroup.extend({ - initialize: function (routes, stops, options) { - L.setOptions(this, options); - - this._layers = {}; - - this._routeMasters = {}; - - for (var i in routes.features) { - var route = routes.features[i]; - - if ("routeMaster" in route.properties) { - var routeMaster = route.properties.routeMaster; - - if (routeMaster in this._routeMasters) { - this._routeMasters[routeMaster].routes.push(route); - } else { - this._routeMasters[routeMaster] = { - name: routeMaster, - routes: [ route ] - }; - } - } else { - this._routeMasters[route.properties.ref] = { - name: route.properties.ref, - routes: [ route ] - }; - } - } - - this._routes = routes; - this._busStops = {}; - - for (i in stops.features) { - var busStop = stops.features[i]; - - this._busStops[busStop.properties.uri] = busStop; - } - - var routeLayers = this._routeLayers = {} - this._routeLayer = new L.GeoJSON(routes, { - onEachFeature: function(feature, layer) { - routeLayers[feature.properties.name] = layer; - - if (options.routeOptions.onEachFeature) { - options.routeOptions.onEachFeature(feature, layer); - } - }, - style: options.routeOptions.style - }); - this.addLayer(this._routeLayer); - - var stopLayers = this._stopLayers = {}; - this._stopLayer = new L.GeoJSON(stops, { - onEachFeature: function(feature, layer) { - stopLayers[feature.properties.uri] = layer; - - if (options.stopOptions.onEachFeature) { - options.stopOptions.onEachFeature(feature, layer); - } - }, - pointToLayer: function (feature, latlng) { - return L.circleMarker(latlng, { - radius: 8, - opacity: 0, - }); - }, - style: function(feature) { - return { - radius: 8, - opacity: 0, - fillOpacity: 0 - }; - } - }); - this.addLayer(this._stopLayer); - }, - getRouteMasters: function() { - return this._routeMasters; - }, - getRoutesForRouteMaster: function(id) { - return this._routeMasters[id]; - }, - getRoutes: function() { - return this._routes; - }, - getStops: function() { - return this._busStops; - }, - highlightRoute: function(id) { - var layer = this._routeLayers[id]; - - layer.setStyle({ - weight: 10, - opacity: 1, - }); - - layer.setText('\u25BA', { - repeat: true, - offset: 3, - attributes: { - fill: 'black', - fontSize: 24 - } - }); - - layer.bringToFront(); - }, - resetRoute: function(id) { - var layer = this._routeLayers[id]; - - this._routeLayer.resetStyle(layer); - - layer.setText(null); - }, - highlightStop: function(id) { - var layer = this._stopLayers[id]; - - layer.setStyle({ - color: '#ff0000', - opacity: 1, - fillOpacity: 0.3, - radius: 16, - stroke: true, - fill: true - }); - }, - resetStop: function(id) { - var layer = this._stopLayers[id]; - - this._stopLayer.resetStyle(layer); - }, - panToStop: function(id, panOptions) { - var layer = this._stopLayers[id]; - - this._map.panTo(layer._latlng, panOptions); - } - }); - - LS.RouteControl = L.Control.extend({ - initialize: function (routeLayer, options) { - L.setOptions(this, options); - - this._routeLayer = routeLayer; - }, - onAdd: function (map) { - this._div = L.DomUtil.create('div', 'ls-route-control'); - - var title = L.DomUtil.create('div', 'ls-routes-title', this._div); - title.textContent = "University Bus Routes"; - - var routeMasters = this._routeLayer.getRouteMasters(); - - var routeMasterRouteLists = {}; - - var ul = L.DomUtil.create('ul', 'ls-route-master-list', this._div); - - var routeMasterNames = Object.keys(routeMasters); - if (this.options.routeMasterSort) { - routeMasterNames.sort(this.options.routeMasterSort); - } - - for (var i in routeMasterNames) { - var routeMasterName = routeMasterNames[i]; - var routeMaster = routeMasters[routeMasterName]; - - var li = L.DomUtil.create('li', '', ul); - var a = L.DomUtil.create('a', '', li); - - a.style.background = routeMaster.routes[0].properties.colour; - - a.textContent = routeMaster.name; - a.onclick = (function(routeMasterName) { - return function() { - routeMasterRouteLists[activeRouteMaster].style.display = "none"; - routeMasterRouteLists[routeMasterName].style.display = "block"; - activeRouteMaster = routeMasterName; - }; - })(routeMasterName); - - // The route lists - - var routeList = this._createRouteList(routeMaster); - routeMasterRouteLists[routeMasterName] = routeList; - - routeList.style.display = "none"; - - this._div.appendChild(routeList); - } - - var activeRouteMaster = "U1"; // TODO: Dont hardcode like this - - routeMasterRouteLists[activeRouteMaster].style.display = "block"; - - return this._div; - }, - _createRouteList: function(routeMaster) { - var div = L.DomUtil.create('div', null); - - var ul = L.DomUtil.create('ul', 'ls-route-list', div); - - var routeLayer = this._routeLayer; - - var stopLists = {}; - for (var i in routeMaster.routes) { - var route = routeMaster.routes[i]; - - var li = L.DomUtil.create('li', '', ul); - var a = L.DomUtil.create('a', null, li); - - //a.style.borderColor = route.properties.colour; - a.style.background = route.properties.colour; - - a.textContent = route.properties.ref; - a.onclick = (function(routeName) { - return function() { - stopLists[activeStopList].style.display = "none"; - stopLists[routeName].style.display = "block"; - activeStopList = routeName; - }; - })(route.properties.name); - - a.onmouseover = (function(name) { - return function() { - routeLayer.highlightRoute(name); - }; - })(route.properties.name); - a.onmouseout = (function(name) { - return function() { - routeLayer.resetRoute(name); - }; - })(route.properties.name); - - // The stops - - var stopList = this._createStopList(route); - stopLists[route.properties.name] = stopList; - - stopList.style.display = "none"; - - div.appendChild(stopList); - } - - var activeStopList = routeMaster.routes[0].properties.name; - stopLists[activeStopList].style.display = "block"; - - return div; - }, - _createStopList: function(route) { - var div = L.DomUtil.create('div', null, this._div); - - var description = L.DomUtil.create('div', 'ls-route-description', div); - description.textContent = route.properties.name.split(": ")[1].replace("=>", "\u2192"); - - var ul = L.DomUtil.create('ul', 'ls-stop-list', div); - - ul.style.listStyleImage = "url(" + LS.imagePath + "bus_stop.png)"; - - var routeLayer = this._routeLayer; - var busStops = routeLayer.getStops(); - - for (var i in route.properties.stops) { - var stop = route.properties.stops[i]; - - var li = L.DomUtil.create('li', '', ul); - - var busStop = busStops[stop]; - - var a = L.DomUtil.create('a', '', li); - - if (typeof(busStop) !== "undefined") { - a.textContent = busStop.properties.name; - a.onclick = (function(uri) { - return function() { - routeLayer.panToStop(uri, { - animate: true - }); - }; - })(busStop.properties.uri); - a.onmouseover = (function(uri) { - return function() { - routeLayer.highlightStop(uri); - }; - })(busStop.properties.uri); - a.onmouseout = (function(uri) { - return function() { - routeLayer.resetStop(uri); - }; - })(busStop.properties.uri); - } else { - a.textContent = "Name Unknown"; - } - } - - return div; - } - }); - // Custom Hash Support if ("Hash" in L) { -- cgit v1.2.3