@opengis/gis 0.2.44 → 0.2.46

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 (30) hide show
  1. package/dist/index.css +1 -1
  2. package/dist/index.js +7874 -7072
  3. package/dist/index.umd.cjs +73 -53
  4. package/module/gis/table/gis.cartocss.table.json +3 -2
  5. package/module/gis/table/gis.maps.table.json +10 -2
  6. package/module/gis/table/gis.ogc_service.table.json +1 -1
  7. package/module/gis/table/gis.rasters.table.json +3 -2
  8. package/module/gis/table/gis.registers.table.json +1 -0
  9. package/module/gis/table/gis.services.table.json +1 -0
  10. package/package.json +75 -75
  11. package/plugin.js +1 -0
  12. package/server/plugins/mapnik/funcs/mapnik.js +83 -78
  13. package/server/plugins/mapnik/map.proto +151 -0
  14. package/server/routes/gis/cartocss/add.cartocss.js +138 -4
  15. package/server/routes/map/index.mjs +4 -4
  16. package/server/routes/map/maps/get.map.js +1 -1
  17. package/server/routes/mapnik/controllers/createXML.js +61 -0
  18. package/server/routes/mapnik/controllers/rtile.js +86 -0
  19. package/server/routes/mapnik/controllers/uploadRaster.js +159 -0
  20. package/server/routes/mapnik/index.js +16 -0
  21. package/server/plugins/mapnik/funcs/createXML.js +0 -72
  22. package/server/plugins/mapnik/funcs/gdalWrapper.js +0 -72
  23. package/server/plugins/mapnik/funcs/map.proto +0 -241
  24. package/server/plugins/mapnik/funcs/rasterConfig.js +0 -11
  25. package/server/plugins/mapnik/funcs/rasterExists.js +0 -21
  26. package/server/plugins/mapnik/funcs/rasterInfo.js +0 -109
  27. package/server/plugins/mapnik/funcs/rasterVrt.js +0 -56
  28. package/server/plugins/mapnik/funcs/rasterXML.js +0 -65
  29. package/server/plugins/mapnik/utils/map.proto +0 -241
  30. package/server/routes/map/controllers/rtile.js +0 -134
