@opengis/gis 0.1.72 → 0.1.74

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.
@@ -0,0 +1,56 @@
1
+ title: Паспорти прив'язки тимчасових споруд
2
+ table_name: ts.linking_passport
3
+ category:
4
+
5
+ style:
6
+ type: polygon
7
+ color: gray
8
+ colorAttr: lp_status
9
+ colorSet:
10
+ 1: '#28a745'
11
+ 2: '#dc3545'
12
+ 3: '#ffc107'
13
+ 4: 'purple'
14
+ width: 2
15
+ border: 1
16
+ nocache: 1
17
+ stroke: "#eee"
18
+
19
+ setting:
20
+ popup: [reg_num]
21
+ title: {
22
+ reg_num: Реєстраційний номер паспорту прив’язки,
23
+ name: Назва тимчасової споруди
24
+ }
25
+ card_type: list
26
+ card_list: [
27
+ reg_num,
28
+ application_id,
29
+ reg_date,
30
+ reg_date_to,
31
+ lp_status,
32
+ done_status,
33
+ name,
34
+ ts_class,
35
+ type_building,
36
+ address_id
37
+ ]
38
+ cls: { application_id: ts.application.table, lp_status: ts.temp_struct_status, done_status: ts.done_status, ts_class: ts.tech_structure, type_building: ts.type_build, address_id: address.address_id}
39
+
40
+ filter_list:
41
+ - name: application_id
42
+ title: Заява
43
+ data: ts.application_id
44
+ type: Autocomplete
45
+ inline: true
46
+ - name: lp_status
47
+ title: Статус паспорту прив’язки
48
+ data: ts.temp_struct_status
49
+ type: Check
50
+ inline: true
51
+ - name: reg_date
52
+ title: Дата видачі паспорту прив’язки
53
+ type: Date
54
+ - name: reg_date_to
55
+ title: Паспорт прив’язки дійсний до
56
+ type: Date
@@ -0,0 +1,51 @@
1
+ title: Тимчасова споруди
2
+ table_name: ts.temp_structure
3
+ category:
4
+
5
+ style:
6
+ type: polygon
7
+ color: purple
8
+ width: 2
9
+ border: 1
10
+ nocache: 1
11
+ stroke: "purple"
12
+
13
+ setting:
14
+ popup: [ name ]
15
+ title: {
16
+ linking_passport_id: Паспорт прив'язки,
17
+ name: Назва тимчасової споруди
18
+ }
19
+ card_type: list
20
+ card_list: [
21
+ name,
22
+ temp_structure_type,
23
+ linking_passport_id,
24
+ reg_date_to,
25
+ lp_status,
26
+ done_status,
27
+ name,
28
+ ts_class,
29
+ type_building,
30
+ address_id
31
+ ]
32
+ cls: { temp_structure_type: ts.temp_type, lp_status: ts.temp_struct_status, linking_passport_id: ts.linking_passport_id, temp_structure_status: ts.temp_status, ts_class: ts.tech_structure, type_building: ts.type_build, address_id: address.address_id }
33
+
34
+ filter_list:
35
+ - name: temp_structure_type
36
+ title: Вид тимчасової споруди
37
+ data: ts.temp_type
38
+ type: Autocomplete
39
+ inline: true
40
+ - name: name
41
+ title: Назва
42
+ type: Text
43
+ - name: linking_passport_id
44
+ title: Паспорт прив'язки
45
+ type: Autocomplete
46
+ data: ts.linking_passport_id
47
+ - name: temp_structure_status
48
+ title: Статус тимчасової споруди
49
+ type: Check
50
+ data: ts.temp_status
51
+ inline: true
@@ -0,0 +1,157 @@
1
+ {
2
+ "history": true,
3
+ "height": "calc(100vh - 65px)",
4
+ "source_path": "ts.temp_structure",
5
+ "widgets": [
6
+ {
7
+ "type": "info",
8
+ "position": "top-left",
9
+ "config": {
10
+ "title": "Тимчасові споруди",
11
+ "content": "<p style='font-size: 14px; color: #34495E;'>Інформація про тимчасові споруди встановлені на території м. Кам'янське</p>",
12
+ "goTo": [
13
+ {
14
+ "label": "Карта адресного реєстру",
15
+ "url": "/map/addr"
16
+ },
17
+ {
18
+ "label": "Карта БП/МУО",
19
+ "url": "/map/bp_myo"
20
+ },
21
+ {
22
+ "label": "Карта містобудівної документації",
23
+ "url": "/map/mbd"
24
+ },
25
+ {
26
+ "label": "Основна карта",
27
+ "url": "/map/main"
28
+ }
29
+ ]
30
+ }
31
+ },
32
+ {
33
+ "type": "attribute",
34
+ "position": "top-left",
35
+ "config": {
36
+ "layer": {
37
+ "id": "temp_structure",
38
+ "service": "vtile",
39
+ "service_key": "ts",
40
+ "map_key": "ts",
41
+ "visible": true,
42
+ "url": "/api/vtile/ts.temp_structure/ua/{z}/{x}/{y}.vmt",
43
+ "style": {
44
+ "type": "polygon",
45
+ "attrType": "icon-by-attribute",
46
+ "attribute": "temp_structure_status",
47
+ "default_icon": "pin6-l-fa-question+4767e6.png",
48
+ "icon_base_url": "https://data.softpro.ua/api-user/markerIcon/",
49
+ "icon_size": 1,
50
+ "iconZoom": 14,
51
+ "pointZoom": 14,
52
+ "icon_anchor": "bottom",
53
+ "rules": [
54
+ {
55
+ "value": "1",
56
+ "label": "Діючий",
57
+ "color": "#28a745",
58
+ "icon": "pin6-l-fa-check+28a745.png"
59
+ },
60
+ {
61
+ "value": "2",
62
+ "label": "Анульований",
63
+ "color": "#dc3545",
64
+ "icon": "pin6-l-fa-exclamation+dc3545.png"
65
+ },
66
+ {
67
+ "value": "3",
68
+ "label": "Призупинений",
69
+ "color": "#ffc107",
70
+ "icon": "pin6-l-ty-lock-closed-outline+ffc107.png"
71
+ },
72
+ {
73
+ "value": "4",
74
+ "label": "Завершився термін дії",
75
+ "color": "purple",
76
+ "icon": "pin6-l-fa-clock-o+800080.png"
77
+ }
78
+ ],
79
+ "popup": {
80
+ "mode": "html",
81
+ "title": "<strong>Тимчасова споруда</strong>",
82
+ "fields": [
83
+ {
84
+ "label": "Назва",
85
+ "value": "name"
86
+ },
87
+ {
88
+ "label": "Кадастровий номер ЗД",
89
+ "value": "cad_num"
90
+ },
91
+ {
92
+ "label": "Площа тимчасової споруди, м. кв.",
93
+ "value": "area"
94
+ }
95
+ ]
96
+ }
97
+ }
98
+ }
99
+ }
100
+ },
101
+ {
102
+ "type": "card",
103
+ "position": "top-right",
104
+ "config": {
105
+ "title": "Інформація про об'єкт"
106
+ }
107
+ },
108
+ {
109
+ "type": "basemaps",
110
+ "position": "bottom-left",
111
+ "title": "Базові карти",
112
+ "config": {
113
+ "default": "voyager",
114
+ "layers": [
115
+ {
116
+ "id": "voyager",
117
+ "title": "Базова карта (voyager)",
118
+ "url": "https://data.gki.com.ua/api-user/rtile/voyager/ua/{z}/{x}/{y}.png",
119
+ "service": "raster",
120
+ "map_key": "ts",
121
+ "service_key": "ts",
122
+ "attribution": "&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a>, &copy; <a href='https://carto.com/attributions'>CARTO</a>",
123
+ "image": "https://data.gki.com.ua/api-user/rtile/voyager/ua/12/2422/1400.png",
124
+ "owner": "OSM",
125
+ "basemap": true
126
+ },
127
+ {
128
+ "id": "esri",
129
+ "title": "ESRI Online Imagery(2022-2023)",
130
+ "url": "https://server.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}.png",
131
+ "service": "raster",
132
+ "image": "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/7/47/69.png"
133
+ }
134
+ ]
135
+ },
136
+ "visible": true
137
+ }
138
+ ],
139
+ "interactions": {
140
+ "hover": {
141
+ "enabled": true
142
+ },
143
+ "navigation": {
144
+ "enabled": true
145
+ },
146
+ "click": {
147
+ "enabled": true
148
+ }
149
+ },
150
+ "tools": [
151
+ "home",
152
+ "geolocation",
153
+ "length",
154
+ "area",
155
+ "print"
156
+ ]
157
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/gis",
3
- "version": "0.1.72",
3
+ "version": "0.1.74",
4
4
  "type": "module",
5
5
  "author": "Softpro",
6
6
  "main": "./dist/index.js",
@@ -15,6 +15,7 @@
15
15
  "LICENSE"
16
16
  ],
