@opengis/gis 0.1.76 → 0.1.78

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.
@@ -0,0 +1,37 @@
1
+ [
2
+ {
3
+ "id": "1",
4
+ "text": "Нове будівництво",
5
+ "color": "#34c0ea"
6
+ },
7
+ {
8
+ "id": "2",
9
+ "text": "Реконструкція",
10
+ "color": "#b54aa2"
11
+ },
12
+ {
13
+ "id": "3",
14
+ "text": "Реставрація",
15
+ "color": "#7f74da"
16
+ },
17
+ {
18
+ "id": "4",
19
+ "text": "Капітальний ремонт",
20
+ "color": "#f8ac59"
21
+ },
22
+ {
23
+ "id": "5",
24
+ "text": "Реконструкція без зміни зовнішніх геометричних розмірів",
25
+ "color": "#17a2b8"
26
+ },
27
+ {
28
+ "id": "6",
29
+ "text": "Реставрація без зміни зовнішніх геометричних розмірів",
30
+ "color": "#F08FA1"
31
+ },
32
+ {
33
+ "id": "7",
34
+ "text": "Існуюча забудова",
35
+ "color": "green"
36
+ }
37
+ ]
@@ -0,0 +1,32 @@
1
+ [
2
+ {
3
+ "id": "1",
4
+ "text": "Діючий",
5
+ "color": "#1ab394"
6
+ },
7
+ {
8
+ "id": "2",
9
+ "text": "Зупинений",
10
+ "color": "#ed82c8"
11
+ },
12
+ {
13
+ "id": "3",
14
+ "text": "Скасований",
15
+ "color": "#4a4a4a"
16
+ },
17
+ {
18
+ "id": "5",
19
+ "text": "Проектний",
20
+ "color": "#6495ed"
21
+ },
22
+ {
23
+ "id": "7",
24
+ "text": "Очікує розгляду",
25
+ "color": "#92b8ef"
26
+ },
27
+ {
28
+ "id": "10",
29
+ "text": "Архівний",
30
+ "color": "#d48428"
31
+ }
32
+ ]
@@ -0,0 +1 @@
1
+ select uid, coalesce(coalesce(sur_name,'')||coalesce(' '||user_name,'') ||coalesce(' '||father_name,''),login) as text from admin.users
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/gis",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "type": "module",
5
5
  "author": "Softpro",
6
6
  "main": "./dist/index.js",
@@ -31,12 +31,17 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@mapbox/sphericalmercator": "1.2.0",
34
- "carto": "0.16.3"
34
+ "carto": "0.16.3",
35
+ "yaml": "2.8.1",
36
+ "@opengis/form": "^0.0.28"
37
+ },
38
+ "peerDependencies": {
39
+ "@opengis/fastify-table": "^1.5.4"
35
40
  },
36
41
  "devDependencies": {
37
- "@opengis/core": "^0.0.18",
42
+ "@opengis/core": "^0.0.23",
38
43
  "@opengis/fastify-auth": "^1.1.22",
39
- "@opengis/fastify-table": "^1.4.77",
44
+ "@opengis/fastify-table": "^1.5.4",
40
45
  "@opengis/filter": "^0.1.7",
41
46
  "@vitejs/plugin-vue": "^5.2.3",
42
47
  "axios": "^1.11.0",
@@ -45,8 +50,12 @@
45
50
  "lucide-vue-next": "^0.514.0",
46
51
  "sass-embedded": "1.86.3",
47
52
  "vite": "^6.3.5",
53
+ "vitest": "^3.2.4",
48
54
  "vue": "^3.5.13",
49
55
  "vue-router": "4.5.1",
50
- "vuedraggable": "^4.1.0"
56
+ "vuedraggable": "^4.1.0",
57
+ "yaml": "^2.8.1",
58
+ "typescript": "^5.9.2",
59
+ "@opengis/table": "^0.0.27"
51
60
  }
52
61
  }
