@opengis/gis 0.2.54 → 0.2.56

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/gis",
3
- "version": "0.2.54",
3
+ "version": "0.2.56",
4
4
  "type": "module",
5
5
  "author": "Softpro",
6
6
  "main": "./dist/index.js",
@@ -20,7 +20,7 @@ function sequence(arr, fn) {
20
20
 
21
21
  const unsafe = true;
22
22
 
23
- export default async function checkRasterFile({ arr, reply }) {
23
+ export default async function checkRasterFile({ arr, reply, nocache }) {
24
24
  const callback = reply ? eventStream(reply) : () => { };
25
25
 
26
26
  let
@@ -34,7 +34,7 @@ export default async function checkRasterFile({ arr, reply }) {
34
34
  const result = await sequence((arr || []), async ({
35
35
  relpath, idx,
36
36
  }) => {
37
- callback(`${idx + 1}/${arr.length}`);
37
+ callback(`${idx + 1}/${arr.length} (${relpath})`);
38
38
 
39
39
  // check raster upload status, skip for directories
40
40
  const uploadStatus = !path.extname(relpath) ? {} : await GetRasterStatus({ path: relpath });
@@ -46,7 +46,7 @@ export default async function checkRasterFile({ arr, reply }) {
46
46
  }
47
47
 
48
48
  // create at xml/:id with name only, always - by path
49
- const resp = await CreateXML({ path: relpath }, (msg) => {
49
+ const resp = await CreateXML({ path: relpath, nocache }, (msg) => {
50
50
  if (typeof msg === 'string' && msg === 'XML creation complete') {
51
51
  createdCount += 1;
52
52
  }
@@ -71,7 +71,6 @@ export default async function checkRasterFile({ arr, reply }) {
71
71
  const data = await RenderTile({
72
72
  path: relpath,
73
73
  width: 256,
74
- height: 256,
75
74
  bbox: [3713463.7081504324, 6088362.176970857, 3713616.5822070027, 6088515.051027427], // /:id/18/155363/91245.png
76
75
  ttl: '0',
77
76
  }).catch(err => ({ err: err.toString() }));
@@ -12,7 +12,7 @@ const dirname = path.dirname(filepath);
12
12
  const PROTO_PATH = path.join(dirname, '../', 'map.proto');
13
13
  // const PROTO_PATH = config.mapServerAddress && config.mapServerAddress.startsWith('127.0.0.1') ? path.join(dirname, '../../../../../../service/mapnikp/map.proto') : path.join(dirname, '../', 'map.proto');
14
14
 
15
- const { mapServerAddress, ready } = config;
15
+ const { mapServerAddress } = config;
16
16
 
17
17
  if (!mapServerAddress) {
18
18
  throw new Error("Map server address is not defined in the configuration.");
@@ -38,11 +38,11 @@ const mapClient = new proto.map.MapService(
38
38
 
39
39
  mapClient.waitForReady(Date.now() + 5000, (err) => {
40
40
  if (err) {
41
- ready.mapnik = false;
41
+ config.ready.mapnik = false;
42
42
  console.error("Mapnik client connection timeout or failure:", err);
43
43
  }
44
44
  else {
45
- ready.mapnik = true;
45
+ config.ready.mapnik = true;
46
46
  console.log("Mapnik client connected successfully.");
47
47
  }
48
48
  });
@@ -131,12 +131,13 @@ message GetRenderOut {
131
131
  }
132
132
 
133
133
  message GetLogsRequest {
134
- string period = 1; // "5m", "1h", "1d"
135
- string type = 2; // optional filter
134
+ string path = 1; // "info", "info/2026-01-01.log" etc.
136
135
  }
137
136
 
138
137
  message GetLogsOut {
139
- repeated string result = 1;
138
+ string data = 1;
139
+ string links = 2;
140
+ string err = 3;
140
141
  }
141
142
 
142
143
  message GetRasterInfoRequest {
@@ -46,7 +46,8 @@ export default async function layerList({
46
46
  // parse service style
47
47
  rows.forEach(row => Object.assign(row, { style: row.style ? yml2json(row.style) : null }));
48
48
  // assign service url
49
- rows.filter(row => row.service !== 'ogc').forEach(row => Object.assign(row, { url: `${config.prefix || '/api'}/vtile/${row.id}/ua/{z}/{x}/{y}.vmt` }));
49
+ rows.filter(row => row.service && ['raster', 'cartocss'].includes(row.service)).forEach(row => Object.assign(row, { url: `${config.prefix || '/api'}/gis-rtile/${row.id}/{z}/{x}/{y}.png` }));
50
+ rows.filter(row => !row.url && row.service !== 'ogc').forEach(row => Object.assign(row, { url: `${config.prefix || '/api'}/vtile/${row.id}/ua/{z}/{x}/{y}.vmt` }));
50
51
  // parse extent string to number[]
51
52
  rows.filter(row => row.extent).forEach(row => Object.assign(row, {
52
53
  extent: row.extent.match(/BOX\(([^)]+)\)/)?.[1]
@@ -85,7 +85,13 @@ export default async function vtile({
85
85
  const select = data.popup?.find(el => el.name === key);
86
86
  // console.log(select);
87
87
  const clsData = select?.data || col.data;
88
- const cls = clsData ? await getSelect(clsData).then(el => ({ name: clsData, type: el.arr ? 'cls' : 'select', sql: el.sql })) : null;
88
+ const cls = clsData ? await getSelect(clsData).then(el => {
89
+ if (!el) {
90
+ console.error('gis vtile: cls not found', clsData);
91
+ return null;
92
+ }
93
+ return { name: clsData, type: el.arr ? 'cls' : 'select', sql: el.sql };
94
+ }) : null;
89
95
  const { name, type: type1, sql: sql1 } = cls || getColumnCLS(table, col.data || key) || {};
90
96
  // console.log({ name, type, sql });
91
97
  if (name && type1 === 'cls') {
@@ -23,7 +23,6 @@ export default async function checkCarto({
23
23
  const rtile = await RenderTile({
24
24
  name: params.id,
25
25
  width: 256,
26
- height: 256,
27
26
  bbox: [3713463.7081504324, 6088362.176970857, 3713616.5822070027, 6088515.051027427],
28
27
  ttl: '0',
29
28
  // debug: query.debug,
@@ -0,0 +1,75 @@
1
+ import {
2
+ applyHookSync, config, pgClients,
3
+ } from '@opengis/fastify-table/utils.js';
4
+
5
+ import checkRasterFile from '../../../plugins/mapnik/funcs/checkRasterFile.js';
6
+
7
+ // todo: add to kamianske, remove custom api and test
8
+ // addHook('beforeCreateXmlMulti', ({ list = [], id }) => {
9
+ // list.push({
10
+ // id: 'mbd_existent_detail_id',
11
+ // table: 'data_mbd.mbd_existent_detail',
12
+ // relpath: 'upfile',
13
+ // q: `select mbd_existent_detail_id, upfile
14
+ // from data_mbd.mbd_existent_detail a
15
+ // left join data_mbd.mbd b on a.mbd_id=b.mbd_id
16
+ // where material_type_id = '3245363247443870877'
17
+ // and upfile is not null
18
+ // and upfile ~ '.tif'
19
+ // and left(upfile,7) != '/files/'
20
+ // ${id ? ` and mbd_existent_detail_id = '${id}'` : ''}`,
21
+ // });
22
+ // });
23
+
24
+ export default async function createXmlMulti({
25
+ pg = pgClients.client, params, query,
26
+ }, reply) {
27
+ if (!config.mapServerAddress) {
28
+ return reply.code(400).send({
29
+ error: 'mapnik grpc service address not set',
30
+ code: 400,
31
+ });
32
+ }
33
+
34
+ if (config.ready?.mapnik !== true) {
35
+ return reply.code(400).send({
36
+ error: 'mapnik grpc service not ready',
37
+ code: 400,
38
+ });
39
+ }
40
+
41
+ const list = [{
42
+ id: 'raster_id',
43
+ relpath: 'source_path',
44
+ table: 'gis.rasters',
45
+ query: `source_path is not null ${params.id ? ` and raster_id = '${params.id}'` : ''}`,
46
+ }];
47
+
48
+ applyHookSync('beforeCreateXmlMulti', { list, id: params.id });
49
+
50
+ const rows = await Promise.all(list.filter(el => el.id && el.table && el.relpath).map(async el => pg.query(el.q ? `select ${el.id} as id, ${el.relpath} as relpath, '${el.table}' as table from (${el.q})q` : `select ${el.id} as id, ${el.relpath} as relpath, '${el.table}' as table from ${el.table} where ${el.query || 'true'}`).then(e => e.rows || []))).then(e => e.flat());
51
+
52
+ if (!rows.length) {
53
+ return reply.status(404).send({ error: 'empty rows', code: 404 });
54
+ }
55
+
56
+ const results = await checkRasterFile({ arr: rows.map(row => row.relpath), reply, nocache: query.nocache });
57
+
58
+ const q = Object.keys(results || {}).filter(key => results[key] && results[key].extent).map(key => {
59
+ const { extent } = results[key];
60
+ const extentString = `ST_MakeEnvelope(${extent[0]}, ${extent[1]}, ${extent[2]}, ${extent[3]}, 4326)`;
61
+
62
+ const { id, table } = rows.find(row => row.relpath === key) || {};
63
+ const pk = pg.pk?.[table];
64
+ return id && pk ? `update ${table} set geom=${extentString} where ${pk}='${id}'` : null;
65
+ })
66
+ .filter(Boolean);
67
+
68
+ if (config.trace) {
69
+ console.log('createXmlMulti: update bounds of', q.length, 'rows');
70
+ }
71
+
72
+ if (q.length) await pg.query(q.join(';\n'));
73
+
74
+ return reply.status(200).send('all finished');
75
+ }
@@ -11,13 +11,19 @@ export default async function mapnikLogger({
11
11
  return reply.status(400).send({ error: 'mapnik server address needed', code: 400 });
12
12
  }
13
13
 
14
- if (!['render', 'error', 'info', 'critical'].includes(params.type)) {
15
- return reply.status(400).send({ error: 'invalid params: type', code: 400 });
14
+ const { data, links, err } = await GetLogs({ path: params['*'] });
15
+
16
+ if (err) {
17
+ return reply.status(400).send(err);
18
+ }
19
+
20
+ if (links) {
21
+ return reply.headers({
22
+ "Content-Type": "text/html; charset=UTF-8",
23
+ "Content-Security-Policy": "default-src 'none'",
24
+ "X-Content-Type-Options": "nosniff",
25
+ }).send(links);
16
26
  }
17
27
 
18
- const { result } = await GetLogs({
19
- period: query.period,
20
- type: params.type,
21
- });
22
- return reply.status(200).send(result.join('\n'));
28
+ return reply.headers({ "Content-type": "text/plain; charset=UTF-8" }).send(data);
23
29
  }
@@ -70,7 +70,6 @@ export default async function rtile({
70
70
  path: cartoExists ? null : raster.source_path, // for rasters only
71
71
  name: id, // required for cartocss
72
72
  width: 256,
73
- height: 256,
74
73
  bbox,
75
74
  ttl: query.nocache ? '0' : '1h',
76
75
  debug: query.debug,
@@ -87,7 +86,7 @@ export default async function rtile({
87
86
 
88
87
  const buffer = Buffer.from(data.base64, 'base64');
89
88
 
90
- return reply.headers({ 'Content-Type': 'image/png', 'Cache-Control': query.nocache ? 'no-cache' : '30d' }).send(buffer);
89
+ return reply.headers({ 'Content-Type': 'image/png', 'Cache-Control': query.nocache ? 'no-store, no-cache, must-revalidate' : 'public, max-age=2592000' }).send(buffer);
91
90
  }
92
91
  catch (err) {
93
92
  logger.file('rtile/error', { error: err.toString(), stack: err.stack });
@@ -5,6 +5,7 @@ import rasterInfo from './controllers/rasterInfo.js';
5
5
  import mapnikStat from './controllers/mapnikStat.js';
6
6
  import mapnikLogger from './controllers/mapnikLogger.js';
7
7
  import checkCarto from './controllers/checkCarto.js';
8
+ import createXmlMulti from './controllers/createXmlMulti.js';
8
9
 
9
10
  const publicParams = { config: { policy: 'L0' }, package: 'gis' }; // L0 === public
10
11
  const adminParams = { config: { policy: 'L1', role: 'admin' }, package: 'gis' };
@@ -16,6 +17,7 @@ export default async function route(app) {
16
17
  app.get('/gis-rtile/:id/:z/:y/:x', publicParams, rtile);
17
18
  app.get('/gis-raster/:id', publicParams, rasterInfo);
18
19
  app.get('/gis-stat/:period?', adminParams, mapnikStat);
19
- app.get('/gis-logger/:type', adminParams, mapnikLogger);
20
+ app.get('/gis-logger/*', adminParams, mapnikLogger);
20
21
  app.get('/gis-css/:id', params, checkCarto);
22
+ app.get('/gis-create-xml/:id?', adminParams, createXmlMulti);
21
23
  }
@@ -1,91 +0,0 @@
1
- import Sphericalmercator from '@mapbox/sphericalmercator';
2
-
3
- import { config, logger, pgClients } from '@opengis/fastify-table/utils.js';
4
-
5
- import mapnik from '../../../plugins/mapnik/funcs/mapnik.js';
6
-
7
- const { RenderTile } = mapnik();
8
-
9
- const mercator = new Sphericalmercator({ size: 256 });
10
-
11
- /**
12
- * Формування растрового tile cartoCss
13
- *
14
- * @method GET
15
- * @alias rtile
16
- * @param {String} bbox - bbox
17
- * @param {Number} height - висота по координатам
18
- * @param {Number} width - ширина по координатам
19
- * @param {String} data - стилізація
20
- * @param {String} lang - мова
21
- * @param {String} z - координата z
22
- * @param {String} x - координата y
23
- * @param {String} y - координата x
24
- */
25
-
26
- export default async function rtileMBD({
27
- pg = pgClients.client, params, query,
28
- }, reply) {
29
- if (!RenderTile) {
30
- return reply.status(400).send({ error: 'mapnik server address needed', code: 400 });
31
- }
32
-
33
- const { id, z, y } = params;
34
-
35
- const x = params.x.split('.')[0] - 0;
36
-
37
- if (!id) {
38
- return reply.status(400).send({ error: 'not enough params: id', code: 400 });
39
- }
40
-
41
- if (!x || !y || !z) {
42
- return reply.status(400).send({ error: 'not enough params: xyz', code: 400 });
43
- }
44
-
45
- const mbd = pg.pk?.['data_mbd.mbd_existent_detail'] ? await pg.query(`
46
- select
47
- mbd_existent_detail_id as id,
48
- upfile
49
- from data_mbd.mbd_existent_detail a
50
- left join data_mbd.mbd b on a.mbd_id=b.mbd_id
51
- where material_type_id = '3245363247443870877'
52
- and upfile is not null
53
- and upfile ~ '.tif'
54
- and mbd_existent_detail_id=$1
55
- `, [id]).then(el => el.rows?.[0]) : [];
56
-
57
- if (!mbd) {
58
- return reply.status(404).send({ error: 'mbd not found', code: 404 });
59
- }
60
-
61
- const bbox = mercator.bbox(y, x, z, false, '900913');
62
-
63
- try {
64
- const data = await RenderTile({
65
- path: mbd.upfile,
66
- name: id,
67
- width: 256,
68
- height: 256,
69
- bbox,
70
- ttl: query.nocache ? '0' : '1h',
71
- debug: query.debug,
72
- });
73
-
74
- if (query.debug) {
75
- return data;
76
- }
77
-
78
- if (data.err) {
79
- logger.file('rtile/mbd/error', { error: data.err });
80
- return reply.status(500).send({ error: config.local ? data.err : 'render error', code: 500 });
81
- }
82
-
83
- const buffer = Buffer.from(data.base64, 'base64');
84
-
85
- return reply.headers({ 'Content-Type': 'image/png' }).send(buffer);
86
- }
87
- catch (err) {
88
- logger.file('rtile/mbd/error', { error: err.toString(), stack: err.stack });
89
- return reply.status(500).send({ error: config.local ? err.toString() : 'rtile error', code: 500 });
90
- }
91
- }