@opengis/gis 0.1.82 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1 -1
- package/dist/index.js +6794 -6952
- package/dist/index.umd.cjs +50 -56
- package/module/cls.json +2 -1
- package/module/gis/form/gis.maps.form.json +2 -2
- package/module/gis/form/gis.registers.form.json +8 -199
- package/module/gis/form/gis.services.form.json +11 -200
- package/module/gis/table/gis.group_list.table.json +36 -0
- package/module/gis/table/gis.maps.table.json +5 -7
- package/module/gis/table/gis.ogc_service.table.json +14 -3
- package/module/gis/table/gis.registers.table.json +2 -3
- package/module/gis/table/gis.services.table.json +19 -6
- package/module/test/cls/doc_status.json +31 -31
- package/module/test/cls/ts.temp_status.json +18 -0
- package/module/test/cls/ts.temp_structure.ts_class.json +50 -0
- package/module/test/cls/ts.temp_type.json +10 -0
- package/module/test/layer/bp.json +60 -0
- package/module/test/map/address4.json +2 -42
- package/module/test/map/bp_myo.json +41 -0
- package/module/test/map/main.json +44 -0
- package/module/test/map/mbd.json +24 -176
- package/module/test/map/ts.json +20 -129
- package/module/test/select/address_id.json +3 -0
- package/module/test/select/address_id.sql +8 -0
- package/module/test/select/core.user_uid.sql +1 -1
- package/module/test/table/data_bp_myo.bp.table.json +123 -0
- package/package.json +61 -61
- package/server/migrations/ogc.sql +107 -0
- package/server/migrations/widgets.sql +21 -0
- package/server/routes/gis/index.mjs +18 -2
- package/server/routes/gis/ogc/map.info.point.js +120 -0
- package/server/routes/gis/registers/funcs/classifiers.js +6 -3
- package/server/routes/gis/registers/funcs/handleRegistryRequest.js +15 -22
- package/server/routes/gis/registers/gis.registry.js +3 -2
- package/server/routes/gis/services/add.service.js +5 -5
- package/server/routes/gis/services/get.services.col.js +19 -11
- package/server/routes/gis/services/get.services.js +29 -14
- package/server/routes/gis/services/legend.auto.js +78 -0
- package/server/routes/map/controllers/layerList.js +14 -2
- package/server/routes/map/controllers/mapFeatures.js +11 -2
- package/server/routes/map/controllers/mapFormat.js +14 -18
- package/server/routes/map/controllers/vtile.js +1 -1
- package/server/routes/map/index.mjs +36 -17
- package/server/routes/map/maps/add.map.js +42 -0
- package/server/routes/map/maps/del.map.js +19 -0
- package/server/routes/map/maps/get.map.js +67 -0
- package/server/routes/map/vtile1.js +42 -6
- package/server/routes/map/widgets/add.widget.js +39 -0
- package/server/routes/map/widgets/del.widget.js +23 -0
- package/server/routes/map/widgets/get.widget.js +41 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { pgClients, handlebars, getMeta, getTemplate, getFilterSQL } from "@opengis/fastify-table/utils.js";
|
|
2
|
+
|
|
3
|
+
const columnType = {
|
|
4
|
+
text: 'text',
|
|
5
|
+
date: 'date',
|
|
6
|
+
bool: 'yes/no',
|
|
7
|
+
numeric: 'number',
|
|
8
|
+
integer: 'number',
|
|
9
|
+
'timestamp without time zone': 'date',
|
|
10
|
+
'timestamp with time zone': 'date',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default async function mapInfoPoint(req, reply) {
|
|
14
|
+
const { pg = pgClients.client, query = {} } = req;
|
|
15
|
+
const { x, y, layers, index = 0 } = query;
|
|
16
|
+
const time = Date.now();
|
|
17
|
+
|
|
18
|
+
if (!x || !y || !layers) {
|
|
19
|
+
return reply.status(400).send('not enough query params: x, y, layers are required');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const rows = pg?.pk?.['gis.ogc_service'] ? await pg.query(`select * from gis.ogc_service WHERE enabled and table_name is not null and ogc_service_id = any($1)`, [layers.split(',')]).then(el => el.rows || []) : [];
|
|
23
|
+
|
|
24
|
+
if (!rows.length) {
|
|
25
|
+
return reply.status(404).send('layers not found');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const point = `srid=4326;point(${x} ${y})`; // 30, 50
|
|
29
|
+
|
|
30
|
+
const srids = pg.queryCache ? await pg.queryCache('select json_agg(srid) as srids from spatial_ref_sys').then(el => el.rows?.[0]?.srids || {}) : {};
|
|
31
|
+
|
|
32
|
+
await Promise.all(rows.map(async (layer, idx) => {
|
|
33
|
+
const { table_name: source, ogc_service_id: layerId } = layer;
|
|
34
|
+
const loadTable = await getTemplate('table', source);
|
|
35
|
+
const meta = await getMeta({ pg, table: loadTable?.table || source });
|
|
36
|
+
const { geom = 'geom' } = meta || {};
|
|
37
|
+
const { table = source, columns = meta?.columns } = loadTable || {};
|
|
38
|
+
const { srid, x } = await pg.query(`select st_srid(${geom}) as srid, st_x(st_pointonsurface(${geom})) as x from ${table} where geom is not null limit 1`).then(el => el.rows?.[0] || {});
|
|
39
|
+
|
|
40
|
+
const cls = columns
|
|
41
|
+
?.filter(el => el.name && el.data)
|
|
42
|
+
?.reduce?.((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {});
|
|
43
|
+
|
|
44
|
+
const pk = pg.pk?.[table] || loadTable?.key;
|
|
45
|
+
|
|
46
|
+
const { optimizedSQL } = await getFilterSQL({ pg, table: source, query: layer?.query });
|
|
47
|
+
|
|
48
|
+
Object.assign(layer, {
|
|
49
|
+
layer: (layerId || idx.toString())?.replace?.(/'/g, "''"),
|
|
50
|
+
table,
|
|
51
|
+
pk,
|
|
52
|
+
cls,
|
|
53
|
+
geomColumn: srid === 4326 ? geom : `st_transform(${geom},4326)`,
|
|
54
|
+
optimizedSQL,
|
|
55
|
+
srid,
|
|
56
|
+
step: srids?.includes?.(srid - 0) && srid !== 4326 && x > 100 ? 10 : 0.0002,
|
|
57
|
+
});
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
const unique = rows.filter((el, idx, arr) => el.table && arr.map((item) => item.table).indexOf(el.table) === idx);
|
|
61
|
+
|
|
62
|
+
const qList = unique.map((el) => ({
|
|
63
|
+
query: `select ${el.pk}::text as pk, '${el.layer}' as layer, '${el.table}' as table,
|
|
64
|
+
row_to_json(t) as data,
|
|
65
|
+
st_distance(${el.geomColumn},'${point.replace(/'/g, "''")}') as distance,
|
|
66
|
+
st_asgeojson(${el.geomColumn})::json as geom from (${el.optimizedSQL}) t
|
|
67
|
+
where 1=1`,
|
|
68
|
+
queryCount: `select 1 from (${el.optimizedSQL})t where 1=1`,
|
|
69
|
+
/*where: `case
|
|
70
|
+
when ST_GeometryType(${el.geomColumn}) in ('ST_Polygon','ST_MultiPolygon')
|
|
71
|
+
then st_intersects(${el.geomColumn},st_buffer('${point.replace(/'/g, "''")}',${el.step}))
|
|
72
|
+
|
|
73
|
+
when ST_GeometryType(${el.geomColumn}) in ('ST_Line','ST_MultiLineString', 'ST_MultiPoint', 'ST_Point')
|
|
74
|
+
then st_distance(${el.geomColumn},'${point.replace(/'/g, "''")}') < ${el.step}
|
|
75
|
+
else false
|
|
76
|
+
end`,*/
|
|
77
|
+
where: `${el.geomColumn} && '${point.replace(/'/g, "''")}'`
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
const q = qList.map(el => el.query + ' and ' + el.where).join(' union all ').concat(` order by distance limit 1 offset ${Math.max(+index || 0, 0)}`);
|
|
81
|
+
|
|
82
|
+
const qCount = qList.map(el => el.queryCount + ' and ' + el.where).join(' union all ');
|
|
83
|
+
|
|
84
|
+
if (query.sql === '1') return q;
|
|
85
|
+
if (query.sql === '2') return qCount;
|
|
86
|
+
|
|
87
|
+
const count = await pg.query(qCount).then(el => el.rowCount);
|
|
88
|
+
|
|
89
|
+
const row = await pg.query(q).then(el => el.rows?.[0]);
|
|
90
|
+
|
|
91
|
+
if (!row) {
|
|
92
|
+
return reply.status(404).send('object not found');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const { columns = [] } = await getMeta({ pg, table: row.table }) || {};
|
|
96
|
+
const fields = columns.map(({ name, dataTypeID, title }) => ({ name, type: columnType[pg.pgType?.[dataTypeID] || 'text'], label: title || name }));
|
|
97
|
+
|
|
98
|
+
const htmlTemplate = pg?.pk?.['admin.template'] && row.layer && false
|
|
99
|
+
? await pg.query('select body from admin.templates where name=$1 limit 1', [row.layer]).then(el => el.rows?.[0]?.body)
|
|
100
|
+
: rows.find(el => el.ogc_service_id === row.layer)?.html;
|
|
101
|
+
|
|
102
|
+
const html = await handlebars.compile(htmlTemplate || 'template not found')(row.data);
|
|
103
|
+
|
|
104
|
+
const res = {
|
|
105
|
+
time: Date.now() - time,
|
|
106
|
+
index,
|
|
107
|
+
count,
|
|
108
|
+
data: {
|
|
109
|
+
layer: row.layer,
|
|
110
|
+
geom: row.geom,
|
|
111
|
+
id: row.pk,
|
|
112
|
+
distance: row.distance,
|
|
113
|
+
data: row.data,
|
|
114
|
+
},
|
|
115
|
+
fields,
|
|
116
|
+
html
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return res;
|
|
120
|
+
}
|
|
@@ -5,11 +5,13 @@ export async function attachClassifiers(rowOrRows, classifiers, table) {
|
|
|
5
5
|
|
|
6
6
|
const list = getTemplates(['cls', 'select']);
|
|
7
7
|
|
|
8
|
+
// classifiers as object
|
|
8
9
|
if (table) {
|
|
9
10
|
await Promise.all(rows.map(async row => {
|
|
10
|
-
await Promise.all(Object.keys(row).filter(key => list.map(el => el[0]).includes(`${table}.${key}`)).map(async key => {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
await Promise.all(Object.keys(row).filter(key => classifiers?.[key] || list.map(el => el[0]).includes(`${table}.${key}`) || list.map(el => el[0]).includes(key)).map(async key => {
|
|
12
|
+
const clsAsColumn = list.map(el => el[0]).includes(key);
|
|
13
|
+
const arr = await getSelectVal({ name: classifiers?.[key] || (clsAsColumn ? key : `${table}.${key}`), values: [row[key]], ar: true });
|
|
14
|
+
const val = arr?.find?.(el => el.id && el.id.toString() === row[key].toString());
|
|
13
15
|
if (val?.text) { row[`${key}_data`] = val; }
|
|
14
16
|
row[key] = val?.text || val || row[key]; // for frontend
|
|
15
17
|
row[`${key}_text`] = val?.text || val || row[key]; // legacy?
|
|
@@ -18,6 +20,7 @@ export async function attachClassifiers(rowOrRows, classifiers, table) {
|
|
|
18
20
|
return rowOrRows;
|
|
19
21
|
}
|
|
20
22
|
|
|
23
|
+
// classifiers as array of objects
|
|
21
24
|
await Promise.all(rows.map(async row => {
|
|
22
25
|
(classifiers || []).filter(item => item.classifier && item.name && row[item.name]).map(async ({ name, classifier }) => {
|
|
23
26
|
const arr = await getSelectVal({ name: classifier, values: [row[name]], ar: true });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { populateFilterOptions } from './classifiers.js';
|
|
2
2
|
import { extractVisibleColumns } from './columns.js';
|
|
3
|
-
import { getMeta, getFilterSQL } from "@opengis/fastify-table/utils.js";
|
|
3
|
+
import { getMeta, getFilterSQL, metaFormat } from "@opengis/fastify-table/utils.js";
|
|
4
4
|
|
|
5
5
|
const columnType = {
|
|
6
6
|
text: 'text',
|
|
@@ -12,11 +12,11 @@ const columnType = {
|
|
|
12
12
|
'timestamp with time zone': 'date',
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export async function handleRegistryRequest({ settings, query, object_id, offset = 0, limit = 16, page = 1, pg }) {
|
|
15
|
+
export async function handleRegistryRequest({ settings, query, object_id, offset = 0, limit = 16, page = 1, pg, sql }) {
|
|
16
16
|
const { name, table_name, description, card, columns, filters, query: whereQuery, order, pk, register_id, is_files, view } = settings;
|
|
17
17
|
const parsedColumns = Array.isArray(columns) ? columns : JSON.parse(columns);
|
|
18
18
|
const filtersParsed = Array.isArray(filters) ? filters : JSON.parse(filters);
|
|
19
|
-
const activeFilters = (filtersParsed || []).filter((f) => f.disabled !== true);
|
|
19
|
+
const activeFilters = (filtersParsed || []).filter((f) => f.disabled !== true).map(el => ({ ...el, id: el.name || el.id, type: el.type, api: '/api/suggest/' + table_name + ':' + el.name + (el.data ? `?sel=${el.data}` : '') }));
|
|
20
20
|
const { columns: fields1, geom } = await getMeta({ pg, table: table_name });
|
|
21
21
|
|
|
22
22
|
const fields = fields1?.map?.(({ name, dataTypeID, title }) => ({ name, type: columnType[pg.pgType?.[dataTypeID] || 'text'] || 'text', label: title || name }));
|
|
@@ -34,7 +34,7 @@ export async function handleRegistryRequest({ settings, query, object_id, offset
|
|
|
34
34
|
|
|
35
35
|
const classifiers = visibleColumns
|
|
36
36
|
.filter((col) => col.data && ["select", "badge", "tags"].includes(col.format))
|
|
37
|
-
.
|
|
37
|
+
.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {});
|
|
38
38
|
|
|
39
39
|
if (object_id) {
|
|
40
40
|
const sql = `
|
|
@@ -42,17 +42,13 @@ export async function handleRegistryRequest({ settings, query, object_id, offset
|
|
|
42
42
|
FROM ${table_name}
|
|
43
43
|
WHERE ${pk} = $1
|
|
44
44
|
`;
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
} = await pg.query(sql, [object_id]);
|
|
45
|
+
const rows = await pg.query(sql, [object_id]).then(el => el.rows || []);
|
|
46
|
+
if (!rows.length) throw new Error('Object not found');
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
if (!row) throw new Error('Object not found');
|
|
51
|
-
|
|
52
|
-
await attachClassifiers(row, classifiers, table_name);
|
|
48
|
+
await metaFormat({ rows, table: table_name, cls: classifiers, sufix: true });
|
|
53
49
|
|
|
54
50
|
return {
|
|
55
|
-
row,
|
|
51
|
+
row: rows[0],
|
|
56
52
|
card,
|
|
57
53
|
d: 1,
|
|
58
54
|
columns: visibleColumns,
|
|
@@ -68,8 +64,7 @@ export async function handleRegistryRequest({ settings, query, object_id, offset
|
|
|
68
64
|
table: table_name,
|
|
69
65
|
search: search,
|
|
70
66
|
filter: filter,
|
|
71
|
-
filterList:
|
|
72
|
-
json: 1
|
|
67
|
+
filterList: activeFilters,
|
|
73
68
|
})
|
|
74
69
|
: { q: '1=1' };
|
|
75
70
|
|
|
@@ -81,15 +76,13 @@ export async function handleRegistryRequest({ settings, query, object_id, offset
|
|
|
81
76
|
|
|
82
77
|
const sqlSelect = `SELECT "${pk}" as id ${selectColumns.length ? `, ${selectColumns.join(", ")}` : ''} ${geom ? `, st_asgeojson(${geom})::json as geom` : ''} ${sqlBase}`;
|
|
83
78
|
const dataQuery = `${sqlSelect} ${sqlOrder} ${sqlLimit}`;
|
|
84
|
-
const totalQuery = `SELECT COUNT(*) ${sqlBase}`;
|
|
85
79
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
]);
|
|
80
|
+
if (sql) return dataQuery;
|
|
81
|
+
|
|
82
|
+
const rows = await pg.query(dataQuery, [limit, offset]).then(el => el.rows || []);
|
|
83
|
+
const total = await pg.query(`SELECT COUNT(*) ${sqlBase}`).then(el => +(el.rows[0]?.count || 0));
|
|
90
84
|
|
|
91
|
-
|
|
92
|
-
const total = parseInt(countRes.rows[0]?.count || 0, 10);
|
|
85
|
+
await metaFormat({ rows, table: table_name, cls: classifiers, sufix: true });
|
|
93
86
|
|
|
94
87
|
const listConfig = {};
|
|
95
88
|
|
|
@@ -9,6 +9,7 @@ export default async function gisRegistry({
|
|
|
9
9
|
pg = pgClients.client, params = {}, query = {},
|
|
10
10
|
}) {
|
|
11
11
|
const { slug, objectId } = params;
|
|
12
|
+
const { sql } = query;
|
|
12
13
|
|
|
13
14
|
const limit = Math.min(query.limit || defaultLimit, maxLimit);
|
|
14
15
|
const offset = query.page && query.page > 0 && !objectId ? (query.page - 1) * limit : 0;
|
|
@@ -26,12 +27,12 @@ export default async function gisRegistry({
|
|
|
26
27
|
);
|
|
27
28
|
|
|
28
29
|
if (registry) {
|
|
29
|
-
return handleRegistryRequest({ settings: registry, query, object_id: objectId, offset, limit, page: query.page, pg });
|
|
30
|
+
return handleRegistryRequest({ settings: registry, query, object_id: objectId, offset, limit, page: query.page, pg, sql });
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const registryTemplate = await getTemplate('registry', slug);
|
|
33
34
|
//if (!registryTemplate) return reply.code(404).send({ message: 'Template not found', status: 404 });
|
|
34
35
|
if (!registryTemplate) throw new Error('Template not found');
|
|
35
36
|
|
|
36
|
-
return handleRegistryRequest({ settings: registryTemplate, query, object_id: objectId, offset, limit, page: query.page, pg });
|
|
37
|
+
return handleRegistryRequest({ settings: registryTemplate, query, object_id: objectId, offset, limit, page: query.page, pg, sql });
|
|
37
38
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { dataDelete, dataInsert, dataUpdate, pgClients } from "@opengis/fastify-table/utils.js";
|
|
2
2
|
|
|
3
|
-
async function updateTemplate(client, body,
|
|
4
|
-
if (!body || !
|
|
3
|
+
async function updateTemplate(client, body, uid, id) {
|
|
4
|
+
if (!body || !id) return;
|
|
5
5
|
|
|
6
6
|
await dataDelete({
|
|
7
7
|
pg: client,
|
|
@@ -13,7 +13,7 @@ async function updateTemplate(client, body, name, uid, id) {
|
|
|
13
13
|
pg: client,
|
|
14
14
|
id,
|
|
15
15
|
table: 'admin.templates',
|
|
16
|
-
data: { body, name },
|
|
16
|
+
data: { body, name: id },
|
|
17
17
|
uid,
|
|
18
18
|
}).then(el => el.rowCount);
|
|
19
19
|
return rowCount;
|
|
@@ -39,7 +39,7 @@ export default async function addService({
|
|
|
39
39
|
data: body,
|
|
40
40
|
uid,
|
|
41
41
|
});
|
|
42
|
-
const istemplate = await updateTemplate(client, body.html,
|
|
42
|
+
const istemplate = await updateTemplate(client, body.html, uid, rows[0]?.service_id);
|
|
43
43
|
await client.query('commit');
|
|
44
44
|
return reply.status(200).send(rows[0]);
|
|
45
45
|
}
|
|
@@ -52,7 +52,7 @@ export default async function addService({
|
|
|
52
52
|
data: body,
|
|
53
53
|
uid,
|
|
54
54
|
});
|
|
55
|
-
const istemplate = await updateTemplate(client, body.html,
|
|
55
|
+
const istemplate = await updateTemplate(client, body.html, uid, params.id);
|
|
56
56
|
await client.query('commit');
|
|
57
57
|
return reply.status(200).send(row);
|
|
58
58
|
|
|
@@ -1,24 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { metaFormat, pgClients } from "@opengis/fastify-table/utils.js";
|
|
4
2
|
|
|
5
3
|
export default async function getServicesCol({ params = {}, pg = pgClients.client }, reply) {
|
|
6
|
-
|
|
7
|
-
|
|
8
4
|
const row = await pg.query(`
|
|
9
5
|
SELECT
|
|
10
|
-
service_id, source_path
|
|
6
|
+
service_id, source_path, attributes
|
|
11
7
|
FROM gis.services where ${params.id ? 'service_id=$1' : '1=1'}
|
|
12
8
|
`, [params.id].filter(Boolean)).then(el => el.rows[0] || {});
|
|
13
9
|
|
|
14
|
-
if (!row.source_path)
|
|
10
|
+
if (!row.source_path) {
|
|
11
|
+
return reply.status(404).send({ code: 404, message: 'source not found' });
|
|
12
|
+
}
|
|
13
|
+
|
|
15
14
|
const fields = await pg.query(` SELECT * FROM ${row.source_path} limit 0`).then(el => el.fields);
|
|
16
15
|
const field = fields.find(el => el.name == params.col);
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
|
|
17
|
+
if (!field) {
|
|
18
|
+
return reply.status(404).send({ message: { error: 'column not found', fields }, code: 404 });
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const rows = await pg.query(`SELECT ${params.col} as id,
|
|
21
|
+
const rows = await pg.query(`SELECT ${params.col}::text as id, ${params.col}, count(*) FROM ${row.source_path}
|
|
22
|
+
where ${params.col} is not null group by ${params.col} order by count(*) desc limit 10`
|
|
23
|
+
).then(el => el.rows || []);
|
|
24
|
+
|
|
25
|
+
const cls = row.attributes?.filter?.((col) => col.name && col.data && ["select", "badge", "tags"].includes(col.format)).reduce((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {}) || {};
|
|
26
|
+
|
|
27
|
+
await metaFormat({ rows, table: row.source_path, cls });
|
|
28
|
+
|
|
29
|
+
const rows1 = rows.map(row => ({ ...row[`${params.col}_data`], ...row, [`${params.col}_data`]: undefined, [params.col]: undefined }));
|
|
22
30
|
|
|
23
|
-
return { rows, field, fields }
|
|
31
|
+
return reply.status(200).send({ rows: rows1, field, fields });
|
|
24
32
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { dataUpdate, getMeta, pgClients, yml2json } from "@opengis/fastify-table/utils.js";
|
|
2
2
|
|
|
3
|
-
const table = 'gis.services';
|
|
4
|
-
|
|
5
3
|
const columnType = {
|
|
6
4
|
text: 'text',
|
|
7
5
|
date: 'date',
|
|
@@ -13,24 +11,38 @@ const columnType = {
|
|
|
13
11
|
};
|
|
14
12
|
|
|
15
13
|
export default async function getServices({ params = {}, pg = pgClients.client }, reply) {
|
|
16
|
-
const { columns = [] } = await getMeta({ pg, table }) || {};
|
|
17
|
-
|
|
18
|
-
const fields = columns.map(({ name, dataTypeID, title }) => ({ name, type: columnType[pg.pgType?.[dataTypeID] || 'text'], label: title || name }));
|
|
19
|
-
|
|
20
14
|
const rows = await pg.query(`
|
|
21
15
|
SELECT
|
|
22
|
-
service_id, service_key, name, description, keywords, category, holder, group_id, service_type,
|
|
16
|
+
service_id, service_key, name, description, keywords, category, holder, group_id, b.group_name, service_type,
|
|
23
17
|
source_type, service_url, source_path, query, geom_type, geometry_column, sql_list, attributes, filters,
|
|
24
18
|
popup, style, legend, card, srid, bbox::box2d as extent, st_asgeojson(bbox)::json as bbox, st_asgeojson(center)::json as center, is_active as enabled, is_public, is_active, is_downloadable, metadata,
|
|
25
19
|
metadata_url, thumbnail_url, created_by, updated_by, updated_at, created_at, template
|
|
26
|
-
FROM gis.services
|
|
20
|
+
FROM gis.services a
|
|
21
|
+
left join lateral (select group_name from gis.group_list where group_id=a.group_id)b on 1=1
|
|
22
|
+
where ${params.id ? 'service_id=$1' : '1=1'}
|
|
27
23
|
`, [params.id].filter(Boolean)).then(el => el.rows || []);
|
|
28
24
|
|
|
29
|
-
if (params.id && rows
|
|
25
|
+
if (params.id && !rows.length) {
|
|
26
|
+
return reply.status(404).send('service not found');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
const totals = pg.queryCache ? await pg.queryCache(`select json_object_agg(oid::regclass, reltuples) from pg_class`)
|
|
31
|
+
.then(el => el.rows?.[0]?.json_object_agg || {}) : {};
|
|
32
|
+
|
|
33
|
+
if (params.id) {
|
|
30
34
|
const html = await pg.query('select body from admin.templates where template_id=$1', [params.id]).then(el => el.rows?.[0]?.body);
|
|
31
35
|
Object.assign(rows[0], { html });
|
|
32
36
|
}
|
|
33
37
|
|
|
38
|
+
rows.filter(row => row.source_path).forEach(row => {
|
|
39
|
+
Object.assign(row, { count: totals[row.source_path] || 0 });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const { columns = [] } = await getMeta({ pg, table: rows[0].source_path }) || {};
|
|
43
|
+
|
|
44
|
+
const fields = columns.map(({ name, dataTypeID, title }) => ({ name, type: columnType[pg.pgType?.[dataTypeID] || 'text'], label: title || name }));
|
|
45
|
+
|
|
34
46
|
const noCenterIds = rows.filter(row => !row.center && row.bbox).map(row => row.service_id);
|
|
35
47
|
|
|
36
48
|
const centers = noCenterIds.length ? await pg.query(`SELECT json_object_agg(service_id, st_pointonsurface(bbox)::json) FROM gis.services where service_id=any($1)`, [noCenterIds].filter(Boolean)).then(el => el.rows?.[0]?.json_object_agg || {}) : {};
|
|
@@ -41,17 +53,20 @@ export default async function getServices({ params = {}, pg = pgClients.client }
|
|
|
41
53
|
Object.assign(row, { center: centers[row.service_id] });
|
|
42
54
|
}));
|
|
43
55
|
|
|
44
|
-
rows.forEach(row => Object.assign(row, {
|
|
45
|
-
|
|
56
|
+
rows.forEach(row => Object.assign(row, {
|
|
57
|
+
center: row.center?.coordinates,
|
|
58
|
+
style: row.style ? yml2json(row.style) : undefined
|
|
59
|
+
}));
|
|
46
60
|
rows.filter(row => row.extent).forEach(row => Object.assign(row, {
|
|
47
61
|
extent: row.extent.match(/BOX\(([^)]+)\)/)?.[1]
|
|
48
62
|
?.replace?.(/ /g, ",")
|
|
49
63
|
?.split?.(",")
|
|
50
64
|
}));
|
|
51
65
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
66
|
+
|
|
67
|
+
rows.filter(row => row.filters).forEach(row => Object.assign(row, {
|
|
68
|
+
filters: row.filters.map(filter => ({ ...filter, api: '/api/suggest/' + row.source_path + ':' + filter.name + (filter.data && filter.name !== filter.data ? `?sel=${filter.data}` : '') }))
|
|
69
|
+
}));
|
|
55
70
|
|
|
56
71
|
if (params.id) {
|
|
57
72
|
return reply.status(200).send({ ...rows[0], fields });
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { dataUpdate, getMeta, getSelectVal, yml2json } from "@opengis/fastify-table/utils.js";
|
|
2
|
+
|
|
3
|
+
export default async function legendAuto({ params = {}, query = {}, user = {}, pg = pgClients.client }, reply) {
|
|
4
|
+
if (!params.id) {
|
|
5
|
+
return reply.status(400).send({ error: "not enough params: id", code: 400 });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const service = await pg.query(`SELECT source_path as table, style, legend as dblegend FROM gis.services where service_id=$1`, [params.id]).then(el => el.rows?.[0]);
|
|
9
|
+
|
|
10
|
+
if (!service) {
|
|
11
|
+
return reply.status(404).send({ error: "Service not found", code: 404 });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { style, dblegend, table } = service;
|
|
15
|
+
|
|
16
|
+
if (!table) {
|
|
17
|
+
return reply.status(400).send({ error: "service table not specified", code: 400 });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (dblegend && !query.nocache) {
|
|
21
|
+
return reply.status(400).send({ error: "legend already exists", code: 400 });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!style) {
|
|
25
|
+
return reply.status(400).send({ error: "empty service style", code: 400 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const { colorAttr, iconAttr } = yml2json(style);
|
|
29
|
+
|
|
30
|
+
if (!colorAttr && !iconAttr) {
|
|
31
|
+
return reply.status(400).send({ error: "colorAttr / iconAttr not specified", code: 400 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { columns = [] } = await getMeta({ pg, table });
|
|
35
|
+
|
|
36
|
+
const attr = columns.find(col => col.name === (colorAttr || iconAttr));
|
|
37
|
+
|
|
38
|
+
if (!attr?.name) {
|
|
39
|
+
return reply.status(404).send({ error: "attribute column not found", code: 404 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const values = await pg.query(`select array_agg(distinct "${attr.name}") from ${table}`).then(el => el.rows?.[0]?.array_agg || []);
|
|
43
|
+
const arr = await getSelectVal({ pg, name: `${table}.${attr.name}`, table, values, ar: true });
|
|
44
|
+
|
|
45
|
+
const colors = {
|
|
46
|
+
1: "#FFCF1F1F",
|
|
47
|
+
2: "#FF1FCF2D",
|
|
48
|
+
3: "#FF1F77CF"
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const colorKeys = Object.keys(colors).map(Number);
|
|
52
|
+
|
|
53
|
+
const legend = arr
|
|
54
|
+
? arr.map((el, idx) => ({
|
|
55
|
+
name: el.id,
|
|
56
|
+
label: el.text,
|
|
57
|
+
icon: iconAttr ? el.icon : undefined,
|
|
58
|
+
color: colorAttr ? (el.color || colors[colorKeys[idx % colorKeys.length]]) : undefined,
|
|
59
|
+
}))
|
|
60
|
+
: values.map((el, idx) => ({
|
|
61
|
+
name: el,
|
|
62
|
+
label: el,
|
|
63
|
+
icon: undefined,
|
|
64
|
+
color: colorAttr ? (colors[colorKeys[idx % colorKeys.length]]) : undefined,
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
const res = await dataUpdate({
|
|
68
|
+
pg,
|
|
69
|
+
table: 'gis.services',
|
|
70
|
+
id: params.id,
|
|
71
|
+
data: {
|
|
72
|
+
legend
|
|
73
|
+
},
|
|
74
|
+
uid: user.uid,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return reply.status(200).send(res);
|
|
78
|
+
}
|
|
@@ -10,15 +10,27 @@ export default async function layerList({
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const q = `
|
|
13
|
-
select service_id as id, name, category, style, bbox::box2d as extent, st_asgeojson(bbox)::json as geom, null as url, coalesce(service_type, 'vtile') as service
|
|
13
|
+
select service_id as id, name, category, style, bbox::box2d as extent, st_asgeojson(bbox)::json as geom, null as url, coalesce(service_type, 'vtile') as service, group_id,
|
|
14
|
+
popup, card, filters, style, source_path
|
|
15
|
+
from gis.services where is_active and ${!user.uid ? 'is_public' : '1=1'}
|
|
14
16
|
union all
|
|
15
|
-
select ogc_service_id as id, name, category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom, url, 'ogc' as service
|
|
17
|
+
select ogc_service_id as id, name, category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom, url, 'ogc' as service, group_id,
|
|
18
|
+
null as popup, null as card, null as filters, null as style, table_name as source_path
|
|
19
|
+
from gis.ogc_service where enabled and ${!user.uid ? 'ispublic' : '1=1'}
|
|
16
20
|
`;
|
|
17
21
|
|
|
18
22
|
if (user.uid && sql) return q;
|
|
19
23
|
|
|
20
24
|
const rows = await pg.query(q).then(el => el.rows || []);
|
|
21
25
|
|
|
26
|
+
const totals = pg.queryCache ? await pg.queryCache(`select json_object_agg(oid::regclass, reltuples) from pg_class`)
|
|
27
|
+
.then(el => el.rows?.[0]?.json_object_agg || {}) : {};
|
|
28
|
+
rows.filter(row => row.source_path).forEach(row => Object.assign(row, { count: totals[row.source_path] || 0 }));
|
|
29
|
+
|
|
30
|
+
const groupIds = rows.map(el => el.group_id).filter(Boolean);
|
|
31
|
+
const groupNames = groupIds.length ? await pg.query('select json_object_agg(group_id,group_name) from gis.group_list where group_id=any($1)', [groupIds]).then(el => el.rows?.[0]?.json_object_agg || {}) : {};
|
|
32
|
+
rows.filter(el => el.group_id).forEach(row => Object.assign(row, { group_name: groupNames[row.group_id] }));
|
|
33
|
+
|
|
22
34
|
if (!rows.length) {
|
|
23
35
|
return reply.status(200).send([]);
|
|
24
36
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getMeta, getFilterSQL, getTemplate, metaFormat, pgClients, yml2json } from '@opengis/fastify-table/utils.js';
|
|
2
2
|
|
|
3
3
|
const layerMetaColumns = ['id', 'name', 'label', 'source', 'table', 'pk', 'geomColumn', 'srid', 'cls', 'htmls'];
|
|
4
4
|
|
|
@@ -22,10 +22,19 @@ export default async function mapFeaturesPoint({ pg = pgClients.client, params =
|
|
|
22
22
|
|
|
23
23
|
const index = loadTemplate?.find?.(el => el[0].split('.').shift() === 'index')?.[1];
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const layers1 = !index
|
|
26
26
|
? loadTemplate?.layers
|
|
27
27
|
: loadTemplate?.filter?.(el => el[0].split('.').shift() !== 'index')?.map(el => ({ ...el[1], id: el[0].replace('.yml', '').replace('.json', '') }));
|
|
28
28
|
|
|
29
|
+
const serviceLayers = await Promise.all(layers1.filter(el => typeof el === 'string').map(async (layer) => {
|
|
30
|
+
const layerData = await getTemplate('layer', layer);
|
|
31
|
+
const serviceData = layerData || await pg.query('select service_id as id, *, st_asgeojson(bbox)::json as bbox, st_asgeojson(center)::json as center from gis.services where service_id=$1', [layer]).then(el => el.rows?.[0]);
|
|
32
|
+
serviceData.style = yml2json(serviceData.style);
|
|
33
|
+
return { ...serviceData, id: layer, visible: true };
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
const layers = layers1.filter(el => el && typeof el === 'object').concat(serviceLayers);
|
|
37
|
+
|
|
29
38
|
if (!layers?.length) {
|
|
30
39
|
return reply.status(404).send('map layers not found');
|
|
31
40
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import { pgClients, getTemplate, handlebars } from "@opengis/fastify-table/utils.js";
|
|
2
|
-
import { attachClassifiers } from '../../gis/registers/funcs/classifiers.js';
|
|
3
|
-
|
|
4
|
-
const pg = pgClients.client;
|
|
1
|
+
import { pgClients, getTemplate, handlebars, metaFormat } from "@opengis/fastify-table/utils.js";
|
|
5
2
|
|
|
6
3
|
export default async function mapFormat(req, reply) {
|
|
7
|
-
const { query } = req;
|
|
4
|
+
const { pg = pgClients.client, query = {} } = req;
|
|
8
5
|
const { layer, id, map } = query;
|
|
9
6
|
const time = Date.now();
|
|
10
7
|
|
|
@@ -65,46 +62,45 @@ export default async function mapFormat(req, reply) {
|
|
|
65
62
|
const excluded = ["created_by", "created_at", "updated_by", "geom", "id", "uid", "updated_at", "cdate", "editor_date", "editor_id"];
|
|
66
63
|
const filteredColumns = columnNames.filter(name => !excluded.includes(name));
|
|
67
64
|
|
|
68
|
-
const
|
|
69
|
-
rows: [row]
|
|
70
|
-
} = await pg.query(
|
|
65
|
+
const rows = await pg.query(
|
|
71
66
|
`SELECT ${pk} as "id", geom::json, ${filteredColumns.map(n => `"${n}"`).join(", ")} FROM ${source_path} WHERE ${pk} = $1`,
|
|
72
67
|
[id]
|
|
73
|
-
);
|
|
68
|
+
).then(el => el.rows || []);
|
|
74
69
|
|
|
75
|
-
if (!
|
|
70
|
+
if (!rows.length) return reply.status(404).send('object not found');
|
|
76
71
|
|
|
77
72
|
const classifiers = columns
|
|
78
73
|
.filter(col => col.data && ["select", "badge", "tags"].includes(col.format))
|
|
79
|
-
.
|
|
74
|
+
.reduce((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {});
|
|
75
|
+
|
|
76
|
+
await metaFormat({ rows, table: source_path, cls: classifiers, sufix: false });
|
|
77
|
+
const fullRow = rows[0];
|
|
80
78
|
|
|
81
|
-
const fullRow = await attachClassifiers(row, classifiers);
|
|
82
79
|
const result = [];
|
|
83
80
|
for await (const col of columns) {
|
|
84
81
|
if (excluded.includes(col.name)) continue;
|
|
85
82
|
if (col.hidden_card === true) continue;
|
|
86
83
|
|
|
87
|
-
const name = col.ua || col.name;
|
|
84
|
+
const name = col.label || col.ua || col.name;
|
|
88
85
|
let value;
|
|
89
86
|
value = fullRow[`${col.name}_data`]?.text
|
|
90
87
|
|| fullRow[`${col.name}_text`]
|
|
91
|
-
|| fullRow[col.name]
|
|
92
|
-
|| '-';
|
|
88
|
+
|| fullRow[col.name];
|
|
93
89
|
|
|
94
90
|
if (col.format === 'date' && fullRow[col.name]) {
|
|
95
91
|
// const dt = formatDate(fullRow[col.name], { hash: { format: 'dd.mm.yy' } });
|
|
96
92
|
const dt = await handlebars.compile(`{{formatDate "${fullRow[col.name]}"}}`)({ format: 'dd.mm.yy' });
|
|
97
93
|
value = dt;
|
|
98
94
|
}
|
|
99
|
-
|
|
95
|
+
if (!value) continue;
|
|
100
96
|
result.push(`<div class="grid grid-cols-1 gap-1 py-3 sm:grid-cols-3 sm:gap-4 even:bg-gray-50 text-[12px]">
|
|
101
97
|
<dt class="text-gray-900">${name}</dt>
|
|
102
|
-
<dd class="text-gray-700 sm:col-span-2">${value}</dd>
|
|
98
|
+
<dd class="text-gray-700 sm:col-span-2">${value || '-'}</dd>
|
|
103
99
|
</div>`);
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
const htmlTemplate = pg?.pk?.['admin.template']
|
|
107
|
-
? await pg.query('select body from admin.templates where name=$1 limit 1').then(el => el.rows?.[0]?.body)
|
|
103
|
+
? await pg.query('select body from admin.templates where name=$1 limit 1', [template || id]).then(el => el.rows?.[0]?.body)
|
|
108
104
|
: null;
|
|
109
105
|
|
|
110
106
|
const html1 = await handlebars.compile(htmlTemplate || 'template not found')(fullRow);
|
|
@@ -80,7 +80,7 @@ export default async function vtile({ pg = pgClients.client, params = {}, query
|
|
|
80
80
|
return reply.status(404).send('layer table not found');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const cls =
|
|
83
|
+
const cls = getColumnCLS(table);
|
|
84
84
|
|
|
85
85
|
const meta = await getMeta({ pg, table });
|
|
86
86
|
const { columns = [] } = meta || {};
|