@opengis/gis 0.2.168 → 0.2.170

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 (192) hide show
  1. package/dist/{CardIcon-DzERD2T8.js → CardIcon-DXaESte6.js} +1 -1
  2. package/dist/{EntityTablePage-DAVJRQLe.js → EntityTablePage-BwyMNhrm.js} +1 -1
  3. package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-B-6axJUY.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-DrHke0pk.js} +1 -1
  4. package/dist/{HeaderActions.vue_vue_type_script_setup_true_lang-D1iwSeRN.js → HeaderActions.vue_vue_type_script_setup_true_lang-9Rm8Dhc4.js} +1 -1
  5. package/dist/{MapSettings-wVix8wMh.js → MapSettings-E1aNltts.js} +1 -1
  6. package/dist/{RastersTablePage-u3oN_h_n.js → RastersTablePage-CJblEi8a.js} +1 -1
  7. package/dist/{cartocss-BIveBlgY.js → cartocss-Dv2QILZ2.js} +5 -5
  8. package/dist/{import-utils-DkKPU_Nd.js → import-utils-CL-V2wSj.js} +1 -1
  9. package/dist/{index-C3bl1qBS.js → index-Bi60lcRm.js} +4768 -4718
  10. package/dist/index.css +1 -1
  11. package/dist/index.js +1 -1
  12. package/dist/index.umd.cjs +31 -31
  13. package/dist/{raster-CfTJZx5p.js → raster-nn-sOUb9.js} +3 -3
  14. package/dist/{register-dV5ipRhb.js → register-CjHXBGr5.js} +3 -3
  15. package/dist/{service-DWC5gq3L.js → service-l3Csdnxm.js} +8 -7
  16. package/dist/{vs-datatable-Cd2Gp4jo.js → vs-datatable-CJlE3-mz.js} +3 -2
  17. package/module/test/map/addr.json +303 -0
  18. package/module/test/map/{addr.yml → addr1.yml} +3 -1
  19. package/module/test/select/address.str_id.sql +3 -0
  20. package/package.json +10 -10
  21. package/plugin.js +21 -12
  22. package/server/plugins/mapnik/funcs/checkRasterFile.js +1 -3
  23. package/server/routes/cartocss/cartocss.route.js +31 -0
  24. package/server/routes/cartocss/cartocss.route.test.js +79 -0
  25. package/server/routes/cartocss/cartocss.schema.js +73 -0
  26. package/server/routes/{gis/cartocss → cartocss/functions}/add.cartocss.js +8 -11
  27. package/server/routes/{gis/cartocss → cartocss/functions}/get.cartocss.js +11 -13
  28. package/server/routes/gis/functions/checkXML.js +107 -0
  29. package/server/routes/gis/functions/dashboard.js +24 -0
  30. package/server/routes/gis/gis.route.js +26 -0
  31. package/server/routes/gis/gis.route.test.js +34 -0
  32. package/server/routes/gis/gis.schema.js +1 -0
  33. package/server/routes/map/{controllers → functions}/clearTiles.js +9 -8
  34. package/server/routes/map/{controllers → functions}/geojson.js +43 -43
  35. package/server/routes/map/{controllers → functions}/gis.convert.js +15 -23
  36. package/server/routes/map/{controllers → functions}/gis.icon.js +23 -10
  37. package/server/routes/map/{controllers → functions}/jsonData.js +72 -71
  38. package/server/routes/map/{controllers → functions}/layerList.js +17 -17
  39. package/server/routes/map/{controllers → functions}/map.js +8 -9
  40. package/server/routes/map/{controllers → functions}/mapCatalog.js +7 -7
  41. package/server/routes/map/functions/mapCatalogAttribute.js +54 -0
  42. package/server/routes/map/{controllers → functions}/mapFeatures.js +22 -18
  43. package/server/routes/map/{controllers → functions}/mapFormat.js +25 -28
  44. package/server/routes/map/{controllers → functions}/mapTiles.js +39 -30
  45. package/server/routes/map/{controllers → functions}/maps.js +3 -3
  46. package/server/routes/map/{controllers → functions}/marker_icon.js +1 -1
  47. package/server/routes/map/{controllers → functions}/parseGeoFile.js +10 -11
  48. package/server/routes/map/{controllers → functions}/pointHeight.js +3 -7
  49. package/server/routes/map/{controllers → functions}/vtile.js +5 -3
  50. package/server/routes/map/{vtile1.js → functions/vtile1.js} +39 -33
  51. package/server/routes/map/map.route.js +280 -0
  52. package/server/routes/map/map.route.test.js +503 -0
  53. package/server/routes/map/map.schema.js +356 -0
  54. package/server/routes/mapnik/{controllers → functions}/clearTiles.js +26 -29
  55. package/server/routes/mapnik/{controllers → functions}/createXml.js +17 -20
  56. package/server/routes/mapnik/{controllers → functions}/createXmlMulti.js +11 -15
  57. package/server/routes/mapnik/{controllers → functions}/fileSearch.js +6 -9
  58. package/server/routes/mapnik/functions/fileStat.js +28 -0
  59. package/server/routes/mapnik/{controllers → functions}/pretile.js +29 -35
  60. package/server/routes/mapnik/{controllers → functions}/rasterInfo.js +19 -10
  61. package/server/routes/mapnik/{controllers → functions}/readDir.js +4 -4
  62. package/server/routes/mapnik/functions/rtile.js +164 -0
  63. package/server/routes/mapnik/{controllers → functions}/uploadRaster.js +19 -24
  64. package/server/routes/mapnik/functions/uploadXML.js +27 -0
  65. package/server/routes/mapnik/mapnik.route.js +102 -0
  66. package/server/routes/mapnik/mapnik.route.test.js +371 -0
  67. package/server/routes/mapnik/mapnik.schema.mjs +190 -0
  68. package/server/routes/mapnik.prefixless/functions/mapnikLogger.js +31 -0
  69. package/server/routes/{mapnik/controllers → mapnik.prefixless/functions}/mapnikStat.js +7 -5
  70. package/server/routes/mapnik.prefixless/mapnik.prefixless.route.js +20 -0
  71. package/server/routes/mapnik.prefixless/mapnik.prefixless.route.test.js +91 -0
  72. package/server/routes/mapnik.prefixless/mapnik.prefixless.schema.mjs +27 -0
  73. package/server/routes/maps/functions/add.map.js +42 -0
  74. package/server/routes/{map/maps → maps/functions}/del.map.js +6 -7
  75. package/server/routes/{map/maps → maps/functions}/get.map.js +12 -11
  76. package/server/routes/maps/maps.route.js +58 -0
  77. package/server/routes/maps/maps.route.test.js +231 -0
  78. package/server/routes/maps/maps.schema.js +10 -0
  79. package/server/routes/metadata/functions/editMetadata.js +35 -0
  80. package/server/routes/metadata/functions/metadataXML.js +19 -0
  81. package/server/routes/metadata/metadata.route.js +26 -0
  82. package/server/routes/metadata/metadata.route.test.js +47 -0
  83. package/server/routes/metadata/metadata.schema.js +53 -0
  84. package/server/routes/{gis/ogc → ogc/functions}/map.info.point.js +22 -29
  85. package/server/routes/ogc/ogc.route.js +21 -0
  86. package/server/routes/ogc/ogc.route.test.js +54 -0
  87. package/server/routes/ogc/ogc.schema.js +16 -0
  88. package/server/routes/permissions/functions/catalog.permissions.edit.js +20 -0
  89. package/server/routes/permissions/functions/catalog.permissions.js +13 -0
  90. package/server/routes/permissions/{controllers → functions}/gis.catalog.js +10 -10
  91. package/server/routes/permissions/permissions.route.js +35 -0
  92. package/server/routes/permissions/permissions.route.test.js +35 -0
  93. package/server/routes/permissions/permissions.schema.js +44 -0
  94. package/server/routes/registers/functions/add.registry.js +37 -0
  95. package/server/routes/{gis/registers/funcs → registers/functions}/classifiers.js +2 -2
  96. package/server/routes/{gis/registers → registers/functions}/del.registry.js +5 -5
  97. package/server/routes/{gis/registers/funcs → registers/functions}/get.info.js +9 -5
  98. package/server/routes/{gis/registers → registers/functions}/gis.export.js +27 -21
  99. package/server/routes/{gis/registers → registers/functions}/gis.registry.js +22 -15
  100. package/server/routes/{gis/registers → registers/functions}/gis.registry.list.js +12 -10
  101. package/server/routes/{gis/registers/funcs → registers/functions}/handleRegistryRequest.js +12 -11
  102. package/server/routes/registers/registers.route.js +80 -0
  103. package/server/routes/registers/registers.route.test.js +145 -0
  104. package/server/routes/registers/registers.schema.js +246 -0
  105. package/server/routes/{gis/services → services/functions}/add.service.js +14 -16
  106. package/server/routes/services/functions/create.route.js +93 -0
  107. package/server/routes/{gis/services → services/functions}/del.service.js +5 -5
  108. package/server/routes/{gis/services → services/functions}/get.service.users.js +15 -13
  109. package/server/routes/services/functions/get.services.col.js +34 -0
  110. package/server/routes/{gis/services → services/functions}/get.services.js +21 -16
  111. package/server/routes/services/services.route.js +76 -0
  112. package/server/routes/services/services.route.test.js +168 -0
  113. package/server/routes/services/services.schema.js +278 -0
  114. package/server/routes/widgets/functions/add.widget.js +39 -0
  115. package/server/routes/widgets/functions/del.widget.js +18 -0
  116. package/server/routes/{map/widgets → widgets/functions}/get.widget.js +12 -11
  117. package/server/routes/widgets/widgets.route.js +61 -0
  118. package/server/routes/widgets/widgets.route.test.js +144 -0
  119. package/server/routes/widgets/widgets.schema.js +25 -0
  120. package/module/cls.json +0 -7
  121. package/module/gis/card/gis.metadata.table/main_info.hbs +0 -21
  122. package/module/gis/card/gis.metadata.table/metadata_info.hbs +0 -28
  123. package/module/gis/card/gis.metadata.table/other.hbs +0 -26
  124. package/module/gis/card/gis.rasters.table/main_info.hbs +0 -27
  125. package/module/gis/card/gis.registers.table/cls.hbs +0 -36
  126. package/module/gis/card/gis.registers.table/columns.hbs +0 -89
  127. package/module/gis/card/gis.registers.table/filters.hbs +0 -80
  128. package/module/gis/card/gis.registers.table/main_info.hbs +0 -35
  129. package/module/gis/card/gis.registers.table/source.hbs +0 -45
  130. package/module/gis/card/gis.services.table/attributes.hbs +0 -91
  131. package/module/gis/card/gis.services.table/filters.hbs +0 -83
  132. package/module/gis/card/gis.services.table/main_info.hbs +0 -27
  133. package/module/gis/card/gis.services.table/source.hbs +0 -26
  134. package/module/test/cls/bp_build_type.json +0 -37
  135. package/module/test/cls/doc_status.json +0 -32
  136. package/module/test/cls/ts.temp_status.json +0 -18
  137. package/module/test/cls/ts.temp_structure.ts_class.json +0 -50
  138. package/module/test/cls/ts.temp_type.json +0 -10
  139. package/module/test/layer/bp.json +0 -60
  140. package/module/test/layer/bp1.yml +0 -33
  141. package/module/test/layer/individual.yml +0 -54
  142. package/module/test/layer/ts.linking_passport.yml +0 -56
  143. package/module/test/layer/ts.temp_structure.yml +0 -51
  144. package/module/test/map/bp_myo.json +0 -44
  145. package/module/test/map/bpf.json +0 -44
  146. package/module/test/map/main.json +0 -39
  147. package/module/test/map/mbd.json +0 -92
  148. package/module/test/map/ts.json +0 -53
  149. package/module/test/select/address_id.json +0 -3
  150. package/module/test/select/address_id.sql +0 -8
  151. package/module/test/select/core.user_uid.sql +0 -1
  152. package/module/test/table/data_address.street.table.json +0 -70
  153. package/module/test/table/data_bp_myo.bp.table.json +0 -123
  154. package/server/routes/gis/checkXML.js +0 -117
  155. package/server/routes/gis/dashboard.js +0 -29
  156. package/server/routes/gis/index.mjs +0 -92
  157. package/server/routes/gis/metadata/editMetadata.js +0 -34
  158. package/server/routes/gis/metadata/metadataXML.js +0 -13
  159. package/server/routes/gis/registers/add.registry.js +0 -35
  160. package/server/routes/gis/registers/insert.columns.js +0 -107
  161. package/server/routes/gis/registers/insert.filters.js +0 -110
  162. package/server/routes/gis/services/create.route.js +0 -96
  163. package/server/routes/gis/services/get.services.col.js +0 -33
  164. package/server/routes/map/controllers/mapCatalogAttribute.js +0 -56
  165. package/server/routes/map/index.mjs +0 -259
  166. package/server/routes/map/maps/add.map.js +0 -43
  167. package/server/routes/map/widgets/add.widget.js +0 -39
  168. package/server/routes/map/widgets/del.widget.js +0 -23
  169. package/server/routes/mapnik/controllers/fileStat.js +0 -28
  170. package/server/routes/mapnik/controllers/mapnikLogger.js +0 -29
  171. package/server/routes/mapnik/controllers/rtile.js +0 -167
  172. package/server/routes/mapnik/index.js +0 -40
  173. package/server/routes/permissions/controllers/catalog.permissions.edit.js +0 -22
  174. package/server/routes/permissions/controllers/catalog.permissions.js +0 -8
  175. package/server/routes/permissions/index.mjs +0 -18
  176. /package/server/routes/map/{controllers/pin → pin}/circle1.svg +0 -0
  177. /package/server/routes/map/{controllers/pin → pin}/circle2.svg +0 -0
  178. /package/server/routes/map/{controllers/pin → pin}/none.svg +0 -0
  179. /package/server/routes/map/{controllers/pin → pin}/pin.svg +0 -0
  180. /package/server/routes/map/{controllers/pin → pin}/pin1.svg +0 -0
  181. /package/server/routes/map/{controllers/pin → pin}/pin2.svg +0 -0
  182. /package/server/routes/map/{controllers/pin → pin}/pin3.svg +0 -0
  183. /package/server/routes/map/{controllers/pin → pin}/pin4.svg +0 -0
  184. /package/server/routes/map/{controllers/pin → pin}/pin5.svg +0 -0
  185. /package/server/routes/map/{controllers/pin → pin}/pin6.svg +0 -0
  186. /package/server/routes/map/{controllers/pin → pin}/pin7.svg +0 -0
  187. /package/server/routes/permissions/{controllers/utils → functions}/get.permissions.js +0 -0
  188. /package/server/routes/{gis/registers/funcs → registers/functions}/columns.js +0 -0
  189. /package/server/routes/{gis/registers/funcs → registers/functions}/content.type.js +0 -0
  190. /package/server/routes/{gis/registers → registers/functions}/map.registry.js +0 -0
  191. /package/server/routes/{gis/services → services/functions}/get.layer.geom.js +0 -0
  192. /package/server/routes/{gis/services → services/functions}/legend.auto.js +0 -0
