From 4e95a9490eb207c7c03afd4a0d682f9a79b8789c Mon Sep 17 00:00:00 2001 From: Thomas Rudin Date: Sat, 20 Apr 2019 17:59:22 +0200 Subject: [PATCH 1/3] shop/poi search --- server/app/config.go | 2 +- server/static/js/SearchControl.js | 9 ++-- server/static/js/overlays/ShopOverlay.js | 2 +- server/static/js/search/SearchMenu.js | 5 ++- server/static/js/search/SearchResult.js | 55 ++++++++++++++++++++---- server/static/js/search/SearchService.js | 26 ++++++++--- 6 files changed, 77 insertions(+), 22 deletions(-) diff --git a/server/app/config.go b/server/app/config.go index f4f2806..9a4b84c 100644 --- a/server/app/config.go +++ b/server/app/config.go @@ -165,7 +165,7 @@ func ParseConfig(filename string) (*Config, error) { Port: 8080, EnableRendering: true, EnablePrometheus: true, - EnableSearch: false, + EnableSearch: true, EnableInitialRendering: true, EnableTransparency: false, Webdev: false, diff --git a/server/static/js/SearchControl.js b/server/static/js/SearchControl.js index 424dbec..7ec2e36 100644 --- a/server/static/js/SearchControl.js +++ b/server/static/js/SearchControl.js @@ -7,11 +7,14 @@ var SearchControl = L.Control.extend({ L.Control.prototype.initialize.call(this, opts); }, - onAdd: function() { + onAdd: function(map) { var div = L.DomUtil.create('div'); - m.mount(div, SearchInput); - m.mount(document.getElementById("search-content"), SearchMenu); + m.mount(document.getElementById("search-content"), { + view: function () { + return m(SearchMenu, {map: map}); + } + }); return div; } diff --git a/server/static/js/overlays/ShopOverlay.js b/server/static/js/overlays/ShopOverlay.js index b4b7d15..cce844e 100644 --- a/server/static/js/overlays/ShopOverlay.js +++ b/server/static/js/overlays/ShopOverlay.js @@ -22,7 +22,7 @@ var ShopOverlay = AbstractIconOverlay.extend({ }, getMaxDisplayedZoom: function(){ - return 5; + return 10; }, getIcon: function(obj){ diff --git a/server/static/js/search/SearchMenu.js b/server/static/js/search/SearchMenu.js index cd35755..a493832 100644 --- a/server/static/js/search/SearchMenu.js +++ b/server/static/js/search/SearchMenu.js @@ -4,7 +4,8 @@ /* globals SearchStore: true */ var SearchMenu = { - view: function(){ + view: function(vnode){ + var style = {}; if (!SearchStore.query) { @@ -21,7 +22,7 @@ var SearchMenu = { "Search", m("i", { class: "fa fa-times float-right", onclick: close }), ]), - m("div", { class: "card-body", style: {overflow: "auto"} }, m(SearchResult)) + m("div", { class: "card-body", style: {overflow: "auto"} }, m(SearchResult, { map: vnode.attrs.map })) ]); } }; diff --git a/server/static/js/search/SearchResult.js b/server/static/js/search/SearchResult.js index e34cece..48223a7 100644 --- a/server/static/js/search/SearchResult.js +++ b/server/static/js/search/SearchResult.js @@ -3,7 +3,8 @@ /* globals layerMgr: true */ var SearchResult = { - view: function(){ + view: function(vnode){ + var map = vnode.attrs.map; function getLayer(obj){ var layer = layerMgr.getLayerByY(obj.y); @@ -12,30 +13,68 @@ var SearchResult = { function getPos(obj){ var layer = layerMgr.getLayerByY(obj.y); - var link = (layer ? layer.id : "0") + "/" + obj.x + "/" + obj.z + "/" + 12; var text = obj.x + "/" + obj.y + "/" + obj.z; - return m("a", { href: "#" + link }, text); + return m("span", {class:"badge badge-success"}, text); } var rows = SearchStore.result.map(function(obj){ - return m("tr", [ - m("td", obj.type), + + var row_classes = ""; + var description = obj.type; + var type = obj.type; + + if (obj.type == "poi"){ + description = m("span", obj.attributes.name); + type = m("img", { src: "css/images/marker-icon.png" }); + } + + if (obj.type == "shop") { + if (obj.attributes.stock == 0){ + row_classes += "table-warning"; + type = m("img", { src: "pics/shop_empty.png" }); + } else { + type = m("img", { src: "pics/shop.png" }); + } + + description = m("span", [ + "Shop, trading ", + m("span", {class:"badge badge-primary"}, obj.attributes.out_count + "x"), + m("span", {class:"badge badge-info"}, obj.attributes.out_item), + " for ", + m("span", {class:"badge badge-primary"}, obj.attributes.in_count + "x"), + m("span", {class:"badge badge-info"}, obj.attributes.in_item), + " Stock: ", + m("span", {class:"badge badge-info"}, obj.attributes.stock) + ]); + } + + function onclick(){ + map.setView([obj.z, obj.x], 12); + } + + return m("tr", {"class": row_classes}, [ + m("td", type), m("td", obj.attributes.owner), m("td", getLayer(obj)), m("td", getPos(obj)), - m("td", "stuff") + m("td", description), + m("button[type=button]", {class: "btn btn-secondary", onclick: onclick }, [ + "Goto ", + m("i", { class: "fas fa-play" }) + ]) ]); }); - return m("table", {class:"table"}, [ + return m("table", {class:"table table-striped"}, [ m("thead", [ m("tr", [ m("th", "Type"), m("th", "Owner"), m("th", "Layer"), m("th", "Position"), - m("th", "Description") + m("th", "Description"), + m("th", "Action") ]) ]), m("tbody", rows) diff --git a/server/static/js/search/SearchService.js b/server/static/js/search/SearchService.js index 32a4415..18f8e14 100644 --- a/server/static/js/search/SearchService.js +++ b/server/static/js/search/SearchService.js @@ -16,18 +16,30 @@ var SearchService = { return; } - api.getMapObjects({ - pos1: { x:-2048, y:-2048, z:-2048 }, - pos2: { x:2048, y:2048, z:2048 }, + function searchFor(q){ + q.pos1 = { x:-2048, y:-2048, z:-2048 }; + q.pos2 = { x:2048, y:2048, z:2048 }; + return api.getMapObjects(q); + } + + var shop_prom = searchFor({ type: "shop", attributelike: { key: "out_item", value: "%" + SearchStore.query + "%" } - }) - .then(function(result){ - SearchStore.result = result; - //console.log(result); //XXX + }); + + var poi_prom = searchFor({ + type: "poi", + attributelike: { + key: "name", + value: "%" + SearchStore.query + "%" + } + }); + + Promise.all([shop_prom, poi_prom]).then(function(results){ + SearchStore.result = results[1].concat(results[0]); }); }, 400), From 2fb64772f4ca05453bdc9e1ed304aacac245c81c Mon Sep 17 00:00:00 2001 From: Thomas Rudin Date: Sun, 21 Apr 2019 21:51:43 +0200 Subject: [PATCH 2/3] improved search --- server/static/css/custom.css | 8 +-- server/static/js/LayerManager.js | 31 +++++++++++- server/static/js/main.js | 15 +----- server/static/js/search/SearchInput.js | 25 +++++++--- server/static/js/search/SearchMenu.js | 12 ++++- server/static/js/search/SearchResult.js | 21 +++++++- server/static/js/search/SearchService.js | 62 +++++++++++++----------- server/static/js/search/SearchStore.js | 2 + 8 files changed, 120 insertions(+), 56 deletions(-) diff --git a/server/static/css/custom.css b/server/static/css/custom.css index 20f2577..f376691 100644 --- a/server/static/css/custom.css +++ b/server/static/css/custom.css @@ -18,9 +18,9 @@ body { #search-menu { position: absolute; - top: 20%; - bottom: 20%; - left: 20%; - right: 20%; + top: 5%; + bottom: 5%; + left: 5%; + right: 5%; z-index: 99999; } diff --git a/server/static/js/LayerManager.js b/server/static/js/LayerManager.js index 91756ec..5557944 100644 --- a/server/static/js/LayerManager.js +++ b/server/static/js/LayerManager.js @@ -1,18 +1,47 @@ /* exported LayerManager */ +/* globals RealtimeTileLayer: true */ -function LayerManager(layers, map){ +function LayerManager(wsChannel, layers, map){ this.listeners = []; this.currentLayer = layers[0]; this.layers = layers; + this.map = map; + this.layerObjects = {}; var self = this; + //All layers + layers.forEach(function(layer){ + var tileLayer = new RealtimeTileLayer(wsChannel, layer.id, map); + self.layerObjects[layer.name] = tileLayer; + }); + map.on('baselayerchange', function (e) { self.setLayerId(e.layer.layerId); }); + //current layer + var currentLayer = this.getCurrentLayer(); + this.layerObjects[currentLayer.name].addTo(map); } +LayerManager.prototype.switchLayer = function(layerId){ + var self = this; + Object.keys(this.layerObjects).forEach(function(key){ + var layerObj = self.layerObjects[key]; + if (self.map.hasLayer(layerObj)){ + self.map.removeLayer(layerObj); + } + }); + + Object.keys(this.layerObjects).forEach(function(key){ + var layerObj = self.layerObjects[key]; + if (layerObj.layerId == layerId){ + self.map.addLayer(layerObj); + } + }); +}; + LayerManager.prototype.setLayerId = function(layerId){ var self = this; this.layers.forEach(function(layer){ diff --git a/server/static/js/main.js b/server/static/js/main.js index 1d53331..b670e09 100644 --- a/server/static/js/main.js +++ b/server/static/js/main.js @@ -15,22 +15,11 @@ api.getConfig().then(function(cfg){ map.attributionControl.addAttribution('Minetest Mapserver'); - var layers = {}; var overlays = {}; - window.layerMgr = new LayerManager(cfg.layers, map); + window.layerMgr = new LayerManager(wsChannel, cfg.layers, map); layerMgr.setLayerId( Hashroute.getLayerId() ); - //All layers - cfg.layers.forEach(function(layer){ - var tileLayer = new RealtimeTileLayer(wsChannel, layer.id, map); - layers[layer.name] = tileLayer; - }); - - //current layer - var currentLayer = layerMgr.getCurrentLayer(); - layers[currentLayer.name].addTo(map); - //All overlays Overlaysetup(cfg, map, overlays, wsChannel, layerMgr); @@ -43,7 +32,7 @@ api.getConfig().then(function(cfg){ } //layer control - L.control.layers(layers, overlays, { position: "topright" }).addTo(map); + L.control.layers(layerMgr.layerObjects, overlays, { position: "topright" }).addTo(map); Hashroute.setup(map, layerMgr); diff --git a/server/static/js/search/SearchInput.js b/server/static/js/search/SearchInput.js index 8a1817b..adaebd8 100644 --- a/server/static/js/search/SearchInput.js +++ b/server/static/js/search/SearchInput.js @@ -5,21 +5,32 @@ var SearchInput = { view: function(){ function handleInput(e){ - SearchService.search(e.target.value); + SearchStore.query = e.target.value; + } + + function handleKeyDown(e){ + if (e.keyCode == 13){ + SearchService.search(); + } + } + + function handleDoSearch(){ + SearchService.search(); } return m("div", { class: "input-group mb-3" }, [ - m("div", { class: "input-group-prepend" }, [ - m("span", { class: "input-group-text" }, [ - m("i", { class: "fa fa-search"}) - ]) - ]), m("input[type=text]", { placeholder: "Search", class: "form-control", oninput: handleInput, + onkeydown: handleKeyDown, value: SearchStore.query - }) + }), + m("div", { class: "input-group-append", onclick: handleDoSearch }, [ + m("span", { class: "input-group-text" }, [ + m("i", { class: "fa fa-search"}) + ]) + ]) ]); } }; diff --git a/server/static/js/search/SearchMenu.js b/server/static/js/search/SearchMenu.js index a493832..f1da9d9 100644 --- a/server/static/js/search/SearchMenu.js +++ b/server/static/js/search/SearchMenu.js @@ -8,7 +8,7 @@ var SearchMenu = { var style = {}; - if (!SearchStore.query) { + if (!SearchStore.show) { style.display = "none"; } @@ -16,13 +16,21 @@ var SearchMenu = { SearchService.clear(); } + function getContent(){ + if (SearchStore.busy){ + return m("div", m("i", { class: "fa fa-spinner"})); + } else { + return m(SearchResult, { map: vnode.attrs.map }); + } + } + return m("div", { class: "card", id: "search-menu", style: style }, [ m("div", { class: "card-header" }, [ m("i", { class: "fa fa-search"}), "Search", m("i", { class: "fa fa-times float-right", onclick: close }), ]), - m("div", { class: "card-body", style: {overflow: "auto"} }, m(SearchResult, { map: vnode.attrs.map })) + m("div", { class: "card-body", style: {overflow: "auto"} }, getContent()) ]); } }; diff --git a/server/static/js/search/SearchResult.js b/server/static/js/search/SearchResult.js index 48223a7..d7da451 100644 --- a/server/static/js/search/SearchResult.js +++ b/server/static/js/search/SearchResult.js @@ -12,7 +12,6 @@ var SearchResult = { } function getPos(obj){ - var layer = layerMgr.getLayerByY(obj.y); var text = obj.x + "/" + obj.y + "/" + obj.z; return m("span", {class:"badge badge-success"}, text); @@ -24,6 +23,21 @@ var SearchResult = { var description = obj.type; var type = obj.type; + if (obj.type == "train"){ + description = [ + m("span", obj.attributes.station), + " ", + m("span", {class:"badge badge-info"}, obj.attributes.line) + ]; + + type = m("i", { class: "fa fa-subway" }); + } + + if (obj.type == "travelnet"){ + description = m("span", obj.attributes.station_name); + type = m("img", { src: "pics/travelnet_inv.png" }); + } + if (obj.type == "poi"){ description = m("span", obj.attributes.name); type = m("img", { src: "css/images/marker-icon.png" }); @@ -50,7 +64,12 @@ var SearchResult = { } function onclick(){ + var layer = layerMgr.getLayerByY(obj.y); + + layerMgr.switchLayer(layer.id); + map.setView([obj.z, obj.x], 12); + SearchStore.show = false; } return m("tr", {"class": row_classes}, [ diff --git a/server/static/js/search/SearchService.js b/server/static/js/search/SearchService.js index 18f8e14..7cf61e8 100644 --- a/server/static/js/search/SearchService.js +++ b/server/static/js/search/SearchService.js @@ -3,49 +3,55 @@ var SearchService = { - search: function(q){ - SearchStore.query = q; - + search: function(){ + SearchStore.show = true; this.fetchData(); }, - fetchData: debounce(function(){ + fetchData: function(){ SearchStore.result = []; if (!SearchStore.query){ return; } - function searchFor(q){ - q.pos1 = { x:-2048, y:-2048, z:-2048 }; - q.pos2 = { x:2048, y:2048, z:2048 }; - return api.getMapObjects(q); + SearchStore.busy = true; + + function searchFor(type, key, valuelike){ + return api.getMapObjects({ + pos1: { x:-2048, y:-2048, z:-2048 }, + pos2: { x:2048, y:2048, z:2048 }, + type: type, + attributelike: { + key: key, + value: "%" + valuelike +"%" + } + }); } - var shop_prom = searchFor({ - type: "shop", - attributelike: { - key: "out_item", - value: "%" + SearchStore.query + "%" - } + var prom_list = []; + + prom_list.push(searchFor("shop", "out_item", SearchStore.query)); + prom_list.push(searchFor("poi", "name", SearchStore.query)); + prom_list.push(searchFor("train", "station", SearchStore.query)); + prom_list.push(searchFor("travelnet", "station_name", SearchStore.query)); + + Promise.all(prom_list) + .then(function(results){ + + var arr = []; + results.forEach(function(r) { + arr = arr.concat(r); + }); + + SearchStore.result = arr; + SearchStore.busy = false; }); - var poi_prom = searchFor({ - type: "poi", - attributelike: { - key: "name", - value: "%" + SearchStore.query + "%" - } - }); - - Promise.all([shop_prom, poi_prom]).then(function(results){ - SearchStore.result = results[1].concat(results[0]); - }); - - }, 400), + }, clear: function(){ - SearchStore.query = ""; SearchStore.result = []; + SearchStore.show = false; } }; diff --git a/server/static/js/search/SearchStore.js b/server/static/js/search/SearchStore.js index 656d4e6..397abb0 100644 --- a/server/static/js/search/SearchStore.js +++ b/server/static/js/search/SearchStore.js @@ -2,5 +2,7 @@ var SearchStore = { query: "", + show: false, + busy: false, result: [] }; From d433897a6a7abfedc65dca9c0155a434aa420116 Mon Sep 17 00:00:00 2001 From: Thomas Rudin Date: Mon, 22 Apr 2019 17:17:41 +0200 Subject: [PATCH 3/3] fix layer switch --- server/static/js/LayerManager.js | 9 +++++---- server/static/js/main.js | 3 +-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/static/js/LayerManager.js b/server/static/js/LayerManager.js index 5557944..4f724fe 100644 --- a/server/static/js/LayerManager.js +++ b/server/static/js/LayerManager.js @@ -1,7 +1,7 @@ /* exported LayerManager */ /* globals RealtimeTileLayer: true */ -function LayerManager(wsChannel, layers, map){ +function LayerManager(wsChannel, layers, map, currentLayerId){ this.listeners = []; this.currentLayer = layers[0]; this.layers = layers; @@ -14,15 +14,16 @@ function LayerManager(wsChannel, layers, map){ layers.forEach(function(layer){ var tileLayer = new RealtimeTileLayer(wsChannel, layer.id, map); self.layerObjects[layer.name] = tileLayer; + if (layer.id == currentLayerId){ + tileLayer.addTo(map); + self.currentLayer = layer; + } }); map.on('baselayerchange', function (e) { self.setLayerId(e.layer.layerId); }); - //current layer - var currentLayer = this.getCurrentLayer(); - this.layerObjects[currentLayer.name].addTo(map); } LayerManager.prototype.switchLayer = function(layerId){ diff --git a/server/static/js/main.js b/server/static/js/main.js index b670e09..53837f6 100644 --- a/server/static/js/main.js +++ b/server/static/js/main.js @@ -17,8 +17,7 @@ api.getConfig().then(function(cfg){ var overlays = {}; - window.layerMgr = new LayerManager(wsChannel, cfg.layers, map); - layerMgr.setLayerId( Hashroute.getLayerId() ); + window.layerMgr = new LayerManager(wsChannel, cfg.layers, map, Hashroute.getLayerId()); //All overlays Overlaysetup(cfg, map, overlays, wsChannel, layerMgr);