@opengis/bi 1.0.13 → 1.0.14

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.
Files changed (50) hide show
  1. package/README.md +2 -4
  2. package/config.js +5 -5
  3. package/dist/bi.js +1 -1
  4. package/dist/bi.umd.cjs +116 -130
  5. package/dist/{import-file-1T7kpSzt.js → import-file-DUp3rsNI.js} +11132 -10748
  6. package/dist/{map-component-mixin-BLM9iEWA.js → map-component-mixin-CGM0P5ub.js} +1135 -1134
  7. package/dist/style.css +1 -1
  8. package/dist/{vs-calendar-WiK1hcHS.js → vs-calendar-cOoinEwc.js} +33 -30
  9. package/dist/vs-funnel-bar-kLkPoIhJ.js +105 -0
  10. package/dist/vs-heatmap-3XAVGTSo.js +98 -0
  11. package/dist/vs-map-B1tr6V5_.js +74 -0
  12. package/dist/{vs-map-cluster-Dfe9INqE.js → vs-map-cluster-BWJPx7wE.js} +28 -25
  13. package/dist/vs-number-CrU7LmkV.js +48 -0
  14. package/dist/{vs-text-DcrAdQ40.js → vs-text-DRPx3aID.js} +2 -1
  15. package/package.json +37 -12
  16. package/plugin.js +4 -4
  17. package/server/migrations/bi.sql +66 -0
  18. package/server/plugins/docs.js +36 -35
  19. package/server/plugins/hook.js +72 -69
  20. package/server/plugins/vite.js +22 -8
  21. package/server/routes/dashboard/controllers/dashboard.delete.js +5 -3
  22. package/server/routes/dashboard/controllers/dashboard.js +66 -32
  23. package/server/routes/dashboard/controllers/dashboard.list.js +2 -5
  24. package/server/routes/dashboard/controllers/utils/yaml.js +1 -2
  25. package/server/routes/dashboard/index.mjs +5 -4
  26. package/server/routes/data/controllers/data.js +94 -34
  27. package/server/routes/data/controllers/util/chartSQL.js +24 -10
  28. package/server/routes/data/controllers/util/normalizeData.js +51 -29
  29. package/server/routes/data/index.mjs +1 -3
  30. package/server/routes/db/controllers/dbTablePreview.js +63 -0
  31. package/server/routes/db/controllers/dbTables.js +36 -0
  32. package/server/routes/db/index.mjs +17 -0
  33. package/server/routes/edit/controllers/dashboard.add.js +6 -5
  34. package/server/routes/edit/controllers/dashboard.edit.js +16 -9
  35. package/server/routes/edit/controllers/widget.add.js +43 -19
  36. package/server/routes/edit/controllers/widget.del.js +13 -6
  37. package/server/routes/edit/controllers/widget.edit.js +34 -13
  38. package/server/routes/edit/index.mjs +14 -10
  39. package/server/routes/map/controllers/cluster.js +89 -60
  40. package/server/routes/map/controllers/clusterVtile.js +154 -84
  41. package/server/routes/map/controllers/geojson.js +48 -22
  42. package/server/routes/map/controllers/map.js +51 -51
  43. package/server/routes/map/controllers/vtile.js +61 -40
  44. package/server/routes/map/index.mjs +1 -1
  45. package/server/utils/getWidget.js +67 -40
  46. package/utils.js +5 -4
  47. package/dist/vs-funnel-bar-CpPbYZ0_.js +0 -92
  48. package/dist/vs-heatmap-BG4eIROH.js +0 -83
  49. package/dist/vs-map-BRk6Fmks.js +0 -66
  50. package/dist/vs-number-CJq-vi95.js +0 -39
