@opengis/gis 0.2.174 → 0.2.176

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 (158) hide show
  1. package/README.md +181 -181
  2. package/dist/{CardIcon-Cg8bUM4G.js → CardIcon-c1Q2M3wQ.js} +1 -1
  3. package/dist/{EntityTablePage-DHQGlLRk.js → EntityTablePage-B5gpkH4S.js} +2 -2
  4. package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BIL1ocLY.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-DnGSvmMU.js} +1 -1
  5. package/dist/{HeaderActions.vue_vue_type_script_setup_true_lang-Buff9gTO.js → HeaderActions.vue_vue_type_script_setup_true_lang-C2B61JP6.js} +1 -1
  6. package/dist/{MapSettings-De-z7r5t.js → MapSettings-6-Xru5Q8.js} +2 -2
  7. package/dist/{RastersTablePage-Cdgkri9K.js → RastersTablePage-CXNOcTy-.js} +2 -2
  8. package/dist/{cartocss-BT50SVhy.js → cartocss-BFVqWvfP.js} +6 -6
  9. package/dist/{import-utils-CMWiuRPp.js → import-utils-uxk4LdUJ.js} +1 -1
  10. package/dist/{index-CHFLgCGW.js → index-CahlV-Xs.js} +39 -39
  11. package/dist/index.css +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/index.umd.cjs +19 -19
  14. package/dist/{raster-dSEb1SIG.js → raster-CEi56f0P.js} +3 -3
  15. package/dist/{register-D7Puupgo.js → register-NFAGBNa3.js} +3 -3
  16. package/dist/{service-Cz6T0O3Z.js → service-BYspag8U.js} +829 -832
  17. package/dist/{vs-datatable-DqVncrSg.js → vs-datatable-CHcBr6zy.js} +2 -2
  18. package/module/gis/cls/service_type.json +42 -42
  19. package/module/gis/form/gis.registers.form.json +4 -1
  20. package/module/test/map/addr.json +302 -302
  21. package/module/test/map/addr1.yml +23 -23
  22. package/module/test/select/address.str_id.sql +3 -3
  23. package/package.json +77 -77
  24. package/plugin.js +64 -64
  25. package/server/migrations/array_intersect.sql +13 -13
  26. package/server/migrations/cartocss.sql +27 -27
  27. package/server/migrations/group_list.sql +74 -74
  28. package/server/migrations/maps.sql +30 -30
  29. package/server/migrations/metadata.sql +415 -415
  30. package/server/migrations/ogc.sql +106 -106
  31. package/server/migrations/rasters.sql +265 -265
  32. package/server/migrations/services.sql +247 -247
  33. package/server/migrations/services_users_rel.sql +22 -22
  34. package/server/migrations/widgets.sql +20 -20
  35. package/server/plugins/crons.js +21 -21
  36. package/server/plugins/mapnik/funcs/checkRasterFile.js +107 -107
  37. package/server/plugins/mapnik/funcs/mapnik.js +151 -151
  38. package/server/plugins/mapnik/funcs/rootFolder.mjs +8 -8
  39. package/server/plugins/mapnik/map.proto +437 -437
  40. package/server/plugins/vite.js +74 -74
  41. package/server/routes/cartocss/cartocss.route.js +31 -31
  42. package/server/routes/cartocss/cartocss.route.test.js +78 -78
  43. package/server/routes/cartocss/cartocss.schema.js +73 -73
  44. package/server/routes/cartocss/functions/add.cartocss.js +39 -39
  45. package/server/routes/cartocss/functions/get.cartocss.js +57 -57
  46. package/server/routes/gis/functions/checkXML.js +107 -107
  47. package/server/routes/gis/functions/dashboard.js +24 -24
  48. package/server/routes/gis/gis.route.js +26 -26
  49. package/server/routes/gis/gis.route.test.js +33 -33
  50. package/server/routes/map/functions/clearTiles.js +35 -35
  51. package/server/routes/map/functions/geojson.js +187 -187
  52. package/server/routes/map/functions/gis.convert.js +65 -65
  53. package/server/routes/map/functions/gis.icon.js +220 -220
  54. package/server/routes/map/functions/jsonData.js +206 -206
  55. package/server/routes/map/functions/layerList.js +102 -102
  56. package/server/routes/map/functions/map.js +122 -122
  57. package/server/routes/map/functions/mapCatalog.js +72 -72
  58. package/server/routes/map/functions/mapCatalogAttribute.js +53 -53
  59. package/server/routes/map/functions/mapFeatures.js +133 -133
  60. package/server/routes/map/functions/mapFormat.js +232 -231
  61. package/server/routes/map/functions/mapTiles.js +162 -162
  62. package/server/routes/map/functions/maps.js +15 -15
  63. package/server/routes/map/functions/marker_icon.js +43 -43
  64. package/server/routes/map/functions/parseGeoFile.js +85 -85
  65. package/server/routes/map/functions/pointHeight.js +51 -51
  66. package/server/routes/map/functions/vtile.js +174 -174
  67. package/server/routes/map/functions/vtile1.js +185 -185
  68. package/server/routes/map/map.route.js +280 -280
  69. package/server/routes/map/map.route.test.js +503 -503
  70. package/server/routes/map/map.schema.js +356 -356
  71. package/server/routes/map/pin/circle1.svg +76 -76
  72. package/server/routes/map/pin/circle2.svg +18 -18
  73. package/server/routes/map/pin/none.svg +12 -12
  74. package/server/routes/map/pin/pin.svg +14 -14
  75. package/server/routes/map/pin/pin1.svg +16 -16
  76. package/server/routes/map/pin/pin2.svg +7 -7
  77. package/server/routes/map/pin/pin3.svg +34 -34
  78. package/server/routes/map/pin/pin4.svg +14 -14
  79. package/server/routes/map/pin/pin5.svg +7 -7
  80. package/server/routes/map/pin/pin6.svg +12 -12
  81. package/server/routes/map/pin/pin7.svg +12 -12
  82. package/server/routes/mapnik/functions/cartoBounds.js +32 -32
  83. package/server/routes/mapnik/functions/clearTiles.js +103 -103
  84. package/server/routes/mapnik/functions/convertBoundToTile.js +27 -27
  85. package/server/routes/mapnik/functions/createXml.js +76 -76
  86. package/server/routes/mapnik/functions/createXmlMulti.js +72 -72
  87. package/server/routes/mapnik/functions/fileSearch.js +35 -35
  88. package/server/routes/mapnik/functions/fileStat.js +28 -28
  89. package/server/routes/mapnik/functions/pretile.js +138 -138
  90. package/server/routes/mapnik/functions/rasterInfo.js +66 -66
  91. package/server/routes/mapnik/functions/readDir.js +22 -22
  92. package/server/routes/mapnik/functions/rtile.js +164 -164
  93. package/server/routes/mapnik/functions/uploadRaster.js +152 -152
  94. package/server/routes/mapnik/functions/uploadXML.js +140 -140
  95. package/server/routes/mapnik/mapnik.route.js +102 -102
  96. package/server/routes/mapnik/mapnik.route.test.js +370 -370
  97. package/server/routes/mapnik/mapnik.schema.mjs +195 -195
  98. package/server/routes/mapnik.prefixless/functions/mapnikLogger.js +31 -31
  99. package/server/routes/mapnik.prefixless/functions/mapnikStat.js +21 -21
  100. package/server/routes/mapnik.prefixless/mapnik.prefixless.route.js +20 -20
  101. package/server/routes/mapnik.prefixless/mapnik.prefixless.route.test.js +91 -91
  102. package/server/routes/mapnik.prefixless/mapnik.prefixless.schema.mjs +27 -27
  103. package/server/routes/maps/functions/add.map.js +42 -42
  104. package/server/routes/maps/functions/del.map.js +17 -17
  105. package/server/routes/maps/functions/get.map.js +119 -119
  106. package/server/routes/maps/maps.route.js +58 -58
  107. package/server/routes/maps/maps.route.test.js +231 -231
  108. package/server/routes/maps/maps.schema.js +9 -9
  109. package/server/routes/metadata/functions/editMetadata.js +35 -35
  110. package/server/routes/metadata/functions/metadataXML.js +19 -19
  111. package/server/routes/metadata/metadata.route.js +26 -26
  112. package/server/routes/metadata/metadata.route.test.js +46 -46
  113. package/server/routes/metadata/metadata.schema.js +53 -53
  114. package/server/routes/ogc/functions/map.info.point.js +117 -117
  115. package/server/routes/ogc/ogc.route.js +21 -21
  116. package/server/routes/ogc/ogc.route.test.js +53 -53
  117. package/server/routes/ogc/ogc.schema.js +16 -16
  118. package/server/routes/permissions/functions/catalog.permissions.edit.js +20 -20
  119. package/server/routes/permissions/functions/catalog.permissions.js +12 -12
  120. package/server/routes/permissions/functions/get.permissions.js +59 -59
  121. package/server/routes/permissions/functions/gis.catalog.js +80 -80
  122. package/server/routes/permissions/permissions.route.js +35 -35
  123. package/server/routes/permissions/permissions.route.test.js +35 -35
  124. package/server/routes/permissions/permissions.schema.js +43 -43
  125. package/server/routes/registers/functions/add.registry.js +37 -37
  126. package/server/routes/registers/functions/classifiers.js +100 -100
  127. package/server/routes/registers/functions/columns.js +3 -3
  128. package/server/routes/registers/functions/content.type.js +9 -9
  129. package/server/routes/registers/functions/del.registry.js +14 -14
  130. package/server/routes/registers/functions/get.info.js +93 -93
  131. package/server/routes/registers/functions/gis.export.js +159 -159
  132. package/server/routes/registers/functions/gis.registry.js +71 -71
  133. package/server/routes/registers/functions/gis.registry.list.js +61 -61
  134. package/server/routes/registers/functions/handleRegistryRequest.js +147 -147
  135. package/server/routes/registers/functions/map.registry.js +87 -87
  136. package/server/routes/registers/registers.route.js +80 -80
  137. package/server/routes/registers/registers.route.test.js +144 -144
  138. package/server/routes/registers/registers.schema.js +246 -246
  139. package/server/routes/root.mjs +3 -3
  140. package/server/routes/services/functions/add.service.js +62 -62
  141. package/server/routes/services/functions/create.route.js +93 -93
  142. package/server/routes/services/functions/del.service.js +12 -12
  143. package/server/routes/services/functions/get.layer.geom.js +27 -27
  144. package/server/routes/services/functions/get.service.users.js +76 -76
  145. package/server/routes/services/functions/get.services.col.js +34 -34
  146. package/server/routes/services/functions/get.services.js +89 -89
  147. package/server/routes/services/functions/legend.auto.js +77 -77
  148. package/server/routes/services/services.route.js +76 -76
  149. package/server/routes/services/services.route.test.js +167 -167
  150. package/server/routes/services/services.schema.js +278 -278
  151. package/server/routes/widgets/functions/add.widget.js +38 -38
  152. package/server/routes/widgets/functions/del.widget.js +17 -17
  153. package/server/routes/widgets/functions/get.widget.js +41 -41
  154. package/server/routes/widgets/widgets.route.js +61 -61
  155. package/server/routes/widgets/widgets.route.test.js +144 -144
  156. package/server/routes/widgets/widgets.schema.js +24 -24
  157. package/server/shared/listFiles.js +15 -0
  158. package/utils.js +13 -13
