Commit 580141b7 authored by Anne Blankert's avatar Anne Blankert

fix special cases, refactor attribute info UI

parent 6f1a7ae7
...@@ -2,13 +2,57 @@ const sqlTableName = require('./utils/sqltablename.js'); ...@@ -2,13 +2,57 @@ const sqlTableName = require('./utils/sqltablename.js');
const sql = (params, query) => { const sql = (params, query) => {
return ` return `
select count(1)::integer as "count", ${params.column} as "value" select count(1)::integer as "count", "${params.column}" as "value"
from ${params.table} from ${sqlTableName(params.table)}
where ${query.geom_column} is not null where ${query.geom_column} is not null
group by ${params.column} order by count(1) desc limit 2000; group by "${params.column}" order by count(1) desc limit 100;
` `
} // TODO, use sql place holders $1, $2 etc. instead of inserting user-parameters into query } // TODO, use sql place holders $1, $2 etc. instead of inserting user-parameters into query
// https://leafo.net/guides/postgresql-calculating-percentile.html
const sqlPercentiles = (params, query) => {
return `
select min(buckets.value) "from", max(buckets.value) "to", count(ntile)::integer "count", ntile as percentile
from
(select "${params.column}" as value, ntile(100) over (order by "${params.column}")
from ${sqlTableName(params.table)}
where "${params.column}" is not null and ${query.geom_column} is not null)
as buckets
group by ntile order by ntile;
`
}
const sqlPercentilesBoolean = (params, query) => {
return `
select case when min(buckets.value) = 0 then false else true end "from", case when max(buckets.value) = 0 then false else true end "to", count(ntile)::integer "count", ntile as percentile
from
(select "${params.column}"::integer as value, ntile(100) over (order by "${params.column}")
from ${sqlTableName(params.table)}
where "${params.column}" is not null and ${query.geom_column} is not null)
as buckets
group by ntile order by ntile;
`
}
let typeMap = null;
async function getTypeName(id, pool) {
if (!typeMap) {
const sql = "select oid,typname from pg_type where oid < 1000000 order by oid";
try {
const queryResult = await pool.query(sql);
typeMap = new Map(queryResult.rows.map(row=>[row.oid, row.typname]));
} catch(err) {
console.log(`error loading types: ${err}`);
return id.toString();
}
}
const result = typeMap.get(id);
if (!result) {
return id.toString();
}
return result;
}
module.exports = function(app, pool, cache) { module.exports = function(app, pool, cache) {
let cacheMiddleWare = async(req, res, next) => { let cacheMiddleWare = async(req, res, next) => {
...@@ -16,7 +60,7 @@ module.exports = function(app, pool, cache) { ...@@ -16,7 +60,7 @@ module.exports = function(app, pool, cache) {
next(); next();
return; return;
} }
const cacheDir = `${req.params.table}/attrstats/`; const cacheDir = `${req.params.table}/colstats/`;
const key = ((req.query.geom_column?req.query.geom_column:'geom') + (req.params.column?','+req.params.column:'')) const key = ((req.query.geom_column?req.query.geom_column:'geom') + (req.params.column?','+req.params.column:''))
.replace(/[\W]+/g, '_'); .replace(/[\W]+/g, '_');
...@@ -108,22 +152,49 @@ module.exports = function(app, pool, cache) { ...@@ -108,22 +152,49 @@ module.exports = function(app, pool, cache) {
if (!req.query.geom_column) { if (!req.query.geom_column) {
req.query.geom_column = 'geom'; // default req.query.geom_column = 'geom'; // default
} }
const sqlString = sql(req.params, req.query); let sqlString = sql(req.params, req.query);
//console.log(sqlString); //console.log(sqlString);
try { try {
const result = await pool.query(sqlString); let queryResult = await pool.query(sqlString);
const stats = result.rows let datatype = await getTypeName(queryResult.fields[1].dataTypeID, pool);
if (stats.length === 0) { if (datatype === "numeric" || datatype === "int8") {
res.status(204).json({}); // numeric datatype, try to convert to Number
return; try {
queryResult.rows = queryResult.rows.map(row=>{row.value=row.value?Number(row.value):row.value; return row});
} catch(err) {
// failed Numeric conversion
}
} }
res.json({ const stats = queryResult.rows
const result = {
table: req.params.table, table: req.params.table,
column: req.params.column, column: req.params.column,
datatype: datatype,
numvalues: stats.length < 2000?stats.length:null, numvalues: stats.length < 2000?stats.length:null,
uniquevalues: stats[0].value !== null?stats[0].count === 1:stats.length>1?stats[1].count === 1:false, uniquevalues: stats.length?stats[0].value !== null?stats[0].count === 1:stats.length>1?stats[1].count === 1:false:[],
values: stats values: stats
}) }
if (stats.length === 0) {
result.percentiles = [];
res.json(result);
return;
}
if (datatype === "bool") {
sqlString = sqlPercentilesBoolean(req.params, req.query);
} else {
sqlString = sqlPercentiles(req.params, req.query);
}
queryResult = await pool.query(sqlString);
if (datatype === "numeric" || datatype === "int8") {
// numeric datatype, try to convert to Number
try {
queryResult.rows = queryResult.rows.map(row=>{row.from=Number(row.from); row.to=Number(row.to); return row});
} catch(err) {
// failed Numeric conversion
}
}
result.percentiles = queryResult.rows;
res.json(result);
} catch(err) { } catch(err) {
console.log(err); console.log(err);
let status = 500; let status = 500;
......
This diff is collapsed.
/* source https://codepen.io/TheDutchCoder/pen/gholk */
/* The loader container */
.loader {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 200px;
margin-top: -100px;
margin-left: -100px;
perspective: 400px;
/* transform-type: preserve-3d; */
}
/* The dot */
.dot {
position: absolute;
top: 50%;
left: 50%;
z-index: 10;
width: 40px;
height: 40px;
margin-top: -20px;
margin-left: -80px;
/* transform-type: preserve-3d;*/
transform-origin: 80px 50%;
transform: rotateY(0);
background-color: #1e3f57;
animation: dot1 2000ms cubic-bezier(.56,.09,.89,.69) infinite;
}
.dot:nth-child(2) {
z-index: 9;
animation-delay: 150ms;
}
.dot:nth-child(3) {
z-index: 8;
animation-delay: 300ms;
}
.dot:nth-child(4) {
z-index: 7;
animation-delay: 450ms;
}
.dot:nth-child(5) {
z-index: 6;
animation-delay: 600ms;
}
.dot:nth-child(6) {
z-index: 5;
animation-delay: 750ms;
}
.dot:nth-child(7) {
z-index: 4;
animation-delay: 900ms;
}
.dot:nth-child(8) {
z-index: 3;
animation-delay: 1050ms;
}
@keyframes dot1 {
0% {
transform: rotateY(0) rotateZ(0) rotateX(0);
background-color: #1e3f57;
}
45% {
transform: rotateZ(180deg) rotateY(360deg) rotateX(90deg);
background-color: #6bb2cd;
animation-timing-function: cubic-bezier(.15,.62,.72,.98);
}
90%, 100% {
transform: rotateY(0) rotateZ(360deg) rotateX(180deg);
background-color: #1e3f57;
}
}
\ No newline at end of file
...@@ -46,13 +46,21 @@ ...@@ -46,13 +46,21 @@
` `
}) })
} else { } else {
bbox.innerHTML = `Error getting bbox, response: response: ${response.status} ${response.statusText?response.statusText:''} ${response.url}` response.json().then(json=>{
bbox.innerHTML = `Error getting bbox: ${json.error}`;
}).catch(err => {
bbox.innerHTML = `Error parsing bbox: ${err}`;
})
} }
}) })
}) })
} else { } else {
const li = document.createElement('li'); const li = document.createElement('li');
li.innerHTML = `Error getting list, response: ${response.status} ${response.statusText?response.statusText:''} ${response.url}` response.json().then(json=>{
li.innerHTML = `Error getting column info: ${json.error}`;
}).catch(err => {
li.innerHTML = `Error parsing column info: ${err}`;
})
list.appendChild(li); list.appendChild(li);
} }
}) })
......
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