@opengis/gis 0.1.25 → 0.1.27
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/import-file.cjs +3 -3
- package/dist/import-file.js +22 -13
- package/module/gis/card/gis.maps.table/index.yml +3 -0
- package/module/gis/card/gis.metadata.table/index.yml +3 -0
- package/module/gis/card/gis.rasters.table/index.yml +3 -0
- package/module/gis/card/gis.registers.table/index.yml +3 -0
- package/module/gis/card/gis.services.table/index.yml +3 -0
- package/package.json +6 -4
- package/plugin.js +0 -2
- package/server/plugins/vite.js +12 -3
- package/server/routes/gis/index.mjs +3 -0
- package/server/routes/gis/registers/funcs/content.type.js +10 -0
- package/server/routes/gis/registers/funcs/get.info.js +89 -0
- package/server/routes/gis/registers/gis.export.js +149 -0
package/dist/import-file.js
CHANGED
|
@@ -25107,7 +25107,8 @@ const Ab = { class: "w-full flex flex-col bg-white border border-stone-200 shado
|
|
|
25107
25107
|
default: tr(() => [
|
|
25108
25108
|
v[1] || (v[1] = Br(" Перейти на карту "))
|
|
25109
25109
|
]),
|
|
25110
|
-
_: 1
|
|
25110
|
+
_: 1,
|
|
25111
|
+
__: [1]
|
|
25111
25112
|
})
|
|
25112
25113
|
]),
|
|
25113
25114
|
default: tr(() => [
|
|
@@ -29804,7 +29805,8 @@ const Iw = {
|
|
|
29804
29805
|
default: tr(() => [
|
|
29805
29806
|
E[1] || (E[1] = Br(" Відкрити бокову панель "))
|
|
29806
29807
|
]),
|
|
29807
|
-
_: 1
|
|
29808
|
+
_: 1,
|
|
29809
|
+
__: [1]
|
|
29808
29810
|
})
|
|
29809
29811
|
]),
|
|
29810
29812
|
_: 1
|
|
@@ -29996,7 +29998,8 @@ function oS(h, d, y, b, k, P) {
|
|
|
29996
29998
|
default: tr(() => [
|
|
29997
29999
|
d[0] || (d[0] = re("div", null, "Повернутися до початкових налаштувань", -1))
|
|
29998
30000
|
]),
|
|
29999
|
-
_: 1
|
|
30001
|
+
_: 1,
|
|
30002
|
+
__: [0]
|
|
30000
30003
|
})
|
|
30001
30004
|
]);
|
|
30002
30005
|
}
|
|
@@ -30168,7 +30171,8 @@ function dS(h, d, y, b, k, P) {
|
|
|
30168
30171
|
default: tr(() => [
|
|
30169
30172
|
d[0] || (d[0] = re("div", null, "Надрукувати", -1))
|
|
30170
30173
|
]),
|
|
30171
|
-
_: 1
|
|
30174
|
+
_: 1,
|
|
30175
|
+
__: [0]
|
|
30172
30176
|
});
|
|
30173
30177
|
}
|
|
30174
30178
|
const fS = /* @__PURE__ */ rr(pS, [["render", dS]]), mS = {
|
|
@@ -30223,7 +30227,8 @@ function gS(h, d, y, b, k, P) {
|
|
|
30223
30227
|
default: tr(() => [
|
|
30224
30228
|
d[1] || (d[1] = re("div", null, "Перемикач виду", -1))
|
|
30225
30229
|
]),
|
|
30226
|
-
_: 1
|
|
30230
|
+
_: 1,
|
|
30231
|
+
__: [1]
|
|
30227
30232
|
});
|
|
30228
30233
|
}
|
|
30229
30234
|
const yS = /* @__PURE__ */ rr(mS, [["render", gS]]), _S = {}, vS = {
|
|
@@ -30443,7 +30448,8 @@ function OS(h, d, y, b, k, P) {
|
|
|
30443
30448
|
default: tr(() => [
|
|
30444
30449
|
d[7] || (d[7] = re("div", null, "Дізнатися геолокацію", -1))
|
|
30445
30450
|
]),
|
|
30446
|
-
_: 1
|
|
30451
|
+
_: 1,
|
|
30452
|
+
__: [7]
|
|
30447
30453
|
})
|
|
30448
30454
|
]);
|
|
30449
30455
|
}
|
|
@@ -31203,7 +31209,8 @@ function dA(h, d, y, b, k, P) {
|
|
|
31203
31209
|
default: tr(() => [
|
|
31204
31210
|
d[2] || (d[2] = Br(" Приховати панель "))
|
|
31205
31211
|
]),
|
|
31206
|
-
_: 1
|
|
31212
|
+
_: 1,
|
|
31213
|
+
__: [2]
|
|
31207
31214
|
})
|
|
31208
31215
|
]),
|
|
31209
31216
|
re("div", cA, [
|
|
@@ -31248,7 +31255,8 @@ function dA(h, d, y, b, k, P) {
|
|
|
31248
31255
|
default: tr(() => [
|
|
31249
31256
|
d[6] || (d[6] = re("div", null, "Виміряти довжину", -1))
|
|
31250
31257
|
]),
|
|
31251
|
-
_: 1
|
|
31258
|
+
_: 1,
|
|
31259
|
+
__: [6]
|
|
31252
31260
|
})
|
|
31253
31261
|
]);
|
|
31254
31262
|
}
|
|
@@ -31549,7 +31557,8 @@ function PA(h, d, y, b, k, P) {
|
|
|
31549
31557
|
default: tr(() => [
|
|
31550
31558
|
d[1] || (d[1] = Br(" Приховати панель "))
|
|
31551
31559
|
]),
|
|
31552
|
-
_: 1
|
|
31560
|
+
_: 1,
|
|
31561
|
+
__: [1]
|
|
31553
31562
|
})
|
|
31554
31563
|
]),
|
|
31555
31564
|
re("div", kA, [
|
|
@@ -31586,7 +31595,8 @@ function PA(h, d, y, b, k, P) {
|
|
|
31586
31595
|
default: tr(() => [
|
|
31587
31596
|
d[4] || (d[4] = re("div", null, "Виміряти площу", -1))
|
|
31588
31597
|
]),
|
|
31589
|
-
_: 1
|
|
31598
|
+
_: 1,
|
|
31599
|
+
__: [4]
|
|
31590
31600
|
})
|
|
31591
31601
|
]);
|
|
31592
31602
|
}
|
|
@@ -32323,9 +32333,8 @@ const IA = /* @__PURE__ */ rr(wA, [["render", PA], ["__scopeId", "data-v-024154e
|
|
|
32323
32333
|
key: 0,
|
|
32324
32334
|
map: y.value,
|
|
32325
32335
|
activeTool: E.value,
|
|
32326
|
-
setActiveTool: Yn
|
|
32327
|
-
|
|
32328
|
-
}, _t === "home" ? { initialView: Fn.value } : {}, { onCardValuesId: oi }), null, 16, ["map", "activeTool"])) : kt("", !0)
|
|
32336
|
+
setActiveTool: Yn
|
|
32337
|
+
}, { ref_for: !0 }, _t === "home" ? { initialView: Fn.value } : {}, { onCardValuesId: oi }), null, 16, ["map", "activeTool"])) : kt("", !0)
|
|
32329
32338
|
], 64))), 128))
|
|
32330
32339
|
])) : kt("", !0)
|
|
32331
32340
|
], 512), [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengis/gis",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Softpro",
|
|
6
6
|
"main": "dist/import-file.js",
|
|
@@ -30,8 +30,10 @@
|
|
|
30
30
|
"@grpc/grpc-js": "1.13.4",
|
|
31
31
|
"@grpc/proto-loader": "0.7.15",
|
|
32
32
|
"@mapbox/sphericalmercator": "1.2.0",
|
|
33
|
-
"@opengis/fastify-
|
|
34
|
-
"@opengis/
|
|
33
|
+
"@opengis/fastify-file": "1.1.2",
|
|
34
|
+
"@opengis/fastify-table": "1.4.15",
|
|
35
|
+
"@opengis/fastify-auth": "1.1.0",
|
|
36
|
+
"@opengis/v3-core": "^0.3.196",
|
|
35
37
|
"@opengis/v3-filter": "0.1.18",
|
|
36
38
|
"@turf/turf": "7.2.0",
|
|
37
39
|
"axios": "1.9.0",
|
|
@@ -55,4 +57,4 @@
|
|
|
55
57
|
"sass-embedded": "1.86.3",
|
|
56
58
|
"vite": "^6.3.5"
|
|
57
59
|
}
|
|
58
|
-
}
|
|
60
|
+
}
|
package/plugin.js
CHANGED
|
@@ -5,8 +5,6 @@ import { config } from '@opengis/fastify-table/utils.js';
|
|
|
5
5
|
config.prefix = config.prefix || '/api';
|
|
6
6
|
|
|
7
7
|
config.admin = true; // for user-menu
|
|
8
|
-
config.auth = config.auth || {};
|
|
9
|
-
config.auth.disable = true; // if fastify-auth not registered, then auth is disabled
|
|
10
8
|
|
|
11
9
|
async function plugin(app, opts = config) {
|
|
12
10
|
// API
|
package/server/plugins/vite.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import { createServer } from 'vite';
|
|
4
4
|
|
|
5
|
+
import { config } from '@opengis/fastify-table/utils.js';
|
|
6
|
+
|
|
5
7
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
6
8
|
|
|
7
9
|
console.log({ isProduction });
|
|
8
10
|
|
|
9
|
-
|
|
10
11
|
async function plugin(fastify, opts) {
|
|
11
12
|
|
|
12
13
|
const viteServer = !isProduction ? await createServer({
|
|
@@ -28,6 +29,10 @@ async function plugin(fastify, opts) {
|
|
|
28
29
|
|
|
29
30
|
// this is middleware for vite's dev server
|
|
30
31
|
fastify.addHook('onRequest', async (request, reply) => {
|
|
32
|
+
const { user } = request.session?.passport || {};
|
|
33
|
+
if (!user && config.pg && !config.auth?.disable) {
|
|
34
|
+
return reply.redirect('/login');
|
|
35
|
+
}
|
|
31
36
|
const next = () => new Promise((resolve) => {
|
|
32
37
|
viteServer.middlewares(request.raw, reply.raw, () => resolve());
|
|
33
38
|
});
|
|
@@ -57,6 +62,10 @@ async function plugin(fastify, opts) {
|
|
|
57
62
|
fastify.get('/public/*', staticFile);
|
|
58
63
|
|
|
59
64
|
fastify.get('*', async (req, reply) => {
|
|
65
|
+
const { user } = req.session?.passport || {};
|
|
66
|
+
if (!user && config.pg && !config.auth?.disable) {
|
|
67
|
+
return reply.redirect('/login');
|
|
68
|
+
}
|
|
60
69
|
if (!isProduction) return null; // admin vite
|
|
61
70
|
const stream = fs.createReadStream('admin/dist/index.html');
|
|
62
71
|
return reply.type('text/html').send(stream);
|
|
@@ -5,6 +5,7 @@ import getLayerGeom from './services/get.layer.geom.js';
|
|
|
5
5
|
import gisRegistry from './registers/gis.registry.js';
|
|
6
6
|
import gisRegistryList from './registers/gis.registry.list.js';
|
|
7
7
|
import mapRegistry from './registers/map.registry.js';
|
|
8
|
+
import gisExport from './registers/gis.export.js';
|
|
8
9
|
|
|
9
10
|
export default async function route(app) {
|
|
10
11
|
app.put('/insert-columns/:token', insertColumns);
|
|
@@ -17,4 +18,6 @@ export default async function route(app) {
|
|
|
17
18
|
app.get('/xml/:id', { config: { policy: ['public'] } }, metadataXML);
|
|
18
19
|
|
|
19
20
|
app.get('/get-layer-geom/:id', { config: { policy: ['public'] } }, getLayerGeom);
|
|
21
|
+
|
|
22
|
+
app.get('/gis-export/:type/:slug', { config: { policy: ['public'] } }, gisExport);
|
|
20
23
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const ContentType = {
|
|
2
|
+
xml: 'application/xml',
|
|
3
|
+
shp: 'application/zip',
|
|
4
|
+
csv: 'text/csv',
|
|
5
|
+
geojson: 'application/vnd.geo+json',
|
|
6
|
+
json: 'application/json',
|
|
7
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export default ContentType;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { pgClients } from '@opengis/fastify-table/utils.js';
|
|
2
|
+
import { attachClassifiers } from '../../../gis/registers/funcs/classifiers.js';
|
|
3
|
+
const pg = pgClients.client;
|
|
4
|
+
|
|
5
|
+
export default async function getInfo({ finalInfo, format }, reply) {
|
|
6
|
+
if (!finalInfo) return { error: 'No data found' };
|
|
7
|
+
|
|
8
|
+
const table = finalInfo?.table_name;
|
|
9
|
+
const queryCondition = finalInfo?.query || '1=1';
|
|
10
|
+
const columns = finalInfo?.columns || finalInfo?.attributes || '[]';
|
|
11
|
+
const exportable = columns.filter(c => c.is_export);
|
|
12
|
+
const selectNames = exportable.map(c => `"${c.name}"`);
|
|
13
|
+
const selectSQL = `
|
|
14
|
+
SELECT ${selectNames?.length ? selectNames.join(', ') : ''} ${format === 'geojson' || format === 'shp'? `, ST_AsGeoJSON(geom)::json as geom` : ``}
|
|
15
|
+
FROM ${table}
|
|
16
|
+
WHERE ${queryCondition} ${format === 'geojson' || format === 'shp'? `and geom is not null` : ``}
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
if (format === 'shp') {
|
|
20
|
+
const { rows: geomTypeRows } = await pg.query(`
|
|
21
|
+
SELECT DISTINCT ST_GeometryType(geom) AS geom_type
|
|
22
|
+
FROM ${table}
|
|
23
|
+
WHERE geom IS NOT NULL
|
|
24
|
+
`);
|
|
25
|
+
const geometryTypes = geomTypeRows.map(row => row.geom_type);
|
|
26
|
+
|
|
27
|
+
const sqlList = geometryTypes.map(type => {
|
|
28
|
+
return {
|
|
29
|
+
type,
|
|
30
|
+
query: `
|
|
31
|
+
SELECT ${selectNames.join(', ')}, ST_AsGeoJSON(geom)::json as geom
|
|
32
|
+
FROM ${table}
|
|
33
|
+
WHERE ${queryCondition} AND ST_GeometryType(geom) = '${type}' AND geom IS NOT NULL
|
|
34
|
+
`,
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const { rows: [{ srid }] } = await pg.query(`
|
|
39
|
+
SELECT ST_SRID(geom) AS srid
|
|
40
|
+
FROM ${table}
|
|
41
|
+
WHERE geom IS NOT NULL
|
|
42
|
+
LIMIT 1;
|
|
43
|
+
`);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
columns: exportable,
|
|
47
|
+
table,
|
|
48
|
+
srid: srid || 4326,
|
|
49
|
+
geomTypes: geometryTypes,
|
|
50
|
+
sqlList
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const { rows } = await pg.query(selectSQL);
|
|
55
|
+
if (!rows?.length) {
|
|
56
|
+
return reply.status(404).send({ message: 'Немає даних, які можна експортувати', status: 404 });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const classifiers = exportable
|
|
60
|
+
.filter(col => col.data && ["select", "badge", "tags"].includes(col.format))
|
|
61
|
+
.map(col => ({ name: col.name, classifier: col.data }));
|
|
62
|
+
|
|
63
|
+
const rowsWithClassifiers = await attachClassifiers(rows, classifiers);
|
|
64
|
+
|
|
65
|
+
const simplifiedRows = rowsWithClassifiers.map(row => {
|
|
66
|
+
const newRow = { ...row };
|
|
67
|
+
|
|
68
|
+
for (const key of Object.keys(newRow)) {
|
|
69
|
+
if (key.endsWith('_data') && typeof newRow[key]?.text === 'string') {
|
|
70
|
+
const baseKey = key.replace(/_data$/, '');
|
|
71
|
+
newRow[baseKey] = newRow[key].text;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (key.endsWith('_text') && typeof newRow[key] === 'string') {
|
|
75
|
+
const baseKey = key.replace(/_text$/, '');
|
|
76
|
+
newRow[baseKey] = newRow[key];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return newRow;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
rows: simplifiedRows,
|
|
85
|
+
data: rows,
|
|
86
|
+
columns: exportable,
|
|
87
|
+
table
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createReadStream } from 'fs';
|
|
2
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { config, getMeta, getFolder } from '@opengis/fastify-table/utils.js';
|
|
5
|
+
import { grpc } from '@opengis/fastify-file/utils.js';
|
|
6
|
+
import convertJSONToCSV from '@opengis/fastify-file/server/routes/file/controllers/utils/convertJSONToCSV.js';
|
|
7
|
+
import getInfo from './funcs/get.info.js';
|
|
8
|
+
import ContentType from './funcs/content.type.js';
|
|
9
|
+
const { jsonToXls, geojsonToShp } = grpc();
|
|
10
|
+
const rootDir = getFolder(config, 'local');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Експорт даних реєстру
|
|
14
|
+
* @param {Object} query - Об'єкт запиту з форматом.
|
|
15
|
+
* @param {Object} params - Об'єкт параметрів з slug та type.
|
|
16
|
+
* @param {Function} reply
|
|
17
|
+
* @returns {boolean} File
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export default async function gisExport({ pg, query = {}, params = {} }, reply) {
|
|
21
|
+
const { format } = query;
|
|
22
|
+
const { slug, type } = params;
|
|
23
|
+
if (!['csv', 'xlsx', 'shp', 'geojson'].includes(format)) {
|
|
24
|
+
return reply.code(401).send({ message: 'Param format is invalid. Allowed: csv, xlsx, shp, geojson', status: 404 });
|
|
25
|
+
}
|
|
26
|
+
if (!slug) return reply.code(401).send({ message: 'slug is not defined', status: 404 });
|
|
27
|
+
if (!type) return reply.code(401).send({ message: 'type is not defined', status: 404 });
|
|
28
|
+
|
|
29
|
+
let registerInfo;
|
|
30
|
+
let serviceInfo;
|
|
31
|
+
|
|
32
|
+
if (type === 'register') {
|
|
33
|
+
registerInfo = await pg.query(
|
|
34
|
+
`SELECT table_name, columns, query
|
|
35
|
+
FROM gis.registers
|
|
36
|
+
WHERE register_key = $1`,
|
|
37
|
+
[slug]
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
if (type === 'service') {
|
|
41
|
+
serviceInfo = await pg.query(
|
|
42
|
+
`SELECT source_path as table_name, attributes as columns, query
|
|
43
|
+
FROM gis.services
|
|
44
|
+
WHERE service_key = $1`,
|
|
45
|
+
[slug]
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
const finalInfo = registerInfo?.rows[0] || serviceInfo?.rows[0];
|
|
49
|
+
|
|
50
|
+
const exportable = finalInfo?.columns?.filter(c => c.is_export);
|
|
51
|
+
const colmodel = exportable.map(col => ({
|
|
52
|
+
name: col.name,
|
|
53
|
+
title: col.ua || col.name
|
|
54
|
+
}));
|
|
55
|
+
const { rows, table, srid, sqlList } = await getInfo({ finalInfo, format }, reply);
|
|
56
|
+
let filePath = ['csv', 'geojson', 'xlsx'].includes(format) ? path.join(rootDir, `/files/tmp/export_${table}_${Date.now()}.${format}`) : path.join(rootDir, `/files/tmp/export_${table}_${Date.now()}.zip`);
|
|
57
|
+
const ext = path.extname(filePath);
|
|
58
|
+
const filePathJSON = ['csv', 'geojson', 'shp'].includes(format) ? filePath.replace(ext, '.json') : filePath;
|
|
59
|
+
let exportedZipPaths = [];
|
|
60
|
+
|
|
61
|
+
const meta = await getMeta({ pg, table: table });
|
|
62
|
+
if ((format === 'geojson' || format === 'shp') && !meta?.geom) {
|
|
63
|
+
return reply.status(400).send('Ця таблиця не містить полів геометрії. Виберіть тип, який не потребує геометрії для вивантаження');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (format === 'xlsx') {
|
|
67
|
+
if (!jsonToXls) {
|
|
68
|
+
const message = 'Сервіс конвертації jsonToXls тимчасово недоступний. Спробуйте експортувати дані у іншому форматі...';
|
|
69
|
+
const error = new Error(message);
|
|
70
|
+
error.status = 503;
|
|
71
|
+
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
const { result } = await jsonToXls({
|
|
75
|
+
json: JSON.stringify(rows),
|
|
76
|
+
colmodel: JSON.stringify(colmodel),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
80
|
+
await writeFile(filePath, result, 'base64');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (format === 'csv') {
|
|
84
|
+
await mkdir(path.dirname(filePathJSON), { recursive: true });
|
|
85
|
+
await writeFile(filePathJSON, JSON.stringify(rows));
|
|
86
|
+
|
|
87
|
+
await convertJSONToCSV({
|
|
88
|
+
filePath: filePathJSON, colmodel, columnList: colmodel.map(c => c.name),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (['shp', 'geojson'].includes(format)) {
|
|
93
|
+
if (format === 'geojson') {
|
|
94
|
+
const geojson = {
|
|
95
|
+
type: 'FeatureCollection',
|
|
96
|
+
features: rows.map((row) => ({
|
|
97
|
+
type: 'Feature',
|
|
98
|
+
geometry: row.geom,
|
|
99
|
+
properties: Object.fromEntries(Object.entries(row).filter(([key]) => key !== 'geom')),
|
|
100
|
+
})),
|
|
101
|
+
};
|
|
102
|
+
filePath = filePathJSON.replace('.json', '.geojson');
|
|
103
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
104
|
+
await writeFile(filePath, JSON.stringify(geojson));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (format === 'shp') {
|
|
108
|
+
const proj = `EPSG:${srid || 4326}`;
|
|
109
|
+
for (const { type, query } of sqlList) {
|
|
110
|
+
const { rows } = await pg.query(query);
|
|
111
|
+
if (!rows.length) continue;
|
|
112
|
+
|
|
113
|
+
const geojson = {
|
|
114
|
+
type: 'FeatureCollection',
|
|
115
|
+
features: rows.map(row => ({
|
|
116
|
+
type: 'Feature',
|
|
117
|
+
geometry: row.geom,
|
|
118
|
+
properties: Object.fromEntries(Object.entries(row).filter(([k]) => k !== 'geom')),
|
|
119
|
+
})),
|
|
120
|
+
};
|
|
121
|
+
const geomShort = type.toLowerCase().replace('st_', '');
|
|
122
|
+
const typedPath = filePath.replace('.zip', `_${geomShort}.zip`);
|
|
123
|
+
|
|
124
|
+
const filePathTyped = filePath.replace('.zip', `_${type.toLowerCase().replace('st_', '')}.zip`);
|
|
125
|
+
const { result: shpZipBuffer } = await geojsonToShp({
|
|
126
|
+
geojson: JSON.stringify(geojson),
|
|
127
|
+
proj,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await mkdir(path.dirname(filePathTyped), { recursive: true });
|
|
131
|
+
await writeFile(filePathTyped, Buffer.from(shpZipBuffer, 'base64'));
|
|
132
|
+
|
|
133
|
+
exportedZipPaths.push(typedPath);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let exportFilePath = filePath;
|
|
139
|
+
|
|
140
|
+
if (format === 'shp' && exportedZipPaths?.length > 0) {
|
|
141
|
+
exportFilePath = exportedZipPaths[0];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
reply.header('Content-Disposition', `attachment; filename=${encodeURIComponent(path.basename(exportFilePath))}`);
|
|
145
|
+
reply.header('Content-Type', ContentType[format]);
|
|
146
|
+
|
|
147
|
+
const fileStream = createReadStream(exportFilePath);
|
|
148
|
+
return reply.send(fileStream);
|
|
149
|
+
}
|