@@ -1,11 +1,22 @@
1
- import dataUpdate from '@opengis/fastify-table/crud/funcs/dataUpdate.js';
2
- export default async function widgetEdit({ pg = {}, funcs = {}, body = {}, params = {} }) {
1
+ import { dataUpdate } from '@opengis/fastify-table/utils.js';
2
+
3
+ import { yamlSafe } from '../../../../utils.js';
4
+
5
+ export default async function widgetEdit({
6
+ pg = {},
7
+ funcs = {},
8
+ body = {},
9
+ params = {},
10
+ }) {
3
11
  const { widget: widgetName, name: dashboardName } = params;
4
- const tableName = body.table_name;
12
+ const data = body.yml ? yamlSafe.load(body.yml) : body;
13
+ const tableName =
14
+ data.data?.table || data?.table || data.table_name || body.table_name;
15
+
5
16
  if (!widgetName || !dashboardName) {
6
17
  return {
7
18
  message: 'not enough params: dashboard and widget name',
8
- status: 400
19
+ status: 400,
9
20
  };
10
21
  }
11
22
 
@@ -17,23 +28,33 @@ export default async function widgetEdit({ pg = {}, funcs = {}, body = {}, param
17
28
  [dashboardName, widgetName]
18
29
  )
19
30
  .then((res1) => res1.rows?.[0] || {});
20
- const checkTable = await pg.query(`select * from bi.widget where $1 in (table_name)`, [
21
- tableName
22
- ]);
23
31
  const { widget_id: widgetId, dashboard_id: dashboardId } = row || {};
24
- if (!checkTable.rows.length) return { message: 'bad params', status: 401 };
32
+
33
+ // const checkTable = await pg.query(
34
+ // `select * from bi.widget where $1 in (table_name)`,
35
+ // [tableName]
36
+ // );
37
+ // if (!checkTable.rows.length) {
38
+ // return { message: 'bad params', status: 401 };
39
+ // }
40
+
41
+ if (!tableName || !pg.pk?.[tableName]) {
42
+ return { message: 'bad params: table', status: 400 };
43
+ }
25
44
 
26
45
  if (!widgetId) {
27
46
  return { message: `widget not found ${widgetName}`, status: 404 };
28
47
  }
29
48
 
30
- const res = await funcs.dataUpdate({
49
+ const widgetData = { ...data, data };
50
+ if (body?.yml) Object.assign(widgetData, { yml: body.yml });
51
+ await funcs.dataUpdate({
31
52
  table: 'bi.widget',
32
53
  id: widgetId,
33
- data: body
54
+ data: widgetData,
34
55
  });
35
56
 
36
- //\====================
57
+ // \====================
37
58
  const currentDashboard = await pg
38
59
  .query(
39
60
  `select * from bi.dashboard
@@ -68,13 +89,13 @@ export default async function widgetEdit({ pg = {}, funcs = {}, body = {}, param
68
89
  const res1 = await dataUpdate({
69
90
  table: 'bi.dashboard',
70
91
  id: dashboardId,
71
- data: bodyDashboard
92
+ data: bodyDashboard,
72
93
  });
73
94
 
74
95
  return {
75
96
  message: `Edited widget ${widgetName}`,
76
97
  status: 200,
77
- rows: res1
98
+ rows: res1,
78
99
  };
79
100
  } catch (err) {
80
101
  return { error: err.toString(), status: 500 };
@@ -1,27 +1,31 @@
1
- import widgetAdd from '../edit/controllers/widget.add.js';
2
- import widgetEdit from '../edit/controllers/widget.edit.js';
3
- import widgetDel from '../edit/controllers/widget.del.js';
4
- import dashboardEdit from '../edit/controllers/dashboard.edit.js';
5
- import dashboardAdd from '../edit/controllers/dashboard.add.js';
1
+ /* eslint-disable import/extensions */
2
+ import widgetAdd from './controllers/widget.add.js';
3
+ import widgetEdit from './controllers/widget.edit.js';
4
+ import widgetDel from './controllers/widget.del.js';
5
+ import dashboardEdit from './controllers/dashboard.edit.js';
6
+ import dashboardAdd from './controllers/dashboard.add.js';
6
7
 
7
8
  const biSchema = {
8
9
  querystring: {
9
10
  widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
10
11
  dashboard: { type: 'string', pattern: '^([\\d\\w]+)$' },
11
12
  list: { type: 'string', pattern: '^([\\d])$' },
12
- sql: { type: 'string', pattern: '^([\\d])$' }
13
+ sql: { type: 'string', pattern: '^([\\d])$' },
13
14
  },
14
15
  params: {
15
16
  name: { type: 'string', pattern: '^([\\d\\w]+)$' },
16
- widget: { type: 'string', pattern: '^([\\d\\w]+)$' }
17
+ widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
17
18
  },
18
19
  };
19
20
 
20
-
21
21
  export default async function route(fastify) {
22
22
  fastify.post(`/bi-dashboard/:name`, { schema: biSchema }, widgetAdd);
23
23
  fastify.put(`/bi-dashboard/:name/:widget`, { schema: biSchema }, widgetEdit);
24
- fastify.delete(`/bi-dashboard/:name/:widget`, { schema: biSchema }, widgetDel);
24
+ fastify.delete(
25
+ `/bi-dashboard/:name/:widget`,
26
+ { schema: biSchema },
27
+ widgetDel
28
+ );
25
29
  fastify.post(`/bi-dashboard`, { schema: biSchema }, dashboardAdd);
26
30
  fastify.put(`/bi-dashboard/:name`, { schema: biSchema }, dashboardEdit);
27
- }
31
+ }
@@ -1,75 +1,104 @@
1
-
2
1
  import { getWidget } from '../../../../utils.js';
3
2
 
4
- export default async function cluster({
5
- pg, funcs, query = {}, log,
6
- }) {
7
- const {
8
- widget, filter, dashboard, search,
9
- } = query;
10
-
11
- if (!widget) {
12
- return { message: 'not enough params: widget', status: 400 };
13
- }
14
-
15
- const { data } = await getWidget({ dashboard, widget })
3
+ export default async function cluster({ pg, funcs, query = {}, log }) {
4
+ const { widget, filter, dashboard, search } = query;
16
5
 
17
- try {
6
+ if (!widget) {
7
+ return { message: 'not enough params: widget', status: 400 };
8
+ }
18
9
 
19
- const pkey = pg.pk?.[data?.table];
10
+ const { data } = await getWidget({ dashboard, widget });
20
11
 
21
- if (!pkey) {
22
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`, status: 400 };
23
- }
12
+ try {
13
+ const pkey = pg.pk?.[data?.table];
24
14
 
25
- // data param
26
- const { table, query: where = '1=1', metrics = [], cluster, clusterTable = {} } = data;
27
-
28
- if (!cluster) {
29
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`, status: 400 };
30
- }
31
-
32
- if (!metrics.length) {
33
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`, status: 400 };
34
- }
35
-
36
- if (!clusterTable?.name) {
37
- return { message: 'not enough widget params: clusterTable name', status: 400 };
38
- }
15
+ if (!pkey) {
16
+ return {
17
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
18
+ status: 400,
19
+ };
20
+ }
39
21
 
40
- if (clusterTable?.name && !pg.pk?.[clusterTable?.name]) {
41
- return { message: 'invalid widget params: clusterTable pkey not found', status: 404 };
42
- }
22
+ // data param
23
+ const {
24
+ table,
25
+ query: where = '1=1',
26
+ metrics = [],
27
+ cluster,
28
+ clusterTable = {},
29
+ } = data;
30
+
31
+ if (!cluster) {
32
+ return {
33
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`,
34
+ status: 400,
35
+ };
36
+ }
43
37
 
44
- const { total = 0 } = pg.pk?.[table]
45
- ? await pg.queryCache(`select oid::regclass as table, reltuples AS total from pg_class`)
46
- .then((res) => res.rows?.find((row) => row.table === table))
47
- : {};
38
+ if (!metrics.length) {
39
+ return {
40
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`,
41
+ status: 400,
42
+ };
43
+ }
48
44
 
49
- const { bbox } = await pg.query(`select st_asgeojson(box2d(geom))::json as bbox from ${table} where ${where || '1=1'}`).then((res1) => res1.rows?.[0] || {});
45
+ if (!clusterTable?.name) {
46
+ return {
47
+ message: 'not enough widget params: clusterTable name',
48
+ status: 400,
49
+ };
50
+ }
50
51
 
51
- // get sql
52
- const { optimizedSQL } = filter || search
53
- ? await funcs.getFilterSQL({ pg, table, filter, search })
54
- : {};
52
+ if (clusterTable?.name && !pg.pk?.[clusterTable?.name]) {
53
+ return {
54
+ message: 'invalid widget params: clusterTable pkey not found',
55
+ status: 404,
56
+ };
57
+ }
55
58
 
56
- const q = `select "${cluster}" as name, sum("${metrics[0]}")::float as metric
59
+ const { total = 0 } = pg.pk?.[table]
60
+ ? await pg
61
+ .queryCache(
62
+ `select oid::regclass as table, reltuples AS total from pg_class`
63
+ )
64
+ .then((res) => res.rows?.find((row) => row.table === table))
65
+ : {};
66
+
67
+ const { bbox } = await pg
68
+ .query(
69
+ `select st_asgeojson(box2d(geom))::json as bbox from ${table} where ${where || '1=1'}`
70
+ )
71
+ .then((res1) => res1.rows?.[0] || {});
72
+
73
+ // get sql
74
+ const { optimizedSQL } =
75
+ filter || search
76
+ ? await funcs.getFilterSQL({ pg, table, filter, search })
77
+ : {};
78
+
79
+ const q = `select "${cluster}" as name, sum("${metrics[0]}")::float as metric
57
80
  from ${optimizedSQL ? `(${optimizedSQL})` : table} q
58
81
  left join lateral (select "${pg.pk?.[clusterTable?.name]}" as id from ${clusterTable?.name} where ${clusterTable?.title}=q."${cluster}" limit 1)b on 1=1
59
82
  where ${where} group by ${cluster}, b.id`;
60
83
 
61
- if (query.sql === '1') return q;
62
-
63
- // auto Index
64
- // funcs.autoIndex({ table, columns: (metrics || []).concat([cluster]) });
65
-
66
- const { rows = [] } = await pg.query(q);
67
- const vals = rows.map(el => el.metric - 0).sort((a, b) => a - b);
68
- const len = vals.length;
69
- const sizes = [vals[0], vals[Math.floor(len / 4)], vals[Math.floor(len / 2)], vals[Math.floor(len * 0.75)], vals[len - 1]]
70
- return { sizes, rows, bbox, count: rows.length, total };
71
- } catch (err) {
72
- log.error('bi/cluster', { error: err.toString(), query });
73
- return { error: err.toString(), status: 500 };
74
- }
75
- }
84
+ if (query.sql === '1') return q;
85
+
86
+ // auto Index
87
+ // funcs.autoIndex({ table, columns: (metrics || []).concat([cluster]) });
88
+
89
+ const { rows = [] } = await pg.query(q);
90
+ const vals = rows.map((el) => el.metric - 0).sort((a, b) => a - b);
91
+ const len = vals.length;
92
+ const sizes = [
93
+ vals[0],
94
+ vals[Math.floor(len / 4)],
95
+ vals[Math.floor(len / 2)],
96
+ vals[Math.floor(len * 0.75)],
97
+ vals[len - 1],
98
+ ];
99
+ return { sizes, rows, bbox, count: rows.length, total };
100
+ } catch (err) {
101
+ log.error('bi/cluster', { error: err.toString(), query });
102
+ return { error: err.toString(), status: 500 };
103
+ }
104
+ }
@@ -11,81 +11,148 @@ const mercator = new Sphericalmercator({ size: 256 });
11
11
  const clusterExists = {};
12
12
 
13
13
  export default async function clusterVtile(req, reply) {
14
- const {
15
- pg, funcs, params = {}, query = {}, log,
16
- } = req;
17
- const { z, y } = params;
18
- const x = params.x?.split('.')[0] - 0;
19
-
20
- if (!x || !y || !z) {
21
- return { message: 'not enough params: xyz', status: 400 };
14
+ const { pg, funcs, params = {}, query = {}, log } = req;
15
+ const { z, y } = params;
16
+ const x = params.x?.split('.')[0] - 0;
17
+
18
+ if (!x || !y || !z) {
19
+ return { message: 'not enough params: xyz', status: 400 };
20
+ }
21
+
22
+ const { widget, filter, dashboard, search, clusterZoom, nocache, pointZoom } =
23
+ query;
24
+
25
+ if (!widget) {
26
+ return { message: 'not enough params: widget', status: 400 };
27
+ }
28
+
29
+ const { data } = await getWidget({ dashboard, widget });
30
+
31
+ const headers = {
32
+ 'Content-Type': 'application/x-protobuf',
33
+ 'Cache-Control':
34
+ nocache || query.sql ? 'no-cache' : 'public, max-age=86400',
35
+ };
36
+
37
+ const hash = [pointZoom, filter].filter((el) => el).join();
38
+
39
+ const root = funcs.getFolder(req);
40
+ const file = path.join(
41
+ root,
42
+ `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`
43
+ );
44
+
45
+ try {
46
+ if (!data?.table) {
47
+ return {
48
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table not specified`,
49
+ status: 400,
50
+ };
22
51
  }
23
52
 
24
- const {
25
- widget, filter, dashboard, search, clusterZoom, nocache, pointZoom,
26
- } = query;
53
+ const pkey = pg.pk?.[data?.table];
27
54
 
28
- if (!widget) {
29
- return { message: 'not enough params: widget', status: 400 };
55
+ if (!pkey) {
56
+ return {
57
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
58
+ status: 400,
59
+ };
30
60
  }
31
61
 
62
+ // data param
63
+ const {
64
+ table,
65
+ query: where = '1=1',
66
+ metrics = [],
67
+ cluster,
68
+ clusterTable = {},
69
+ } = data;
70
+ if (!clusterTable?.name) {
71
+ Object.assign(clusterTable, {
72
+ name: 'bi.cluster',
73
+ title: 'title',
74
+ query: `type='${data.cluster}'`,
75
+ });
76
+ }
32
77
 
33
- const { data } = await getWidget({ dashboard, widget })
34
-
35
-
36
- const headers = {
37
- 'Content-Type': 'application/x-protobuf',
38
- 'Cache-Control': nocache || query.sql
39
- ? 'no-cache'
40
- : 'public, max-age=86400',
41
- };
42
-
43
- const hash = [pointZoom, filter].filter((el) => el).join();
44
-
45
- const root = funcs.getFolder(req);
46
- const file = path.join(root, `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`);
47
-
48
- try {
49
-
50
-
51
- if (!data?.table) {
52
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table not specified`, status: 400 };
53
- }
54
-
55
- const pkey = pg.pk?.[data?.table];
56
-
57
- if (!pkey) {
58
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`, status: 400 };
78
+ if (cluster && !clusterExists[data.cluster]) {
79
+ const res = await fetch(
80
+ `https://cdn.softpro.ua/data/bi/${data.cluster}-ua.geojson`
81
+ );
82
+ if (res?.status !== 200) {
83
+ return {
84
+ message: `cluster file not found: ${cluster}-ua.json`,
85
+ status: 404,
86
+ };
87
+ }
88
+ try {
89
+ const geojson = await res.json();
90
+
91
+ const features = geojson?.features?.filter(
92
+ (el, idx, arr) =>
93
+ el?.geometry &&
94
+ arr
95
+ .map((item) => item.properties.name)
96
+ .indexOf(el.properties.name) === idx
97
+ ); // unique
98
+ if (!features?.length) {
99
+ return {
100
+ message: `cluster file empty: ${cluster}-ua.json`,
101
+ status: 400,
102
+ };
59
103
  }
60
-
61
- // data param
62
- const { table, query: where = '1=1', metrics = [], cluster, clusterTable = {} } = data;
63
- if (!clusterTable?.name) {
64
- Object.assign(data, { clusterTable: { name: 'bi.cluster', title, query: `type='${data.cluster}'` } });
65
- }
66
-
67
- if (!clusterExists[data?.cluster]) {
68
- // const res = await fetch('https://cdn.softpro.ua/data/bi/region-ua.json');
69
- // if (res?.status !== 200) {
70
- // }
71
- // await pg.query(`insert into bi.cluster (title,type,geom) values()`);
72
- // clusterExists[data?.cluster] = 1;
104
+ const { count = 0 } = await pg
105
+ .query(`select count(*)::int from bi.cluster where type=$1`, [
106
+ cluster,
107
+ ])
108
+ .then((res1) => res1.rows?.[0] || {});
109
+ if (count !== features?.length) {
110
+ // await pg.query(`delete from bi.cluster where type=$1`, [cluster]);
111
+ const values = features
112
+ ?.map(
113
+ (el) =>
114
+ `('${el.properties.name?.replace(/'/g, "''") || ''}', '${cluster}', ST_GeomFromGeoJSON('${JSON.stringify(el.geometry)}')::geometry)`
115
+ )
116
+ .join(',');
117
+ const { rowCount } = await pg.query(
118
+ `insert into bi.cluster (title,type,geom) values ${values} on conflict(title,type) do update set geom=excluded.geom`
119
+ );
120
+ log.info('bi/clusterVtile', { cluster, rowCount });
73
121
  }
122
+ clusterExists[cluster] = 1;
123
+ } catch (err) {
124
+ log.error('bi/clusterVtile', {
125
+ error: err.toString(),
126
+ filename: `${cluster}-ua.json`,
127
+ });
128
+ return {
129
+ message: `cluster file import error: ${cluster}-ua.json`,
130
+ status: 500,
131
+ };
132
+ }
133
+ }
74
134
 
75
- if (!cluster) {
76
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`, status: 400 };
77
- }
135
+ if (!cluster) {
136
+ return {
137
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: cluster column not specified`,
138
+ status: 400,
139
+ };
140
+ }
78
141
 
79
- if (!metrics.length) {
80
- return { message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`, status: 400 };
81
- }
142
+ if (!metrics.length) {
143
+ return {
144
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: metric columns not found`,
145
+ status: 400,
146
+ };
147
+ }
82
148
 
83
- // get sql
84
- const { optimizedSQL } = filter || search
85
- ? await funcs.getFilterSQL({ pg, table, filter, search })
86
- : {};
149
+ // get sql
150
+ const { optimizedSQL } =
151
+ filter || search
152
+ ? await funcs.getFilterSQL({ pg, table, filter, search })
153
+ : {};
87
154
 
88
- const q = `select "${cluster}" as name, sum("${metrics[0]}")::float as metric, b.*
155
+ const q = `select "${cluster}" as name, sum("${metrics[0]}")::float as metric, b.*
89
156
  from ${optimizedSQL ? `(${optimizedSQL})` : table} q
90
157
  left join lateral (select "${pg.pk?.[clusterTable?.name]}" as id,
91
158
  ${clusterTable?.geom || 'geom'} as geom from ${clusterTable?.name}
@@ -94,14 +161,17 @@ export default async function clusterVtile(req, reply) {
94
161
  where ${where} group by
95
162
  ${cluster}, b.id, b.${clusterTable?.geom || 'geom'}`;
96
163
 
97
- if (query.sql === '1') return q;
164
+ if (query.sql === '1') return q;
98
165
 
99
- const geomCol = parseInt(z, 10) < parseInt(pointZoom, 10) ? `ST_Centroid(${clusterTable?.geom || data?.geom || 'geom'})` : clusterTable?.geom || data?.geom || 'geom';
166
+ const geomCol =
167
+ parseInt(z, 10) < parseInt(pointZoom, 10)
168
+ ? `ST_Centroid(${clusterTable?.geom || data?.geom || 'geom'})`
169
+ : clusterTable?.geom || data?.geom || 'geom';
100
170
 
101
- const bbox = mercator.bbox(+y, +x, +z, false/* , '900913' */);
102
- const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
171
+ const bbox = mercator.bbox(+y, +x, +z, false /* , '900913' */);
172
+ const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
103
173
 
104
- const q1 = `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
174
+ const q1 = `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
105
175
  FROM (
106
176
  SELECT
107
177
  floor(random() * 100000 + 1)::int + row_number() over() as row,
@@ -119,25 +189,25 @@ export default async function clusterVtile(req, reply) {
119
189
  limit 3000)q
120
190
  ) q`;
121
191
 
122
- if (query.sql === '2') return q1;
192
+ if (query.sql === '2') return q1;
123
193
 
124
- // auto Index
125
- funcs.autoIndex({ table, columns: (metrics || []).concat([cluster]) });
194
+ // auto Index
195
+ funcs.autoIndex({ table, columns: (metrics || []).concat([cluster]) });
126
196
 
127
- const { rows = [] } = await pg.query(q1);
197
+ const { rows = [] } = await pg.query(q1);
128
198
 
129
- if (query.sql === '3') return rows.map((el) => el.tile);
199
+ if (query.sql === '3') return rows.map((el) => el.tile);
130
200
 
131
- const buffer = Buffer.concat(rows.map((el) => Buffer.from(el.tile)));
201
+ const buffer = Buffer.concat(rows.map((el) => Buffer.from(el.tile)));
132
202
 
133
- if (!nocache) {
134
- await mkdir(path.dirname(file), { recursive: true });
135
- await writeFile(file, buffer, 'binary');
136
- }
137
-
138
- return reply.headers(headers).send(buffer);
139
- } catch (err) {
140
- log.error('bi/clusterVtile', { error: err.toString(), query, params });
141
- return { error: err.toString(), status: 500 };
203
+ if (!nocache) {
204
+ await mkdir(path.dirname(file), { recursive: true });
205
+ await writeFile(file, buffer, 'binary');
142
206
  }
143
- }
207
+
208
+ return reply.headers(headers).send(buffer);
209
+ } catch (err) {
210
+ log.error('bi/clusterVtile', { error: err.toString(), query, params });
211
+ return { error: err.toString(), status: 500 };
212
+ }
213
+ }