@opengis/gis 0.2.160 → 0.2.162
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CardIcon-CklSJTtC.js → CardIcon-DcriKHRQ.js} +1 -1
- package/dist/{EntityTablePage-BzWYtKWK.js → EntityTablePage-44UmAQh0.js} +1 -1
- package/dist/{ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-Rw9Gg_TG.js → ExtentOutlineLayer.vue_vue_type_script_setup_true_lang-BAaobvwj.js} +1 -1
- package/dist/{HeaderActions.vue_vue_type_script_setup_true_lang-Ddh9Cmkh.js → HeaderActions.vue_vue_type_script_setup_true_lang-CLEpHbVL.js} +1 -1
- package/dist/{MapSettings-8X4kAyPU.js → MapSettings-C7qDO9gp.js} +1 -1
- package/dist/{RastersTablePage-Dj-flTFQ.js → RastersTablePage-DmOpbseA.js} +1 -1
- package/dist/{cartocss-CilZ3kmC.js → cartocss-DGvNrARb.js} +5 -5
- package/dist/{import-utils-C5dvkoG1.js → import-utils-DYDa34GH.js} +1 -1
- package/dist/index-DCKH-TcG.js +13556 -0
- package/dist/index.css +1 -1
- package/dist/index.js +1 -1
- package/dist/index.umd.cjs +55 -42
- package/dist/{raster-CO1uIsxP.js → raster-CAi6K97q.js} +3 -3
- package/dist/{register-dMnCEwXf.js → register-C6IbQrEK.js} +3 -3
- package/dist/service-B_C_D5nN.js +3471 -0
- package/dist/{vs-datatable-B989esR4.js → vs-datatable-aAXTRfDg.js} +2 -3
- package/module/test/map/main.json +10 -10
- package/package.json +2 -2
- package/server/plugins/mapnik/map.proto +93 -1
- package/server/routes/gis/index.mjs +3 -0
- package/server/routes/gis/services/create.route.js +96 -0
- package/server/routes/map/controllers/gis.convert.js +3 -2
- package/server/routes/map/controllers/pointHeight.js +55 -0
- package/server/routes/map/index.mjs +52 -1
- package/server/routes/map/vtile1.js +1 -1
- package/dist/index-CIF8ZBn4.js +0 -11606
- package/dist/service-CPxxx_z0.js +0 -2628
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defineComponent as F, mergeModels as O, defineAsyncComponent as I, useModel as R, ref as y, watch as K, openBlock as o, createElementBlock as s, createElementVNode as e, createVNode as $, unref as x, withCtx as S, createStaticVNode as V, useSlots as P, computed as H, Fragment as b, createTextVNode as U, createCommentVNode as N, renderList as A, normalizeStyle as Q, toDisplayString as M, normalizeClass as z, withModifiers as X, createBlock as Y, resolveDynamicComponent as ee, withDirectives as te, renderSlot as ne } from "vue";
|
|
2
2
|
import { notify as E } from "@opengis/core";
|
|
3
3
|
import { Edit as le, Trash as oe } from "lucide-vue-next";
|
|
4
|
-
import { _ as u } from "./index-
|
|
4
|
+
import { _ as u } from "./index-DCKH-TcG.js";
|
|
5
5
|
const se = { class: "flex items-center justify-center col-span-3 gap-2" }, re = { class: "flex justify-end p-[20px] gap-[10px] border-t w-full" }, ie = /* @__PURE__ */ F({
|
|
6
6
|
__name: "vs-datatable-controls",
|
|
7
7
|
props: /* @__PURE__ */ O({
|
|
@@ -725,12 +725,11 @@ const kt = /* @__PURE__ */ u(pt, [["render", wt]]), W = {
|
|
|
725
725
|
])
|
|
726
726
|
])) : (o(), s(b, { key: 1 }, [
|
|
727
727
|
a.slot && g(a.slot) ? ne(i.$slots, a.slot, {
|
|
728
|
-
key: 0,
|
|
729
728
|
row: l,
|
|
730
729
|
column: a,
|
|
731
730
|
value: l[a.key],
|
|
732
731
|
rowIndex: f
|
|
733
|
-
}, void 0, !0) : a.type === "color" ? (o(), s("div", zt, [
|
|
732
|
+
}, void 0, !0, 0) : a.type === "color" ? (o(), s("div", zt, [
|
|
734
733
|
e("div", {
|
|
735
734
|
class: z(["w-4 h-4 rounded-full align-middle", `bg-[${l[a.key]}]`])
|
|
736
735
|
}, null, 2)
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"history": true,
|
|
3
3
|
"height": "calc(100vh - 65px)",
|
|
4
|
-
"center": [
|
|
4
|
+
"center": [
|
|
5
5
|
34.65450,
|
|
6
|
-
48.51174
|
|
6
|
+
48.51174
|
|
7
7
|
],
|
|
8
8
|
"zoom": 11,
|
|
9
9
|
"widgets": [
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
{
|
|
12
12
|
"type": "catalog",
|
|
13
13
|
"position": "top-left"
|
|
14
|
-
|
|
14
|
+
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"type": "legend",
|
|
18
|
-
"position": "bottom-right"
|
|
18
|
+
"position": "bottom-right"
|
|
19
19
|
},
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
{
|
|
22
22
|
"type": "basemaps",
|
|
23
23
|
"position": "bottom-left",
|
|
24
|
-
"title": "Базові карти",
|
|
24
|
+
"title": "Базові карти",
|
|
25
25
|
"visible": true
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
],
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
"tools": [
|
|
31
31
|
"home",
|
|
32
32
|
"geolocation",
|
|
@@ -36,4 +36,4 @@
|
|
|
36
36
|
"print",
|
|
37
37
|
"add-data"
|
|
38
38
|
]
|
|
39
|
-
}
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -10,7 +10,12 @@ package map;
|
|
|
10
10
|
service MapService {
|
|
11
11
|
rpc PrintMap(PrintMapRequest) returns (PrintMapOut);
|
|
12
12
|
rpc RenderTile(RenderTileRequest) returns (RenderTileOut);
|
|
13
|
+
rpc GetTile(GetTileRequest) returns (GetTileOut);
|
|
14
|
+
rpc VectorTile(VectorTileRequest) returns (VectorTileOut);
|
|
15
|
+
rpc PoolClear(PoolClearRequest) returns (PoolClearOut);
|
|
13
16
|
rpc ClearTile(ClearTileRequest) returns (ClearTileOut);
|
|
17
|
+
rpc ReadFile(ReadFileRequest) returns (ReadFileOut);
|
|
18
|
+
rpc WriteFile(WriteFileRequest) returns (WriteFileOut);
|
|
14
19
|
rpc ReadDir(ReadDirRequest) returns (ReadDirOut);
|
|
15
20
|
rpc FileStat(FileStatRequest) returns (FileStatOut);
|
|
16
21
|
rpc FileSearch(FileSearchRequest) returns (FileSearchOut);
|
|
@@ -20,8 +25,10 @@ service MapService {
|
|
|
20
25
|
rpc ConvertCog(ConvertCogRequest) returns (stream JobProgress); // streaming COG conversion
|
|
21
26
|
rpc ConvertVector(ConvertVectorRequest) returns (ConvertVectorOut); // vector file/archive -> GeoJSON
|
|
22
27
|
rpc ImportData(ImportDataRequest) returns (stream JobProgress); // streaming SHP -> PostGIS import
|
|
28
|
+
rpc DumpTable(DumpTableRequest) returns (stream JobProgress); // streaming PostGIS -> SHP dump
|
|
23
29
|
rpc UploadXML(UploadXMLRequest) returns (UploadXMLOut);
|
|
24
30
|
rpc UploadRaster(UploadRasterRequest) returns (UploadRasterOut);
|
|
31
|
+
rpc UploadChunk(UploadChunkRequest) returns (UploadChunkOut);
|
|
25
32
|
rpc GetRasterStatus(GetRasterStatusRequest) returns (GetStatusOut);
|
|
26
33
|
rpc GetStat(GetStatRequest) returns (GetStatOut);
|
|
27
34
|
rpc GetLogs(GetLogsRequest) returns (GetLogsOut);
|
|
@@ -43,6 +50,7 @@ message PrintMapRequest {
|
|
|
43
50
|
int32 opacity = 8; // 0-255
|
|
44
51
|
repeated string colors = 9; // [ #00FF00, #0000FF]
|
|
45
52
|
repeated string layers = 10;
|
|
53
|
+
optional bool ignoreBusy = 11; // when true fallback to one-off renderer if pool is exhausted
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
message PrintMapOut {
|
|
@@ -65,6 +73,8 @@ message RenderTileRequest {
|
|
|
65
73
|
bool meta = 10; // when true return tile metadata/path only (without base64 image payload)
|
|
66
74
|
optional bool base64 = 11; // optional output mode flag: true -> base64, false -> raw bytes
|
|
67
75
|
optional string format = 12; // optional tile image format: png|jpg|jpeg|webp
|
|
76
|
+
optional bool ignoreBusy = 13; // when true fallback to one-off renderer if pool is exhausted
|
|
77
|
+
optional int32 maxZoom = 14; // optional per-request max zoom override
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
message RenderTileOut {
|
|
@@ -77,16 +87,71 @@ message RenderTileOut {
|
|
|
77
87
|
bytes data = 7; // raw PNG bytes (used when request.base64=false)
|
|
78
88
|
}
|
|
79
89
|
|
|
90
|
+
message GetTileRequest {
|
|
91
|
+
string path = 1; // tile path relative to tiles root
|
|
92
|
+
bool meta = 2; // when true return metadata/path only without binary payload
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
message GetTileOut {
|
|
96
|
+
string tile_path = 1; // normalized relative tile path under tiles/
|
|
97
|
+
bytes data = 2; // raw tile bytes when meta=false
|
|
98
|
+
string err = 3;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
message VectorTileRequest {
|
|
102
|
+
string path = 1; // vector tile folder/path relative to tiles root
|
|
103
|
+
int32 z = 2;
|
|
104
|
+
int32 x = 3;
|
|
105
|
+
int32 y = 4;
|
|
106
|
+
bool meta = 5; // when true return metadata/path only without binary payload
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
message VectorTileOut {
|
|
110
|
+
string tile_path = 1; // normalized relative tile path under tiles/
|
|
111
|
+
bytes data = 2; // raw vector tile bytes when meta=false
|
|
112
|
+
string err = 3;
|
|
113
|
+
}
|
|
114
|
+
|
|
80
115
|
message ClearTileRequest {
|
|
81
116
|
string name = 1;
|
|
82
117
|
string path = 2;
|
|
83
118
|
}
|
|
84
119
|
|
|
120
|
+
message PoolClearRequest {
|
|
121
|
+
string path = 1;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
message PoolClearOut {
|
|
125
|
+
string result = 1;
|
|
126
|
+
string err = 2;
|
|
127
|
+
}
|
|
128
|
+
|
|
85
129
|
message ClearTileOut {
|
|
86
130
|
string result = 1;
|
|
87
131
|
string err = 2;
|
|
88
132
|
}
|
|
89
133
|
|
|
134
|
+
message ReadFileRequest {
|
|
135
|
+
string path = 1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
message ReadFileOut {
|
|
139
|
+
string data = 1;
|
|
140
|
+
string path = 2;
|
|
141
|
+
string err = 3;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
message WriteFileRequest {
|
|
145
|
+
string path = 1;
|
|
146
|
+
string data = 2;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
message WriteFileOut {
|
|
150
|
+
string status = 1;
|
|
151
|
+
string path = 2;
|
|
152
|
+
string err = 3;
|
|
153
|
+
}
|
|
154
|
+
|
|
90
155
|
message FileStatRequest {
|
|
91
156
|
string path = 1;
|
|
92
157
|
}
|
|
@@ -210,6 +275,7 @@ message ConvertVectorRequest {
|
|
|
210
275
|
string format = 2; // optional input format hint: zip|shp|kml|gml|geojson|json
|
|
211
276
|
int32 srs = 3; // target EPSG code, default 4326
|
|
212
277
|
string filename = 4; // optional original filename for extension/layer detection
|
|
278
|
+
string encoding = 5; // optional encoding, default UTF-8
|
|
213
279
|
}
|
|
214
280
|
|
|
215
281
|
message ConvertVectorOut {
|
|
@@ -221,13 +287,22 @@ message ConvertVectorOut {
|
|
|
221
287
|
message ImportDataRequest {
|
|
222
288
|
string path = 1; // path to .shp file or directory under vector root
|
|
223
289
|
bool recursive = 2; // process nested folders when path is directory
|
|
224
|
-
bool force = 3; // overwrite existing tables when true
|
|
290
|
+
optional bool force = 3; // overwrite existing tables when true; defaults to true when omitted
|
|
225
291
|
string mode = 4; // import mode: file|type (aliases: 1|2)
|
|
226
292
|
string db = 5; // PostgreSQL URL: postgresql://user:pass@host:port/dbname
|
|
227
293
|
string schema = 6; // target schema override
|
|
228
294
|
string type = 7; // optional geometry type override, e.g. LineString
|
|
229
295
|
}
|
|
230
296
|
|
|
297
|
+
message DumpTableRequest {
|
|
298
|
+
string path = 1; // target directory under vector/root where SHP files will be written
|
|
299
|
+
optional bool overwrite = 2; // overwrite existing shapefiles when true
|
|
300
|
+
repeated string include = 3; // optional allow-list of table names to dump
|
|
301
|
+
optional bool explodeMulti = 4; // dump multipart geometries as separate single-part features
|
|
302
|
+
repeated string ignore = 5; // optional deny-list of table names to skip
|
|
303
|
+
int32 limit = 6; // optional max number of tables to dump after filtering
|
|
304
|
+
}
|
|
305
|
+
|
|
231
306
|
message UploadXMLRequest {
|
|
232
307
|
string name = 1;
|
|
233
308
|
string xml = 2;
|
|
@@ -253,6 +328,21 @@ message UploadRasterOut {
|
|
|
253
328
|
bool finished = 3;
|
|
254
329
|
}
|
|
255
330
|
|
|
331
|
+
message UploadChunkRequest {
|
|
332
|
+
string path = 1;
|
|
333
|
+
int64 part = 2;
|
|
334
|
+
int64 offset = 3;
|
|
335
|
+
bytes data = 4;
|
|
336
|
+
int64 filesize = 5;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
message UploadChunkOut {
|
|
340
|
+
string status = 1;
|
|
341
|
+
int64 uploaded = 2;
|
|
342
|
+
bool finished = 3;
|
|
343
|
+
string path = 4;
|
|
344
|
+
}
|
|
345
|
+
|
|
256
346
|
message GetRasterStatusRequest {
|
|
257
347
|
string path = 1;
|
|
258
348
|
bool md5 = 2;
|
|
@@ -266,6 +356,8 @@ message GetStatusOut {
|
|
|
266
356
|
|
|
267
357
|
message GetStatRequest {
|
|
268
358
|
string period = 1; // "5m", "1h", "1d"
|
|
359
|
+
string path = 2; // optional map_id or XML/map path filter
|
|
360
|
+
bool recursive = 3; // when path is a directory/map prefix, include nested maps
|
|
269
361
|
}
|
|
270
362
|
|
|
271
363
|
message GetStatOut {
|
|
@@ -25,6 +25,7 @@ import checkXML from './checkXML.js';
|
|
|
25
25
|
|
|
26
26
|
import getCartocss from './cartocss/get.cartocss.js';
|
|
27
27
|
import addCartocss from './cartocss/add.cartocss.js';
|
|
28
|
+
import createRoute from './services/create.route.js';
|
|
28
29
|
|
|
29
30
|
const mapInfoPointSchema = {
|
|
30
31
|
querystring: {
|
|
@@ -83,6 +84,8 @@ async function route(app) {
|
|
|
83
84
|
|
|
84
85
|
app.get('/dashboard', publicParams, getDashboard);
|
|
85
86
|
|
|
87
|
+
app.get('/routes', publicParams, createRoute);
|
|
88
|
+
|
|
86
89
|
app.get('/gis-css/:id', publicParams, getCartocss);
|
|
87
90
|
app.post('/gis-css/:id', { config: { policy: 'L0', allowSql: true, package: 'gis' } }, addCartocss);
|
|
88
91
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const ROUTE_URL = 'https://data.gki.com.ua/api/osrm-routes'
|
|
2
|
+
|
|
3
|
+
function parseCoordPair(raw) {
|
|
4
|
+
const [lngRaw, latRaw] = String(raw || '').split(',')
|
|
5
|
+
const lng = Number(lngRaw)
|
|
6
|
+
const lat = Number(latRaw)
|
|
7
|
+
|
|
8
|
+
if (
|
|
9
|
+
!Number.isFinite(lng)
|
|
10
|
+
|| !Number.isFinite(lat)
|
|
11
|
+
|| lng < -180
|
|
12
|
+
|| lng > 180
|
|
13
|
+
|| lat < -90
|
|
14
|
+
|| lat > 90
|
|
15
|
+
) {
|
|
16
|
+
throw new Error('invalid coordinate')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return [lng, lat]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function buildRoutePoints(from, to, via) {
|
|
23
|
+
const points = [parseCoordPair(from)]
|
|
24
|
+
if (via) {
|
|
25
|
+
for (const segment of String(via).split(';')) {
|
|
26
|
+
const trimmed = segment.trim()
|
|
27
|
+
if (trimmed) points.push(parseCoordPair(trimmed))
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
points.push(parseCoordPair(to))
|
|
31
|
+
return points
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function routeOptionsQuery(query) {
|
|
35
|
+
const params = new URLSearchParams()
|
|
36
|
+
for (const key of ['optimize', 'surface', 'weight', 'mode']) {
|
|
37
|
+
const value = typeof query?.[key] === 'string' ? query[key].trim() : ''
|
|
38
|
+
if (value) params.set(key, value)
|
|
39
|
+
}
|
|
40
|
+
const qs = params.toString()
|
|
41
|
+
return qs ? `&${qs}` : ''
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function buildSegmentsBody(points) {
|
|
45
|
+
const segments = []
|
|
46
|
+
for (let i = 0; i < points.length - 1; i += 1) {
|
|
47
|
+
segments.push([
|
|
48
|
+
[points[i][0], points[i][1]],
|
|
49
|
+
[points[i + 1][0], points[i + 1][1]],
|
|
50
|
+
])
|
|
51
|
+
}
|
|
52
|
+
return JSON.stringify(segments)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default async function createRouteHandler(req, reply) {
|
|
56
|
+
try {
|
|
57
|
+
const points = buildRoutePoints(req.query.from, req.query.to, req.query.via)
|
|
58
|
+
const url = `${ROUTE_URL}?steps=true&alternatives=true${routeOptionsQuery(req.query)}`
|
|
59
|
+
|
|
60
|
+
const response = await fetch(url, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
Accept: 'application/json',
|
|
65
|
+
},
|
|
66
|
+
body: buildSegmentsBody(points),
|
|
67
|
+
signal: AbortSignal.timeout(10_000),
|
|
68
|
+
}).catch(error => ({
|
|
69
|
+
ok: false,
|
|
70
|
+
status: 502,
|
|
71
|
+
headers: new Headers({ 'content-type': 'application/json' }),
|
|
72
|
+
text: async () => JSON.stringify({ error: String(error), code: 502 }),
|
|
73
|
+
}))
|
|
74
|
+
|
|
75
|
+
const contentType = response.headers?.get?.('content-type') || 'application/json'
|
|
76
|
+
const text = await response.text()
|
|
77
|
+
let payload = text
|
|
78
|
+
|
|
79
|
+
if (contentType.includes('application/json')) {
|
|
80
|
+
try {
|
|
81
|
+
payload = text ? JSON.parse(text) : null
|
|
82
|
+
} catch {
|
|
83
|
+
payload = text
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return reply
|
|
88
|
+
.status(response.status || 200)
|
|
89
|
+
.header('Content-Type', contentType)
|
|
90
|
+
.send(payload)
|
|
91
|
+
} catch (err) {
|
|
92
|
+
const message = err?.message || 'external route provider request failed'
|
|
93
|
+
const statusCode = /invalid coordinate/i.test(message) ? 400 : 502
|
|
94
|
+
return reply.status(statusCode).send({ error: message, code: statusCode })
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
|
|
3
|
-
import mapnik from "../../../plugins/mapnik/funcs/mapnik.js";
|
|
4
3
|
import { downloadFile } from "@opengis/fastify-table/utils.js";
|
|
4
|
+
import mapnik from "../../../plugins/mapnik/funcs/mapnik.js";
|
|
5
5
|
|
|
6
6
|
const { ConvertVector } = mapnik();
|
|
7
7
|
|
|
@@ -15,7 +15,7 @@ const ALLOWED_FORMAT = new Set(["zip", "shp", "kml", "gml", "geojson", "json"]);
|
|
|
15
15
|
* @body {number} [srid=4326] - цільовий EPSG код
|
|
16
16
|
*/
|
|
17
17
|
export default async function gisConvert({ body = {} }, reply) {
|
|
18
|
-
const { filepath, srid = 4326 } = body;
|
|
18
|
+
const { filepath, srid = 4326, encoding } = body;
|
|
19
19
|
|
|
20
20
|
if (!filepath || typeof filepath !== "string") {
|
|
21
21
|
return reply
|
|
@@ -42,6 +42,7 @@ export default async function gisConvert({ body = {} }, reply) {
|
|
|
42
42
|
data,
|
|
43
43
|
format: ext,
|
|
44
44
|
srs: +srid,
|
|
45
|
+
encoding,
|
|
45
46
|
filename: path.basename(filepath),
|
|
46
47
|
}).catch((err) => ({ err }));
|
|
47
48
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const DEFAULT_ENDPOINT = 'https://api.opentopodata.org/v1/srtm90m';
|
|
2
|
+
|
|
3
|
+
function parseLatLng(query) {
|
|
4
|
+
const lat = Number(query?.lat);
|
|
5
|
+
const lng = Number(query?.lng);
|
|
6
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
|
|
7
|
+
throw new Error('lat and lng are required');
|
|
8
|
+
}
|
|
9
|
+
if (Math.abs(lat) > 90 || Math.abs(lng) > 180) {
|
|
10
|
+
throw new Error('coordinates out of range');
|
|
11
|
+
}
|
|
12
|
+
return { lat, lng };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getEndpoint() {
|
|
16
|
+
const configured = process.env.ELEVATION_API_URL?.trim();
|
|
17
|
+
return configured || DEFAULT_ENDPOINT;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function fetchHeight(lat, lng) {
|
|
21
|
+
const base = getEndpoint();
|
|
22
|
+
const url = new URL(base);
|
|
23
|
+
url.searchParams.set('locations', `${lat},${lng}`);
|
|
24
|
+
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeout = setTimeout(() => controller.abort(), 12_000);
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`Elevation API returned ${response.status}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const body = await response.json();
|
|
35
|
+
const result = body?.results?.[0] || null;
|
|
36
|
+
if (!result || result.elevation == null) return null;
|
|
37
|
+
const height = Number(result.elevation);
|
|
38
|
+
return Number.isFinite(height) ? height : null;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
clearTimeout(timeout);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default async function pointHeight(ctx = {}) {
|
|
46
|
+
const query = ctx?.query ?? ctx ?? {};
|
|
47
|
+
const { lat, lng } = parseLatLng(query);
|
|
48
|
+
const height = await fetchHeight(lat, lng);
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
lat,
|
|
52
|
+
lng,
|
|
53
|
+
height,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -26,6 +26,28 @@ import addWidget from './widgets/add.widget.js';
|
|
|
26
26
|
import delWidget from './widgets/del.widget.js';
|
|
27
27
|
|
|
28
28
|
import gisConvert from './controllers/gis.convert.js';
|
|
29
|
+
import pointHeight from './controllers/pointHeight.js';
|
|
30
|
+
|
|
31
|
+
const gisConvertSchema = {
|
|
32
|
+
body: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
encoding: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
// python is case insensitive and ignores "-"
|
|
38
|
+
enum: ['LATIN-1', 'CP1251', 'UTF-8'],
|
|
39
|
+
},
|
|
40
|
+
srid: {
|
|
41
|
+
type: 'number',
|
|
42
|
+
minimum: 1,
|
|
43
|
+
},
|
|
44
|
+
filepath: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
required: ['filepath'],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
29
51
|
|
|
30
52
|
const schemaInfo = {
|
|
31
53
|
type: 'object',
|
|
@@ -74,6 +96,27 @@ const mapFormatSchema = {
|
|
|
74
96
|
},
|
|
75
97
|
};
|
|
76
98
|
|
|
99
|
+
const pointHeightSchema = {
|
|
100
|
+
querystring: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
required: ['lat', 'lng'],
|
|
103
|
+
properties: {
|
|
104
|
+
lat: { type: 'number', minimum: -90, maximum: 90 },
|
|
105
|
+
lng: { type: 'number', minimum: -180, maximum: 180 },
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
response: {
|
|
109
|
+
200: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
lat: { type: 'number' },
|
|
113
|
+
lng: { type: 'number' },
|
|
114
|
+
height: { anyOf: [{ type: 'number' }, { type: 'null' }] },
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
77
120
|
const publicParams = {
|
|
78
121
|
config: { policy: 'L0' },
|
|
79
122
|
schema: schemaInfo,
|
|
@@ -184,6 +227,14 @@ export default async function route(app) {
|
|
|
184
227
|
);
|
|
185
228
|
}
|
|
186
229
|
|
|
230
|
+
if (!app.hasRoute({ method: 'GET', url: '/api/coords/height' })) {
|
|
231
|
+
app.get(
|
|
232
|
+
'/coords/height',
|
|
233
|
+
{ ...publicParams, schema: pointHeightSchema },
|
|
234
|
+
pointHeight,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
187
238
|
if (!app.hasRoute({ method: 'POST', url: '/api/gis-parse-geo' })) {
|
|
188
239
|
app.post('/gis-parse-geo', publicParams, parseGeoFile);
|
|
189
240
|
}
|
|
@@ -191,7 +242,7 @@ export default async function route(app) {
|
|
|
191
242
|
if (!app.hasRoute({ method: 'POST', url: '/api/gis-convert' })) {
|
|
192
243
|
app.post(
|
|
193
244
|
'/gis-convert',
|
|
194
|
-
{ config: { policy: 'L0' }, schema:
|
|
245
|
+
{ config: { policy: 'L0' }, schema: gisConvertSchema, package: 'gis' },
|
|
195
246
|
gisConvert,
|
|
196
247
|
);
|
|
197
248
|
}
|
|
@@ -20,7 +20,7 @@ const mercator = new Sphericalmercator({ size: 256 });
|
|
|
20
20
|
import {
|
|
21
21
|
getColumnCLS, getTemplate, getMeta, getFilterSQL, getSelect,
|
|
22
22
|
} from '@opengis/fastify-table/utils.js';
|
|
23
|
-
import yaml from 'js-yaml';
|
|
23
|
+
import * as yaml from 'js-yaml';
|
|
24
24
|
import rootFolder from '../../plugins/mapnik/funcs/rootFolder.mjs';
|
|
25
25
|
|
|
26
26
|
const propsCache = {};
|