UNPKG

@opengis/bi

Version:

BI data visualization module

128 lines (103 loc) 4.3 kB
import path from 'path'; import { createHash } from 'crypto'; import { writeFile, mkdir, readFile, stat } from 'fs/promises'; import { existsSync, /* readdirSync, */ readFileSync } from 'fs'; import { getFolder, getFilterSQL, autoIndex, logger, pgClients } from '@opengis/fastify-table/utils.js'; import normalizeData from '../../data/controllers/util/normalizeData.js'; import { getWidget } from '../../../../utils.js'; const types = { point: 'ST_Point' /* ,ST_MultiPoint */, polygon: 'ST_Polygon,ST_MultiPolygon', }; const hourMs = 3.6e6; export default async function geojson(req, reply) { const { query = {} } = req; const { filter, widget, sql, type, nocache, id, dashboard, geom = 'geom', pointZoom = 0, } = query; if (!widget && !dashboard) { return { message: 'not enough params: widget', status: 400 }; } const data = await getWidget({ pg: req.pg, dashboard, widget }); if (data.status) return data; const pg = data.pg || req.pg || pgClients.client; const hash = [pointZoom, filter].filter((el) => el).join(); const root = getFolder(req); const file = path.join( root, `/map/geojson/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}.geojson` ); if (existsSync(file)) { const timeNow = Date.now(); const stats = await stat(file); const birthTime = new Date(stats.birthtime).getTime(); if (!(birthTime - timeNow > hourMs * 24) && !nocache) { const res = JSON.parse((await readFile(file, 'utf-8')) || {}); return res; } } try { const pkey = pg.pk?.[data?.table]; if (!pkey) { return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`, status: 400, }; } // data param const { table, where = '1=1', xName, x } = normalizeData(data, query); if (!xName && !x) { return { message: `invalid ${widget ? 'widget' : 'dashboard'}: x axis column not specified`, status: 400, }; } // get sql const filterQ = filter ? await getFilterSQL({ pg, table, filter, query }) : undefined; const q = `select "${pkey}", "${xName || x}", /* st_asgeojson(geom)::json as */ ${geom} as geom from ${filterQ ? `(${filterQ})` : table} q where ${where}`; if (sql === '1') return q; const { st_geometrytype: geomType = 'point' } = await pg .query( `select st_geometrytype(${geom}), count(*) from ${table} where ${where} group by st_geometrytype(${geom})` ) .then((res) => res.rows?.[0] || {}); const q1 = `SELECT 'FeatureCollection' As type, json_agg(f) As features FROM ( SELECT 'Feature' As type, row_number() over() as id, st_asgeojson(st_force2d(${query.srid ? `st_transform(${type === 'centroid' ? `st_centroid(${geom})` : geom},${query.srid})` : `${type === 'centroid' || query.point || query.centroid ? `st_centroid(${geom})` : geom}` }), 6, 0)::json as geometry, (select row_to_json(tc) from (select ${'' ? `${''} as status, ` : ''} ${xName ? `${xName},` : ''} ${data.style?.colorAttr ? `${data.style.colorAttr},` : ''} ${pkey} as id,(select file_path from crm.files where entity_id=q.${pkey}::text and file_status <>'3' and ext in ('png','jpg') limit 1) as image )tc) as properties from (${q})q where ${id && pkey ? ` ${pkey} = '${id}' and ` : ''} ${geom} is not null ${data.query ? ` and ${data.query}` : ''} ${query.extent ? `and ${geom} && 'BOX(${query.extent.split(',').reduce((p, el, i) => p + el + (i % 2 ? ',' : ' '), '')})'::box2d` : ''} ${types[type] ? ` and ST_GeometryType(${geom}) = any ('{ ${types[type]} }') ` : ''} limit ${geomType?.toLowerCase()?.includes('point') ? '15000' : '2500'})f`; if (sql === '2') return q1; // auto Index autoIndex({ table, columns: [xName] }); const res = await pg.query(q1).then((res) => res.rows?.[0] || {}); await mkdir(path.dirname(file), { recursive: true }); await writeFile(file, JSON.stringify(res)); return res; } catch (err) { logger.file('bi/geojson', { level: 'ERROR', error: err.toString(), query }); return { error: err.toString(), status: 500 }; } }