@@ -1,3 +1,5 @@
1
+ import fp from 'fastify-plugin';
2
+
1
3
  import insertColumns from './registers/insert.columns.js';
2
4
  import insertFilters from './registers/insert.filters.js';
3
5
  import metadataXML from './metadata/metadataXML.js';
@@ -6,8 +8,11 @@ import gisRegistry from './registers/gis.registry.js';
6
8
  import gisRegistryList from './registers/gis.registry.list.js';
7
9
  import mapRegistry from './registers/map.registry.js';
8
10
  import gisExport from './registers/gis.export.js';
11
+ import getServices from './services/get.services.js';
12
+ import deleteService from './services/del.service.js';
13
+ import addService from './services/add.service.js';
9
14
 
10
- export default async function route(app) {
15
+ async function route(app) {
11
16
  app.put('/insert-columns/:token', insertColumns);
12
17
  app.put('/insert-filters/:token', insertFilters);
13
18
  app.get('/gis-registry/:slug', { config: { policy: ['public'] } }, gisRegistry);
@@ -20,4 +25,11 @@ export default async function route(app) {
20
25
  app.get('/get-layer-geom/:id', { config: { policy: ['public'] } }, getLayerGeom);
21
26
 
22
27
  app.get('/gis-export/:type/:slug', { config: { policy: ['public'] } }, gisExport);
28
+
29
+ app.get('/gis-service/:id?', { config: { policy: ['public'] } }, getServices);
30
+ app.post('/gis-service/:id?', { config: { policy: ['public'] } }, addService);
31
+ app.put('/gis-service/:id', { config: { policy: ['public'] } }, addService);
32
+ app.delete('/gis-service/:id', { config: { policy: ['public'] } }, deleteService);
23
33
  }
34
+
35
+ export default route;
@@ -1,4 +1,4 @@
1
- import { getSelectVal } from '@opengis/fastify-table/utils.js';
1
+ import { getSelectVal, getSelect } from '@opengis/fastify-table/utils.js';
2
2
 
3
3
  export async function attachClassifiers(rowOrRows, classifiers = []) {
4
4
  const rows = Array.isArray(rowOrRows) ? rowOrRows : [rowOrRows];
@@ -35,3 +35,49 @@ export async function attachClassifiers(rowOrRows, classifiers = []) {
35
35
 
36
36
  return rowOrRows;
37
37
  }
38
+
39
+ export async function populateFilterOptions({ pg, table, filters, columns, whereConditions }) {
40
+ if (!filters?.length || !table || !columns?.length) return;
41
+
42
+ const optimizedSQL = `select * from ${table} where ${whereConditions || '1=1'}`;
43
+
44
+ await Promise.all(
45
+ filters
46
+ .filter(
47
+ (el) =>
48
+ el.id &&
49
+ el.type !== "Autocomplete" &&
50
+ !el.sql &&
51
+ !el.options?.find?.((item) => item.sql)
52
+ )
53
+ .map(async (el) => {
54
+ const cls = el.data ? await getSelect(el.data, pg) : null;
55
+
56
+ if (!cls && !el.type?.includes?.("Check")) return;
57
+ const { dataTypeID } = (columns || []).find((item) => item.name === el.id) || {};
58
+
59
+ const q = pg.pgType[dataTypeID || '']?.includes("[]")
60
+ ? `select unnest(${el.id})::text as id,count(*) from (${optimizedSQL})q group by unnest(${el.id}) limit 100`
61
+ : `select ${el.id}::text as id,count(*) from (${optimizedSQL})q group by ${el.id} limit 100`;
62
+
63
+ const countArr = await pg.queryCache(q, { table });
64
+
65
+ if (countArr.timeout) {
66
+ Object.assign(el, { timeout: countArr.timeout });
67
+ console.warn("timeout filter", table, el.id);
68
+ }
69
+
70
+ const ids = countArr.rows.map((el1) => el1.id);
71
+
72
+ const clsData = await getSelectVal({ pg, values: ids, name: el.data });
73
+
74
+ const options = countArr.rows.map((cel) => {
75
+ const data = cls?.arr?.find?.(
76
+ (c) => c.id?.toString?.() === cel.id?.toString?.()
77
+ ) || { text: clsData?.[cel.id]?.text || clsData?.[cel.id] || cel.id };
78
+ return { ...cel, id: cel.id === null ? "null" : cel.id, ...data };
79
+ });
80
+ Object.assign(el, { options });
81
+ })
82
+ );
83
+ }
@@ -1,4 +1,4 @@
1
- import { attachClassifiers } from './classifiers.js';
1
+ import { attachClassifiers, populateFilterOptions } from './classifiers.js';
2
2
  import { extractVisibleColumns } from './columns.js';
3
3
  import { getMeta, getFilterSQL } from "@opengis/fastify-table/utils.js";
4
4
 
@@ -26,7 +26,7 @@ export async function handleRegistryRequest({ settings, query, object_id, offset
26
26
 
27
27
  if (object_id) {
28
28
  const sql = `
29
- SELECT "${pk}" as id, ${selectColumns.join(", ")} ${geom ? `, st_asgeojson(${geom})::json as geom` : ''}
29
+ SELECT "${pk}" as id,* , ${selectColumns.join(", ")} ${geom ? `, st_asgeojson(${geom})::json as geom` : ''}
30
30
  FROM ${table_name}
31
31
  WHERE ${pk} = $1
32
32
  `;
@@ -84,6 +84,14 @@ export async function handleRegistryRequest({ settings, query, object_id, offset
84
84
  if (col.view_type === 'subtitle') listConfig.subtitle = col.name;
85
85
  }
86
86
 
87
+ await populateFilterOptions({
88
+ pg,
89
+ table: table_name,
90
+ filters: activeFilters,
91
+ columns,
92
+ whereConditions
93
+ });
94
+
87
95
  return {
88
96
  register_id,
89
97
  name,
@@ -0,0 +1,30 @@
1
+ import { dataInsert, dataUpdate, pgClients } from "@opengis/fastify-table/utils.js";
2
+
3
+ export default async function addService({
4
+ method, params = {}, body, pg = pgClients.client, user = {},
5
+ }, reply) {
6
+ if (method === 'PUT' && !params.id) {
7
+ return reply.status(400).send('not enough params: id');
8
+ }
9
+
10
+ if (method === 'POST') {
11
+ const { rows = [] } = await dataInsert({
12
+ pg,
13
+ id: params.id,
14
+ table: 'gis.services',
15
+ data: body,
16
+ uid: user?.uid,
17
+ });
18
+ return reply.status(200).send(rows[0]);
19
+ }
20
+
21
+ const row = await dataUpdate({
22
+ pg,
23
+ id: params.id,
24
+ table: 'gis.services',
25
+ data: body,
26
+ uid: user?.uid,
27
+ });
28
+
29
+ return reply.status(200).send(row);
30
+ }
@@ -0,0 +1,13 @@
1
+ import { dataDelete, pgClients } from "@opengis/fastify-table/utils.js";
2
+
3
+ export default async function deleteService({
4
+ params = {}, pg = pgClients.client, user = {},
5
+ }, reply) {
6
+ const row = await dataDelete({
7
+ pg,
8
+ id: params.id,
9
+ table: 'gis.services',
10
+ uid: user?.uid,
11
+ });
12
+ return reply.status(200).send(row);
13
+ }
@@ -0,0 +1,43 @@
1
+ import { getMeta, pgClients, yml2json } from "@opengis/fastify-table/utils.js";
2
+
3
+ const table = 'gis.services';
4
+
5
+ const columnType = {
6
+ text: 'text',
7
+ date: 'date',
8
+ bool: 'yes/no',
9
+ numeric: 'number',
10
+ integer: 'number',
11
+ 'timestamp without time zone': 'date',
12
+ 'timestamp with time zone': 'date',
13
+ };
14
+
15
+ 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
+ const rows = await pg.query(`
21
+ SELECT
22
+ service_id, service_key, name, description, keywords, category, holder, group_id, service_type,
23
+ source_type, service_url, source_path, query, geom_type, geometry_column, sql_list, attributes, filters,
24
+ popup, style, legend, card, srid, st_asgeojson(bbox)::json as bbox, st_asgeojson(center)::json as center, is_active as enabled, is_public, is_active, is_downloadable, metadata,
25
+ metadata_url, thumbnail_url, created_by, updated_by, updated_at, created_at
26
+ FROM gis.services where ${params.id ? 'service_id=$1' : '1=1'}
27
+ `, [params.id].filter(Boolean)).then(el => el.rows || []);
28
+
29
+ rows.forEach(row => Object.assign(row, { style: row.style ? yml2json(row.style) : undefined }));
30
+
31
+ if (params.id && !rows.length) {
32
+ return reply.status(404).send('service not found');
33
+ }
34
+
35
+ if (params.id) {
36
+ return reply.status(200).send({ ...rows[0], fields });
37
+ }
38
+
39
+ return reply.status(200).send({
40
+ rows,
41
+ fields,
42
+ });
43
+ }
@@ -5,7 +5,7 @@ import { readFile, stat, mkdir, writeFile } from 'node:fs/promises';
5
5
 
6
6
  import Sphericalmercator from '@mapbox/sphericalmercator';
7
7
 
8
- import { getTemplate, pgClients, getFilterSQL, getMeta, yml2json } from '@opengis/fastify-table/utils.js';
8
+ import { getTemplate, pgClients, getFilterSQL, getMeta, yml2json, getColumnCLS, getSelect } from '@opengis/fastify-table/utils.js';
9
9
 
10
10
  import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
11
11
 
@@ -80,6 +80,8 @@ 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 = await getColumnCLS(table);
84
+
83
85
  const meta = await getMeta({ pg, table });
84
86
  const { columns = [] } = meta || {};
85
87
 
@@ -100,13 +102,29 @@ export default async function vtile({ pg = pgClients.client, params = {}, query
100
102
  : `${el.name}${['int4'].includes(el.type) ? '::text' : ''}`;
101
103
  });
102
104
 
105
+ const propsColumns = props.length ? await Promise.all(props.map(async colname => {
106
+ if (!cls?.[colname]) return `"${colname}"`;
107
+ const { type, data } = await pg.queryCache('select type, data from admin.cls where parent is null and name=$1 limit 1', { args: [cls[colname]], table: 'admin.cls' })
108
+ .then(el => el.rows?.[0] || {}) || {};
109
+
110
+ if (!type || type === 'sql' && !data) {
111
+ return `"${colname}"`;
112
+ }
113
+
114
+ if (type === 'json') {
115
+ return `(select name from admin.cls where parent='${cls[colname]}' and code="${colname}"::text) as "${colname}"`;
116
+ }
117
+
118
+ return `(with c(id,text) as (select * from (${data})q limit 1) select text from c where id::text="${colname}"::text) as "${colname}"`;
119
+ })) : '';
120
+
103
121
  const q = `SELECT ST_AsMVT(q, '${id.replace(/'/g, "''")}', 4096, 'geom','row') as tile
104
122
  FROM (
105
123
  SELECT
106
124
 
107
125
  floor(random() * 100000 + 1)::int + row_number() over() as row,
108
126
 
109
- ${style?.colorAttr ? `${style.colorAttr},` : ''} ${props?.length ? `${props.map(el => `"${el}"`).join(',')},` : ''}
127
+ ${style?.colorAttr ? `${style.colorAttr},` : ''} ${propsColumns ? `${propsColumns.filter(Boolean).join('\n,') || ''},` : ''}
110
128
 
111
129
  ${pk} as id,
112
130
  ST_AsMVTGeom(st_transform(${style?.type === 'point' ? `st_pointonsurface(${geomCol})` : geomCol},3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,true) geom