17
17
  "scripts": {
18
+ "patch": "npm version patch && git push && npm publish",
18
19
  "dev": "node server",
19
20
  "front": "vite dev",
20
21
  "build-lib": "vite build",
@@ -32,21 +33,20 @@
32
33
  "@mapbox/sphericalmercator": "1.2.0",
33
34
  "carto": "0.16.3"
34
35
  },
35
- "peerDependencies": {
36
- "@opengis/fastify-table": "^1.4.24"
37
- },
38
36
  "devDependencies": {
39
- "lucide-vue-next": "^0.514.0",
40
- "vue": "^3.5.13",
41
- "vue-router": "4.5.1",
42
- "vuedraggable": "^4.1.0",
43
37
  "@opengis/core": "^0.0.18",
44
- "@opengis/filter": "^0.1.7",
38
+ "@opengis/fastify-auth": "^1.1.22",
45
39
  "@opengis/fastify-table": "^1.4.77",
40
+ "@opengis/filter": "^0.1.7",
46
41
  "@vitejs/plugin-vue": "^5.2.3",
42
+ "axios": "^1.11.0",
47
43
  "eslint": "8.49.0",
48
44
  "eslint-config-airbnb": "19.0.4",
45
+ "lucide-vue-next": "^0.514.0",
49
46
  "sass-embedded": "1.86.3",
50
- "vite": "^6.3.5"
47
+ "vite": "^6.3.5",
48
+ "vue": "^3.5.13",
49
+ "vue-router": "4.5.1",
50
+ "vuedraggable": "^4.1.0"
51
51
  }
