From deb8f4ac1eb06221783ead9ff71bbddf6823c651 Mon Sep 17 00:00:00 2001 From: Christopher Baines Date: Mon, 4 Aug 2014 19:04:48 +0100 Subject: Add points of service Currently, this is limited to food outlets. Not all food outlets display, as some are missing from OSM/Univeristy data. Other modifications include, better support custom display for the interactive information. This also includes an example, that uses jquery, typeahead and leaflet-sidebar. --- .gitmodules | 9 ++ create-data.js | 123 ++++++++++++++++++++++- examples/pointofservice.html | 228 +++++++++++++++++++++++++++++++++++++++++++ resources/jquery | 1 + resources/leaflet-sidebar | 1 + resources/typeahead.js | 1 + src/leaflet-soton.js | 175 +++++++++++++++++++++++---------- 7 files changed, 488 insertions(+), 50 deletions(-) create mode 100644 examples/pointofservice.html create mode 160000 resources/jquery create mode 160000 resources/leaflet-sidebar create mode 160000 resources/typeahead.js diff --git a/.gitmodules b/.gitmodules index 4c35d3b..9bd7205 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,12 @@ [submodule "resources/leaflet-textpath"] path = resources/leaflet-textpath url = https://github.com/makinacorpus/Leaflet.TextPath.git +[submodule "resources/typeahead.js"] + path = resources/typeahead.js + url = https://github.com/twitter/typeahead.js.git +[submodule "resources/leaflet-sidebar"] + path = resources/leaflet-sidebar + url = https://github.com/Turbo87/leaflet-sidebar.git +[submodule "resources/jquery"] + path = resources/jquery + url = https://github.com/jquery/jquery.git diff --git a/create-data.js b/create-data.js index d6eda79..fc956ec 100755 --- a/create-data.js +++ b/create-data.js @@ -50,6 +50,37 @@ pgql.connect('tcp://' + config.user + ':' + createTables, // Get the data from these tables createCollections, + function(collections, callback) { + + async.each(collections.pointsOfService.features, function(feature, callback) { + + if ("uri" in feature.properties) { + async.parallel([ + function(callback) { + getOfferings(feature.properties.uri, function(err, offerings) { + feature.properties.offerings = offerings; + + callback(); + }); + }, + function(callback) { + getDescription(feature.properties.uri, function(err, description) { + feature.properties.description = description; + + callback(); + }); + }], + callback + ); + } else { + console.warn("missing uri for point of service"); + + callback(); + } + }, function(err) { + callback(null, collections); + }); + }, // Now the basic collections have been created, handle the more complicated // ones: // - busStops @@ -192,7 +223,8 @@ function createCollections(callback) { parking: 'select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as polygon,\ name,access,capacity,"capacity:disabled",fee from uni_parking', bicycleParking: 'select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as polygon,capacity,bicycle_parking,covered from uni_bicycle_parking', - sites: 'select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as polygon,name,loc_ref,uri from uni_site' + sites: 'select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as polygon,name,loc_ref,uri from uni_site', + pointsOfService: "select ST_AsGeoJSON(ST_Transform(way, 4326), 10) as polygon,ST_AsText(ST_Transform(ST_Centroid(way), 4326)) as center,name,shop,amenity,uri from planet_osm_polygon where (amenity in ('cafe', 'bar', 'restaurant') or shop in ('kiosk', 'convenience')) and ST_Contains((select ST_Union(way) from uni_site), way);" }; var names = Object.keys(collectionQueries); @@ -243,6 +275,7 @@ function createCollection(name, query, callback) { var center = feature.properties.center; center = center.slice(6, -1); center = center.split(" ").reverse(); + center = center.map(parseFloat); feature.properties.center = center; } @@ -523,6 +556,90 @@ function getLibraryData(library_data, callback) { })); } +function getOfferings(uri, callback) { + + var query = "PREFIX ns0: \ + PREFIX oo: \ + PREFIX rdfs: \ + SELECT DISTINCT ?includes ?label ?section ?sectionLabel WHERE {\ + ?offering a ns0:Offering ;\ + ns0:includes ?includes ;\ + ns0:availableAtOrFrom ?pos ;\ + oo:priceListSection ?section .\ + ?includes rdfs:label ?label .\ + ?section rdfs:label ?sectionLabel\ + FILTER (\ + ?pos = \ + )\ + }"; + + query = query.replace("URI", uri); + + sparqlQuery(query, function(err, data) { + if (err) { + console.error("Query " + query); + console.error(err); + callback(err); + return; + } + + var offerings = {}; + + data.results.bindings.forEach(function(result) { + var section = result.section.value; + + var item = { + label: result.label.value, + uri: result.includes.value + }; + + if (section in offerings) { + offerings[section].items.push(item); + } else { + offerings[section] = { + label: result.sectionLabel.value, + items: [ item ] + }; + } + }); + + callback(null, offerings); + }); +} + +function getDescription(uri, callback) { + + var query = "PREFIX dcterms: \ + SELECT ?description WHERE {\ + ?uri dcterms:description ?description;\ + FILTER (\ + ?uri = \ + )\ + }"; + + query = query.replace("URI", uri); + + sparqlQuery(query, function(err, data) { + if (err) { + console.error("Query " + query); + console.error(err); + callback(err); + return; + } + + var b = data.results.bindings; + + var desc; + if (b.length == 0) { + desc = ""; + } else { + desc = b[0].description.value + } + + callback(null, desc); + }); +} + function createBuildingParts(buildings, callback) { console.info("creating buildingParts collection"); @@ -676,6 +793,10 @@ function createBuildingParts(buildings, callback) { for (var i=1; i + + + Points of Service + + + + + + + + + + +
+ + + + + + + + + + + + + + + diff --git a/resources/jquery b/resources/jquery new file mode 160000 index 0000000..4dec426 --- /dev/null +++ b/resources/jquery @@ -0,0 +1 @@ +Subproject commit 4dec426aa2a6cbabb1b064319ba7c272d594a688 diff --git a/resources/leaflet-sidebar b/resources/leaflet-sidebar new file mode 160000 index 0000000..39c89d3 --- /dev/null +++ b/resources/leaflet-sidebar @@ -0,0 +1 @@ +Subproject commit 39c89d3b5d60ff0eaacdd8c91192ebbe42b44767 diff --git a/resources/typeahead.js b/resources/typeahead.js new file mode 160000 index 0000000..f835e16 --- /dev/null +++ b/resources/typeahead.js @@ -0,0 +1 @@ +Subproject commit f835e162ec4551479114fd245c907ae032de692a diff --git a/src/leaflet-soton.js b/src/leaflet-soton.js index 1d995f7..9e4b72f 100644 --- a/src/leaflet-soton.js +++ b/src/leaflet-soton.js @@ -108,7 +108,51 @@ var content = vendingPopupTemplate(feature.properties); - showPopup(map, content, e.latlng, popupOptions); + // TODO: Unsure if map is accessible? + map.showInfo(content, e.latlng, popupOptions); + }); + } + }); + + return layer; + }, + getPointOfServiceLayer: function() { + var features = this.data.pointsOfService.features; + + var pointFeatures = features.map(function(feature) { + var pointFeature = { + type: "Feature", + geometry: { + type: "Point" + }, + properties: feature.properties + }; + + console.log(feature.properties); + var c = feature.properties.center; + c = [c[1], c[0]]; + pointFeature.geometry.coordinates = c; + + return pointFeature; + }); + + var layer = new L.GeoJSON(pointFeatures, { + pointToLayer: function(feature, latlng) { + var icon; + + icon = icons.vendingHotDrinks; + + return L.marker(latlng, {icon: icon}); + }, + onEachFeature: function(feature, layer) { + layer.on('click', function(e) { + var popupOptions = { + offset: icons.vendingHotDrinks.options.popupAnchor + }; + + var content = pointOfServiceTemplate(feature.properties); + + map.showInfo(content, e.latlng, popupOptions); }); } }); @@ -375,7 +419,7 @@ SELECT * WHERE {\ map, function() { close(); }); - close = showPopup(map, content, e.latlng); + close = map.showInfo(content, e.latlng); }); }; } else { @@ -384,7 +428,7 @@ SELECT * WHERE {\ layer.on('click', function(e) { var content = popupTemplates[layerName](feature.properties); - showPopup(map, content, e.latlng); + map.showInfo(content, e.latlng); }); }; } @@ -618,7 +662,7 @@ SELECT * WHERE {\ } } - showPopup(map, content, e.latlng, popupOptions); + map.showInfo(content, e.latlng, popupOptions); }); }, pointToLayer: function (feature, latlng) { @@ -742,7 +786,7 @@ SELECT * WHERE {\ map.panTo(center); - close = showPopup(map, content, center); + close = map.showInfo(content, center); return; } @@ -798,6 +842,49 @@ SELECT * WHERE {\ } else { throw "unable to handle " + feature.geometry.type; } + }, + showInfo: function(content, latlng, options) { + options = options || {}; + + options.maxWidth = map.getContainer().offsetWidth; + + var close; + + if (false && smallScreen()) { + // Just in case there is a popup open, as the screen has just shrunk + map.closePopup(); + + var containerWrapper = document.getElementById('dynamicContentWrapper'); + containerWrapper.style.display = 'block'; + + var container = document.getElementById('dynamicContent'); + + var contentDiv = document.createElement('div'); + + var closeButton = L.DomUtil.create('button', 'close', container); + closeButton.setAttribute('aria-hidden', 'true'); + closeButton.setAttribute('type', 'button'); + closeButton.textContent = 'x'; + + close = closeButton.onclick = function() { + container.innerHTML = ''; + containerWrapper.style.display = 'none'; + }; + + container.appendChild(content); + } else { + var popup = L.popup(options).setLatLng(latlng); + + popup.setContent(content); + + popup.openOn(map); + + close = function() { + map.closePopup(popup); + }; + } + + return close; } }); @@ -819,50 +906,6 @@ SELECT * WHERE {\ return L.marker(latlng, {icon: icon}); } - function showPopup(map, content, latlng, popupOptions) { - popupOptions = popupOptions || {}; - - popupOptions.maxWidth = map.getContainer().offsetWidth; - - var close; - - if (false && smallScreen()) { - // Just in case there is a popup open, as the screen has just shrunk - map.closePopup(); - - var containerWrapper = document.getElementById('dynamicContentWrapper'); - containerWrapper.style.display = 'block'; - - var container = document.getElementById('dynamicContent'); - - var contentDiv = document.createElement('div'); - - var closeButton = L.DomUtil.create('button', 'close', container); - closeButton.setAttribute('aria-hidden', 'true'); - closeButton.setAttribute('type', 'button'); - closeButton.textContent = 'x'; - - close = closeButton.onclick = function() { - container.innerHTML = ''; - containerWrapper.style.display = 'none'; - }; - - container.appendChild(content); - } else { - var popup = L.popup(popupOptions).setLatLng(latlng); - - popup.setContent(content); - - popup.openOn(map); - - close = function() { - map.closePopup(popup); - }; - } - - return close; - } - // Template functions for creating the popups var popupTemplates = { @@ -1304,6 +1347,40 @@ SELECT * WHERE {\ }); } + function pointOfServiceTemplate(properties) { + return getTemplateWrapper(properties, function(content) { + + var description = document.createElement("div"); + if ("description" in properties) { + description.innerHTML = properties.description; + } else { + description.textContent = "No Description Available"; + } + content.appendChild(description); + + if ("offerings" in properties) { + Object.keys(properties.offerings).forEach(function(sectionURI) { + var section = properties.offerings[sectionURI]; + + var header = document.createElement("h4"); + header.textContent = section.label; + + content.appendChild(header); + + section.items.forEach(function(item) { + var a = document.createElement("a"); + + a.textContent = item.label; + a.href = item.uri; + + content.appendChild(a); + content.appendChild(document.createElement("br")); + }); + }); + } + }); + } + function parkingTemplate(properties) { if (!('name' in properties)) properties.name = 'Car Park'; -- cgit v1.2.3