From 25e177fbb9a1c8e46eae864f62d4a7ddc6350b1c Mon Sep 17 00:00:00 2001 From: Peter Nerlich Date: Mon, 17 Jan 2022 08:53:45 +0100 Subject: [PATCH] Fix Trainlines (#177) (#230) * add global getMapDataWithAttributeLikeGlobalQuery * add getMapDataWithAttributeLikeGlobalQuery for postgres * perform global search when position missing (sqlite) * perform global query when pos missing (postgres) * TrainelineOverlay: query full information on visible lines before drawing * hopefully fix errors, minor efficiency improvements * stupid mistake * you know it's probably late when mistakes like this happen * same for postgres * more optimizations --- mapobjectdb/postgres/mapobjects.go | 25 ++-- mapobjectdb/postgres/sql.go | 14 ++ mapobjectdb/sqlite/mapobjects.go | 25 ++-- mapobjectdb/sqlite/sql.go | 15 ++ public/js/map/overlays/TrainlineOverlay.js | 160 ++++++++++++--------- 5 files changed, 152 insertions(+), 87 deletions(-) diff --git a/mapobjectdb/postgres/mapobjects.go b/mapobjectdb/postgres/mapobjects.go index 695ef97..ee5cb7a 100644 --- a/mapobjectdb/postgres/mapobjects.go +++ b/mapobjectdb/postgres/mapobjects.go @@ -28,14 +28,23 @@ func (db *PostgresAccessor) GetMapData(q *mapobjectdb.SearchQuery) ([]*mapobject ) } else { - //attribute like search - rows, err = db.db.Query(getMapDataWithAttributeLikePosQuery, - q.Type, - q.Pos1.X, q.Pos1.Y, q.Pos1.Z, - q.Pos2.X, q.Pos2.Y, q.Pos2.Z, - q.AttributeLike.Key, q.AttributeLike.Value, - limit, - ) + if (q.Pos1 == nil || q.Pos2 == nil) { + //global attribute like search + rows, err = db.db.Query(getMapDataWithAttributeLikeGlobalQuery, + q.AttributeLike.Key, q.AttributeLike.Value, + q.Type, + limit, + ) + } else { + //attribute like search + rows, err = db.db.Query(getMapDataWithAttributeLikePosQuery, + q.Type, + q.Pos1.X, q.Pos1.Y, q.Pos1.Z, + q.Pos2.X, q.Pos2.Y, q.Pos2.Z, + q.AttributeLike.Key, q.AttributeLike.Value, + limit, + ) + } } if err != nil { diff --git a/mapobjectdb/postgres/sql.go b/mapobjectdb/postgres/sql.go index 7fca499..579733a 100644 --- a/mapobjectdb/postgres/sql.go +++ b/mapobjectdb/postgres/sql.go @@ -29,6 +29,20 @@ and o.posx <= $5 and o.posy <= $6 and o.posz <= $7 order by o.id limit $10 ` +const getMapDataWithAttributeLikeGlobalQuery = ` +select o.id, o.type, o.mtime, + o.x, o.y, o.z, + o.posx, o.posy, o.posz, + oa.key, oa.value +from objects o +left join object_attributes oa on o.id = oa.objectid +where o.id in ( + select objectid from object_attributes where key = $2 and value ilike $3 +) +and o.type = $1 +order by o.id +limit $4 +` const removeMapDataQuery = ` delete from objects where posx = $1 and posy = $2 and posz = $3 diff --git a/mapobjectdb/sqlite/mapobjects.go b/mapobjectdb/sqlite/mapobjects.go index ba378c0..557cc6a 100644 --- a/mapobjectdb/sqlite/mapobjects.go +++ b/mapobjectdb/sqlite/mapobjects.go @@ -28,14 +28,23 @@ func (db *Sqlite3Accessor) GetMapData(q *mapobjectdb.SearchQuery) ([]*mapobjectd ) } else { - //attribute like search - rows, err = db.db.Query(getMapDataWithAttributeLikePosQuery, - q.AttributeLike.Key, q.AttributeLike.Value, - q.Type, - q.Pos1.X, q.Pos1.Y, q.Pos1.Z, - q.Pos2.X, q.Pos2.Y, q.Pos2.Z, - limit, - ) + if (q.Pos1 == nil || q.Pos2 == nil) { + //global attribute like search + rows, err = db.db.Query(getMapDataWithAttributeLikeGlobalQuery, + q.AttributeLike.Key, q.AttributeLike.Value, + q.Type, + limit, + ) + } else { + //attribute like search + rows, err = db.db.Query(getMapDataWithAttributeLikePosQuery, + q.AttributeLike.Key, q.AttributeLike.Value, + q.Type, + q.Pos1.X, q.Pos1.Y, q.Pos1.Z, + q.Pos2.X, q.Pos2.Y, q.Pos2.Z, + limit, + ) + } } if err != nil { diff --git a/mapobjectdb/sqlite/sql.go b/mapobjectdb/sqlite/sql.go index 6517bc8..6d5f979 100644 --- a/mapobjectdb/sqlite/sql.go +++ b/mapobjectdb/sqlite/sql.go @@ -31,6 +31,21 @@ order by o.id limit ? ` +const getMapDataWithAttributeLikeGlobalQuery = ` +select o.id, o.type, o.mtime, + o.x, o.y, o.z, + o.posx, o.posy, o.posz, + oa.key, oa.value +from objects o +left join object_attributes oa on o.id = oa.objectid +where o.id in ( + select objectid from object_attributes where key = ? and value like ? +) +and o.type = ? +order by o.id +limit ? +` + const removeMapDataQuery = ` delete from objects where posx = ? and posy = ? and posz = ? ` diff --git a/public/js/map/overlays/TrainlineOverlay.js b/public/js/map/overlays/TrainlineOverlay.js index bf8a1cf..73cf129 100644 --- a/public/js/map/overlays/TrainlineOverlay.js +++ b/public/js/map/overlays/TrainlineOverlay.js @@ -1,13 +1,16 @@ import AbstractGeoJsonOverlay from './AbstractGeoJsonOverlay.js'; +import { getMapObjects } from '../../api.js'; export default AbstractGeoJsonOverlay.extend({ initialize: function() { AbstractGeoJsonOverlay.prototype.initialize.call(this, "train"); - }, - - createGeoJson: function(objects){ - - var geoJsonLayer = L.geoJSON([], { + this.cache = { + lines: {}, // { "A1":[] } + lineColors: {}, // { "A1": "red" } + lineFeat: [] + }; + this.pendingQueries = []; + this.lastLayer = L.geoJSON([], { onEachFeature: function(feature, layer){ if (feature.properties && feature.properties.popupContent) { layer.bindPopup(feature.properties.popupContent); @@ -29,86 +32,101 @@ export default AbstractGeoJsonOverlay.extend({ } } }); + }, + + createGeoJson: function(objects){ + var self = this; - var lines = {}; // { "A1":[] } - var lineColors = {}; // { "A1": "red" } - - //Sort and add lines + // which unique lines do objects belong to? + var lines = []; objects.forEach(function(obj){ - if (!obj.attributes.line) - return; - - var line = lines[obj.attributes.line]; - if (!line){ - line = []; - lines[obj.attributes.line] = line; - //default or new color - lineColors[obj.attributes.line] = "#ff7800"; + if (obj.attributes.line && lines.indexOf(obj.attributes.line) == -1) { + lines.push(obj.attributes.line); } - - if (obj.attributes.color){ - //new color - lineColors[obj.attributes.line] = obj.attributes.color; - } - - line.push(obj); }); - //Order by index and display - Object.keys(lines).forEach(function(linename){ - lines[linename].sort(function(a,b){ - return parseInt(a.attributes.index) - parseInt(b.attributes.index); - }); + // query for each line, add to cache + lines.forEach(function(linename){ + if (!self.cache.lines[linename]){ + // only request if not in cache. + // if someone changed the train lines, the user has to reload. sorry. + self.pendingQueries.push(linename); + getMapObjects({ + type: self.type, + attributelike: { + key: "line", + value: linename + } + }) + .then(function(objects){ + objects.sort(function(a,b){ + return parseInt(a.attributes.index) - parseInt(b.attributes.index); + }); - var coords = []; - var stations = []; + self.cache.lines[linename] = objects; + // already sorted, determine color + self.cache.lineColors[linename] = "#ff7800"; + for (var i = objects.length-1; i >= 0; i--) { + // find the last element specifying a color + // as was previous behaviour, but be more efficient + if (objects[i].attributes.color){ + self.cache.lineColors[linename] = objects[i].attributes.color; + break; + } + } - //Add stations - lines[linename].forEach(function(entry){ - coords.push([entry.x, entry.z]); + var feat = { + coords: [], + stations: [], + feature: null + }; + //Add stations + objects.forEach(function(entry){ + feat.coords.push([entry.x, entry.z]); - if (entry.attributes.station) { - stations.push({ - "type": "Feature", - "properties": { - "name": entry.attributes.station, - "color": lineColors[linename], - "popupContent": "Train-station (Line " + entry.attributes.line + ")
" + - entry.attributes.station - }, - "geometry": { - "type": "Point", - "coordinates": [entry.x, entry.z] + if (entry.attributes.station) { + feat.stations.push({ + "type": "Feature", + "properties": { + "name": entry.attributes.station, + "color": self.cache.lineColors[linename], + "popupContent": "Train-station (Line " + entry.attributes.line + ")
" + + entry.attributes.station + }, + "geometry": { + "type": "Point", + "coordinates": [entry.x, entry.z] + } + }); } }); - } - }); - var feature = { - "type":"Feature", - "geometry": { - "type":"LineString", - "coordinates":coords - }, - "properties":{ - "name": linename, - "color": lineColors[linename], - "popupContent": "Train-line (" + linename + ")" - } - }; + feat.feature = { + "type":"Feature", + "geometry": { + "type":"LineString", + "coordinates": feat.coords + }, + "properties":{ + "name": linename, + "color": self.cache.lineColors[linename], + "popupContent": "Train-line (" + linename + ")" + } + }; - //line-points - geoJsonLayer.addData(feature); - - //stations - stations.forEach(function(stationfeature){ - geoJsonLayer.addData(stationfeature); - }); + self.cache.lineFeat[linename] = feat; + //line-points + self.lastLayer.addData(feat.feature); + //stations + feat.stations.forEach(function(stationfeature){ + self.lastLayer.addData(stationfeature); + }); + }); + } }); - return geoJsonLayer; - } - + return self.lastLayer; + }, });