v2.1.1 Update (29/01/2020)
Release Update v2.1.1 - January 29 2020
Enhancement 🚀
Interaction/edit - hover class on each menu row. (489ff92)
const menu = _xyz.utils.wire()`<ul>`;
- _xyz.mapview.interaction.edit.feature.length && menu.appendChild(_xyz.utils.wire()`<li onclick=${update}>Update</li>`);
+ _xyz.mapview.interaction.edit.feature.length && menu.appendChild(_xyz.utils.wire()`<li class="off-white-hover" onclick=${update}>Update</li>`);
- _xyz.mapview.interaction.edit.feature.length && menu.appendChild(_xyz.utils.wire()`<li onclick=${undo}>Undo</li>`);
+ _xyz.mapview.interaction.edit.feature.length && menu.appendChild(_xyz.utils.wire()`<li class="off-white-hover" onclick=${undo}>Undo</li>`);
Info tip - (d2c5044)
- HTML content render correction
if (this.node) this.node.remove();
- this.node = _xyz.utils.wire()`<div class="infotip">${info}`;
+ this.node = _xyz.utils.wire()`<div class="infotip">`;
+
+ this.node.innerHTML = info;
_xyz.mapview.node.appendChild(this.node);
New Logout Button (ec7a0c1)
Added 'webkit' prefixes for background-size (b8c110a)
+ -webkit-background-size: contain;
OpenCage Gazetteer (6a049a2)
- Added environment variable 'KEY_OPENCAGE'.
"gazetteer": {
"provider": "OPENCAGE",
"placeholder": "e.g. Venice",
"bounds": "-13,48,5,62",
"code": "GB"
}
mod/gazetteer/opencage.js
const env = require('../env');
const fetch = require('../fetch');
module.exports = async (term, gazetteer) => {
const url = `https://api.opencagedata.com/geocode/v1/json?q=${encodeURIComponent(term)}`
+ `${gazetteer.code ? `&countrycode=${gazetteer.code}` : ''}`
+ `${gazetteer.bounds ? '&bounds=' + decodeURIComponent(gazetteer.bounds) : ''}`
+ `&key=${env.keys.OPENCAGE}`;
const fetched = await fetch(url);
if (fetched._err) return fetched;
return await fetched.results.map(f => ({
id: f.annotations.geohash,
label: f.formatted,
marker: [f.geometry.lng, f.geometry.lat],
source: 'opencage'
}));
}
Styling - added hide all for legends in categorized themes (f508ebf)
public/js/layers/view/style/_styles.mjs
// Allow hide all from legend
panel.appendChild(_xyz.utils.wire()`
<div class="switch-all" style="font-size: 90%; display:none;">Click on labels to switch visibity or
<a class="primary-colour" style="cursor: pointer;"
onclick=${e => {
e.stopPropagation();
layer.style.theme.hideAll = layer.style.theme.hideAll ? false : true; // control flag
if(!layer.filter.legend[layer.style.theme.field]) layer.filter.legend[layer.style.theme.field] = {};
layer.filter.legend[layer.style.theme.field].ni = []; // set initial values for filters
layer.filter.legend[layer.style.theme.field].in = [];
if(layer.style.theme.hideAll) { // apply all exclusions
Object.keys(layer.style.theme.cat).map(c => layer.filter.legend[layer.style.theme.field].ni.push(c));
layer.filter.legend[layer.style.theme.field].in = Object.keys(layer.style.theme.cat);
}
// count nodes to update excluding 'Multiple locations on cluster layers
let childNodes = layer.format === 'cluster' ? e.target.parentElement.nextSibling.children.length - 2 : e.target.parentElement.nextSibling.children.length;
for(let i = 0; i < childNodes; i++){ // apply styling
e.target.parentElement.nextSibling.children[i].style.textDecoration = layer.style.theme.hideAll ? 'line-through' : 'none';
e.target.parentElement.nextSibling.children[i].style.opacity = layer.style.theme.hideAll ? 0.8 : 1;
e.target.parentElement.nextSibling.children[i].style.fillOpacity = layer.style.theme.hideAll ? 0.8 : 1;
}
layer.reload(); // reload layer
}}>switch all</a>.`);
public/js/layers/view/style/_styles.mjs
function applyTheme(layer) {
+ // enable or hide 'switch all' filter.
+ panel.querySelector('.switch-all').style.display = layer.style.theme && layer.style.theme.type === 'categorized' ? 'block' : 'none';
// Empty legend.
layer.style.legend && layer.style.legend.remove();
Location/List ⚠️
- Added check on 'where' parameter (dd95d6a)
/routes/api/location/list.js
let q = `
SELECT ${fields.join(',')}
FROM ${tableDef.from}
+ ${tableDef.where ? `WHERE ${tableDef.where}` : ``}
ORDER BY ${tableDef.orderby || ''} NULLS LAST
LIMIT ${tableDef.limit || 100};`;
- Added check on 'orderby' parameter (97c192d)
/routes/api/location/list.js
let q = `
SELECT ${fields.join(',')}
FROM ${tableDef.from}
${tableDef.where ? `WHERE ${tableDef.where}` : ``}
+ ${tableDef.orderby ? `ORDER BY ${tableDef.orderby}` : ``} NULLS LAST
LIMIT ${tableDef.limit || 100};`;
Legend for categorized clusters (f517441)
- removed svg container
public/js/layers/view/style/legend/clusterCategorized.mjs
let image_container = _xyz.utils.wire()`<div style="height: 24px; width: 24px;">`;
- let svg = _xyz.utils.wire()`<svg>`;
+ let image = _xyz.utils.wire()`<img height=20 width=20>`;
- let image = _xyz.utils.wire(null, 'svg')`
- <image x=0 y=0 width=20 height=20>`;
+ image.setAttribute('src', _xyz.utils.svg_symbols(Object.assign({}, layer.style.marker, cat[1].style || cat[1])));
- image.setAttribute('href', _xyz.utils.svg_symbols(Object.assign({}, layer.style.marker, cat[1].style || cat[1])));
-
- svg.appendChild(image);
-
- image_container.appendChild(svg);
+ image_container.appendChild(image);
legend.appendChild(image_container);
public/js/layers/view/style/legend/clusterCategorized.mjs
let imageMulti_container = _xyz.utils.wire()`<div style="height: 40px; width: 40px;">`;
- let svgMulti = _xyz.utils.wire()`<svg>`;
-
- imageMulti_container.appendChild(svgMulti);
-
- let imageMulti = _xyz.utils.wire(null, 'svg')`
- <image x=0 width=40 height=40 />`;
+ let imageMulti = _xyz.utils.wire()`<img height=40 width=40>`;
- imageMulti.setAttribute('href', _xyz.utils.svg_symbols(layer.style.markerMulti));
+ imageMulti.setAttribute('src', _xyz.utils.svg_symbols(layer.style.markerMulti));
- svgMulti.appendChild(imageMulti);
+ imageMulti_container.appendChild(imageMulti);
legend.appendChild(imageMulti_container);
Legend for cluster graduated theme (d7b38a3)
- removed svg container
public/js/layers/view/style/legend/clusterGraduated.mjs
let image_container = _xyz.utils.wire()`<div style="height: 24px; width: 24px;">`;
- let svg = _xyz.utils.wire()`<svg>`;
+ let image = _xyz.utils.wire()`<img width=20 height=20>`;
- let image = _xyz.utils.wire(null, 'svg')`
- <image
- x=0 y=0
- width=20
- height=20>`;
+ image.setAttribute('src', _xyz.utils.svg_symbols(Object.assign({}, layer.style.marker, cat.style)));
- image.setAttribute('href', _xyz.utils.svg_symbols(Object.assign({}, layer.style.marker, cat.style)));
-
- svg.appendChild(image);
-
- image_container.appendChild(svg);
+ image_container.appendChild(image);
legend.appendChild(image_container);
Geometry entry (d2f40fb)
- Now drawn underneath source layer
function drawGeom() {
+
entry.geometry = entry.value && _xyz.mapview.geoJSON({
geometry: JSON.parse(entry.value),
dataProjection: '4326',
+ zIndex: _xyz.layers.list[entry.location.layer.key].L.getZIndex() - 1,
style: new _xyz.mapview.lib.style.Style({
stroke: entry.style.strokeColor && new _xyz.mapview.lib.style.Stroke({
color: _xyz.utils.Chroma(entry.style.color || entry.style.strokeColor).alpha(1),
Added in 'Bring Later to front (b0b3089)
- The button will only be enabled on layers that are active
public/js/layers/bringToFront.mjs
export default _xyz => function () {
const layer = this;
if(layer.L.getZIndex() === 1000) return;
Object.values(_xyz.layers.list).map(_layer => {
if( _layer.format !== 'tiles') _layer.L.setZIndex(_layer.style.zIndex || 1) && (_layer.display && _layer.reload());
});
layer.L.setZIndex(1000);
layer.reload();
}
Cluster endpoint ⚠️
routes/api/layer/cluster.js
var q = `
${with_sql || ''}
SELECT
count(1) count,
SUM(size) size,
- label,
+ ${label && label !== 'count' ? 'label,' : ''}
${cat_sql || ''}
${xy_sql}
FROM ${agg_sql}`;
Sql text filter (9306a2c)
- Added regular expression for single quote
if((filter.like)) {
const likes = decodeURIComponent(filter.like).split(',')
.filter(like => like.length > 0)
- .map(like => `${field}::text ILIKE '${like.trim()}%'`);
+ .map(like => `${field}::text ILIKE '${like.trim().replace(/'/g, "''")}%'`);
sql_filter.push(likes.join(' OR '));
sql_filter.push(conjunction);
}
if((filter.match)) {
- sql_filter.push(`${field}::text ILIKE '${decodeURIComponent(filter.match)}'`);
+ sql_filter.push(`${field}::text ILIKE '${decodeURIComponent(filter.match.replace(/'/g, "''"))}'`);
sql_filter.push(conjunction);
}
Cluster label Count (1ccb7cd)
- Count can be set if defined.
public/js/mapview/layer/clusterLabel.mjs
text: properties.label || `${properties.count > 1 ? properties.count : ''}`,
Bug Fixes 🐛
Fix to marker post geometry edit (16043dc)
- Now location geometries updated after geometry editing.
public/js/mapview/interaction/edit.mjs
_xyz.map.removeLayer(_xyz.mapview.interaction.edit.location.Marker); // remove Marker from map
_xyz.mapview.interaction.edit.location.geometry = feature.geometry; // assign new geometry
// make new marker
_xyz.mapview.interaction.edit.location.marker = _xyz.mapview.lib.proj.transform(
_xyz.utils.turf.pointOnFeature(feature.geometry).geometry.coordinates,
'EPSG:' + _xyz.mapview.interaction.edit.location.layer.srid,
'EPSG:' + _xyz.mapview.srid);
// draw updated Marker
_xyz.mapview.interaction.edit.location.Marker = _xyz.mapview.geoJSON({
geometry: {
type: 'Point',
coordinates: _xyz.mapview.interaction.edit.location.marker,
},
style: new _xyz.mapview.lib.style.Style({
image: _xyz.mapview.icon({
type: 'markerLetter',
letter: String.fromCharCode(65 + _xyz.locations.list.indexOf(_xyz.mapview.interaction.edit.location.record)),
color: _xyz.mapview.interaction.edit.location.style.strokeColor,
scale: 0.05,
anchor: [0.5, 1]
})
})
});
// reload layer
_xyz.mapview.interaction.edit.location.layer.reload();
Fix - Function chart not loading (a9a056b) 🚧
- This is a temporary fix due to the dataview design issue 🚨
public/js/dataview/dashboard.mjs
entry.update = () => {
- entry.target.innerHTML = '';
+ if(!document.getElementById(entry.target_id)) entry.target.innerHTML = '';
let flex_container = _xyz.utils.wire()`<div
style="display: flex; flex-wrap: wrap; position: relative; padding: 20px;">`;
public/js/dataview/dashboard.mjs
if(val.type === 'pgFunction' && val.dashboard && entry.title === val.dashboard) {
- _xyz.dataview.pgFunction({
+ _xyz.dataview.pgFunction({
entry: val,
container: document.getElementById(entry.target_id) || flex_container
});
}
});
};
Fix to query on cluster layer endpoint (942577e) ⚠️
routes/api/layer/cluster.js
if (dbscan) {
dbscan *= rows[0].xdistance;
cluster_sql = `
(SELECT
cat,
size,
geom,
- ${label && label !== 'count' ? label + ' AS label,' : ''}
+ ${label && label !== 'count' ? 'label,' : ''}
kmeans_cid,
ST_ClusterDBSCAN(geom, ${dbscan}, 1) OVER (PARTITION BY kmeans_cid) dbscan_cid
FROM ${cluster_sql}) dbscan`;
}
Multiple Filters Bug (2a18af2)
- Clustering after including multiple filter parameters has now been corrected.
- sql_filter.push(likes.join(' OR '));
+ sql_filter.push(`(${likes.join(' OR ')})`);