52
52
  }
@@ -4,7 +4,7 @@ import { createServer } from 'vite';
4
4
 
5
5
  import { config } from '@opengis/fastify-table/utils.js';
6
6
 
7
- const isProduction = process.env.NODE_ENV === 'production';
7
+ const isProduction = process.env.NODE_ENV === 'production' || config.production;
8
8
 
9
9
  console.log({ isProduction });
10
10
 
@@ -64,7 +64,7 @@ async function plugin(fastify, opts) {
64
64
  fastify.get('*', async (req, reply) => {
65
65
  const { user } = req.session?.passport || {};
66
66
  if (!user && config.pg && !config.auth?.disable) {
67
- return reply.redirect('/login');
67
+ // return reply.redirect('/login');
68
68
  }
69
69
  if (!isProduction) return null; // admin vite
70
70
  const stream = fs.createReadStream('admin/dist/index.html');
@@ -0,0 +1,38 @@
1
+ import { config, pgClients, yml2json } from '@opengis/fastify-table/utils.js';
2
+
3
+ export default async function layerList({
4
+ pg = pgClients.client, query = {}, user = {},
5
+ }, reply) {
6
+ const { sql } = query;
7
+
8
+ if (!pg.pk?.['gis.services']) {
9
+ return reply.status(404).send('services table not found');
10
+ }
11
+
12
+ const q = `
13
+ select service_id as id, name, category, style, bbox::box2d as extent, st_asgeojson(bbox)::json as geom, null as url, coalesce(service_type, 'vtile') as service from gis.services where is_active and ${!user.uid ? 'is_public' : '1=1'}
14
+ union all
15
+ select ogc_service_id as id, name, category, null as style, geom::box2d as extent, st_asgeojson(geom)::json as geom, url, 'ogc' as service from gis.ogc_service where enabled and ${!user.uid ? 'ispublic' : '1=1'}
16
+ `;
17
+
18
+ if (user.uid && sql) return q;
19
+
20
+ const rows = await pg.query(q).then(el => el.rows || []);
21
+
22
+ if (!rows.length) {
23
+ return reply.status(200).send([]);
24
+ }
25
+
26
+ // parse service style
27
+ rows.forEach(row => Object.assign(row, { style: row.style ? yml2json(row.style) : null }));
28
+ // assign service url
29
+ rows.filter(row => row.service !== 'ogc').forEach(row => Object.assign(row, { url: `${config.prefix || '/api'}/vtile/${row.id}/ua/{z}/{x}/{y}.vmt` }));
30
+ // parse extent string to number[]
31
+ rows.filter(row => row.extent).forEach(row => Object.assign(row, {
32
+ extent: row.extent.match(/BOX\(([^)]+)\)/)?.[1]
33
+ ?.replace?.(/ /g, ",")
34
+ ?.split?.(",")
35
+ }));
36
+
37
+ return reply.status(200).send(rows);
38
+ }
@@ -7,6 +7,7 @@ import mapCatalogAttribute from './controllers/mapCatalogAttribute.js';
7
7
  import rtile from './controllers/rtile.js';
8
8
  import vtile from './controllers/vtile.js';
9
9
  import vtile1 from './vtile1.js';
10
+ import layerList from './controllers/layerList.js';
10
11
 
11
12
  import mapFormat from './controllers/mapFormat.js';
12
13
  import maps from './controllers/maps.js';
@@ -50,10 +51,14 @@ export default async function route(app) {
50
51
  app.get('/layer-rtile/:id/:z/:y/:x', { config: { policy }, schema: {} }, rtile);
51
52
  app.get('/layer-vtile/:id/:z/:y/:x', { config: { policy }, schema: schemaInfo }, vtile);
52
53
 
53
- if (!app.hasRoute({ method: 'GET', url: '/api/vtile/:layer/:lang/:z/:y/:x', })) {
54
- console.log("\x1b[34m%s\x1b[0m",'add vtile from gis');
54
+ if (!app.hasRoute({ method: 'GET', url: '/api/vtile/:layer/:lang/:z/:y/:x', })) {
55
+ console.log("\x1b[34m%s\x1b[0m", 'add vtile from gis');
55
56
  app.get('/vtile/:layer/:lang/:z/:y/:x', { config: { policy }, schema: schemaInfo }, vtile1);
56
57
  }
58
+ if (!app.hasRoute({ method: 'GET', url: '/api/gis-layer-list', })) {
59
+ console.log("\x1b[34m%s\x1b[0m", 'add gis-layer-list from gis');
60
+ app.get('/gis-layer-list', { config: { policy }, }, layerList);
61
+ }
57
62
 
58
63
  app.get('/marker-icon/*', { config: { policy }, schema: {} }, markerIconApi);
59
64
  app.get('/map-format', { config: { policy }, schema: {} }, mapFormat);
@@ -6,23 +6,33 @@ import Sphericalmercator from '@mapbox/sphericalmercator';
6
6
  const mercator = new Sphericalmercator({ size: 256 });
7
7
  import { getTemplate, getMeta, getFilterSQL } from '@opengis/fastify-table/utils.js';
8
8
 
9
- export default async function vtile({ params = {}, query, pg }, reply) {
10
- const { y, z, layer } = params;
9
+ export default async function vtile({ params = {}, query, pg, user }, reply) {
10
+ const { y, z } = params;
11
11
  const x = params.x.split('.')[0] - 0;
12
12
 
13
13
  // layer
14
+ const [map, layer] = params.layer.includes(':') ? params.layer.split(':') : [null, params.layer];
14
15
  const data = await getTemplate('layer', layer);
15
- if (!data) return { status: 404, message: 'not found' }
16
+ // service
17
+ const data1 = await pg.query(`select * from gis.services where is_active and ${!user?.uid ? 'is_public' : '1=1'} and service_id=$1`, [layer]).then(el => el.rows?.[0]);
18
+ if (!data && !data1) return { status: 404, message: 'not found' }
19
+
20
+ // style
21
+ const mapData = map ? await getTemplate('map', map) : {};
22
+ const mapStyle = mapData?.widgets?.find?.(el => el.type === 'attribute')?.config?.layer?.style;
23
+
24
+ const geom = data1?.geometry_column || 'geom';
25
+ const table = data?.table_name || data1?.source_path;
26
+ const style = mapStyle || data?.style || data1?.style;
27
+ const filterList = data?.filter_list || data1?.filters;
28
+ const layerQuery = data?.query || data1?.query;
16
29
 
17
30
  // bbox
18
31
  const bbox = mercator.bbox(+y, +x, +z, false/* , '900913' */);
19
32
  const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
20
33
 
21
- // columns
22
- const geom = 'geom';
23
- const table = data.table_name;
24
34
  const filterData = query.filter ? await getFilterSQL({
25
- filter: query.filter, table, filterList: data.filter_list,
35
+ filter: query.filter, table, filterList,
26
36
  }) : {};
27
37
 
28
38
  // meta
@@ -36,8 +46,8 @@ export default async function vtile({ params = {}, query, pg }, reply) {
36
46
  return el.type?.[0] === '_' ? `${el.name}[1] as ${el.name}` : `${el.name}${['int4'].includes(el.type) ? '::text' : ''}`;
37
47
  });
38
48
 
39
- const geomCol = (data?.style?.type === 'point' ? `ST_Centroid(${geom})` : null)
40
- || (parseInt(z, 10) < parseInt(query.pointZoom || data?.style?.pointZoom || data?.style?.iconZoom || '0', 10) ? `ST_Centroid(${geom})` : geom);
49
+ const geomCol = (style?.type === 'point' ? `ST_Centroid(${geom})` : null)
50
+ || (parseInt(z, 10) < parseInt(query.pointZoom || style?.pointZoom || style?.iconZoom || '0', 10) ? `ST_Centroid(${geom})` : geom);
41
51
 
42
52
  const koef = {
43
53
  10: 0.1, 11: 0.05, 12: 0.005, 13: 0.001, 14: 0.001, 15: 0.0005,
@@ -51,7 +61,7 @@ export default async function vtile({ params = {}, query, pg }, reply) {
51
61
  SELECT floor(random() * 100000 + 1)::int + row_number() over() as row, point_count, ST_AsMVTGeom(st_transform(geom,3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) geom
52
62
  FROM (
53
63
  SELECT st_centroid(st_union(geom)) as geom, count(*) as point_count
54
- FROM ${table} where ${data?.query || 'true'} and ${filterData?.q || 'true'} and ${geom} && ${bbox2d} ) j
64
+ FROM ${table} where ${layerQuery || 'true'} and ${filterData?.q || 'true'} and ${geom} && ${bbox2d} ) j
55
65
  )q`: null
56
66
  ) || ((query.clusterZoom - z) > 0 ?
57
67
  `SELECT ST_AsMVT(q, '${layer}', 4096, 'geom','row') as tile
@@ -59,7 +69,7 @@ export default async function vtile({ params = {}, query, pg }, reply) {
59
69
  SELECT floor(random() * 100000 + 1)::int +row_number() over() as row, count(*) as point_count, ST_AsMVTGeom(st_transform(st_centroid(ST_Union(geom)),3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) geom
60
70
  FROM (
61
71
  SELECT geom, ST_ClusterDBSCAN(geom,${koef},1) OVER () AS cluster
62
- FROM ${table} where ${data?.query || 'true'} and ${filterData?.q || 'true'} and ${geom} && ${bbox2d} ) j
72
+ FROM ${table} where ${layerQuery || 'true'} and ${filterData?.q || 'true'} and ${geom} && ${bbox2d} ) j
63
73
  WHERE cluster IS NOT NULL
64
74
  GROUP BY cluster
65
75
  ORDER BY 1 DESC
@@ -76,7 +86,7 @@ export default async function vtile({ params = {}, query, pg }, reply) {
76
86
  ${pkey} as id,
77
87
  ST_AsMVTGeom(st_transform(${geomCol},3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) geom
78
88
 
79
- FROM (select * from ${table} where ${data.query || 'true'} and ${filterData?.q || 'true'} and ${geom} && ${bbox2d}
89
+ FROM (select * from ${table} where ${layerQuery || 'true'} and ${filterData?.q || 'true'} and ${geom} && ${bbox2d}
80
90
 
81
91
  and ${geom} is not null and st_srid(${geom}) >0
82
92
 
@@ -84,9 +94,9 @@ export default async function vtile({ params = {}, query, pg }, reply) {
84
94
 
85
95
  ${types[type] ? ` and ST_GeometryType(geom) = any ('{ ${types[type]} }') ` : ''}
86
96
 
87
- and ${data?.query || 'true'} and ${filterData?.q || 'true'} and ${data?.filter || '1=1'}
97
+ and ${layerQuery || 'true'} and ${filterData?.q || 'true'} and ${data?.filter || '1=1'}
88
98
 
89
- ${(data?.style?.type || data?.geometry_type) === 'polygon' ? `order by st_area(st_transform(${geom},3857)) desc` : ''}
99
+ ${(style?.type || data?.geometry_type) === 'polygon' ? `order by st_area(st_transform(${geom},3857)) desc` : ''}
90
100
 
91
101
  limit 3000)q
92
102
  ) q`);