Commit 1db69b4b authored by Adrien Pavie's avatar Adrien Pavie
Browse files

Better filtering of background imagery

parent eb0789be
/node_modules
/build
/res
......@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## Next version (to be released)
### Added
- In API, list of background imagery can be stored locally
### Changed
- List of available background imagery is limited to most pertinent ones
### Fixed
- Geometry sorted by date
- Avoid partial drawing when double-tap on mobile
......
......@@ -63,6 +63,11 @@ npm run start
PORT=12345 npm run start
```
Environment variables which can be used for API :
* `PORT` : for changing listening port (defaults to 3000)
* `BG_DL` : for setting how to retrieve background imagery file (values being `0` to disable usage, `1` for downloading from Internet, and `2` for reading from `res/imagery.geojson` file)
## License
......
......@@ -7,6 +7,7 @@ const express = require('express');
const cors = require('cors');
const db = require("./db");
const request = require("request-promise-native");
const fs = require("fs");
const booleanContains = require("@turf/boolean-contains").default;
// Init API
......@@ -26,69 +27,93 @@ app.use(require("compression")());
let backgroundLayers = [
{ id: "MAPNIK", name: "OpenStreetMap (standard)", type: "tms", category: "osmbasedmap", url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution: "© OpenStreetMap contributors, CC-BY-SA 2.0", geometry: null }
];
const exclusionList = [ "EOXAT2018CLOUDLESS" ];
const fixTypes = { "Bing": "photo", "fr.ign.bdortho": "photo" };
const exclusionList = [
"EOXAT2018CLOUDLESS", "osmbe-nl", "osmbe-fr", "osmbe", "openpt_map", "osm-mapnik-no_labels",
"osmfr", "osm-mapnik-german_style", "tf-landscape", "tf-cycle", "wikimedia-map", "HDM_HOT",
"osmie_tie_not_counties", "GSGS4136"
];
const fixCategory = { "Bing": "photo", "fr.ign.bdortho": "photo" };
function parseSaveBackgroundLayers(geojson) {
const toKeep = geojson.features.filter(layer => {
const p = layer.properties;
p.type = p.type || fixCategory[p.id] || "other";
return p.id && p.url && p.name
&& !exclusionList.includes(p.id)
&& ["tms", "wms", "bing", "wmts"].includes(p.type)
&& !["qa", "elevation", "other"].includes(p.category)
&& (!p.min_zoom || p.min_zoom <= 5)
&& (!p.max_zoom || p.max_zoom >= 14)
&& !p.overlay;
})
.map(layer => {
const p = layer.properties;
function retrieveBackgroundLayers() {
request("https://osmlab.github.io/editor-layer-index/imagery.geojson")
.then(txt => JSON.parse(txt))
.then(geojson => {
const toKeep = geojson.features.filter(layer => {
const p = layer.properties;
return p.id && p.url && p.name
&& !exclusionList.includes(p.id)
&& ["tms", "wms", "bing", "wmts"].includes(p.type)
&& (!p.category || !["qa", "elevation", "other"].includes(p.category))
&& (!p.min_zoom || p.min_zoom <= 5)
&& (!p.max_zoom || p.max_zoom >= 14)
&& !p.overlay;
})
.map(layer => {
const p = layer.properties;
let subdomains = undefined;
if(p.url.includes("{switch")) {
const search = /{switch:(.+?)}/g.exec(p.url);
if(search.length >= 1) { subdomains = search[1].split(","); }
}
let subdomains = undefined;
if(p.url.includes("{switch")) {
const search = /{switch:(.+?)}/g.exec(p.url);
if(search.length >= 1) { subdomains = search[1].split(","); }
}
let attribution = p.attribution && p.attribution.text ? p.attribution.text : undefined;
if(p.attribution && p.attribution.url) {
attribution = `<a href="${p.attribution.url}" target="_blank">${attribution || "License"}</a>`;
}
let attribution = p.attribution && p.attribution.text ? p.attribution.text : undefined;
if(p.attribution && p.attribution.url) {
attribution = `<a href="${p.attribution.url}" target="_blank">${attribution || "License"}</a>`;
}
return {
id: p.id,
name: p.name,
type: p.type,
category: fixCategory[p.id] || p.category || "other",
url: p.url
.replace(/{zoom}/g, '{z}')
.replace(/{switch:.+?}/g, '{s}'),
attribution: attribution,
geometry: layer.geometry,
best: p.best || false,
minZoom: p.min_zoom || 0,
maxZoom: p.max_zoom || 18,
subdomains: subdomains,
end_date: p.end_date || Date.now()
};
});
return {
id: p.id,
name: p.name,
type: p.type,
category: fixTypes[p.id] || p.category || "other",
url: p.url
.replace(/{zoom}/g, '{z}')
.replace(/{switch:.+?}/g, '{s}'),
attribution: attribution,
geometry: layer.geometry,
best: p.best || false,
minZoom: p.min_zoom || 0,
maxZoom: p.max_zoom || 18,
subdomains: subdomains
};
});
if(toKeep.length > 0) {
backgroundLayers = toKeep;
console.log("Updated background layers");
}
}
if(toKeep.length > 0) {
backgroundLayers = toKeep;
console.log("Updated background layers");
}
})
.catch(e => {
console.error(e);
console.error("Can't retrieve background layers");
});
function retrieveBackgroundLayers() {
// Read from local file
if(process.env.BG_DL && parseInt(process.env.BG_DL) === 2) {
fs.readFile("res/imagery.geojson", (err, data) => {
if(err) { console.error(err); }
else {
parseSaveBackgroundLayers(JSON.parse(data));
}
});
}
// Download from Internet
else {
request("https://osmlab.github.io/editor-layer-index/imagery.geojson")
.then(txt => JSON.parse(txt))
.then(parseSaveBackgroundLayers)
.catch(e => {
console.error(e);
console.error("Can't retrieve background layers");
});
}
}
if(!process.env.BG_DL || process.env.BG_DL === 1) {
if(!process.env.BG_DL || parseInt(process.env.BG_DL) === 1) {
setTimeout(retrieveBackgroundLayers, 15000); // Delay first call
setInterval(retrieveBackgroundLayers, 1000*60*60*24); // Once per day
}
else if(process.env.BG_DL && parseInt(process.env.BG_DL) === 2) {
setTimeout(retrieveBackgroundLayers, 2500); // Delay first call
console.log("Read background imagery config from local file");
}
else {
console.log("Background imagery download disabled");
}
......@@ -118,9 +143,33 @@ app.use((req, res) => {
io.on("connection", socket => {
// Find available backgrounds
socket.on("background_find", coordinates => {
socket.emit("background_found", backgroundLayers.filter(layer => (
// Get all layers available in this area
const potential = {};
backgroundLayers.filter(layer => (
layer.geometry === null || booleanContains(layer.geometry, { type: "Point", coordinates: coordinates })
)));
))
.forEach(layer => {
if(!potential[layer.category]) { potential[layer.category] = [ layer ]; }
else { potential[layer.category].push(layer); }
});
// Limit to 2 per category
let toSend = [];
Object.entries(potential)
.forEach(e => {
const [ cat, layers ] = e;
layers.sort((a, b) => {
if(a.best || b.best) {
return a.best ? -1 : 1;
}
else {
return (new Date(a.end_date)).getTime() - (new Date(b.end_date)).getTime();
}
});
toSend = toSend.concat(["photo","historicphoto"].includes(cat) ? layers.slice(0, 2) : layers);
});
socket.emit("background_found", toSend);
});
// Create new sketch
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment