summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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) {