@@ -1,18 +1,152 @@
1
+ import carto from 'carto';
2
+
3
+ import { dataUpdate } from '@opengis/fastify-table/utils.js';
4
+ import mapnik from '../../../plugins/mapnik/funcs/mapnik.js';
5
+
6
+ const { UploadXML } = mapnik();
7
+
8
+ const srsList = {};
9
+
10
+ const getGeomColumn = (geoms, customColumn) => {
11
+ if (customColumn && geoms.includes(customColumn)) {
12
+ return customColumn;
13
+ }
14
+ if (geoms.includes('geom')) {
15
+ return 'geom';
16
+ }
17
+ return geoms[0];
18
+ };
19
+
20
+ async function cartoBounds({ id, dataset }, pg) {
21
+ const sqlBounds = dataset.map(el => ({ tbl: el.table.split('.'), geom: el.gcol || 'geom' })).map(({ tbl, geom }) => `select ST_EstimatedExtent('${tbl[0]}','${tbl[1]}', '${geom}')`);
22
+
23
+ const geom = await pg.query(
24
+ `update gis.style set
25
+ geom=(select st_extent(st_estimatedextent) from ( ${sqlBounds.join(' union all ')})q )
26
+ where style_id=$1 returning geom::box2d`,
27
+ [id],
28
+ ).then(e => e.rows?.[0]?.geom);
29
+
30
+ const bounds = geom
31
+ ? geom.replace(/[A-Z\)\(]+/g, '').split(/[ ,]+/).map(el => el - 0)
32
+ : null;
33
+
34
+ if (!bounds) {
35
+ throw new Error('empty bounds');
36
+ }
37
+
38
+ return bounds;
39
+ }
40
+
41
+ function cartoData(el, extent, pg) {
42
+ const minzoom = el.zoom ? (+el.zoom || 5) : 5;
43
+ const maxzoom = el.maxzoom ? (+el.maxzoom || 22) : 22;
44
+ const view = `(select ${el.columns || '1'}, ${el.gcol} from ${el.table} where ${el.query || 'true'} ) as data`;
45
+ const layerBound = extent || [-180, -85, 180, 85];
46
+
47
+ return {
48
+ id: el.caption,
49
+ name: el.caption,
50
+ properties: { minzoom, maxzoom },
51
+ srs: el.proj4text || '+proj=longlat +datum=WGS84 +no_defs',
52
+ Datasource: {
53
+ simplify_geometries: true,
54
+ type: 'postgis',
55
+ extent: layerBound.join(','),
56
+ table: view,
57
+ geometry_field: el.gcol,
58
+ host: pg.options?.host || 'localhost',
59
+ dbname: pg.options?.database || 'postgres',
60
+ port: pg.options?.port || 5432,
61
+ user: pg.options?.user || 'postgres',
62
+ password: pg.options?.password || 'postgres',
63
+ },
64
+ };
65
+ }
66
+
67
+ async function uploadXML({ id, dataset, style }, pg) {
68
+ if (!id || !dataset?.length) {
69
+ return null;
70
+ }
71
+ await Promise.all(dataset.filter((el) => el.active !== false).map(async el => {
72
+ const { table } = el;
73
+
74
+ const geoms = await pg.query(`SELECT json_agg(attname) as geoms FROM pg_attribute
75
+ where attrelid = to_regclass($1::text)
76
+ and attnum > 0
77
+ AND NOT attisdropped
78
+ and atttypid::regtype::text ='geometry'`, [table]).then(e => e.rows?.[0]?.geoms || []);
79
+
80
+ const gcol = getGeomColumn(geoms, el.gcol);
81
+ Object.assign(el, { gcol });
82
+
83
+ const srid = el.srid ? el.srid
84
+ : await pg.query(`select st_srid(${gcol}) as srid from ${table} where ${gcol} is not null limit 1`).then(e => e.rows[0]?.srid);
85
+
86
+ if (srid && !srsList[srid] && (srid - 0 > 0)) {
87
+ srsList[srid] = await pg.query(`SELECT proj4text from spatial_ref_sys where srid=$1`, [srid])
88
+ .then(e => e.rows[0]?.proj4text);
89
+ }
90
+ Object.assign(el, { gcol, proj4text: srsList[srid] });
91
+ }));
92
+
93
+ const bounds = await cartoBounds({ id, dataset }, pg);
94
+
95
+ const arrayLayers = dataset.filter((el) => el.active !== false).map((el) => cartoData(el, bounds, pg));
96
+
97
+ const configJson = {
98
+ format: 'png',
99
+ Layer: arrayLayers.reverse(),
100
+ srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over',
101
+ bounds,
102
+ 'maximum-extent': '-20037508.34,-20037508.34,20037508.34,20037508.34',
103
+ center: [0, 0, 2],
104
+ minzoom: 1,
105
+ maxzoom: 25,
106
+ Stylesheet: [{
107
+ id: 'carto_css_style',
108
+ data: style || '',
109
+ }],
110
+ };
111
+
112
+ // generate
113
+ const res = new carto.Renderer().render(configJson);
114
+ const xmlFileText = typeof res === 'string' ? res : res?.data;
115
+ if (!xmlFileText) {
116
+ throw new Error('xml generation error');
117
+ }
118
+ const { status } = await UploadXML({
119
+ name: id,
120
+ xml: xmlFileText,
121
+ });
122
+ if (status !== 'success') {
123
+ throw new Error('xml upload error');
124
+ }
125
+ return xmlFileText;
126
+ }
127
+
1
128
  export default async function addCartocss(req, reply) {
2
129
  const { pg, body, params } = req;
3
130
 
4
131
  const { id } = params;
5
132
  if (!id) return reply.status(400).send({ message: 'id is empty', status: 400 });
6
133
 
7
- const { config, style } = body;
8
- if (!config && !style) return reply.status(400).send({ message: 'config, style is empty', status: 400 });
134
+ const { config: dataset, style } = body;
135
+ if (!dataset && !style) return reply.status(400).send({ message: 'config, style is empty', status: 400 });
9
136
 
10
137
  const selectCartocss = `select cartocss_id from gis.cartocss where cartocss_id=$1`;
11
138
  const cartocss = await pg.one(selectCartocss, [id]);
12
139
  if (!cartocss) return reply.status(404).send({ message: 'cartocss not found', status: 404 });
13
140
 
14
- const updateCartocss = `update gis.cartocss set config=$2::jsonb, style=$3::text where cartocss_id=$1`;
15
- await pg.query(updateCartocss, [id, config, style]);
141
+ await dataUpdate({
142
+ pg,
143
+ id,
144
+ table: 'gis.cartocss',
145
+ data: { config: dataset, style },
146
+ uid: req.user?.uid,
147
+ });
148
+
149
+ await uploadXML({ id, dataset, style }, pg);
16
150
 
17
151
  return reply.status(200).send({ message: 'cartocss updated', status: 200 });
18
152
  }
