summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Baines <cb15g11@soton.ac.uk>2014-06-06 11:52:55 +0100
committerChristopher Baines <cb15g11@soton.ac.uk>2014-06-06 12:14:23 +0100
commit586cda60293cebd617f0af275d63343f68abd525 (patch)
tree8cf5b916ce24d6c35031b3fc0eb5b6a80d48c24a
parentcfa92e45a7f4550885917022ee8e19ee2a410de1 (diff)
downloadleaflet-soton-586cda60293cebd617f0af275d63343f68abd525.tar
leaflet-soton-586cda60293cebd617f0af275d63343f68abd525.tar.gz
Add a RouteLayer and RouteControl
Add the leaflet-textpath submodule as it is used in the RouteLayer Improve the bus route extraction: - Now supports polygons for platforms - Supports routes not in a route master - Filter out all but one U1C route (in an attempt to make the interface less confusing) Add an example using the RouteLayer and RouteControl. Hard code the centers for some "University" buildings on the Isle of White and in Basingstoke, such that workstations listed as in these buildings appear on the map.
-rw-r--r--.gitmodules3
-rwxr-xr-xcreate-data.js330
-rw-r--r--examples/bus.html47
-rw-r--r--examples/index.html1
m---------resources/leaflet-textpath0
-rw-r--r--src/leaflet-soton.css70
-rw-r--r--src/leaflet-soton.js376
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) {