mirror of
https://gitlab.com/wgp/dougal/software.git
synced 2025-12-06 10:37:07 +00:00
Merge branch '114-allow-users-to-show-arbitrary-geojson-on-the-map' into 'devel'
Resolve "Allow users to show arbitrary GeoJSON on the map." Closes #114 See merge request wgp/dougal/software!37
This commit is contained in:
@@ -375,7 +375,8 @@ export default {
|
||||
}
|
||||
],
|
||||
labels: {},
|
||||
hashMarker: null
|
||||
hashMarker: null,
|
||||
references: {}
|
||||
};
|
||||
},
|
||||
|
||||
@@ -474,7 +475,7 @@ export default {
|
||||
bounds._northEast.lng,
|
||||
bounds._northEast.lat
|
||||
].map(i => i.toFixed(bboxScale)).join(",");
|
||||
const limit = 10000;
|
||||
const limit = 10000; // Empirical value
|
||||
|
||||
const query = new URLSearchParams({bbox, limit});
|
||||
|
||||
@@ -520,7 +521,9 @@ export default {
|
||||
|
||||
l.layer.lastRequestURL = url;
|
||||
} else {
|
||||
console.warn("Too much data from", url);
|
||||
console.warn(`Too much data from ${url} (${layer.features.length ?? layer.length} ≥ ${limit} features)`);
|
||||
|
||||
this.showSnack([`Layer ‘${l.layer.options.userLayerName}’ is too large: ${layer.features.length ?? layer.length} features; maximum is ${limit}`, "error"]);
|
||||
}
|
||||
})
|
||||
.finally( () => {
|
||||
@@ -677,7 +680,109 @@ export default {
|
||||
this.labels = await this.api([url]) || [];
|
||||
},
|
||||
|
||||
...mapActions(["api"])
|
||||
|
||||
removeUserLayers () {
|
||||
map.eachLayer( layer => {
|
||||
if (layer.options.userLayer === true) {
|
||||
console.log("Removing", layer);
|
||||
layer.eachLayer( sublayer => {
|
||||
const idx = this.layerRefreshConfig.findIndex(i => i.layer == layer);
|
||||
if (idx != -1) {
|
||||
this.layerRefreshConfig.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
map.removeLayer(layer);
|
||||
this.references.layerControl.removeLayer(layer);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
async addUserLayers (userLayers) {
|
||||
|
||||
const options = {
|
||||
userLayer: true,
|
||||
style (feature) {
|
||||
const style = {
|
||||
stroke: undefined,
|
||||
color: "grey",
|
||||
weight: 2,
|
||||
opacity: 0.5,
|
||||
lineCap: undefined,
|
||||
lineJoin: undefined,
|
||||
dashArray: undefined,
|
||||
dashOffset: undefined,
|
||||
fill: undefined,
|
||||
fillColor: "lightgrey",
|
||||
fillOpacity: 0.5,
|
||||
fillRule: undefined
|
||||
};
|
||||
|
||||
for (let key in style) {
|
||||
switch (key) {
|
||||
case "color":
|
||||
style[key] = feature.properties?.colour ?? feature.properties?.color ?? style[key];
|
||||
break;
|
||||
case "fillColor":
|
||||
style[key] = feature.properties?.fillColour ?? feature.properties?.fillColor ?? style[key];
|
||||
break;
|
||||
default:
|
||||
style[key] = feature.properties?.[key] ?? style[key];
|
||||
}
|
||||
|
||||
if (typeof style[key] === "undefined") {
|
||||
delete style[key];
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
};
|
||||
|
||||
const userLayerGroups = {};
|
||||
userLayers.forEach(layer => {
|
||||
if (!(layer.name in userLayerGroups)) {
|
||||
userLayerGroups[layer.name] = [];
|
||||
}
|
||||
userLayerGroups[layer.name].push(layer);
|
||||
});
|
||||
|
||||
for (let userLayerName in userLayerGroups) {
|
||||
const userLayerGroup = userLayerGroups[userLayerName];
|
||||
|
||||
const layer = L.featureGroup(null, {userLayer: true, userLayerGroup: true, userLayerName});
|
||||
userLayerGroup.forEach(l => {
|
||||
const sublayer = L.geoJSON(null, {...options, userLayerName});
|
||||
layer.addLayer(sublayer);
|
||||
sublayer.on('add', ({target}) => {
|
||||
this.refreshLayers([target])
|
||||
});
|
||||
|
||||
const refreshConfig = {
|
||||
layer: sublayer,
|
||||
url: (query = "") => {
|
||||
return `/files/${l.path}`;
|
||||
}
|
||||
};
|
||||
|
||||
this.layerRefreshConfig.push(refreshConfig);
|
||||
});
|
||||
|
||||
layer.on('add', ({target}) => {
|
||||
this.refreshLayers(target.getLayers())
|
||||
});
|
||||
this.references.layerControl.addOverlay(layer, `<span title="User layer" style="text-decoration: dotted underline;">${userLayerName}</span>`);
|
||||
}
|
||||
},
|
||||
|
||||
async fetchUserLayers () {
|
||||
const url = `/project/${this.$route.params.project}/gis/layer`;
|
||||
const userLayers = await this.api([url]) || [];
|
||||
|
||||
this.removeUserLayers();
|
||||
this.addUserLayers(userLayers);
|
||||
},
|
||||
|
||||
...mapActions(["api", "showSnack"])
|
||||
|
||||
},
|
||||
|
||||
@@ -756,6 +861,9 @@ export default {
|
||||
const layerControl = L.control.layers(tileMaps, layers).addTo(map);
|
||||
const scaleControl = L.control.scale().addTo(map);
|
||||
|
||||
this.references.layerControl = layerControl;
|
||||
this.references.scaleControl = scaleControl;
|
||||
|
||||
if (init.position) {
|
||||
map.setView(init.position.slice(1), init.position[0]);
|
||||
} else {
|
||||
@@ -783,10 +891,13 @@ export default {
|
||||
map.on('layeradd', this.updateURL);
|
||||
map.on('layerremove', this.updateURL);
|
||||
|
||||
|
||||
this.layerRefreshConfig.forEach( l => {
|
||||
l.layer.on('add', ({target}) => this.refreshLayers([target]));
|
||||
});
|
||||
|
||||
this.fetchUserLayers();
|
||||
|
||||
if (init.position) {
|
||||
this.refreshLayers();
|
||||
} else {
|
||||
|
||||
@@ -123,6 +123,12 @@ app.map({
|
||||
'/project/:project/gis/final/:featuretype(line|point)': {
|
||||
get: [ mw.gis.project.final ]
|
||||
},
|
||||
'/project/:project/gis/layer': {
|
||||
get: [ mw.etag.noSave, mw.gis.project.layer.get ]
|
||||
},
|
||||
'/project/:project/gis/layer/:name': {
|
||||
get: [ mw.etag.noSave, mw.gis.project.layer.get ]
|
||||
},
|
||||
|
||||
/*
|
||||
* Line endpoints
|
||||
|
||||
@@ -2,5 +2,6 @@ module.exports = {
|
||||
bbox: require('./bbox'),
|
||||
preplot: require('./preplot'),
|
||||
raw: require('./raw'),
|
||||
final: require('./final')
|
||||
final: require('./final'),
|
||||
layer: require('./layer')
|
||||
};
|
||||
|
||||
18
lib/www/server/api/middleware/gis/project/layer/get.js
Normal file
18
lib/www/server/api/middleware/gis/project/layer/get.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
const { gis } = require('../../../../../lib/db');
|
||||
|
||||
module.exports = async function (req, res, next) {
|
||||
|
||||
try {
|
||||
const layers = await gis.project.layer.get(req.params.project, req.params.name);
|
||||
if (req.params.name && (!layers || !layers.length)) {
|
||||
res.status(404).json({message: "Not found"});
|
||||
} else {
|
||||
res.status(200).send(layers ?? []);
|
||||
}
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
};
|
||||
3
lib/www/server/api/middleware/gis/project/layer/index.js
Normal file
3
lib/www/server/api/middleware/gis/project/layer/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
get: require('./get')
|
||||
};
|
||||
@@ -3,5 +3,6 @@ module.exports = {
|
||||
bbox: require('./bbox'),
|
||||
preplot: require('./preplot'),
|
||||
raw: require('./raw'),
|
||||
final: require('./final')
|
||||
final: require('./final'),
|
||||
layer: require('./layer')
|
||||
};
|
||||
|
||||
31
lib/www/server/lib/db/gis/project/layer/get.js
Normal file
31
lib/www/server/lib/db/gis/project/layer/get.js
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
const { setSurvey } = require('../../../connection');
|
||||
|
||||
async function get (projectId, layerName = null, options = {}) {
|
||||
|
||||
const client = await setSurvey(projectId);
|
||||
|
||||
const text = `
|
||||
SELECT path, (data - 'type') data
|
||||
FROM files f
|
||||
INNER JOIN file_data
|
||||
USING (hash)
|
||||
WHERE data->>'type' = 'map_layer'
|
||||
AND data->>'format' = 'geojson'
|
||||
AND (data->>'name' = $1
|
||||
OR $1 IS NULL);
|
||||
`;
|
||||
|
||||
const values = [ layerName ];
|
||||
|
||||
const res = await client.query(text, values);
|
||||
client.release();
|
||||
|
||||
if (res.rows && res.rows.length) {
|
||||
return res.rows.map(row => ({...row.data, path: row.path}));
|
||||
} else {
|
||||
throw {status: 404, message: "Not found"};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = get;
|
||||
4
lib/www/server/lib/db/gis/project/layer/index.js
Normal file
4
lib/www/server/lib/db/gis/project/layer/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
module.exports = {
|
||||
get: require('./get')
|
||||
};
|
||||
Reference in New Issue
Block a user