Skip to content

Commit eb43cd3

Browse files
committed
initial commit
0 parents  commit eb43cd3

13 files changed

+1143
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
config/dbconfig.json
3+
public/files

config/dbconfig.example.json

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"host": "host.example.com",
3+
"user": "dbuser",
4+
"password": "dbpassword",
5+
"database": "dbname",
6+
"ssl": true,
7+
"port": 5432,
8+
"max": 20,
9+
"idleTimeoutMillis": 30000,
10+
"connectionTimeoutMillis": 2000
11+
}

config/pgserver.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"port": 8090
3+
}

login.js

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
function login(app) {
2+
3+
/**
4+
* @swagger
5+
*
6+
* /login:
7+
* post:
8+
* description: Login to the application
9+
* produces:
10+
* - application/json
11+
* parameters:
12+
* - name: username
13+
* description: Username to use for login.
14+
* in: formData
15+
* required: true
16+
* type: string
17+
* - name: password
18+
* description: User's password.
19+
* in: formData
20+
* required: true
21+
* type: string
22+
* responses:
23+
* 200:
24+
* description: login
25+
*/
26+
app.post('/login', (req, res) => {
27+
// Your implementation comes here ...
28+
});
29+
app.get('/login', (req, res) => {
30+
res.json({result:"ok"})
31+
});
32+
}
33+
module.exports = login

mvt.js

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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

Comments
 (0)