@@ -1,3 +1,4 @@
1
+ import { BadRequestError } from '@opengis/fastify-table/errors.js';
1
2
  import {
2
3
  pgClients, getTemplate, handlebars, metaFormat,
3
4
  getMeta,
@@ -15,11 +16,11 @@ function getLayerTableQuery({
15
16
  const q = `SELECT
16
17
  ${pk} as "id"
17
18
  ,'${el.key}' as "key"
18
- ${nogeom ? '' : `,st_asgeojson(${srid !== 4326 ? `st_transform(${geom}, 4326)` : geom})::json as geom`}, ${srid !== 4326 ? `st_transform(${geom}, 4326)` : geom}::box2d as box2d, ${columns ? `row_to_json(t)` : 'null'} as "data"
19
- ${point ? `,st_distance(${el.srid !== 4326 ? `st_transform(${geom}, 4326)` : geom},'${point} ') as distance` : ''}
20
- FROM ${table} t WHERE ${tableQuery || '1 = 1'} and st_isvalid(${geom}) and st_srid(${geom}) > 0
19
+ ${nogeom ? '' : `,st_asgeojson(${srid !== 4326 ? `st_transform(${geom || 'geom'}, 4326)` : (geom || 'geom')})::json as geom`}, ${srid !== 4326 ? `st_transform(${geom || 'geom'}, 4326)` : (geom || 'geom')}::box2d as box2d, ${columns ? `row_to_json(t)` : 'null'} as "data"
20
+ ${point ? `,st_distance(${el.srid !== 4326 ? `st_transform(${geom || 'geom'}, 4326)` : (geom || 'geom')},'${point} ') as distance` : ''}
21
+ FROM ${table} t WHERE ${tableQuery || '1 = 1'} and st_isvalid(${geom || 'geom'}) and st_srid(${geom || 'geom'}) > 0
21
22
  and ${point
22
- ? `case when ST_GeometryType(${geom}) in ('ST_Polygon','ST_MultiPolygon', 'ST_LineString','ST_MultiLineString', 'ST_MultiPoint', 'ST_Point') then st_intersects(${srid !== 4326 ? `st_transform(${geom}, 4326)` : geom}, ${buffer}) else false end`
23
+ ? `case when ST_GeometryType(${geom || 'geom'}) in ('ST_Polygon','ST_MultiPolygon', 'ST_LineString','ST_MultiLineString', 'ST_MultiPoint', 'ST_Point') then st_intersects(${srid !== 4326 ? `st_transform(${geom || 'geom'}, 4326)` : (geom || 'geom')}, ${buffer}) else false end`
23
24
  : '1=1'}
24
25
  and ${id ? `${pk}='${id.replace(/'/g, "")}'` : '2=2'}
25
26
  ${point ? 'order by distance' : ''} limit 10`;
@@ -53,14 +54,14 @@ async function getLayersData({
53
54
  radius,
54
55
  }, pg)
55
56
  : cartocss.config
56
- .filter((el, idx, arr) => el.key && el.table && pg.pk?.[el.table]
57
- && arr.findIndex(item => item.table === el.table && item.query === el.query) === idx)
58
- .map(el => getLayerTableQuery({
59
- el, id, point, nogeom, radius,
60
- }, pg)).join(' union all ');
57
+ .filter((el, idx, arr) => el.key && el.table && pg.pk?.[el.table]
58
+ && arr.findIndex(item => item.table === el.table && item.query === el.query) === idx)
59
+ .map(el => getLayerTableQuery({
60
+ el, id, point, nogeom, radius,
61
+ }, pg)).join(' union all ');
61
62
 
62
63
  // console.time(q);
63
- const rows = await pg.query(q).then(el => el.rows || []);
64
+ const rows = await pg.query(q).then(r => r.rows || []);
64
65
  // console.timeEnd(q);
65
66
 
66
67
  if (!rows.length) {
@@ -96,13 +97,13 @@ async function getLayersData({
96
97
  }
97
98
 
98
99
  async function getTableData({
99
- service, layer, id, user, nogeom,
100
+ service, layer, id, uid, nogeom,
100
101
  }, pg = pgClients.client) {
101
102
  const {
102
103
  source_path: tname, card, template,
103
104
  } = service || {};
104
105
 
105
- const columns = (card || []).filter((col) => (user?.uid ? true : !col.is_admin));
106
+ const columns = (card || []).filter((col) => (uid ? true : !col.is_admin));
106
107
 
107
108
  if (!tname) {
108
109
  return { error: 'missing source_path', code: 400 };
@@ -125,7 +126,7 @@ async function getTableData({
125
126
  const rows = await pg.query(
126
127
  `SELECT ${pk} as "id", ${!nogeom ? 'st_asgeojson(geom)::json as geom, geom::box2d as box2d' : ''} ${!nogeom && filteredColumns?.length > 0 ? ',' : ''} ${filteredColumns.map((n) => `"${n}"`).join(', ')} FROM ${tname} WHERE ${pk} = $1`,
127
128
  [id],
128
- ).then(el => el.rows || []);
129
+ ).then(r => r.rows || []);
129
130
 
130
131
  if (!rows.length) {
131
132
  return { error: 'object not found', code: 404 };
@@ -136,7 +137,7 @@ async function getTableData({
136
137
  .reduce((acc, curr) => ({ ...acc, [curr.name]: curr.data }), {});
137
138
 
138
139
  const htmlTemplate = pg?.pk?.['admin.templates'] && service?.card_mode === 'html'
139
- ? await pg.query('select body from admin.templates where name=$1 limit 1', [template || layer]).then(el => el.rows?.[0]?.body)
140
+ ? await pg.query('select body from admin.templates where name=$1 limit 1', [template || layer]).then(r => r.rows?.[0]?.body)
140
141
  : null;
141
142
 
142
143
  return {
@@ -148,44 +149,40 @@ async function getTableData({
148
149
  };
149
150
  }
150
151
 
151
- export default async function mapFormat({ pg = pgClients.client, query = {}, user }, reply) {
152
- const {
153
- layer, id, lat, lng, nogeom, radius = 50,
154
- } = query;
155
-
152
+ export default async function mapFormat({ layer, id, lat, lng, nogeom, radius = 50, uid }, pg = pgClients.client) {
156
153
  const t1 = Date.now();
157
154
 
158
155
  if (!layer) {
159
- return reply.status(400).send({ error: 'not enough query params: layer', code: 400 });
156
+ throw BadRequestError('not enough query params: layer');
160
157
  }
161
158
 
162
- const service = await getTemplate('layer', layer) || (pg.pk?.['gis.services'] ? await pg.query('SELECT * FROM gis.services WHERE service_id = $1', [layer]).then(el => el.rows[0]) : null);
163
- const cartocss = pg.pk?.['gis.cartocss'] ? await pg.query('SELECT * FROM gis.cartocss WHERE cartocss_id = $1', [layer]).then(el => el.rows[0]) : null;
159
+ const service = await getTemplate('layer', layer) || (pg.pk?.['gis.services'] ? await pg.query('SELECT * FROM gis.services WHERE service_id = $1', [layer]).then(r => r.rows[0]) : null);
160
+ const cartocss = pg.pk?.['gis.cartocss'] ? await pg.query('SELECT * FROM gis.cartocss WHERE cartocss_id = $1', [layer]).then(r => r.rows[0]) : null;
164
161
 
165
162
  if (!cartocss && !id) {
166
- return reply.status(400).send({ error: 'not enough query params: id', code: 400 });
163
+ throw BadRequestError('not enough query params: id');
167
164
  }
168
165
 
169
166
  if (cartocss && !(cartocss.config?.length || cartocss.card_table)) {
170
- return reply.status(400).send({ error: 'invalid layer settings: empty config', code: 400 });
167
+ throw BadRequestError('invalid layer settings: empty config');
171
168
  }
172
169
 
173
170
  if (cartocss && !(lat && lng) && !id) {
174
- return reply.status(400).send({ error: 'not enough query params: lat and lng / id', code: 400 });
171
+ throw BadRequestError('not enough query params: lat and lng / id');
175
172
  }
176
173
 
177
174
  const {
178
- ids, rows, data, columns, classifiers, tname, template, error, code = 500, htmlTemplate,
175
+ ids, rows, data, columns, classifiers, tname, template, error, htmlTemplate,
179
176
  } = cartocss
180
177
  ? await getLayersData({
181
178
  cartocss, id, lng, lat, radius, nogeom,
182
179
  }, pg)
183
180
  : await getTableData({
184
- service, layer, id, user, nogeom,
181
+ service, layer, id, uid, nogeom,
185
182
  }, pg);
186
183
 
187
184
  if (error) {
188
- return reply.status(code).send({ error, code });
185
+ throw new Error(error);
189
186
  }
190
187
 
191
188
  rows.forEach(row => Object.assign(row, { box2d: undefined, bounds: row.box2d ? row.box2d.replace(/[A-Z\)\(]+/g, '').split(/[ ,]+/).map(el => el - 0) : null }));
@@ -1,30 +1,37 @@
1
1
  import path from 'node:path';
2
2
  import { existsSync } from 'node:fs';
3
3
  import { createHash } from 'node:crypto';
4
- import { readFile, stat, mkdir, writeFile } from 'node:fs/promises';
4
+ import {
5
+ readFile, stat, mkdir, writeFile,
6
+ } from 'node:fs/promises';
5
7
 
6
- import Sphericalmercator from '@mapbox/sphericalmercator';
8
+ import { SphericalMercator } from '@mapbox/sphericalmercator';
7
9
 
8
- import { getTemplate, pgClients, getFilterSQL, getMeta } from '@opengis/fastify-table/utils.js';
10
+ const mercator = new SphericalMercator({ size: 256 });
11
+
12
+ import {
13
+ getTemplate, pgClients, getFilterSQL, getMeta,
14
+ } from '@opengis/fastify-table/utils.js';
9
15
 
10
16
  import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
17
+ import { BadRequestError, NotFoundError } from '@opengis/fastify-table/errors.js';
11
18
 
12
19
  const limit = 3000;
13
- const mercator = new Sphericalmercator({ size: 256 });
20
+
14
21
  const types = { point: 'ST_Point', polygon: 'ST_Polygon,ST_MultiPolygon' };
15
22
  const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at'];
16
23
 
17
- export default async function mapTiles({ pg = pgClients.client, params = {}, query = {}, unittest }, reply) {
18
- const { slug, y, z } = params;
19
- const { sql, nocache, filter, pointZoom, type, layers, layer } = query;
20
- const x = params.x?.split?.('.')?.[0] - 0;
24
+ const unittest = process.env.NODE_ENV === 'test' || process.env.VITEST;
21
25
 
26
+ export default async function mapTiles({
27
+ slug, x, y, z, sql, nocache, filter, pointZoom, type, layers, layer,
28
+ }, pg = pgClients.client) {
22
29
  if (!slug) {
23
- return reply.status(400).send('not enough params: slug');
30
+ throw BadRequestError('not enough params: slug');
24
31
  }
25
32
 
26
33
  if (!x || !y || !z) {
27
- return reply.status(400).send('not enough params: xyz');
34
+ throw BadRequestError('not enough params: xyz');
28
35
  }
29
36
 
30
37
  const bbox = mercator.bbox(+y, +x, +z, false);
@@ -39,7 +46,7 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
39
46
 
40
47
  const filepath = path.join(
41
48
  rootFolder,
42
- `/map/vtile/${slug}/${hash ? `${createHash('md5').update(hash).digest('hex')}/` : ''}${z}/${x}/${y}.pbf`
49
+ `/map/vtile/${slug}/${hash ? `${createHash('md5').update(hash).digest('hex')}/` : ''}${z}/${x}/${y}.pbf`,
43
50
  );
44
51
 
45
52
  if (existsSync(filepath) && z < 13 && !nocache && !sql && !unittest) {
@@ -47,14 +54,14 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
47
54
  const ageInMs = Date.now() - birthtimeMs;
48
55
  if (ageInMs < 86400000) {
49
56
  const buffer = await readFile(filepath);
50
- return reply.status(200).headers(headers).send(buffer);
57
+ return { headers, data: buffer };
51
58
  }
52
59
  }
53
60
 
54
61
  const loadTemplate = await getTemplate('map', slug);
55
62
 
56
63
  if (!loadTemplate) {
57
- return reply.status(404).send('map not found');
64
+ throw NotFoundError('map not found');
58
65
  }
59
66
 
60
67
  const index = loadTemplate?.find?.(el => el[0].split('.').shift() === 'index')?.[1];
@@ -64,22 +71,24 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
64
71
  : loadTemplate?.filter?.(el => el[0].split('.').shift() !== 'index')?.map(el => ({ ...el[1], id: el[0].replace('.yml', '').replace('.json', '') }));
65
72
 
66
73
  if (!layers1?.length) {
67
- return reply.status(404).send('map layers not found');
74
+ throw NotFoundError('map layers not found');
68
75
  }
69
76
 
70
77
  layers1.forEach(el => Object.assign(el, { name: el.name || el.id }));
71
78
 
72
- 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);
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));
73
80
 
74
81
  if (!layers2.length) {
75
- return reply.status(404).send('map layers sources not provided / invalid query params');
82
+ throw NotFoundError('map layers sources not provided / invalid query params');
76
83
  }
77
84
 
78
85
  const allFilters = loadTemplate.widgets?.filter?.(el => el.type === 'filters') || [];
79
- const { filters: filterList = [], layer_id: layerId } = allFilters.find(el => layer ? el.config?.layer_id === layer : el.default)?.config || allFilters[0]?.config || {};
86
+ const { filters: filterList = [], layer_id: layerId } = allFilters.find(el => (layer ? el.config?.layer_id === layer : el.default))?.config || allFilters[0]?.config || {};
80
87
 
81
88
  const q = (await Promise.all(layers2.map(async (layer) => {
82
- const { source_path: source, id, name, style, geometry_type: gt, query: lquery, geometry_column: gCol } = layer;
89
+ const {
90
+ source_path: source, id, name, style, geometry_type: gt, query: lquery, geometry_column: gCol,
91
+ } = layer;
83
92
  const loadTable = await getTemplate('table', source);
84
93
 
85
94
  const table = loadTable?.table || source;
@@ -89,7 +98,9 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
89
98
  if (!columns?.length) return null;
90
99
 
91
100
  const pk = pg.pk?.[table] || loadTable?.key;
92
- const fData = filter && layerId === name ? await getFilterSQL({ pg, table: source, filterList, filter }) : {};
101
+ const fData = filter && layerId === name ? await getFilterSQL({
102
+ pg, table: source, filterList, filter,
103
+ }) : {};
93
104
 
94
105
  const geomCol = parseInt(z, 10) < parseInt(pointZoom || '0', 10) || (style?.centroid && gt === 'point')
95
106
  ? `ST_Centroid(${geom})`
@@ -98,11 +109,9 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
98
109
  const columnsFiltered = columns
99
110
  .filter(el => !['json', 'geometry'].includes(pg.pgType?.[el.dataTypeID]) && !systemColumns.includes(el.name));
100
111
 
101
- const props = columnsFiltered.map(el => {
102
- return el.type?.[0] === '_'
103
- ? `${el.name}[1] as ${el.name}`
104
- : `${el.name}${['int4'].includes(el.type) ? '::text' : ''}`;
105
- });
112
+ const props = columnsFiltered.map(el => (el.type?.[0] === '_'
113
+ ? `${el.name}[1] as ${el.name}`
114
+ : `${el.name}${['int4'].includes(el.type) ? '::text' : ''}`));
106
115
 
107
116
  return `SELECT ST_AsMVT(q, '${(id || name).replace(/'/g, "''")}', 4096, 'geom','row') as tile
