Skip to content

Commit 222d6c1

Browse files
author
Miko
committed
handle local geojson files in styles and rendered tiles
- use 'file://' as indicator for local files - add directory as default directory - serve local files at - add documentation for static file serving - add some minor fixes (icon directory, directory checking, decodeURL, extend error message)
1 parent cb757b9 commit 222d6c1

File tree

11 files changed

+68
-17
lines changed

11 files changed

+68
-17
lines changed

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
!package.json
66
!package-lock.json
77
!docker-entrypoint.sh
8+
**.gitignore

.github/workflows/ct.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
https://github.com/maptiler/tileserver-gl/releases/download/v1.3.0/test_data.zip
4747
4848
- name: Prepare test data 📦
49-
run: unzip -q test_data.zip -d test_data
49+
run: unzip -q test_data.zip
5050

5151
- name: Run tests 🧪
5252
run: xvfb-run --server-args="-screen 0 1024x768x24" npm test

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
docs/_build
22
node_modules
3-
test_data
3+
test_data.zip
44
data
55
light
66
plugins
77
config.json
88
*.mbtiles
9+
styles
10+
fonts

docs/config.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ Example:
1717
"icons": "icons",
1818
"styles": "styles",
1919
"mbtiles": "data",
20-
"pmtiles": "data"
20+
"pmtiles": "data",
21+
"files": "public/files"
2122
},
2223
"domains": [
2324
"localhost:8080",

docs/endpoints.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ Source data
100100

101101
* TileJSON at ``/data/{id}.json``
102102

103+
Static files
104+
===========
105+
* Static files are served at ``/files/{filename}``
106+
107+
* The source folder can be configured (``options.paths.files``), default is ``public/files``
108+
109+
* This feature can be used to serve ``geojson`` files for styles and rendered tiles.
110+
111+
* Keep in mind, that each rendered tile loads the whole geojson file, if performance matters a conversion to a tiled format (e.g. with https://github.com/felt/tippecanoe)may be a better approch.
112+
113+
* Use ``file://{filename}`` to have matching paths for both endoints
114+
103115
TileJSON arrays
104116
===============
105117
Array of all TileJSONs is at ``[/{tileSize}]/index.json`` (``[/{tileSize}]/rendered.json``; ``/data.json``)

public/files/.gitignore

Whitespace-only changes.

src/main.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ const startWithInputFile = async (inputFile) => {
109109
'../node_modules/tileserver-gl-styles/',
110110
);
111111

112+
const filesDir = path.resolve(__dirname, '../public/files');
113+
112114
const config = {
113115
options: {
114116
paths: {
@@ -117,6 +119,7 @@ const startWithInputFile = async (inputFile) => {
117119
styles: 'styles',
118120
mbtiles: inputFilePath,
119121
pmtiles: inputFilePath,
122+
files: filesDir,
120123
},
121124
},
122125
styles: {},

src/serve_rendered.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ import {
4242
} from './pmtiles_adapter.js';
4343
import { renderOverlay, renderWatermark, renderAttribution } from './render.js';
4444
import fsp from 'node:fs/promises';
45-
import { gunzipP } from './promises.js';
45+
import { existsP, gunzipP } from './promises.js';
4646

4747
const FLOAT_PATTERN = '[+-]?(?:\\d+|\\d+.?\\d+)';
4848
const PATH_PATTERN =
@@ -893,13 +893,15 @@ export const serve_rendered = {
893893
// console.log('Handling request:', req);
894894
if (protocol === 'sprites') {
895895
const dir = options.paths[protocol];
896-
const file = unescape(req.url).substring(protocol.length + 3);
896+
const file = decodeURI(req.url)
897+
.substring(protocol.length + 3)
898+
.replaceAll('%2c', ',');
897899
fs.readFile(path.join(dir, file), (err, data) => {
898900
callback(err, { data: data });
899901
});
900902
} else if (protocol === 'fonts') {
901903
const parts = req.url.split('/');
902-
const fontstack = unescape(parts[2]);
904+
const fontstack = decodeURI(parts[2]).replaceAll('%2c', ',');
903905
const range = parts[3].split('.')[0];
904906

905907
try {
@@ -1039,6 +1041,25 @@ export const serve_rendered = {
10391041
const format = extensionToFormat[extension] || '';
10401042
createEmptyResponse(format, '', callback);
10411043
}
1044+
} else if (protocol === 'file') {
1045+
const name = decodeURI(req.url).substring(protocol.length + 3);
1046+
const file = path.join(options.paths['files'], name);
1047+
if (await existsP(file)) {
1048+
const inputFileStats = await fsp.stat(file);
1049+
if (!inputFileStats.isFile() || inputFileStats.size === 0) {
1050+
throw Error(
1051+
`File is not valid: "${req.url}" - resolved to "${file}"`,
1052+
);
1053+
}
1054+
1055+
fs.readFile(file, (err, data) => {
1056+
callback(err, { data: data });
1057+
});
1058+
} else {
1059+
throw Error(
1060+
`File does not exist: "${req.url}" - resolved to "${file}"`,
1061+
);
1062+
}
10421063
}
10431064
},
10441065
});

src/serve_style.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export const serve_style = {
2626
for (const name of Object.keys(styleJSON_.sources)) {
2727
const source = styleJSON_.sources[name];
2828
source.url = fixUrl(req, source.url, item.publicUrl);
29+
if (typeof source.data == 'string') {
30+
source.data = fixUrl(req, source.data, item.publicUrl);
31+
}
2932
}
3033
// mapbox-gl-js viewer cannot handle sprite urls with query
3134
if (styleJSON_.sprite) {
@@ -89,7 +92,7 @@ export const serve_style = {
8992
try {
9093
styleFileData = fs.readFileSync(styleFile); // TODO: could be made async if this function was
9194
} catch (e) {
92-
console.log('Error reading style file');
95+
console.log(`Error reading style file "${params.style}"`);
9396
return false;
9497
}
9598

@@ -128,6 +131,16 @@ export const serve_style = {
128131
}
129132
source.url = `local://data/${identifier}.json`;
130133
}
134+
135+
let data = source.data;
136+
if (data && typeof data == 'string' && data.startsWith('file://')) {
137+
source.data =
138+
'local://files' +
139+
path.resolve(
140+
'/',
141+
data.replace('file://', '').replace(options.paths.files, ''),
142+
);
143+
}
131144
}
132145

133146
for (const obj of styleJSON.layers) {

src/server.js

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,24 +94,22 @@ function start(opts) {
9494
paths.sprites = path.resolve(paths.root, paths.sprites || '');
9595
paths.mbtiles = path.resolve(paths.root, paths.mbtiles || '');
9696
paths.pmtiles = path.resolve(paths.root, paths.pmtiles || '');
97-
paths.icons = path.resolve(paths.root, paths.icons || '');
97+
paths.icons = path.resolve(
98+
paths.root,
99+
paths.icons || 'public/resources/images',
100+
);
101+
paths.files = path.resolve(paths.root, paths.files || 'public/files');
98102

99103
const startupPromises = [];
100104

101-
const checkPath = (type) => {
105+
for (const type of Object.keys(paths)) {
102106
if (!fs.existsSync(paths[type])) {
103107
console.error(
104108
`The specified path for "${type}" does not exist (${paths[type]}).`,
105109
);
106110
process.exit(1);
107111
}
108-
};
109-
checkPath('styles');
110-
checkPath('fonts');
111-
checkPath('sprites');
112-
checkPath('mbtiles');
113-
checkPath('pmtiles');
114-
checkPath('icons');
112+
}
115113

116114
/**
117115
* Recursively get all files within a directory.
@@ -161,6 +159,7 @@ function start(opts) {
161159
}
162160

163161
app.use('/data/', serve_data.init(options, serving.data));
162+
app.use('/files/', express.static(paths.files));
164163
app.use('/styles/', serve_style.init(options, serving.styles));
165164
if (!isLight) {
166165
startupPromises.push(

0 commit comments

Comments
 (0)