@@ -4,7 +4,7 @@ import mapFeatures from './controllers/mapFeatures.js';
4
4
 
5
5
  import mapCatalog from './controllers/mapCatalog.js';
6
6
  import mapCatalogAttribute from './controllers/mapCatalogAttribute.js';
7
- import rtile from './controllers/rtile.js';
7
+ // import rtile from './controllers/rtile.js';
8
8
  import vtile from './controllers/vtile.js';
9
9
  import vtile1 from './vtile1.js';
10
10
  import geojson from './controllers/geojson.js';
@@ -99,9 +99,9 @@ export default async function route(app) {
99
99
  if (!app.hasRoute({ method: 'GET', url: '/map-catalog/:service/:attr' })) {
100
100
  app.get('/map-catalog/:service/:attr', publicParams, mapCatalogAttribute);
101
101
  }
102
- if (!app.hasRoute({ method: 'GET', url: '/layer-rtile/:id/:z/:y/:x' })) {
103
- app.get('/layer-rtile/:id/:z/:y/:x', publicParams, rtile);
104
- }
102
+ // if (!app.hasRoute({ method: 'GET', url: '/layer-rtile/:id/:z/:y/:x' })) {
103
+ // app.get('/layer-rtile/:id/:z/:y/:x', publicParams, rtile);
104
+ // }
105
105
  if (!app.hasRoute({ method: 'GET', url: '/layer-vtile/:id/:z/:y/:x' })) {
106
106
  app.get('/layer-vtile/:id/:z/:y/:x', publicParams, vtile);
107
107
  }
@@ -32,7 +32,7 @@ export default async function getMap({ params = {}, pg = pgClients.client }, rep
32
32
  const totals = pg.queryCache ? await pg.queryCache('select json_object_agg(oid::regclass, reltuples) from pg_class')
33
33
  .then(el => el.rows?.[0]?.json_object_agg || {}) : {};
34
34
  const maps = pg.pk?.['gis.maps'] ? await pg.query(
35
- 'SELECT map_id as id, map_key as slug, name FROM gis.maps',
35
+ 'SELECT map_id as id, map_key as slug, name FROM gis.maps where is_active and is_public order by name',
36
36
  ).then(el => el.rows || []) : [];
37
37
 
38
38
  if (params.id) {
@@ -0,0 +1,61 @@
1
+ import path from 'node:path';
2
+
3
+ import { pgClients, eventStream } from '@opengis/fastify-table/utils.js';
4
+
5
+ import mapnik from '../../../plugins/mapnik/funcs/mapnik.js';
6
+
7
+ const { GetRasterStatus, CreateXML } = mapnik();
8
+
9
+ export default async function createXml({
10
+ pg = pgClients.client, params, query,
11
+ }, reply) {
12
+ if (!GetRasterStatus || !CreateXML) {
13
+ return reply.status(400).send({ error: 'mapnik server address needed', code: 400 });
14
+ }
15
+
16
+ const { id } = params;
17
+
18
+ if (!id) {
19
+ return reply.status(400).send({ error: 'not enough params: id', code: 400 });
20
+ }
21
+
22
+ const data = pg.pk?.['gis.rasters']
23
+ ? await pg.query('select raster_id as id, source_path, srid, raster_zoom from gis.rasters where raster_id=$1::text', [id])
24
+ .then(el => el.rows?.[0])
25
+ : null;
26
+
27
+ if (!data?.id) {
28
+ return reply.status(404).send({ error: 'raster not found', code: 404 });
29
+ }
30
+
31
+ if (!data.source_path) {
32
+ return reply.status(400).send({ error: 'raster source_path not set', code: 400 });
33
+ }
34
+
35
+ const relpath = `/map/raster/${data.source_path}`;
36
+
37
+ // check raster upload status, skip for directories
38
+ const uploadStatus = path.extname(relpath) ? await GetRasterStatus({ path: relpath }) : {};
39
+
40
+ if (uploadStatus.exists === false) {
41
+ return reply.status(400).send({ error: 'raster not uploaded', code: 400 });
42
+ }
43
+
44
+ if (uploadStatus.finished === false) {
45
+ return reply.status(400).send({ error: 'upload not finished', code: 400 });
46
+ }
47
+
48
+ const callback = eventStream(reply);
49
+
50
+ // create at xml/:id with name only, always - by path
51
+ const resp = await CreateXML({
52
+ path: data.source_path,
53
+ name: id,
54
+ previewZoom: data.raster_zoom,
55
+ srid: data.srid,
56
+ vrt: query.vrt === '1' /* || !data.source_path.toLowerCase().endsWith('.tif') */,
57
+ }, callback);
58
+
59
+ callback('finish', 1);
60
+ return reply.status(200).send(resp);
61
+ }
@@ -0,0 +1,86 @@
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 rtile({
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 raster = pg.pk?.['gis.rasters']
46
+ ? await pg.query('select raster_id, source_path from gis.rasters where raster_id=$1::text', [id])
47
+ .then(el => el.rows?.[0])
48
+ : null;
49
+
50
+ // const relpath = raster ? `/map/raster/${raster.source_path}` : null;
51
+
52
+ const cartoExists = pg.pk?.['gis.cartocss']
53
+ ? await pg.query('select cartocss_id from gis.cartocss where cartocss_id=$1::text', [id])
54
+ .then(el => el.rows?.[0]?.cartocss_id)
55
+ : null;
56
+
57
+ if (!raster && !cartoExists) {
58
+ return reply.status(404).send({ error: 'raster / cartocss 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: raster ? raster.source_path : null,
66
+ name: id,
67
+ width: 256,
68
+ height: 256,
69
+ bbox,
70
+ ttl: query.nocache ? '0' : '1h',
71
+ });
72
+
73
+ if (data.err) {
74
+ logger.file('rtile/error', { error: data.err });
75
+ return reply.status(500).send({ error: config.local ? data.err : 'render error', code: 500 });
76
+ }
77
+
78
+ const buffer = Buffer.from(data.base64, 'base64');
79
+
80
+ return reply.headers({ 'Content-Type': 'image/png' }).send(buffer);
81
+ }
82
+ catch (err) {
83
+ logger.file('rtile/error', { error: err.toString(), stack: err.stack });
84
+ return reply.status(500).send({ error: config.local ? err.toString() : 'rtile error', code: 500 });
85
+ }
86
+ }
@@ -0,0 +1,159 @@
1
+ import path from 'node:path';
2
+ import { ListObjectsV2Command } from '@aws-sdk/client-s3';
3
+
4
+ /* eslint-disable no-await-in-loop */
5
+ import {
6
+ config, isFileExists, logger, pgClients, eventStream, downloadFile, s3Client,
7
+ } from '@opengis/fastify-table/utils.js';
8
+
9
+ import mapnik from '../../../plugins/mapnik/funcs/mapnik.js';
10
+
11
+ const { UploadRaster, GetRasterStatus } = mapnik();
12
+
13
+ const CHUNK_SIZE = 1024 * 1024; // 1 MB per chunk
14
+
15
+ function sequence(files, data, fn) {
16
+ return files.reduce(
17
+ (promise, relpath) => promise.then(() => fn({
18
+ ...data,
19
+ relpath, // allow upload multiple to one directory for vrt
20
+ })),
21
+ Promise.resolve(),
22
+ );
23
+ }
24
+
25
+ async function uploadRasterFile({
26
+ id, prefix, relpath: relpathOriginal, callback = () => { },
27
+ }) {
28
+ const name = path.basename(relpathOriginal);
29
+ const metadata = await isFileExists(relpathOriginal);
30
+ const relpath = relpathOriginal.replace(config.folder, '').replace(prefix, '');
31
+
32
+ if (!metadata || !metadata.ContentLength) {
33
+ callback(`File not found at s3: ${relpath}`);
34
+ return { error: `File not found at s3: ${relpath}`, code: 404 };
35
+ }
36
+
37
+ let offset = 0;
38
+
39
+ try {
40
+ const uploadStatus = await GetRasterStatus({ path: relpath, md5: true });
41
+
42
+ offset = uploadStatus.exists ? +uploadStatus.size : 0;
43
+
44
+ if (offset === metadata.ContentLength) {
45
+ callback(`Already uploaded: ${relpath}`);
46
+ return { message: 'already uploaded', status: 200 };
47
+ }
48
+
49
+ while (offset < metadata.ContentLength) {
50
+ const end = Math.min(offset + CHUNK_SIZE - 1, metadata.ContentLength - 1);
51
+ const Range = `bytes=${offset}-${end}`;
52
+
53
+ callback(`Uploading chunk ${(end / 1024 / 1024).toFixed(0)}/${(metadata.ContentLength / 1024 / 1024).toFixed(0)} MB...`);
54
+
55
+ const chunk = await downloadFile(relpathOriginal, { Range, fallback: false });
56
+ // const { Body: chunk } = await s3Client.send(new GetObjectCommand({ Bucket, Key, Range }));
57
+ const bufferPart = chunk ? await chunk.transformToByteArray() : [];
58
+
59
+ if (!bufferPart.length) {
60
+ callback(`Server error: file not found / zero bytes: ${relpath}`);
61
+ break;
62
+ }
63
+
64
+ const result = await UploadRaster({
65
+ path: relpath,
66
+ filesize: metadata.ContentLength,
67
+ data: bufferPart,
68
+ chunked: true,
69
+ });
70
+
71
+ if (result.finished) {
72
+ callback(`Upload completed: ${relpath}`);
73
+ break;
74
+ }
75
+
76
+ if (!result.uploaded) {
77
+ callback(`Chunked upload error: ${relpath}`);
78
+ break;
79
+ }
80
+
81
+ offset = +result.uploaded;
82
+ }
83
+
84
+ return null;
85
+ }
86
+ catch (err) {
87
+ logger.file('mapnik/upload/error', { error: err.toString(), stack: err.stack });
88
+ callback(config.local ? err.toString() : `Upload error: ${relpath}`);
89
+ return null;
90
+ }
91
+ }
92
+
93
+ export default async function uploadRaster({
94
+ pg = pgClients.client, params,
95
+ }, reply) {
96
+ if (!UploadRaster || !GetRasterStatus) {
97
+ return reply.status(400).send({ error: 'mapnik server address needed', code: 400 });
98
+ }
99
+
100
+ const { id } = params;
101
+
102
+ if (!id) {
103
+ return reply.status(400).send({ error: 'not enough params: id', code: 400 });
104
+ }
105
+
106
+ const data = pg.pk?.['gis.rasters']
107
+ ? await pg.query('select raster_id as id, source_path from gis.rasters where raster_id=$1::text', [id])
108
+ .then(el => el.rows?.[0])
109
+ : null;
110
+
111
+ if (!data?.id) {
112
+ return reply.status(404).send({ error: 'raster not found', code: 404 });
113
+ }
114
+
115
+ if (!data.source_path) {
116
+ return reply.status(400).send({ error: 'raster source_path not set', code: 400 });
117
+ }
118
+
119
+ const prefix = '/map/raster';
120
+ const relpath = `${prefix}/${data.source_path}`;
121
+
122
+ if (!path.extname(relpath)) {
123
+ const metadata = await s3Client.send(new ListObjectsV2Command({
124
+ Bucket: config.s3?.containerName || 'work',
125
+ Prefix: path.join(config.folder, relpath).replace(/\\/g, '/'),
126
+ }));
127
+ if (!metadata.Contents?.length) {
128
+ return reply.status(404).send({
129
+ error: 'invalid: raster source_path: not found / empty directory',
130
+ code: 404,
131
+ });
132
+ }
133
+
134
+ // list rasters, skip preview subdir content, skip folders from s3 directory name of which only starts with same characters, but does not exactly match
135
+ const files = metadata.Contents.map(file => file.Key).filter((file) => path.basename(path.dirname(file)) === path.basename(relpath) && !path.basename(path.dirname(file)).startsWith('preview') && path.extname(file).toLowerCase() === '.tif');
136
+
137
+ if (!files.length) {
138
+ return reply.status(400).send({
139
+ error: 'invalid: raster source_path: no raster files at directory',
140
+ code: 400,
141
+ });
142
+ }
143
+ const callback = eventStream(reply);
144
+ await sequence(files, { id, prefix, callback }, uploadRasterFile);
145
+ return callback('finish', 1);
146
+ }
147
+
148
+ const metadata = await isFileExists(relpath);
149
+
150
+ if (!metadata || !metadata.ContentLength) {
151
+ return reply.status(404).send({ error: `File not found at s3: ${data.source_path}`, code: 404 });
152
+ }
153
+
154
+ const callback = eventStream(reply);
155
+ await uploadRasterFile({
156
+ id, prefix, callback, relpath,
157
+ });
158
+ return callback('finish', 1);
159
+ }
@@ -0,0 +1,16 @@
1
+ import uploadRaster from './controllers/uploadRaster.js';
2
+ import createXML from './controllers/createXML.js';
3
+ import rtile from './controllers/rtile.js';
4
+
5
+ const publicParams = { config: { policy: 'L0' }, package: 'gis' }; // L0 === public
6
+ const params = { config: { policy: 'L1' }, package: 'gis' }; // L1 === authorized only
7
+
8
+ export default async function route(app) {
9
+ app.get('/raster-upload/:id', params, uploadRaster);
10
+ app.get('/raster-xml/:id', publicParams, createXML);
11
+ app.get('/raster-tile/:id/:z/:y/:x', publicParams, rtile);
12
+ // temporary support of old alias
13
+ if (!app.hasRoute({ method: 'GET', url: '/layer-rtile/:id/:z/:y/:x' })) {
14
+ app.get('/layer-rtile/:id/:z/:y/:x', publicParams, rtile);
15
+ }
16
+ }
@@ -1,72 +0,0 @@
1
- import carto from 'carto';
2
-
3
- import rasterExists from './rasterExists.js';
4
- import rasterConfig from './rasterConfig.js';
5
-
6
- export default async function createXML({
7
- fullPath, srs, extent: bounds, send, size
8
- }) {
9
-
10
- const configFilePath = fullPath.replace('/preview1', '').replace('mosaic.vrt', 'config.yml')
11
- const configData = await rasterConfig(configFilePath);
12
-
13
- const rasterPreview = await rasterExists(fullPath, 1);
14
-
15
- if (rasterPreview) {
16
- send('preview:' + rasterPreview);
17
- }
18
-
19
- const rasterZoom = configData?.previewZoom ?? (rasterPreview && size?.includes('G') ? 16 : 0);
20
-
21
- const rasterScaling = 'bilinear';
22
-
23
- const Layer = [].concat([
24
- {
25
- id: 'test',
26
- name: 'test',
27
- geometry: 'raster',
28
- properties: { minzoom: +rasterZoom + 1 },
29
- srs: srs || '+proj=longlat +datum=WGS84 +no_defs ',
30
- Datasource: {
31
- // extent: bounds.join(','),
32
- file: fullPath,
33
- type: 'gdal',
34
- },
35
- }]).concat(
36
- rasterZoom && rasterPreview ? [{
37
- id: 'testpreview',
38
- name: 'testpreview',
39
- geometry: 'raster',
40
- properties: { maxzoom: +rasterZoom },
41
- srs: srs || '+proj=longlat +datum=WGS84 +no_defs ',
42
- Datasource: {
43
- // extent: bounds.join(','),
44
- file: rasterPreview,
45
- type: 'gdal',
46
- },
47
- }]
48
- : [],
49
- );
50
-
51
- const data = `#testpreview{raster-opacity:1; raster-scaling:${rasterScaling};} #test{raster-opacity:1;raster-scaling:${rasterScaling}; } `;
52
-
53
- const configJson = {
54
- format: 'png',
55
- Layer,
56
- srs: '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over',
57
- bounds: bounds || [-180, -85.05112877980659, 180, 85.05112877980659],
58
- 'maximum-extent': '-20037508.34,-20037508.34,20037508.34,20037508.34',
59
- center: [0, 0, 2],
60
- minzoom: 1,
61
- maxzoom: 25,
62
- Stylesheet: [{ id: 'carto_css_style', data }],
63
- };
64
-
65
- const xmlFileText = new carto.Renderer().render(configJson);
66
-
67
- if (!xmlFileText) {
68
- return { status: 500, error: `Cant create xml for ${fullPath}` };
69
- }
70
-
71
- return xmlFileText;
72
- }
@@ -1,72 +0,0 @@
1
- import path from 'path';
2
- import { randomUUID as uuidv4 } from 'crypto';
3
- import mapnik from './mapnik.js';
4
-
5
- import { config } from '@opengis/fastify-table/utils.js';
6
-
7
- const { mapServerAddress } = mapnik();
8
-
9
- function unixPath(data) {
10
- if (mapServerAddress && !mapServerAddress?.includes('localhost') && data?.[1] === ':') {
11
- return path.posix.join('/data/softpro', data.substr(3).replace(/\\/g, '/'));
12
- }
13
- return data;
14
- }
15
- const gdalWrapper = async ({
16
- name, pathIn, pathOut, parameters, funcs, send = console.log,
17
- }) => {
18
- const timeStart = new Date();
19
- const pref = name === 'gdalbuildvrt' ? ' -o ' : '';
20
- const cmd = `${name} ${parameters || ''} ${unixPath(pathIn) || ''} ${pref} ${unixPath(pathOut) || ''}`;
21
-
22
- if (mapServerAddress) {
23
- const out = pref + (unixPath(pathOut) || '');
24
- const obj = {
25
- name, path: unixPath(pathIn), out, params: parameters,
26
- };
27
-
28
- const time = Date.now();
29
- const { result, err } = await mapnik().gdal(obj);
30
-
31
- if (config.local && false) {
32
- send(cmd);
33
- send(JSON.stringify(obj));
34
- send(`${Date.now() - time}`);
35
- }
36
-
37
- if (err) {
38
- const logObj = {
39
- level: 'ERROR',
40
- uuid: uuidv4(),
41
- name: 'GDAL',
42
- subname: 'GDALWRAPPER',
43
- msec: new Date() - timeStart,
44
- param: { cmd },
45
- status: '500',
46
- response: { error: err },
47
- };
48
- // log.error({ name: 'gdal', ...logObj });
49
- send(err);
50
- }
51
- return { err, result };
52
- }
53
-
54
- const result = await funcs.exec(cmd, { send });
55
- if (!result) {
56
- const logObj = {
57
- level: 'ERROR',
58
- uuid: uuidv4(),
59
- name: 'GDAL',
60
- subname: 'GDALWRAPPER',
61
- msec: new Date() - timeStart,
62
- param: { cmd },
63
- status: '500',
64
- response: { error: `Cant execute ${cmd}` },
65
- };
66
- // log.error({ name: 'gdal', ...logObj });
67
- return { err: `Cant execute ${cmd}` };
68
- }
69
- return { result };
70
- };
71
-
72
- export default gdalWrapper;