Skip to content

v2.1.1 Update (29/01/2020)

Compare
Choose a tag to compare
@RobAndrewHurst RobAndrewHurst released this 29 Jan 13:21

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) Logout Button

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

image

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 ⚠️

  • label included in all queries (7663dfc)
  • Removed hard coded label (bed0222)

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.

image

- sql_filter.push(likes.join(' OR '));
+ sql_filter.push(`(${likes.join(' OR ')})`);