diff options
-rw-r--r-- | .gitmodules | 3 | ||||
-rwxr-xr-x | create-data.js | 330 | ||||
-rw-r--r-- | examples/bus.html | 47 | ||||
-rw-r--r-- | examples/index.html | 1 | ||||
m--------- | resources/leaflet-textpath | 0 | ||||
-rw-r--r-- | src/leaflet-soton.css | 70 | ||||
-rw-r--r-- | src/leaflet-soton.js | 376 |
7 files changed, 714 insertions, 113 deletions
diff --git a/.gitmodules b/.gitmodules index 3565164..1abfbca 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "resources/leaflet-heat"] path = resources/leaflet-heat url = https://github.com/Leaflet/Leaflet.heat.git +[submodule "resources/leaflet-textpath"] + path = resources/leaflet-textpath + url = https://github.com/makinacorpus/Leaflet.TextPath.git diff --git a/create-data.js b/create-data.js index 9b4cfe3..efe21b0 100755 --- a/create-data.js +++ b/create-data.js @@ -101,7 +101,7 @@ pgql.connect('tcp://' + config.user + ':' + process.exit(1); } - console.log("ending database connection"); + console.info("ending database connection"); pgql.end(); writeDataFiles(collections, function() { @@ -111,7 +111,7 @@ pgql.connect('tcp://' + config.user + ':' + } }); - console.log("complete"); + console.info("complete"); process.exit(0); }); @@ -148,7 +148,7 @@ function createTables(callback) { function createTable(name, query, callback) { var tableName = tablePrefix + name; - console.log("creating table " + tableName); + console.info("creating table " + tableName); pg.query("drop table if exists " + tableName, function(err, results) { var fullQuery = "create table " + tableName + " as " + query; @@ -157,7 +157,7 @@ function createTable(name, query, callback) { console.error("error creating table " + tableName); console.error("query: " + fullQuery); } else { - console.log("finished creating table " + tableName); + console.info("finished creating table " + tableName); } callback(err); }); @@ -255,7 +255,7 @@ function getBuildingFeatures(buildings, callback) { } function getBuildingImages(buildings, callback) { - console.log("getting building images"); + console.info("getting building images"); async.each(Object.keys(buildings), function(uri, callback) { getImagesFor(uri, function(err, images) { buildings[uri].properties.images = images; @@ -267,7 +267,7 @@ function getBuildingImages(buildings, callback) { // buildingParts function createBuildingParts(buildings, callback) { - console.log("creating buildingParts collection"); + console.info("creating buildingParts collection"); var workstations = {}; @@ -741,7 +741,7 @@ SELECT * WHERE {\ // buildingFeatures function getPrinters(buildings, callback) { - console.log("begining create printers"); + console.info("begining create printers"); var query = "PREFIX spacerel: <http://data.ordnancesurvey.co.uk/ontology/spatialrelations/>\ PREFIX soton: <http://id.southampton.ac.uk/ns/>\ @@ -840,7 +840,7 @@ SELECT * WHERE {\ } }); - console.log("finished processing printers (" + printersWithLocations + "/" + Object.keys(openDataPrinterURIs).length + ")"); + console.info("finished processing printers (" + printersWithLocations + "/" + Object.keys(openDataPrinterURIs).length + ")"); async.filter(results, function(printer, callback) { @@ -855,7 +855,7 @@ SELECT * WHERE {\ } function getVendingMachines(buildings, callback) { - console.log("begin getVendingMachines"); + console.info("begin getVendingMachines"); var query = "PREFIX spacerel: <http://data.ordnancesurvey.co.uk/ontology/spatialrelations/>\ PREFIX soton: <http://id.southampton.ac.uk/ns/>\ @@ -946,7 +946,94 @@ SELECT * WHERE {\ // busStops and busRoutes +function processRoute(route, routeMaster, stopAreaRoutes, callback) { + + var ways = []; + var stopRefs = []; + + async.eachSeries(route.members, function(member /* either a stop_area, or a road */, callback) { + if (member.type === "relation") { // Then its a stop_area + // Add the stop to the list (stopAreas) + if (member.ref in stopAreaRoutes) { + if (stopAreaRoutes[member.ref].indexOf(route.tags.ref) < 0) + stopAreaRoutes[member.ref].push(route.tags.ref); + } else { + stopAreaRoutes[member.ref] = [route.tags.ref]; + } + + stopRefs.push(member.ref); + + callback(); + } else { + var query = "select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as way from planet_osm_line where osm_id = " + member.ref; + + pg.query(query, function(err, results) { + if (err) callback(err); + + ways.push(JSON.parse(results.rows[0].way).coordinates); + + callback(); + }); + } + }, function(err) { + // Now to create the route geometry + + getRelations(stopRefs, function(err, areas) { + + var stopURIs = areas.map(function(area) { + var uri = area.tags.uri; + + if (uri.indexOf("http://transport.data.gov.uk/id/stop-point/") === 0) { + uri = "http://id.southampton.ac.uk/bus-stop/" + uri.slice(43); + } else { + console.warn("Unrecognised bus stop uri " + uri); + } + + return uri; + }); + + createRouteGeometry(ways, function(err, routeCoords) { + if (err) { + console.error("geometry errors for route " + route.tags.name); + err.forEach(function(error) { + console.error(" " + error); + }); + } + + var busRoute = { + type: "Feature", + geometry: { + type: "LineString", + coordinates: routeCoords + }, + properties: { + name: route.tags.name, + ref: route.tags.ref, + stops: stopURIs + } + } + + if ('colour' in route.tags) { + busRoute.properties.colour = route.tags.colour; + } + + if (routeMaster !== null) { + busRoute.properties.routeMaster = routeMaster.tags.ref; + + if (!('colour' in route.tags) && 'colour' in routeMaster.tags) { + busRoute.properties.colour = routeMaster.tags.colour; + } + } + + callback(null, busRoute); + }); + }); + }); +} + function loadBusData(collections, callback) { + var stopAreaRoutes = {} // Mapping from id to stop area, also contains the route names for that stop area + async.waterfall([ function(callback) { var query = "select id,parts,members,tags from planet_osm_rels where tags @> array['type', 'route_master', 'Uni-link']"; @@ -958,86 +1045,91 @@ function loadBusData(collections, callback) { }, callback); }, function(routeMasters, callback) { - var stopAreaRoutes = {} // Mapping from id to stop area, also contains the route names for that stop area - collections.busRoutes = { type: "FeatureCollection", features: [] }; - async.each(routeMasters, function(routeMaster, callback) { - async.each(routeMaster.members, function(member, callback) { + async.eachSeries(routeMasters, function(routeMaster, callback) { + async.eachSeries(routeMaster.members, function(member, callback) { + + // Pull in the route in the route master getRelation(member.ref, function(err, route) { if (err) callback(err); - var ways = []; - var stopAreasRoutes = {}; - - async.eachSeries(route.members, function(member /* either a stop_area, or a road */, callback) { - if (member.type === "relation") { // Then its a stop_area - // Add the stop to the list (stopAreas) - if (member.ref in stopAreaRoutes) { - if (stopAreaRoutes[member.ref].indexOf(route.tags.ref) < 0) - stopAreaRoutes[member.ref].push(route.tags.ref); - } else { - stopAreaRoutes[member.ref] = [route.tags.ref]; - } - callback(); - } else { - var query = "select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as way from planet_osm_line where osm_id = " + member.ref; - - pg.query(query, function(err, results) { - if (err) callback(err); - - ways.push(JSON.parse(results.rows[0].way).coordinates); + processRoute(route, routeMaster, stopAreaRoutes, function(err, feature) { + collections.busRoutes.features.push(feature); - callback(); - }); - } - }, function(err) { - // Now to create the route geometry - - createRouteGeometry(ways, function(err, routeCoords) { - if (err) { - console.error("geometry errors for route " + route.tags.name); - err.forEach(function(error) { - console.error(" " + error); - }); - } - - var colour = ('colour' in route.tags) ? route.tags.colour : routeMaster.tags.colour; - - var busRoute = { - type: "Feature", - geometry: { - type: "LineString", - coordinates: routeCoords - }, - properties: { - colour: colour, - name: route.tags.name, - ref: route.tags.ref - } - } - - collections.busRoutes.features.push(busRoute); - - callback(); - }); + callback(err); }); }); }, callback); - }, function(err) { - callback(err, stopAreaRoutes); - }); + }, callback); + }, + // Now look at the individual routes that are not in route masters + function(callback) { + var query = "select id,parts,members,tags from planet_osm_rels where tags @> array['type', 'route', 'Uni-link']"; + pg.query(query, callback); + }, + function(results, callback) { + async.map(results.rows, function(relation, callback) { + processRelation(relation, callback); + }, callback); + }, + function(routes, callback) { + async.eachSeries(routes, function(route, callback) { + processRoute(route, null, stopAreaRoutes, function(err, feature) { + collections.busRoutes.features.push(feature); + + callback(err); + }); + }, callback); }, // Now the route processing has finished, the bus stops can be created - createBusStops + function(callback) { + createBusStops(stopAreaRoutes, callback); + } ], function(err, busStops) { collections.busStops = busStops; - console.log("finished loadBusData"); + // Now remove all but the longest U1C route... + + var longestRoute = 0; + for (var i in collections.busRoutes.features) { + var route = collections.busRoutes.features[i]; + + if (route.properties.ref !== "U1C") { + continue; + } + + var stops = route.properties.stops.length; + console.log(stops); + if (stops > longestRoute) { + longestRoute = stops; + } + } + + console.log("longest route " + longestRoute); + + i = collections.busRoutes.features.length; + while (i--) { + route = collections.busRoutes.features[i]; + + if (route.properties.ref !== "U1C") { + continue; + } + + var stops = route.properties.stops.length; + + if (stops !== longestRoute) { + console.log("removing " + i); + + collections.busRoutes.features.splice(i, 1); + } + } + + console.info("finished loadBusData"); if (err) console.error(err); @@ -1107,7 +1199,7 @@ function createBusStops(stopAreaRoutes, callback) { }); }, function(callback) { - console.log("creating uni_bus_stop"); + console.info("creating uni_bus_stop"); pg.query('create table uni_bus_stop ( way geometry, name text, uri text, routes text array);', function(err, results) { callback(err); }); @@ -1138,7 +1230,7 @@ function createBusStops(stopAreaRoutes, callback) { if (err) console.error(err); - console.log("finished createBusStops"); + console.info("finished createBusStops"); callback(err, busStops); }); @@ -1149,23 +1241,27 @@ function createBusStop(stopArea, routes, callback) { var member = stopArea.members[i]; if (member.role === "platform") { var ref = member.ref; - switch (member.type) { - case "node": - getNode(ref, function(err, node) { - var name = stopArea.tags.name; - if (name !== undefined) { - name = name.replace("'", "''"); - } else { - name = ''; - } - var routeArray = "{" + routes.join(", ") + "}"; + var name = stopArea.tags.name; + if (name !== undefined) { + name = name.replace("'", "''"); + } else { + name = ''; + } - var uri = stopArea.tags.uri; + var routeArray = "{" + routes.join(", ") + "}"; - if (uri.indexOf("http://transport.data.gov.uk/id/stop-point/") === 0) { - uri = "http://id.southampton.ac.uk/bus-stop/" + uri.slice(43); - } + var uri = stopArea.tags.uri; + + if (uri.indexOf("http://transport.data.gov.uk/id/stop-point/") === 0) { + uri = "http://id.southampton.ac.uk/bus-stop/" + uri.slice(43); + } else { + console.warn("Unrecognised bus stop uri " + uri); + } + + switch (member.type) { + case "node": + getNode(ref, function(err, node) { var pgQuery = "insert into uni_bus_stop values(ST_SetSRID(ST_MakePoint(" pgQuery = pgQuery + node.geometry.coordinates[0] + ", " + node.geometry.coordinates[1]; @@ -1191,8 +1287,27 @@ function createBusStop(stopArea, routes, callback) { break; case "way": + var query = "select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as way from planet_osm_polygon where osm_id = " + member.ref; + + pg.query(query, function(err, results) { + if (err) { + console.error("Query: " + pgQuery); + callback(err); + return; + } - callback("unable to handle ways"); + var feature = { + type: "Feature", + geometry: JSON.parse(results.rows[0].way), + properties: { + name: name, + uri: uri, + routes: routes + } + } + + callback(err, feature); + }); break; } @@ -1245,6 +1360,9 @@ function processRelation(relation, callback) { obj.id = parseInt(relation.id, 10); + // This seems to make things right, unsure why? + obj.members.reverse(); + callback(null, obj); } @@ -1269,11 +1387,41 @@ function getRelations(ids, callback) { async.map(results.rows, function(relation, callback) { processRelation(relation, callback); - }, callback); + }, function(err, relations) { + + var relsByID = {}; + + relations.forEach(function(relation) { + relsByID[relation.id] = relation; + }); + + var orderedRelations = ids.map(function(id) { + return relsByID[id]; + }); + + callback(null, orderedRelations); + }); }); } function getBuildingCenter(uri, callback) { + + var hardcodedBuildings = { + "http://id.southampton.ac.uk/building/9591": { + type: "Point", + coordinates: [-1.10957, 51.28056] + }, + "http://id.southampton.ac.uk/building/9594": { + type: "Point", + coordinates: [-1.30083, 50.71109] + } + } + + if (uri in hardcodedBuildings) { + callback(null, hardcodedBuildings[uri]); + return; + } + var query = "select ST_AsGeoJSON(ST_Centroid(ST_Transform(way, 4326)), 10) as center from uni_building where uri='" + uri + "';"; pg.query(query, function(err, results) { @@ -1468,7 +1616,7 @@ function writeDataFiles(data, callback) { // Validation Functions function validateBuildingParts(buildingParts, callback) { - console.log("begining validating buildingparts"); + console.info("begining validating buildingparts"); async.each(Object.keys(uniRooms), function(room, callback) { var type = uniRooms[room].type; @@ -1509,7 +1657,7 @@ SELECT * WHERE {\ callback(); }, function(err) { - console.log("finished validateBuildings"); + console.info("finished validateBuildings"); callback(err); }); }); diff --git a/examples/bus.html b/examples/bus.html new file mode 100644 index 0000000..a936262 --- /dev/null +++ b/examples/bus.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<head> + <title>Map - University of Southampton</title> + <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> + + <link rel="stylesheet" href="../resources/leaflet-locatecontrol/src/L.Control.Locate.css" /> + <link rel="stylesheet" href="../resources/leaflet/dist/leaflet.css" /> + + <link rel="stylesheet" href="../src/leaflet-soton.css" /> + + <style> + body { + padding: 0; + margin: 0; + } + + html, body, #map { + height: 100%; + } + </style> +</head> +<body> + <div id="map"></div> + + <script src="../resources/leaflet/dist/leaflet-src.js"></script> + <script src="../resources/leaflet-markercluster/dist/leaflet.markercluster.js"></script> + <script src="../resources/leaflet-locatecontrol/src/L.Control.Locate.js"></script> + <script src="../resources/leaflet-hash/leaflet-hash.js"></script> + <script src="../resources/leaflet-textpath/leaflet.textpath.js"></script> + <script src="../resources/leaflet-indoor/leaflet-indoor.js"></script> + + <script src="../src/leaflet-soton.js"></script> + + <script type="text/javascript"> + LS.imagePath = '../resources/images/'; + LS.dataPath = '../data.json'; + + var map = LS.map('map', { + busRoutes: true, + busRouteControl: true + }); + + L.control.locate().addTo(map); + </script> +</body> +</html> diff --git a/examples/index.html b/examples/index.html index fb1095b..782314a 100644 --- a/examples/index.html +++ b/examples/index.html @@ -95,6 +95,7 @@ h6 { font-size: 1em; line-height: 1.3125; } <li><a href="zepler.html">Zepler Example</a></li> <li><a href="vendingmachines.html">Vending Machines Example</a></li> <li><a href="cycleparkingheatmap.html">Cycle Parking Heatmap Example</a></li> + <li><a href="bus.html">Bus Routes Example</a></li> </ul> <h2>OpenStreetMap</h2> diff --git a/resources/leaflet-textpath b/resources/leaflet-textpath new file mode 160000 +Subproject db419983df3e3a8f5040cd48edff2785ba55b99 diff --git a/src/leaflet-soton.css b/src/leaflet-soton.css index af1ecda..336d273 100644 --- a/src/leaflet-soton.css +++ b/src/leaflet-soton.css @@ -114,6 +114,7 @@ border-bottom: 1px solid #ddd; } .ls-nav-tabs > li { + display: inline; margin-bottom: -1px; } .ls-nav-tabs > li > a { @@ -150,3 +151,72 @@ color: #ffffff; background-color: #0088cc; } + +.ls-route-control { + padding: 6px 8px; + font: 14px/16px Arial, Helvetica, sans-serif; + background: white; + background: rgba(255,255,255,1); + box-shadow: 0 0 15px rgba(0,0,0,0.2); + border-radius: 5px; +} + +.ls-routes-title { + font: 30px/32px Arial, Helvetica, sans-serif; +} + +.ls-route-description { + font: 18px/20px Arial, Helvetica, sans-serif; +} + +.ls-route-master-list { + list-style: none; + padding-left: 0px; +} + +.ls-route-master-list li { + display: inline; +} + +.ls-route-master-list li a { + font: 28px/30px Arial, Helvetica, sans-serif; + padding-right: 10px; + padding-left: 10px; + margin-right: 5px; + margin-left: 5px; + border-radius: 25px; + border: 4px; + border-style: solid; + border-color: #000000; + background: #a1a1a1; + color: white; + font-weight: bold; +} + +.ls-route-list { + list-style: none; + padding-left: 0px; +} + +.ls-route-list li { + display: inline; +} + +.ls-route-list li a { + font: 20px/22px Arial, Helvetica, sans-serif; + padding-right: 10px; + padding-left: 10px; + margin-right: 3px; + margin-left: 3px; + border-radius: 25px; + border: 3px; + border-style: solid; + border-color: #000000; + background: #a1a1a1; + color: white; + font-weight: bold; +} + +.ls-stop-list { + padding-left: 25px; +} diff --git a/src/leaflet-soton.js b/src/leaflet-soton.js index 3e030dc..aea9f1c 100644 --- a/src/leaflet-soton.js +++ b/src/leaflet-soton.js @@ -70,21 +70,25 @@ var contents = part.properties.contents; - for (var j=0; j<contents.length; j++) { - var content = contents[j]; + if (contents) { + for (var j=0; j<contents.length; j++) { + var content = contents[j]; - if (content.feature === uri) { - return part; + if (content.feature === uri) { + return part; + } } } var features = part.properties.features; - for (var j=0; j<features.length; j++) { - var feature = features[j]; + if (features) { + for (var j=0; j<features.length; j++) { + var feature = features[j]; - if (feature.feature === uri) { - return part; + if (feature.feature === uri) { + return part; + } } } } @@ -315,7 +319,7 @@ SELECT * WHERE {\ var emptyFeatureCollection = { type: "FeatureCollection", features: [] }; var transparaentStyle = function(feature) {return {weight: 0, opacity: 0, fillOpacity: 0};}; - var layerNames = ['sites', 'parking', 'bicycleParking', 'buildings', 'busStops', 'busRoutes']; + var layerNames = ['sites', 'parking', 'bicycleParking', 'buildings']; var busRouteStyle = function(feature) { return {weight: 5, opacity: 0.5, color: feature.properties.colour}; @@ -325,6 +329,8 @@ SELECT * WHERE {\ options: { center: [50.9354, -1.3964], indoor: false, + busRoutes: false, + busRouteControl: false, workstations: false, zoom: 17, tileUrl: 'http://bus.southampton.ac.uk/graphics/map/tiles/{z}/{x}/{y}.png', @@ -379,8 +385,6 @@ SELECT * WHERE {\ options.highlight[feature.properties.uri]) { return {weight: 5, opacity: 0.5, color: 'blue'}; - } else if (layerName === "busRoutes") { - return busRouteStyle(feature); } else { return blankStyle(); } @@ -421,15 +425,6 @@ SELECT * WHERE {\ }; } - if (layerName === "busStops") { - layerOptions.pointToLayer = function (feature, latlng) { - return L.circleMarker(latlng, { - radius: 8, - opacity: 1, - }); - }; - } - layers[layerName] = L.geoJson(emptyFeatureCollection, layerOptions).addTo(map); }); @@ -458,6 +453,46 @@ SELECT * WHERE {\ layer.addData(data[layerName]); } + var routeLayer = new LS.RouteLayer(data.busRoutes, data.busStops, { + routeOptions: { + onEachFeature: function(feature, layer) { + layer.on('click', function(e) { + var content = busRouteTemplate(feature.properties); + + showPopup(map, content, e.latlng); + }); + }, + style: options.busRoutes ? busRouteStyle : blankStyle + }, + stopOptions: { + onEachFeature: function(feature, layer) { + layer.on('click', function(e) { + var content = busStopTemplate(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 + }; + + return refs[a] - refs[b]; + } + }); + routeControl.addTo(map); + } + LS.getWorkstationData(function(workstationData) { if (options.indoor) { @@ -893,8 +928,6 @@ SELECT * WHERE {\ buildings: buildingTemplate, bicycleParking: bicycleParkingTemplate, parking: parkingTemplate, - busStops: busStopTemplate, - busRoutes: busRouteTemplate }; function roomPopupTemplate(properties) { @@ -1592,6 +1625,305 @@ 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) { + console.log("stops"); + 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) { |