@@ -1,162 +1,162 @@
1
- import path from 'node:path';
2
- import { existsSync } from 'node:fs';
3
- import { createHash } from 'node:crypto';
4
- import {
5
- readFile, stat, mkdir, writeFile,
6
- } from 'node:fs/promises';
7
-
8
- import { SphericalMercator } from '@mapbox/sphericalmercator';
9
-
10
- const mercator = new SphericalMercator({ size: 256 });
11
-
12
- import {
13
- getTemplate, pgClients, getFilterSQL, getMeta,
14
- } from '@opengis/fastify-table/utils.js';
15
-
16
- import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
17
- import { BadRequestError, NotFoundError } from '@opengis/fastify-table/errors.js';
18
-
19
- const limit = 3000;
20
-
21
- const types = { point: 'ST_Point', polygon: 'ST_Polygon,ST_MultiPolygon' };
22
- const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at'];
23
-
24
- const unittest = process.env.NODE_ENV === 'test' || process.env.VITEST;
25
-
26
- export default async function mapTiles({
27
- slug, x, y, z, sql, nocache, filter, pointZoom, type, layers, layer,
28
- }, pg = pgClients.client) {
29
- if (!slug) {
30
- throw BadRequestError('not enough params: slug');
31
- }
32
-
33
- if (!x || !y || !z) {
34
- throw BadRequestError('not enough params: xyz');
35
- }
36
-
37
- const bbox = mercator.bbox(+y, +x, +z, false);
38
- const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
39
-
40
- const headers = {
41
- 'Content-Type': 'application/x-protobuf',
42
- 'Cache-Control': nocache || sql ? 'no-cache' : 'public, max-age=86400',
43
- };
44
-
45
- const hash = [filter, layer].filter(Boolean).join();
46
-
47
- const filepath = path.join(
48
- rootFolder,
49
- `/map/vtile/${slug}/${hash ? `${createHash('md5').update(hash).digest('hex')}/` : ''}${z}/${x}/${y}.pbf`,
50
- );
51
-
52
- if (existsSync(filepath) && z < 13 && !nocache && !sql && !unittest) {
53
- const { birthtimeMs = Date.now() } = await stat(filepath);
54
- const ageInMs = Date.now() - birthtimeMs;
55
- if (ageInMs < 86400000) {
56
- const buffer = await readFile(filepath);
57
- return { headers, data: buffer };
58
- }
59
- }
60
-
61
- const loadTemplate = await getTemplate('map', slug);
62
-
63
- if (!loadTemplate) {
64
- throw NotFoundError('map not found');
65
- }
66
-
67
- const index = loadTemplate?.find?.(el => el[0].split('.').shift() === 'index')?.[1];
68
-
69
- const layers1 = !index
70
- ? loadTemplate?.layers
71
- : loadTemplate?.filter?.(el => el[0].split('.').shift() !== 'index')?.map(el => ({ ...el[1], id: el[0].replace('.yml', '').replace('.json', '') }));
72
-
73
- if (!layers1?.length) {
74
- throw NotFoundError('map layers not found');
75
- }
76
-
77
- layers1.forEach(el => Object.assign(el, { name: el.name || el.id }));
78
-
79
- const layers2 = layers1.filter(el => el.name && el.source_path).filter(el => (layers ? layers.includes(el.name) : true)).filter(el => (layer ? el.name === layer : true));
80
-
81
- if (!layers2.length) {
82
- throw NotFoundError('map layers sources not provided / invalid query params');
83
- }
84
-
85
- const allFilters = loadTemplate.widgets?.filter?.(el => el.type === 'filters') || [];
86
- const { filters: filterList = [], layer_id: layerId } = allFilters.find(el => (layer ? el.config?.layer_id === layer : el.default))?.config || allFilters[0]?.config || {};
87
-
88
- const q = (await Promise.all(layers2.map(async (layer) => {
89
- const {
90
- source_path: source, id, name, style, geometry_type: gt, query: lquery, geometry_column: gCol,
91
- } = layer;
92
- const loadTable = await getTemplate('table', source);
93
-
94
- const table = loadTable?.table || source;
95
- const meta = await getMeta({ pg, table });
96
- const { columns = [] } = meta || {};
97
- const geom = gCol || meta?.geom || 'geom';
98
- if (!columns?.length) return null;
99
-
100
- const pk = pg.pk?.[table] || loadTable?.key;
101
- const fData = filter && layerId === name ? await getFilterSQL({
102
- pg, table: source, filterList, filter,
103
- }) : {};
104
-
105
- const geomCol = parseInt(z, 10) < parseInt(pointZoom || '0', 10) || (style?.centroid && gt === 'point')
106
- ? `ST_Centroid(${geom})`
107
- : geom;
108
-
109
- const columnsFiltered = columns
110
- .filter(el => !['json', 'geometry'].includes(pg.pgType?.[el.dataTypeID]) && !systemColumns.includes(el.name));
111
-
112
- const props = columnsFiltered.map(el => (el.type?.[0] === '_'
113
- ? `${el.name}[1] as ${el.name}`
114
- : `${el.name}${['int4'].includes(el.type) ? '::text' : ''}`));
115
-
116
- return `SELECT ST_AsMVT(q, '${(id || name).replace(/'/g, "''")}', 4096, 'geom','row') as tile
117
- FROM (
118
- SELECT /*substr(${pk},12)::int*/ /*${(y - 0 + x) * 10000} + row_number() over()*/
119
-
120
- floor(random() * 100000 + 1)::int + row_number() over() as row,
121
-
122
- ${style?.colorAttr ? `${style.colorAttr},` : ''} ${props?.length ? `${props.map(el => `"${el}"`).join(',')},` : ''}
123
-
124
- ${pk} as id,
125
- ST_AsMVTGeom(st_transform(${geomCol},3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,true) geom
126
-
127
- FROM (select * from ${table} where ${loadTable?.query || '1=1'} and ${fData?.q || 'true'} and ${geom} && ${bbox2d}
128
-
129
- and ${geom} is not null and st_srid(${geom}) >0
130
-
131
- ${types[type] ? ` and ST_GeometryType(${geom}) = any ('{ ${types[type]} }') ` : ''}
132
-
133
- and ${lquery || '1=1'}
134
-
135
- ${gt === 'polygon' ? `order by st_area(st_transform(${geom},3857)) desc` : ''}
136
-
137
- limit ${limit})q
138
- ) q`;
139
- }))).filter(el => el).join(' union all ');
140
-
141
- if (sql === 1) return q;
142
- if (sql === 2) return filepath.replace(/\\/g, '/');
143
-
144
- const tile = await pg.query(q);
145
-
146
- if (unittest && !tile.rows?.length) {
147
- console.log(slug, q);
148
- }
149
-
150
- if (sql === 3) return tile.rows.map((el) => el.tile);
151
-
152
- if (sql === 4) return tile.rows.map((el) => el.tile);
153
-
154
- const buffer = Buffer.concat(tile.rows.map((el) => Buffer.from(el.tile)));
155
-
156
- if (!nocache) {
157
- await mkdir(path.dirname(filepath), { recursive: true });
158
- await writeFile(filepath, buffer, 'binary');
159
- }
160
-
161
- return { headers, data: buffer };
162
- }
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { createHash } from 'node:crypto';
4
+ import {
5
+ readFile, stat, mkdir, writeFile,
6
+ } from 'node:fs/promises';
7
+
8
+ import { SphericalMercator } from '@mapbox/sphericalmercator';
9
+
10
+ const mercator = new SphericalMercator({ size: 256 });
11
+
12
+ import {
13
+ getTemplate, pgClients, getFilterSQL, getMeta,
14
+ } from '@opengis/fastify-table/utils.js';
15
+
16
+ import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
17
+ import { BadRequestError, NotFoundError } from '@opengis/fastify-table/errors.js';
18
+
19
+ const limit = 3000;
20
+
21
+ const types = { point: 'ST_Point', polygon: 'ST_Polygon,ST_MultiPolygon' };
22
+ const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at'];
23
+
24
+ const unittest = process.env.NODE_ENV === 'test' || process.env.VITEST;
25
+
26
+ export default async function mapTiles({
27
+ slug, x, y, z, sql, nocache, filter, pointZoom, type, layers, layer,
28
+ }, pg = pgClients.client) {
29
+ if (!slug) {
30
+ throw BadRequestError('not enough params: slug');
31
+ }
32
+
33
+ if (!x || !y || !z) {
34
+ throw BadRequestError('not enough params: xyz');
35
+ }
36
+
37
+ const bbox = mercator.bbox(+y, +x, +z, false);
38
+ const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
39
+
40
+ const headers = {
41
+ 'Content-Type': 'application/x-protobuf',
42
+ 'Cache-Control': nocache || sql ? 'no-cache' : 'public, max-age=86400',
43
+ };
44
+
45
+ const hash = [filter, layer].filter(Boolean).join();
46
+
47
+ const filepath = path.join(
48
+ rootFolder,
49
+ `/map/vtile/${slug}/${hash ? `${createHash('md5').update(hash).digest('hex')}/` : ''}${z}/${x}/${y}.pbf`,
50
+ );
51
+
52
+ if (existsSync(filepath) && z < 13 && !nocache && !sql && !unittest) {
53
+ const { birthtimeMs = Date.now() } = await stat(filepath);
54
+ const ageInMs = Date.now() - birthtimeMs;
55
+ if (ageInMs < 86400000) {
56
+ const buffer = await readFile(filepath);
57
+ return { headers, data: buffer };
58
+ }
59
+ }
60
+
61
+ const loadTemplate = await getTemplate('map', slug);
62
+
63
+ if (!loadTemplate) {
64
+ throw NotFoundError('map not found');
65
+ }
66
+
67
+ const index = loadTemplate?.find?.(el => el[0].split('.').shift() === 'index')?.[1];
68
+
69
+ const layers1 = !index
70
+ ? loadTemplate?.layers
71
+ : loadTemplate?.filter?.(el => el[0].split('.').shift() !== 'index')?.map(el => ({ ...el[1], id: el[0].replace('.yml', '').replace('.json', '') }));
72
+
73
+ if (!layers1?.length) {
74
+ throw NotFoundError('map layers not found');
75
+ }
76
+
77
+ layers1.forEach(el => Object.assign(el, { name: el.name || el.id }));
78
+
79
+ const layers2 = layers1.filter(el => el.name && el.source_path).filter(el => (layers ? layers.includes(el.name) : true)).filter(el => (layer ? el.name === layer : true));
80
+
81
+ if (!layers2.length) {
82
+ throw NotFoundError('map layers sources not provided / invalid query params');
83
+ }
84
+
85
+ const allFilters = loadTemplate.widgets?.filter?.(el => el.type === 'filters') || [];
86
+ const { filters: filterList = [], layer_id: layerId } = allFilters.find(el => (layer ? el.config?.layer_id === layer : el.default))?.config || allFilters[0]?.config || {};
87
+
88
+ const q = (await Promise.all(layers2.map(async (layer) => {
89
+ const {
90
+ source_path: source, id, name, style, geometry_type: gt, query: lquery, geometry_column: gCol,
91
+ } = layer;
92
+ const loadTable = await getTemplate('table', source);
93
+
94
+ const table = loadTable?.table || source;
95
+ const meta = await getMeta({ pg, table });
96
+ const { columns = [] } = meta || {};
97
+ const geom = gCol || meta?.geom || 'geom';
98
+ if (!columns?.length) return null;
99
+
100
+ const pk = pg.pk?.[table] || loadTable?.key;
101
+ const fData = filter && layerId === name ? await getFilterSQL({
102
+ pg, table: source, filterList, filter,
103
+ }) : {};
104
+
105
+ const geomCol = parseInt(z, 10) < parseInt(pointZoom || '0', 10) || (style?.centroid && gt === 'point')
106
+ ? `ST_Centroid(${geom})`
107
+ : geom;
108
+
109
+ const columnsFiltered = columns
110
+ .filter(el => !['json', 'geometry'].includes(pg.pgType?.[el.dataTypeID]) && !systemColumns.includes(el.name));
111
+
112
+ const props = columnsFiltered.map(el => (el.type?.[0] === '_'
113
+ ? `${el.name}[1] as ${el.name}`
114
+ : `${el.name}${['int4'].includes(el.type) ? '::text' : ''}`));
115
+
116
+ return `SELECT ST_AsMVT(q, '${(id || name).replace(/'/g, "''")}', 4096, 'geom','row') as tile
117
+ FROM (
118
+ SELECT /*substr(${pk},12)::int*/ /*${(y - 0 + x) * 10000} + row_number() over()*/
119
+
120
+ floor(random() * 100000 + 1)::int + row_number() over() as row,
121
+
122
+ ${style?.colorAttr ? `${style.colorAttr},` : ''} ${props?.length ? `${props.map(el => `"${el}"`).join(',')},` : ''}
123
+
124
+ ${pk} as id,
125
+ ST_AsMVTGeom(st_transform(${geomCol},3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,true) geom
126
+
127
+ FROM (select * from ${table} where ${loadTable?.query || '1=1'} and ${fData?.q || 'true'} and ${geom} && ${bbox2d}
128
+
129
+ and ${geom} is not null and st_srid(${geom}) >0
130
+
131
+ ${types[type] ? ` and ST_GeometryType(${geom}) = any ('{ ${types[type]} }') ` : ''}
132
+
133
+ and ${lquery || '1=1'}
134
+
135
+ ${gt === 'polygon' ? `order by st_area(st_transform(${geom},3857)) desc` : ''}
136
+
137
+ limit ${limit})q
138
+ ) q`;
139
+ }))).filter(el => el).join(' union all ');
140
+
141
+ if (sql === 1) return q;
142
+ if (sql === 2) return filepath.replace(/\\/g, '/');
143
+
144
+ const tile = await pg.query(q);
145
+
146
+ if (unittest && !tile.rows?.length) {
147
+ console.log(slug, q);
148
+ }
149
+
150
+ if (sql === 3) return tile.rows.map((el) => el.tile);
151
+
152
+ if (sql === 4) return tile.rows.map((el) => el.tile);
153
+
154
+ const buffer = Buffer.concat(tile.rows.map((el) => Buffer.from(el.tile)));
155
+
156
+ if (!nocache) {
157
+ await mkdir(path.dirname(filepath), { recursive: true });
158
+ await writeFile(filepath, buffer, 'binary');
159
+ }
160
+
161
+ return { headers, data: buffer };
162
+ }
@@ -1,16 +1,16 @@
1
- import { getTemplatePath, getTemplateSync } from "@opengis/fastify-table/utils.js";
2
-
3
- const arr = getTemplatePath('map') || [];
4
-
5
- const list = arr.map(el => {
6
- const { name = el[0] } = getTemplateSync('map', el[0]);
7
- return { name, slug: el[0] };
8
- });
9
-
10
- export default async function mapList() {
11
- if (!arr.length) {
12
- return [];
13
- }
14
-
15
- return list;
1
+ import { getTemplatePath, getTemplateSync } from "@opengis/fastify-table/utils.js";
2
+
3
+ const arr = getTemplatePath('map') || [];
4
+
5
+ const list = arr.map(el => {
6
+ const { name = el[0] } = getTemplateSync('map', el[0]);
7
+ return { name, slug: el[0] };
8
+ });
9
+
10
+ export default async function mapList() {
11
+ if (!arr.length) {
12
+ return [];
13
+ }
14
+
15
+ return list;
16
16
  }
@@ -1,43 +1,43 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
-
5
- async function downloadImage(url, file) {
6
- const resp = await fetch(url);
7
- if (resp.status !== 200) throw new Error(`${url} - not found`);
8
- const blob = await resp.blob();
9
-
10
- const arrayBuffer = await blob.arrayBuffer();
11
-
12
- const buffer = Buffer.from(arrayBuffer);
13
-
14
- fs.writeFileSync(file, buffer);
15
- }
16
-
17
- const filename = fileURLToPath(import.meta.url);
18
- const dirname = path.dirname(filename);
19
-
20
- const fileIconDir = path.join(dirname, '../../../../log/marker-icon');
21
-
22
- fs.mkdirSync(fileIconDir, { recursive: true });
23
-
24
- export default async function markerIconApi(req, reply) {
25
- const { params = {} } = req;
26
- params.data = params['*'];
27
- if (!params.data) {
28
- return reply.status(400).send('not enough params: data');
29
- }
30
-
31
- const iconPath = path.join(fileIconDir, params?.data);
32
-
33
- if (fs.existsSync(iconPath)) {
34
- const buffer = fs.readFileSync(iconPath); // works for small files same as stream, but also unit-test-friendly
35
- return reply.type('image/png').send(buffer);
36
- }
37
-
38
- await downloadImage(`https://data.softpro.ua/api-user/marker/${params.data}`, iconPath).catch(err => {
39
- return reply.status(404).send('icon not found');
40
- });
41
- return fs.createReadStream(iconPath);
42
-
43
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ async function downloadImage(url, file) {
6
+ const resp = await fetch(url);
7
+ if (resp.status !== 200) throw new Error(`${url} - not found`);
8
+ const blob = await resp.blob();
9
+
10
+ const arrayBuffer = await blob.arrayBuffer();
11
+
12
+ const buffer = Buffer.from(arrayBuffer);
13
+
14
+ fs.writeFileSync(file, buffer);
15
+ }
16
+
17
+ const filename = fileURLToPath(import.meta.url);
18
+ const dirname = path.dirname(filename);
19
+
20
+ const fileIconDir = path.join(dirname, '../../../../log/marker-icon');
21
+
22
+ fs.mkdirSync(fileIconDir, { recursive: true });
23
+
24
+ export default async function markerIconApi(req, reply) {
25
+ const { params = {} } = req;
26
+ params.data = params['*'];
27
+ if (!params.data) {
28
+ return reply.status(400).send('not enough params: data');
29
+ }
30
+
31
+ const iconPath = path.join(fileIconDir, params?.data);
32
+
33
+ if (fs.existsSync(iconPath)) {
34
+ const buffer = fs.readFileSync(iconPath); // works for small files same as stream, but also unit-test-friendly
35
+ return reply.type('image/png').send(buffer);
36
+ }
37
+
38
+ await downloadImage(`https://data.softpro.ua/api-user/marker/${params.data}`, iconPath).catch(err => {
39
+ return reply.status(404).send('icon not found');
40
+ });
41
+ return fs.createReadStream(iconPath);
42
+
43
+ }
@@ -1,85 +1,85 @@
1
- import { BadRequestError } from "@opengis/fastify-table/errors.js";
2
-
3
- const GEOMETRY_TYPES = new Set([
4
- 'Point',
5
- 'MultiPoint',
6
- 'LineString',
7
- 'MultiLineString',
8
- 'Polygon',
9
- 'MultiPolygon',
10
- 'GeometryCollection',
11
- ]);
12
-
13
- const MAX_FILE_SIZE = 50 * 1024 * 1024;
14
-
15
- function normalizeToFeatureCollection(input) {
16
- if (!input || typeof input !== 'object') return null;
17
-
18
- const { type } = input;
19
-
20
- if (type === 'FeatureCollection' && Array.isArray(input.features)) {
21
- return {
22
- type: 'FeatureCollection',
23
- features: input.features.filter((feature) => feature?.geometry),
24
- };
25
- }
26
-
27
- if (type === 'Feature' && input.geometry) {
28
- return {
29
- type: 'FeatureCollection',
30
- features: [input],
31
- };
32
- }
33
-
34
- if (typeof type === 'string' && GEOMETRY_TYPES.has(type)) {
35
- return {
36
- type: 'FeatureCollection',
37
- features: [{
38
- type: 'Feature',
39
- geometry: input,
40
- properties: {},
41
- }],
42
- };
43
- }
44
-
45
- return null;
46
- }
47
-
48
- /**
49
- * Парсить GeoJSON з тексту файлу (клієнтський шар, без збереження).
50
- *
51
- * @method POST
52
- * @param {string} body.content - вміст файлу
53
- * @param {string} [body.filename] - ім'я файлу
54
- */
55
-
56
- export default async function parseGeoFile({ content, filename = '' }) {
57
- if (!content || typeof content !== 'string') {
58
- throw BadRequestError('Потрібен вміст файлу (content)');
59
- }
60
-
61
- if (content.length > MAX_FILE_SIZE) {
62
- throw BadRequestError('Максимальний розмір файлу — 50MB');
63
- }
64
-
65
- const lowerName = String(filename).toLowerCase();
66
- if (lowerName && !lowerName.endsWith('.json') && !lowerName.endsWith('.geojson')) {
67
- throw BadRequestError('Підтримуються лише файли GeoJSON (.geojson, .json)');
68
- }
69
-
70
- let parsed;
71
- try {
72
- parsed = JSON.parse(content);
73
- }
74
- catch {
75
- throw BadRequestError('Некоректний JSON');
76
- }
77
-
78
- const normalized = normalizeToFeatureCollection(parsed);
79
-
80
- if (!normalized?.features?.length) {
81
- throw BadRequestError('Не вдалося розпізнати геометрію GeoJSON');
82
- }
83
-
84
- return normalized;
85
- }
1
+ import { BadRequestError } from "@opengis/fastify-table/errors.js";
2
+
3
+ const GEOMETRY_TYPES = new Set([
4
+ 'Point',
5
+ 'MultiPoint',
6
+ 'LineString',
7
+ 'MultiLineString',
8
+ 'Polygon',
9
+ 'MultiPolygon',
10
+ 'GeometryCollection',
11
+ ]);
12
+
13
+ const MAX_FILE_SIZE = 50 * 1024 * 1024;
14
+
15
+ function normalizeToFeatureCollection(input) {
16
+ if (!input || typeof input !== 'object') return null;
17
+
18
+ const { type } = input;
19
+
20
+ if (type === 'FeatureCollection' && Array.isArray(input.features)) {
21
+ return {
22
+ type: 'FeatureCollection',
23
+ features: input.features.filter((feature) => feature?.geometry),
24
+ };
25
+ }
26
+
27
+ if (type === 'Feature' && input.geometry) {
28
+ return {
29
+ type: 'FeatureCollection',
30
+ features: [input],
31
+ };
32
+ }
33
+
34
+ if (typeof type === 'string' && GEOMETRY_TYPES.has(type)) {
35
+ return {
36
+ type: 'FeatureCollection',
37
+ features: [{
38
+ type: 'Feature',
39
+ geometry: input,
40
+ properties: {},
41
+ }],
42
+ };
43
+ }
44
+
45
+ return null;
46
+ }
47
+
48
+ /**
49
+ * Парсить GeoJSON з тексту файлу (клієнтський шар, без збереження).
50
+ *
51
+ * @method POST
52
+ * @param {string} body.content - вміст файлу
53
+ * @param {string} [body.filename] - ім'я файлу
54
+ */
55
+
56
+ export default async function parseGeoFile({ content, filename = '' }) {
57
+ if (!content || typeof content !== 'string') {
58
+ throw BadRequestError('Потрібен вміст файлу (content)');
59
+ }
60
+
61
+ if (content.length > MAX_FILE_SIZE) {
62
+ throw BadRequestError('Максимальний розмір файлу — 50MB');
63
+ }
64
+
65
+ const lowerName = String(filename).toLowerCase();
66
+ if (lowerName && !lowerName.endsWith('.json') && !lowerName.endsWith('.geojson')) {
67
+ throw BadRequestError('Підтримуються лише файли GeoJSON (.geojson, .json)');
68
+ }
69
+
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(content);
73
+ }
74
+ catch {
75
+ throw BadRequestError('Некоректний JSON');
76
+ }
77
+
78
+ const normalized = normalizeToFeatureCollection(parsed);
79
+
80
+ if (!normalized?.features?.length) {
81
+ throw BadRequestError('Не вдалося розпізнати геометрію GeoJSON');
82
+ }
83
+
84
+ return normalized;
85
+ }