From 9ec78c79f88a54ed924b1b54b6eacb234e165b9a Mon Sep 17 00:00:00 2001 From: Candid Dauth Date: Tue, 11 Oct 2016 14:28:16 +0300 Subject: [PATCH] Show markers and popups for search results --- frontend/app/app.js | 1 + frontend/app/map/markers/markers.js | 17 ++- frontend/app/map/search/result-popup.html | 21 +++ frontend/app/map/search/search.html | 3 + frontend/app/map/search/search.js | 157 +++++++++++++++++++--- frontend/app/utils/utils.js | 21 ++- frontend/bower.json | 9 +- server/lib/search.js | 18 ++- 8 files changed, 211 insertions(+), 36 deletions(-) create mode 100644 frontend/app/map/search/result-popup.html diff --git a/frontend/app/app.js b/frontend/app/app.js index d9a0b4c8..2774c4cc 100644 --- a/frontend/app/app.js +++ b/frontend/app/app.js @@ -13,6 +13,7 @@ var FacilPad = { }); fp.app.constant("L", L); + fp.app.constant("linkifyStr", linkifyStr); // Dereferrer $(document).on("click", "a", function(e) { diff --git a/frontend/app/map/markers/markers.js b/frontend/app/map/markers/markers.js index 534151b8..257553d3 100644 --- a/frontend/app/map/markers/markers.js +++ b/frontend/app/map/markers/markers.js @@ -140,15 +140,18 @@ var listener = map.addClickListener(function(pos) { message.close(); - map.socket.emit("addMarker", { lon: pos.lon, lat: pos.lat, typeId: type.id }, function(err, marker) { - if(err) - return map.messages.showMessage("danger", err); + markersUi.createMarker(pos, type); + }); + }, + createMarker: function(pos, type, properties) { + map.socket.emit("addMarker", $.extend({ lon: pos.lon, lat: pos.lat, typeId: type.id }, properties), function(err, marker) { + if(err) + return map.messages.showMessage("danger", err); - markersUi._addMarker(marker); + markersUi._addMarker(marker); - markersById[marker.id].openPopup(); - markersUi.editMarker(marker); - }); + markersById[marker.id].openPopup(); + markersUi.editMarker(marker); }); } }; diff --git a/frontend/app/map/search/result-popup.html b/frontend/app/map/search/result-popup.html new file mode 100644 index 00000000..a1881fa4 --- /dev/null +++ b/frontend/app/map/search/result-popup.html @@ -0,0 +1,21 @@ +
+

{{result.short_name}}

+

{{result.display_name}} ({{result.type}})

+

{{result.type}}

