1
+ const sm = require ( '@mapbox/sphericalmercator' ) ;
2
+ const fs = require ( 'fs' ) ;
3
+ const { Pool} = require ( 'pg' ) ;
4
+ const fsPromises = fs . promises ;
5
+ const path = require ( 'path' ) ;
6
+ const merc = new sm ( {
7
+ size : 256
8
+ } )
9
+
10
+ const dbconfig = require ( './config/dbconfig.json' ) ;
11
+
12
+ const pool = new Pool ( dbconfig ) ;
13
+
14
+ pool . connect ( ) ;
15
+
16
+ // route query
17
+ const sql = ( params , query ) => {
18
+ let bounds = merc . bbox ( params . x , params . y , params . z , false , '900913' )
19
+
20
+ return `
21
+ SELECT
22
+ ST_AsMVT(q, '${ params . table } ', 4096, 'geom')
23
+
24
+ FROM (
25
+ SELECT
26
+ ${ query . columns ? `${ query . columns } ,` : '' }
27
+ ST_AsMVTGeom(
28
+ ST_Transform(${ query . geom_column } , 3857),
29
+ ST_MakeBox2D(ST_Point(${ bounds [ 0 ] } , ${ bounds [ 1 ] } ), ST_Point(${
30
+ bounds [ 2 ]
31
+ } , ${ bounds [ 3 ] } ))
32
+ ) geom
33
+
34
+ FROM (
35
+ SELECT
36
+ ${ query . columns ? `${ query . columns } ,` : '' }
37
+ ${ query . geom_column } ,
38
+ srid
39
+ FROM
40
+ ${ params . table } ,
41
+ (SELECT ST_SRID(${ query . geom_column } ) AS srid FROM ${
42
+ params . table
43
+ } LIMIT 1) a
44
+
45
+ WHERE
46
+ ST_transform(
47
+ ST_MakeEnvelope(${ bounds . join ( ) } , 3857),
48
+ srid
49
+ ) &&
50
+ ${ query . geom_column }
51
+
52
+ -- Optional Filter
53
+ ${ query . filter ? `AND ${ query . filter } ` : '' }
54
+ ) r
55
+
56
+ ) q
57
+ `
58
+ }
59
+
60
+
61
+ // TODO add flat-cache
62
+
63
+ module . exports = function ( app ) {
64
+ /**
65
+ * @swagger
66
+ *
67
+ * /data/{datasource}/mvt/{z}/{x}/{y}?columns={columns}:
68
+ * get:
69
+ * description: get mapbox vector tile (mvt)
70
+ * produces:
71
+ * - application/x-protobuf
72
+ * parameters:
73
+ * - name: datasource
74
+ * description: name of postgis datasource
75
+ * in: path
76
+ * required: true
77
+ * type: string
78
+ * - name: z
79
+ * description: zoom level of tile
80
+ * in: path
81
+ * required: true
82
+ * type: number
83
+ * - name: x
84
+ * description: x value (column number) of tile
85
+ * in: path
86
+ * required: true
87
+ * type: number
88
+ * - name: y
89
+ * description: y value (row number) of tile
90
+ * in: path
91
+ * required: true
92
+ * type: number
93
+ * - name: geom_column
94
+ * description: name of geometry column (default 'geom')
95
+ * in: query
96
+ * default: geom
97
+ * required: false
98
+ * - name: columns
99
+ * description: optional comma seperated list of attribute columns to be added to the mvt geometries
100
+ * in: query
101
+ * required: false
102
+ * type: string
103
+ * default: null
104
+ * allowEmptyValue: true
105
+ * responses:
106
+ * 200:
107
+ * description: vector tile
108
+ * 204:
109
+ * description: no data (empty tile)
110
+ * 422:
111
+ * description: invalid datasource or columnname
112
+ */
113
+ app . get ( '/data/:datasource/mvt/:z/:x/:y' , async ( req , res ) => {
114
+ if ( ! req . query . geom_column ) {
115
+ req . query . geom_column = 'geom' ;
116
+ }
117
+ req . params . table = req . params . datasource ;
118
+ const sqlString = sql ( req . params , req . query ) ;
119
+ console . log ( sqlString ) ;
120
+ try {
121
+ const result = await pool . query ( sqlString ) ;
122
+ const mvt = result . rows [ 0 ] . st_asmvt
123
+ if ( mvt . length === 0 ) {
124
+ res . status ( 204 )
125
+ }
126
+ res . header ( 'Content-Type' , 'application/x-protobuf' ) . send ( mvt ) ;
127
+ } catch ( err ) {
128
+ console . log ( err ) ;
129
+ switch ( err . code ) {
130
+ case '42P01' :
131
+ err . name = `table ${ req . params . table } does not exist` ;
132
+ break ;
133
+ case '42703' :
134
+ err . name = `column does not exist` ;
135
+ break ;
136
+ }
137
+ res . status ( 422 ) . json ( { error :err } )
138
+ }
139
+ } )
140
+ }
0 commit comments