tileserver-gl-light 4.5.2 → 4.6.0

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/src/serve_data.js CHANGED
@@ -10,7 +10,12 @@ import MBTiles from '@mapbox/mbtiles';
10
10
  import Pbf from 'pbf';
11
11
  import { VectorTile } from '@mapbox/vector-tile';
12
12
 
13
- import { getTileUrls, fixTileJSONCenter } from './utils.js';
13
+ import { getTileUrls, isValidHttpUrl, fixTileJSONCenter } from './utils.js';
14
+ import {
15
+ PMtilesOpen,
16
+ GetPMtilesInfo,
17
+ GetPMtilesTile,
18
+ } from './pmtiles_adapter.js';
14
19
 
15
20
  export const serve_data = {
16
21
  init: (options, repo) => {
@@ -18,7 +23,7 @@ export const serve_data = {
18
23
 
19
24
  app.get(
20
25
  '/:id/:z(\\d+)/:x(\\d+)/:y(\\d+).:format([\\w.]+)',
21
- (req, res, next) => {
26
+ async (req, res, next) => {
22
27
  const item = repo[req.params.id];
23
28
  if (!item) {
24
29
  return res.sendStatus(404);
@@ -48,71 +53,118 @@ export const serve_data = {
48
53
  ) {
49
54
  return res.status(404).send('Out of bounds');
50
55
  }
51
- item.source.getTile(z, x, y, (err, data, headers) => {
52
- let isGzipped;
53
- if (err) {
54
- if (/does not exist/.test(err.message)) {
55
- return res.status(204).send();
56
- } else {
57
- return res
58
- .status(500)
59
- .header('Content-Type', 'text/plain')
60
- .send(err.message);
61
- }
56
+ if (item.source_type === 'pmtiles') {
57
+ let tileinfo = await GetPMtilesTile(item.source, z, x, y);
58
+ if (tileinfo == undefined || tileinfo.data == undefined) {
59
+ return res.status(404).send('Not found');
62
60
  } else {
63
- if (data == null) {
64
- return res.status(404).send('Not found');
61
+ let data = tileinfo.data;
62
+ let headers = tileinfo.header;
63
+ if (tileJSONFormat === 'pbf') {
64
+ if (options.dataDecoratorFunc) {
65
+ data = options.dataDecoratorFunc(id, 'data', data, z, x, y);
66
+ }
67
+ }
68
+ if (format === 'pbf') {
69
+ headers['Content-Type'] = 'application/x-protobuf';
70
+ } else if (format === 'geojson') {
71
+ headers['Content-Type'] = 'application/json';
72
+
73
+ if (isGzipped) {
74
+ data = zlib.unzipSync(data);
75
+ isGzipped = false;
76
+ }
77
+
78
+ const tile = new VectorTile(new Pbf(data));
79
+ const geojson = {
80
+ type: 'FeatureCollection',
81
+ features: [],
82
+ };
83
+ for (const layerName in tile.layers) {
84
+ const layer = tile.layers[layerName];
85
+ for (let i = 0; i < layer.length; i++) {
86
+ const feature = layer.feature(i);
87
+ const featureGeoJSON = feature.toGeoJSON(x, y, z);
88
+ featureGeoJSON.properties.layer = layerName;
89
+ geojson.features.push(featureGeoJSON);
90
+ }
91
+ }
92
+ data = JSON.stringify(geojson);
93
+ }
94
+ delete headers['ETag']; // do not trust the tile ETag -- regenerate
95
+ headers['Content-Encoding'] = 'gzip';
96
+ res.set(headers);
97
+
98
+ data = zlib.gzipSync(data);
99
+
100
+ return res.status(200).send(data);
101
+ }
102
+ } else if (item.source_type === 'mbtiles') {
103
+ item.source.getTile(z, x, y, (err, data, headers) => {
104
+ let isGzipped;
105
+ if (err) {
106
+ if (/does not exist/.test(err.message)) {
107
+ return res.status(204).send();
108
+ } else {
109
+ return res
110
+ .status(500)
111
+ .header('Content-Type', 'text/plain')
112
+ .send(err.message);
113
+ }
65
114
  } else {
66
- if (tileJSONFormat === 'pbf') {
67
- isGzipped =
68
- data.slice(0, 2).indexOf(Buffer.from([0x1f, 0x8b])) === 0;
69
- if (options.dataDecoratorFunc) {
115
+ if (data == null) {
116
+ return res.status(404).send('Not found');
117
+ } else {
118
+ if (tileJSONFormat === 'pbf') {
119
+ isGzipped =
120
+ data.slice(0, 2).indexOf(Buffer.from([0x1f, 0x8b])) === 0;
121
+ if (options.dataDecoratorFunc) {
122
+ if (isGzipped) {
123
+ data = zlib.unzipSync(data);
124
+ isGzipped = false;
125
+ }
126
+ data = options.dataDecoratorFunc(id, 'data', data, z, x, y);
127
+ }
128
+ }
129
+ if (format === 'pbf') {
130
+ headers['Content-Type'] = 'application/x-protobuf';
131
+ } else if (format === 'geojson') {
132
+ headers['Content-Type'] = 'application/json';
133
+
70
134
  if (isGzipped) {
71
135
  data = zlib.unzipSync(data);
72
136
  isGzipped = false;
73
137
  }
74
- data = options.dataDecoratorFunc(id, 'data', data, z, x, y);
75
- }
76
- }
77
- if (format === 'pbf') {
78
- headers['Content-Type'] = 'application/x-protobuf';
79
- } else if (format === 'geojson') {
80
- headers['Content-Type'] = 'application/json';
81
-
82
- if (isGzipped) {
83
- data = zlib.unzipSync(data);
84
- isGzipped = false;
85
- }
86
138
 
87
- const tile = new VectorTile(new Pbf(data));
88
- const geojson = {
89
- type: 'FeatureCollection',
90
- features: [],
91
- };
92
- for (const layerName in tile.layers) {
93
- const layer = tile.layers[layerName];
94
- for (let i = 0; i < layer.length; i++) {
95
- const feature = layer.feature(i);
96
- const featureGeoJSON = feature.toGeoJSON(x, y, z);
97
- featureGeoJSON.properties.layer = layerName;
98
- geojson.features.push(featureGeoJSON);
139
+ const tile = new VectorTile(new Pbf(data));
140
+ const geojson = {
141
+ type: 'FeatureCollection',
142
+ features: [],
143
+ };
144
+ for (const layerName in tile.layers) {
145
+ const layer = tile.layers[layerName];
146
+ for (let i = 0; i < layer.length; i++) {
147
+ const feature = layer.feature(i);
148
+ const featureGeoJSON = feature.toGeoJSON(x, y, z);
149
+ featureGeoJSON.properties.layer = layerName;
150
+ geojson.features.push(featureGeoJSON);
151
+ }
99
152
  }
153
+ data = JSON.stringify(geojson);
100
154
  }
101
- data = JSON.stringify(geojson);
102
- }
103
- delete headers['ETag']; // do not trust the tile ETag -- regenerate
104
- headers['Content-Encoding'] = 'gzip';
105
- res.set(headers);
155
+ delete headers['ETag']; // do not trust the tile ETag -- regenerate
156
+ headers['Content-Encoding'] = 'gzip';
157
+ res.set(headers);
106
158
 
107
- if (!isGzipped) {
108
- data = zlib.gzipSync(data);
109
- isGzipped = true;
110
- }
159
+ if (!isGzipped) {
160
+ data = zlib.gzipSync(data);
161
+ }
111
162
 
112
- return res.status(200).send(data);
163
+ return res.status(200).send(data);
164
+ }
113
165
  }
114
- }
115
- });
166
+ });
167
+ }
116
168
  },
117
169
  );
118
170
 
@@ -137,55 +189,103 @@ export const serve_data = {
137
189
 
138
190
  return app;
139
191
  },
140
- add: (options, repo, params, id, publicUrl) => {
141
- const mbtilesFile = path.resolve(options.paths.mbtiles, params.mbtiles);
192
+ add: async (options, repo, params, id, publicUrl) => {
193
+ let inputFile;
194
+ let inputType;
195
+ if (params.pmtiles) {
196
+ inputType = 'pmtiles';
197
+ if (isValidHttpUrl(params.pmtiles)) {
198
+ inputFile = params.pmtiles;
199
+ } else {
200
+ inputFile = path.resolve(options.paths.pmtiles, params.pmtiles);
201
+ }
202
+ } else if (params.mbtiles) {
203
+ inputType = 'mbtiles';
204
+ if (isValidHttpUrl(params.mbtiles)) {
205
+ console.log(
206
+ `ERROR: MBTiles does not support web based files. "${params.mbtiles}" is not a valid data file.`,
207
+ );
208
+ process.exit(1);
209
+ } else {
210
+ inputFile = path.resolve(options.paths.mbtiles, params.mbtiles);
211
+ }
212
+ }
213
+
142
214
  let tileJSON = {
143
215
  tiles: params.domains || options.domains,
144
216
  };
145
217
 
146
- const mbtilesFileStats = fs.statSync(mbtilesFile);
147
- if (!mbtilesFileStats.isFile() || mbtilesFileStats.size === 0) {
148
- throw Error(`Not valid MBTiles file: ${mbtilesFile}`);
218
+ if (!isValidHttpUrl(inputFile)) {
219
+ const inputFileStats = fs.statSync(inputFile);
220
+ if (!inputFileStats.isFile() || inputFileStats.size === 0) {
221
+ throw Error(`Not valid input file: "${inputFile}"`);
222
+ }
149
223
  }
224
+
150
225
  let source;
151
- const sourceInfoPromise = new Promise((resolve, reject) => {
152
- source = new MBTiles(mbtilesFile + '?mode=ro', (err) => {
153
- if (err) {
154
- reject(err);
155
- return;
156
- }
157
- source.getInfo((err, info) => {
226
+ let source_type;
227
+ if (inputType === 'pmtiles') {
228
+ source = PMtilesOpen(inputFile);
229
+ source_type = 'pmtiles';
230
+ const metadata = await GetPMtilesInfo(source);
231
+
232
+ tileJSON['name'] = id;
233
+ tileJSON['format'] = 'pbf';
234
+ Object.assign(tileJSON, metadata);
235
+
236
+ tileJSON['tilejson'] = '2.0.0';
237
+ delete tileJSON['filesize'];
238
+ delete tileJSON['mtime'];
239
+ delete tileJSON['scheme'];
240
+
241
+ Object.assign(tileJSON, params.tilejson || {});
242
+ fixTileJSONCenter(tileJSON);
243
+
244
+ if (options.dataDecoratorFunc) {
245
+ tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
246
+ }
247
+ } else if (inputType === 'mbtiles') {
248
+ source_type = 'mbtiles';
249
+ const sourceInfoPromise = new Promise((resolve, reject) => {
250
+ source = new MBTiles(inputFile + '?mode=ro', (err) => {
158
251
  if (err) {
159
252
  reject(err);
160
253
  return;
161
254
  }
162
- tileJSON['name'] = id;
163
- tileJSON['format'] = 'pbf';
255
+ source.getInfo((err, info) => {
256
+ if (err) {
257
+ reject(err);
258
+ return;
259
+ }
260
+ tileJSON['name'] = id;
261
+ tileJSON['format'] = 'pbf';
164
262
 
165
- Object.assign(tileJSON, info);
263
+ Object.assign(tileJSON, info);
166
264
 
167
- tileJSON['tilejson'] = '2.0.0';
168
- delete tileJSON['filesize'];
169
- delete tileJSON['mtime'];
170
- delete tileJSON['scheme'];
265
+ tileJSON['tilejson'] = '2.0.0';
266
+ delete tileJSON['filesize'];
267
+ delete tileJSON['mtime'];
268
+ delete tileJSON['scheme'];
171
269
 
172
- Object.assign(tileJSON, params.tilejson || {});
173
- fixTileJSONCenter(tileJSON);
270
+ Object.assign(tileJSON, params.tilejson || {});
271
+ fixTileJSONCenter(tileJSON);
174
272
 
175
- if (options.dataDecoratorFunc) {
176
- tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
177
- }
178
- resolve();
273
+ if (options.dataDecoratorFunc) {
274
+ tileJSON = options.dataDecoratorFunc(id, 'tilejson', tileJSON);
275
+ }
276
+ resolve();
277
+ });
179
278
  });
180
279
  });
181
- });
182
280
 
183
- return sourceInfoPromise.then(() => {
184
- repo[id] = {
185
- tileJSON,
186
- publicUrl,
187
- source,
188
- };
189
- });
281
+ await sourceInfoPromise;
282
+ }
283
+
284
+ repo[id] = {
285
+ tileJSON,
286
+ publicUrl,
287
+ source,
288
+ source_type,
289
+ };
190
290
  },
191
291
  };