+
+
Coordinates
+
{{round(result.lat, 5)}}, {{round(result.lon, 5)}}
+ +
{{key}}
+
{{value}}
+
+
+
+
+ + +
+ +
\ No newline at end of file diff --git a/frontend/app/map/search/search.html b/frontend/app/map/search/search.html index de9c62f8..1c71c68b 100644 --- a/frontend/app/map/search/search.html +++ b/frontend/app/map/search/search.html @@ -20,5 +20,8 @@ +
+ +
\ No newline at end of file diff --git a/frontend/app/map/search/search.js b/frontend/app/map/search/search.js index c78273c2..34970c92 100644 --- a/frontend/app/map/search/search.js +++ b/frontend/app/map/search/search.js @@ -1,46 +1,161 @@ (function(fp, $, ng, undefined) { - fp.app.factory("fpMapSearch", function($rootScope, $templateCache, $compile, fpUtils) { + fp.app.factory("fpMapSearch", function($rootScope, $templateCache, $compile, fpUtils, L, $timeout) { return function(map) { + var iconSuffix = ".n.32.png"; + var scope = $rootScope.$new(true); scope.searchString = ""; scope.searchResults = null; + scope.showAll = false; + scope.activeResult = null; scope.search = function() { - fpMapSearch.search(); + scope.searchResults = null; + clearRenders(); + + if(scope.searchString.trim() != "") { + map.loadStart(); + map.socket.emit("find", { query: scope.searchString }, function(err, results) { + map.loadEnd(); + + if(err) + map.messages.showMessage("danger", err); + + scope.searchResults = results; + + if(results && results.length > 0) + scope.showAll ? scope.showAllResults() : scope.showResult(results[0]); + }); + } }; scope.showResult = function(result) { - if(result.boundingbox) - map.map.flyToBounds([ [ result.boundingbox[0], result.boundingbox[3 ] ], [ result.boundingbox[1], result.boundingbox[2] ] ]); - else - map.map.flyTo([ result.lat, result.lon ], result.zoom); + if(scope.showAll) { + map.map.flyToBounds(layerGroup.getBounds()); + result.marker.openPopup(); + } else { + clearRenders(); + renderResult(result, true); + + if(result.boundingbox) + map.map.flyToBounds([ [ result.boundingbox[0], result.boundingbox[3 ] ], [ result.boundingbox[1], result.boundingbox[2] ] ]); + else + map.map.flyTo([ result.lat, result.lon ], result.zoom); + } }; + scope.showAllResults = function() { + clearRenders(); + + for(var i=0; i 0) + scope.showResult(scope.activeResult || scope.searchResults[0]); + }); + var el = $($templateCache.get("map/search/search.html")).insertAfter(map.map.getContainer()); $compile(el)(scope); scope.$evalAsync(); // $compile only replaces variables on next digest + var layerGroup = L.featureGroup([]).addTo(map.map); + + function renderResult(result, showPopup) { + layerGroup.addLayer( + L.geoJson(result.geojson, { + pointToLayer: function(geoJsonPoint, latlng) { + return null; + } + }) + .bindPopup($("
")[0], map.popupOptions) + .on("popupopen", function(e) { + scope.activeResult = result; + renderResultPopup(result, e.popup); + }) + .on("popupclose", function(e) { + scope.activeResult = null; + ng.element(e.popup.getContent()).scope().$destroy(); + }) + .bindTooltip(result.display_name, $.extend({}, map.tooltipOptions)) + ); + + result.marker = L.marker([ result.lat, result.lon ], { + icon: L.icon({ + iconUrl: result.icon.replace(/\.[a-z]+\.[0-9]+\.png$/, iconSuffix), + iconSize: [ 32, 32 ], + iconAnchor: [ 16, 16 ], + popupAnchor: [ 0, -16 ] + }) + }) + .bindPopup($("
")[0], map.popupOptions) + .on("popupopen", function(e) { + scope.activeResult = result; + renderResultPopup(result, e.popup); + }) + .on("popupclose", function(e) { + scope.activeResult = null; + ng.element(e.popup.getContent()).scope().$destroy(); + }) + .bindTooltip(result.display_name, $.extend({}, map.tooltipOptions, { offset: [ 20, 0 ] })); + + layerGroup.addLayer(result.marker); + + if(showPopup) + result.marker.openPopup(); + } + + function clearRenders() { + layerGroup.clearLayers(); + } + + function renderResultPopup(result, popup) { + var scope = map.socket.$new(); + + scope.result = result; + + scope.addToMap = function(type) { + if(type == null) { + for(var i in map.socket.types) { + if(map.socket.types[i].type == "marker") { + type = map.socket.types[i]; + break; + } + } + } + + map.markersUi.createMarker(result, type, { name: result.display_name }); + }; + + var el = popup.getContent(); + $(el).html($templateCache.get("map/search/result-popup.html")); + $compile(el)(scope); + + // Prevent popup close on button click + $("button", el).click(function(e) { + e.preventDefault(); + }); + + $timeout(function() { $timeout(function() { // $compile only replaces variables on next digest + popup.update(); + }); }); + } + var fpMapSearch = { search: function(query) { if(query != null) scope.searchString = query; - scope.searchResults = null; - if(scope.searchString.trim() != "") { - map.loadStart(); - map.socket.emit("find", { query: scope.searchString }, function(err, results) { - map.loadEnd(); - - if(err) - map.messages.showMessage("danger", err); - - scope.searchResults = results; - - if(results.length > 0) - scope.showResult(results[0]); - }); - } + scope.search(); }, showFiles: function(files) { diff --git a/frontend/app/utils/utils.js b/frontend/app/utils/utils.js index 480d3643..e27d7f11 100644 --- a/frontend/app/utils/utils.js +++ b/frontend/app/utils/utils.js @@ -31,7 +31,7 @@ }; fpUtils.createMarkerIcon = function(colour, huge) { - return new L.Icon({ + return L.icon({ iconUrl: fpUtils.createMarkerGraphic(colour, huge), iconSize: huge ? [10000, 10000] : [21, 25], iconAnchor: huge ? [5010, 5025] : [10, 25], @@ -218,4 +218,23 @@ }; }); + fp.app.filter('fpRenderOsmTag', function($sce, linkifyStr, fpUtils) { + return function(value, key) { + if(key.match(/^wikipedia(:|$)/)) { + return $sce.trustAsHtml(value.split(";").map(function(it) { + var m = it.match(/^(\s*)((([-a-z]+):)?(.*))(\s*)$/); + var url = "https://" + (m[4] || "en") + ".wikipedia.org/wiki/" + m[5]; + return m[1] + '' + fpUtils.quoteHtml(m[2]) + '' + m[6]; + }).join(";")); + } else if(key.match(/^wikidata(:|$)/)) { + return $sce.trustAsHtml(value.split(";").map(function(it) { + var m = it.match(/^(\s*)(.*?)(\s*)$/); + return m[1] + '' + fpUtils.quoteHtml(m[2]) + '' + m[3]; + }).join(";")); + } else { + return $sce.trustAsHtml(linkifyStr(value)); + } + }; + }); + })(FacilPad, jQuery, angular); \ No newline at end of file diff --git a/frontend/bower.json b/frontend/bower.json index 7032f961..98f3cbda 100644 --- a/frontend/bower.json +++ b/frontend/bower.json @@ -26,7 +26,8 @@ "bootstrap-touchspin": "^3.1.2", "leaflet": "^1.0.1", "leaflet-geometry-util": "https://github.com/makinacorpus/Leaflet.GeometryUtil/raw/master/src/leaflet.geometryutil.js", - "leaflet-almostover": "https://github.com/makinacorpus/Leaflet.AlmostOver/raw/gh-pages/src/leaflet.almostover.js" + "leaflet-almostover": "https://github.com/makinacorpus/Leaflet.AlmostOver/raw/gh-pages/src/leaflet.almostover.js", + "linkifyjs": "^2.1.3" }, "overrides": { "bootstrap": { @@ -36,6 +37,12 @@ "dist/js/bootstrap.js", "dist/fonts/glyphicons-halflings-regular.ttf" ] + }, + "linkifyjs": { + "main": [ + "linkify.js", + "linkify-string.js" + ] } } } diff --git a/server/lib/search.js b/server/lib/search.js index b3564589..27ac273d 100644 --- a/server/lib/search.js +++ b/server/lib/search.js @@ -51,14 +51,16 @@ function find(query, callback) { lat: lonlat.lat, lon : lonlat.lon, type : "coordinates", + short_name: lonlat.lat + ", " + lonlat.lon, display_name : lonlat.lat + ", " + lonlat.lon, - zoom: lonlat.zoom + zoom: lonlat.zoom, + icon: "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png" } ]; return callback(null, results); } request({ - url: nameFinderUrl + "?format=jsonv2&polygon_geojson=1&addressdetails=1&limit=" + encodeURIComponent(limit) + "&extratags=1&q=" + encodeURIComponent(query), + url: nameFinderUrl + "?format=jsonv2&polygon_geojson=1&addressdetails=1&namedetails=1&limit=" + encodeURIComponent(limit) + "&extratags=1&q=" + encodeURIComponent(query), json: true }, function(err, response, body) { if(err) @@ -68,13 +70,17 @@ function find(query, callback) { var results = [ ]; body.forEach(function(result) { + var displayName = makeDisplayName(result); results.push({ - display_name: makeDisplayName(result), + short_name: displayName.split(',')[0], + display_name: displayName, boundingbox: result.boundingbox, + lat: result.lat, + lon: result.lon, extratags: result.extratags, geojson: result.geojson, - icon: result.icon, - type: result.type, + icon: result.icon || "https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png", + type: result.type == "yes" ? result.category : result.type, osm_id: result.osm_id, osm_type: result.osm_type }); @@ -95,7 +101,7 @@ function makeDisplayName(result) { // address notation guidelines var type = result.type; - var name = result.address[result.type] || result.display_name.split(',')[0] || ""; + var name = result.namedetails.name; var countryCode = result.address.country_code; var road = result.address.road;