108
117
  FROM (
@@ -129,8 +138,8 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
129
138
  ) q`;
130
139
  }))).filter(el => el).join(' union all ');
131
140
 
132
- if (sql === '1') return q;
133
- if (sql === '2') return filepath.replace(/\\/g, '/');
141
+ if (sql === 1) return q;
142
+ if (sql === 2) return filepath.replace(/\\/g, '/');
134
143
 
135
144
  const tile = await pg.query(q);
136
145
 
@@ -138,9 +147,9 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
138
147
  console.log(slug, q);
139
148
  }
140
149
 
141
- if (sql === '3') return tile.rows.map((el) => el.tile);
150
+ if (sql === 3) return tile.rows.map((el) => el.tile);
142
151
 
143
- if (sql === '4') return tile.rows.map((el) => el.tile);
152
+ if (sql === 4) return tile.rows.map((el) => el.tile);
144
153
 
145
154
  const buffer = Buffer.concat(tile.rows.map((el) => Buffer.from(el.tile)));
146
155
 
@@ -149,5 +158,5 @@ export default async function mapTiles({ pg = pgClients.client, params = {}, que
149
158
  await writeFile(filepath, buffer, 'binary');
150
159
  }
151
160
 
152
- return reply.status(200).headers(headers).send(buffer);
153
- }
161
+ return { headers, data: buffer };
162
+ }
@@ -7,10 +7,10 @@ const list = arr.map(el => {
7
7
  return { name, slug: el[0] };
8
8
  });
9
9
 
10
- export default async function mapList(req, reply) {
10
+ export default async function mapList() {
11
11
  if (!arr.length) {
12
- return reply.status(200).send([]);
12
+ return [];
13
13
  }
14
14
 
15
- return reply.status(200).send(list);
15
+ return list;
16
16
  }
@@ -1,6 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { fileURLToPath } from 'url';
3
+ import { fileURLToPath } from 'node:url';
4
4
 
5
5
  async function downloadImage(url, file) {
6
6
  const resp = await fetch(url);
@@ -1,3 +1,5 @@
1
+ import { BadRequestError } from "@opengis/fastify-table/errors.js";
2
+
1
3
  const GEOMETRY_TYPES = new Set([
2
4
  'Point',
3
5
  'MultiPoint',
@@ -50,20 +52,19 @@ function normalizeToFeatureCollection(input) {
50
52
  * @param {string} body.content - вміст файлу
51
53
  * @param {string} [body.filename] - ім'я файлу
52
54
  */
53
- export default async function parseGeoFile({ body = {} }, reply) {
54
- const { content, filename = '' } = body;
55
55
 
56
+ export default async function parseGeoFile({ content, filename = '' }) {
56
57
  if (!content || typeof content !== 'string') {
57
- return reply.status(400).send({ error: 'Потрібен вміст файлу (content)' });
58
+ throw BadRequestError('Потрібен вміст файлу (content)');
58
59
  }
59
60
 
60
61
  if (content.length > MAX_FILE_SIZE) {
61
- return reply.status(400).send({ error: 'Максимальний розмір файлу — 50MB' });
62
+ throw BadRequestError('Максимальний розмір файлу — 50MB');
62
63
  }
63
64
 
64
65
  const lowerName = String(filename).toLowerCase();
65
66
  if (lowerName && !lowerName.endsWith('.json') && !lowerName.endsWith('.geojson')) {
66
- return reply.status(400).send({ error: 'Підтримуються лише файли GeoJSON (.geojson, .json)' });
67
+ throw BadRequestError('Підтримуються лише файли GeoJSON (.geojson, .json)');
67
68
  }
68
69
 
69
70
  let parsed;
@@ -71,16 +72,14 @@ export default async function parseGeoFile({ body = {} }, reply) {
71
72
  parsed = JSON.parse(content);
72
73
  }
73
74
  catch {
74
- return reply.status(400).send({ error: 'Некоректний JSON' });
75
+ throw BadRequestError('Некоректний JSON');
75
76
  }
76
77
 
77
78
  const normalized = normalizeToFeatureCollection(parsed);
79
+
78
80
  if (!normalized?.features?.length) {
79
- return reply.status(400).send({ error: 'Не вдалося розпізнати геометрію GeoJSON' });
81
+ throw BadRequestError('Не вдалося розпізнати геометрію GeoJSON');
80
82
  }
81
83
 
82
- return reply
83
- .status(200)
84
- .header('Content-Type', 'application/json')
85
- .send(normalized);
84
+ return normalized;
86
85
  }
@@ -1,15 +1,12 @@
1
1
  const DEFAULT_ENDPOINT = 'https://api.opentopodata.org/v1/srtm90m';
2
2
 
3
- function parseLatLng(query) {
4
- const lat = Number(query?.lat);
5
- const lng = Number(query?.lng);
3
+ function parseLatLng({ lat, lng }) {
6
4
  if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
7
5
  throw new Error('lat and lng are required');
8
6
  }
9
7
  if (Math.abs(lat) > 90 || Math.abs(lng) > 180) {
10
8
  throw new Error('coordinates out of range');
11
9
  }
12
- return { lat, lng };
13
10
  }
14
11
 
15
12
  function getEndpoint() {
@@ -42,9 +39,8 @@ async function fetchHeight(lat, lng) {
42
39
  }
43
40
  }
44
41
 
45
- export default async function pointHeight(ctx = {}) {
46
- const query = ctx?.query ?? ctx ?? {};
47
- const { lat, lng } = parseLatLng(query);
42
+ export default async function pointHeight({ lat, lng }) {
43
+ parseLatLng({ lat, lng });
48
44
  const height = await fetchHeight(lat, lng);
49
45
 
50
46
  return {
@@ -5,16 +5,18 @@ import {
5
5
  readFile, stat, mkdir, writeFile,
6
6
  } from 'node:fs/promises';
7
7
 
8
- import Sphericalmercator from '@mapbox/sphericalmercator';
8
+ import { SphericalMercator } from '@mapbox/sphericalmercator';
9
+
10
+ const mercator = new SphericalMercator({ size: 256 });
9
11
 
10
12
  import {
11
- getTemplate, pgClients, getFilterSQL, getMeta, yml2json, getColumnCLS, getSelect,
13
+ getTemplate, pgClients, getFilterSQL, getMeta, yml2json, getColumnCLS,
12
14
  } from '@opengis/fastify-table/utils.js';
13
15
 
14
16
  import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
15
17
 
16
18
  const limit = 3000;
17
- const mercator = new Sphericalmercator({ size: 256 });
19
+
18
20
  const types = { point: 'ST_Point', polygon: 'ST_Polygon,ST_MultiPolygon' };
19
21
  const systemColumns = ['uid', 'cdate', 'editor_id', 'editor_date', 'created_by', 'created_at', 'updated_by', 'updated_at'];
20
22
 
@@ -10,43 +10,46 @@ import {
10
10
  readFile, stat, mkdir, writeFile,
11
11
  } from 'node:fs/promises';
12
12
 
13
- import Sphericalmercator from '@mapbox/sphericalmercator';
13
+ import { load } from 'js-yaml';
14
14
 
15
- const mercator = new Sphericalmercator({ size: 256 });
15
+ import { SphericalMercator } from '@mapbox/sphericalmercator';
16
16
 
17
- // import { SphericalMercator } from '@mapbox/sphericalmercator';
18
- // const mercator = new SphericalMercator({ size: 256 });
17
+ const mercator = new SphericalMercator({ size: 256 });
19
18
 
20
19
  import {
21
20
  getColumnCLS, getTemplate, getMeta, getFilterSQL, getSelect,
21
+ pgClients,
22
+ getFilterSQLParametrized,
22
23
  } from '@opengis/fastify-table/utils.js';
23
- import * as yaml from 'js-yaml';
24
- import rootFolder from '../../plugins/mapnik/funcs/rootFolder.mjs';
24
+
25
+ import rootFolder from '../../../plugins/mapnik/funcs/rootFolder.mjs';
26
+ import { NotFoundError } from '@opengis/fastify-table/errors.js';
25
27
 
26
28
  const propsCache = {};
27
29
 
28
- export default async function vtile({
29
- params = {}, query, pg, user,
30
- }, reply) {
31
- const { y, z } = params;
32
- const x = params.x.split('.')[0] - 0;
33
- const {
34
- type, nocache, sql, clusterZoom, filter, nottl,
35
- } = query;
30
+ const types = { point: 'ST_Point' /* ,ST_MultiPoint */, polygon: 'ST_Polygon,ST_MultiPolygon' };
36
31
 
32
+ export default async function vtile({
33
+ x, y, z, layer: paramsLayer, type, nocache, sql, clusterZoom, filter, nottl, uid, filter: queryFilter, all: queryAll, sql: querySql, pointZoom,
34
+ }, pg = pgClients.client) {
37
35
  const hash = [type, clusterZoom, filter].join();
38
36
 
39
37
  const filepath = path.join(
40
38
  rootFolder,
41
- `/vtile/${params.layer}/${hash ? `${createHash('md5').update(hash).digest('hex')}/` : ''}${z}/${x}/${y}.vmt`,
39
+ `/vtile/${paramsLayer}/${hash ? `${createHash('md5').update(hash).digest('hex')}/` : ''}${z}/${x}/${y}.vmt`,
42
40
  );
43
41
 
44
42
  // layer
45
- const [map, layer] = params.layer.includes(':') ? params.layer.split(':') : [null, params.layer];
43
+ const [_, layer] = paramsLayer.includes(':') ? paramsLayer.split(':') : [null, paramsLayer];
46
44
 
47
45
  // service
48
- const data = await getTemplate('layer', layer) || await pg.query(`select * from gis.services where ${!user?.uid ? 'is_public and is_active' : '1=1'} and service_id=$1`, [layer]).then(el => el.rows?.[0]);
49
- const style = typeof data?.style === 'object' ? data?.style : yaml.load(data?.style);
46
+ const data = await getTemplate('layer', layer) || await pg.query(`select * from gis.services where ${!uid ? 'is_public and is_active' : '1=1'} and service_id=$1`, [layer]).then(r => r.rows?.[0]);
47
+
48
+ if (!data) {
49
+ throw NotFoundError('layer not found');
50
+ }
51
+
52
+ const style = typeof data?.style === 'object' ? data?.style : load(data?.style);
50
53
 
51
54
  const cacheZoom = style?.cacheZoom || 13;
52
55
  const ttl = typeof style?.ttl === 'number' && style.ttl >= 0 ? style.ttl : 86400000;
@@ -56,7 +59,7 @@ export default async function vtile({
56
59
  const ageInMs = Date.now() - birthtimeMs;
57
60
  if (ageInMs < ttl) {
58
61
  const buffer = await readFile(filepath);
59
- return reply.status(200).headers(headers).send(buffer);
62
+ return { headers, data: buffer };
60
63
  }
61
64
  }
62
65
 
@@ -69,20 +72,20 @@ export default async function vtile({
69
72
  const bbox = mercator.bbox(+y, +x, +z, false/* , '900913' */);
70
73
  const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
71
74
 
72
- const filterData = query.filter ? await getFilterSQL({
73
- filter: query.filter, table, filterList,
74
- }) : {};
75
+ const filterData = queryFilter ? await getFilterSQLParametrized({
76
+ filter: queryFilter, table, filterList, pg,
77
+ }) : { args: [] };
75
78
 
76
79
  // meta
77
- const metaTable = await getMeta({ pg, table });
78
- if (metaTable.error) { return metaTable; }
79
- const { pk: pkey = data.key, columns } = metaTable; // if view - specify via template
80
+ const metaTable = table ? await getMeta({ pg, table }) : {};
81
+
82
+ const { pk: pkey = data.key, columns = [] } = metaTable; // if view - specify via template
80
83
 
81
84
  // props
82
85
  const statusColumn = style?.colorAttr || style?.iconAttr ? columns.filter(col => col.name === style.colorAttr || style?.iconAttr) : [];
83
- const props = !query.all ? (statusColumn || []).concat(data.popup || []).concat(data.card || []) : columns.filter(el => !['json', 'geometry'].includes(pg.pgType[el.dataTypeID]) && !['uid', 'cdate', 'editor_id', 'editor_date'].includes(el.name));
86
+ const props = !queryAll ? (statusColumn || []).concat(data.popup || []).concat(data.card || []) : columns.filter(el => !['json', 'geometry'].includes(pg.pgType[el.dataTypeID]) && !['uid', 'cdate', 'editor_id', 'editor_date'].includes(el.name));
84
87
 
85
- const cache = layer + (query.all ? '-all' : '') + JSON.stringify(data.popup || []).length;
88
+ const cache = layer + (queryAll ? '-all' : '') + JSON.stringify(data.popup || []).length;
86
89
  const propsWithCls = propsCache[cache] ?? await Promise.all(props.map(async (col, idx) => {
87
90
  const key = col.name;
88
91
  const select = data.popup?.find(el => el.name === key);
@@ -109,17 +112,16 @@ export default async function vtile({
109
112
  propsCache[cache] = propsCache[cache] || propsWithCls;
110
113
 
111
114
  const geomCol = (style?.type === 'point' ? `ST_Centroid(${geom})` : null)
112
- || (parseInt(z, 10) < parseInt(query.pointZoom || style?.pointZoom || style?.iconZoom || '0', 10) ? `ST_Centroid(${geom})` : geom);
115
+ || (parseInt(z, 10) < parseInt(pointZoom || style?.pointZoom || style?.iconZoom || '0', 10) ? `ST_Centroid(${geom})` : geom);
113
116
 
114
117
  const koef = {
115
118
  10: 0.1, 11: 0.05, 12: 0.005, 13: 0.001, 14: 0.001, 15: 0.0005,
116
119
  }[z] || 0.0005;
117
120
 
118
- const types = { point: 'ST_Point' /* ,ST_MultiPoint */, polygon: 'ST_Polygon,ST_MultiPolygon' };
119
121
  const clusterZoom1 = style?.clusterZoom || clusterZoom;
120
122
  const queryMode = ((clusterZoom1 - z) > 2 ? 1 : null) || ((clusterZoom1 - z) > 0 ? 2 : null) || 3;
121
123
  // console.log(z, clusterZoom, queryMode);
122
- const props1 = props.filter((el, idx, arr) => idx === arr.findIndex(item => item.name === el.name));
124
+ // const props1 = props.filter((el, idx, arr) => idx === arr.findIndex(item => item.name === el.name));
123
125
  const q = {
124
126
  1: `SELECT ST_AsMVT(q, '${layer}', 4096, 'geom','row') as tile
125
127
  FROM (
@@ -166,8 +168,12 @@ export default async function vtile({
166
168
  ) q`,
167
169
  }[queryMode];
168
170
 
169
- if (query.sql === '1') { return q; }
170
- const tile = await pg.query(q);
171
+ if (querySql === 1) {
172
+ return q;
173
+ }
174
+
175
+ const args = filterData?.args || [];
176
+ const tile = await pg.query(q, args);
171
177
  const buffer = Buffer.concat(tile.rows.map((el) => Buffer.from(el.tile)));
172
178
 
173
179
  if (!nocache) {
@@ -175,5 +181,5 @@ export default async function vtile({
175
181
  await writeFile(filepath, buffer, 'binary');
176
182
  }
177
183
 
178
- return reply.headers({ ...headers, 'Cache-Control': nocache || nottl ? 'no-cache' : headers['Cache-Control'] }).send(buffer);
184
+ return { headers: { ...headers, 'Cache-Control': nocache || nottl ? 'no-cache' : headers['Cache-Control'] }, data: buffer };
179
185
  }