chiitiler 1.12.2 → 1.12.3

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,4 @@
1
+ import { Context } from 'hono';
2
+ declare function getDebugPage(c: Context): Response;
3
+ declare function getEditorgPage(c: Context): Response;
4
+ export { getDebugPage, getEditorgPage };
@@ -0,0 +1,201 @@
1
+ function getDebugPage(c) {
2
+ //demo tile
3
+ const url = c.req.query('url') ?? 'https://demotiles.maplibre.org/style.json';
4
+ const margin = Number(c.req.query('margin') ?? 0);
5
+ const quality = Number(c.req.query('quality') ?? 100);
6
+ const tileSize = Number(c.req.query('tileSize') ?? 512);
7
+ // show tile in MapLibre GL JS
8
+ return c.html(`<!DOCTYPE html>
9
+ <html>
10
+ <head>
11
+ <meta charset="utf-8" />
12
+ <title>MapLibre GL JS</title>
13
+ <!-- maplibre gl js-->
14
+ <script src="https://unpkg.com/maplibre-gl@^4.0/dist/maplibre-gl.js"></script>
15
+ <link
16
+ rel="stylesheet"
17
+ href="https://unpkg.com/maplibre-gl@^4.0/dist/maplibre-gl.css"
18
+ />
19
+ <style>
20
+ body {
21
+ margin: 0;
22
+ padding: 0;
23
+ }
24
+ #map {
25
+ position: absolute;
26
+ top: 0;
27
+ bottom: 0;
28
+ width: 100%;
29
+ }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <div id="map" style="height: 100vh"></div>
34
+ <script>
35
+ // hostname
36
+ const tileUrl = window.location.origin + '/tiles/{z}/{x}/{y}.webp?url=${url}&quality=${quality}&margin=${margin}&tileSize=${tileSize}';
37
+
38
+ const map = new maplibregl.Map({
39
+ hash: true,
40
+ container: 'map', // container id
41
+ style: {
42
+ version: 8,
43
+ sources: {
44
+ chiitiler: {
45
+ type: 'raster',
46
+ tiles: [tileUrl],
47
+ tileSize: ${tileSize},
48
+ }
49
+ },
50
+ layers: [
51
+ {
52
+ id: 'chiitiler',
53
+ type: 'raster',
54
+ source: 'chiitiler',
55
+ minzoom: 0,
56
+ maxzoom: 22,
57
+ }
58
+ ],
59
+ },
60
+ center: [0, 0], // starting position [lng, lat]
61
+ zoom: 1, // starting zoom
62
+ });
63
+ </script>
64
+ </body>
65
+ </html>`);
66
+ }
67
+ function getEditorgPage(c) {
68
+ return c.html(`<!DOCTYPE html>
69
+ <html>
70
+ <head>
71
+ <meta charset="utf-8" />
72
+ <title>MapLibre GL JS</title>
73
+ <!-- maplibre gl js-->
74
+ <script src="https://unpkg.com/maplibre-gl@^4.0/dist/maplibre-gl.js"></script>
75
+ <link
76
+ rel="stylesheet"
77
+ href="https://unpkg.com/maplibre-gl@^4.0/dist/maplibre-gl.css"
78
+ />
79
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/10.0.3/jsoneditor.css" rel="stylesheet" type="text/css">
80
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/10.0.3/jsoneditor.min.js"></script>
81
+ <style>
82
+ body {
83
+ margin: 0;
84
+ padding: 0;
85
+ }
86
+ </style>
87
+ </head>
88
+ <body>
89
+ <div id="map" style="height: 50vh"></div>
90
+ <div id="jsoneditor" style="height: 50vh"></div>
91
+ <script>
92
+ const container = document.getElementById("jsoneditor")
93
+ const editor = new JSONEditor(container, {
94
+ mode: 'code',
95
+ onChange: function() {
96
+ reloadStyle()
97
+ localStorage.setItem('style', JSON.stringify(editor.get()))
98
+ }
99
+ })
100
+
101
+ const initialStyle = localStorage.getItem('style') ? JSON.parse(localStorage.getItem('style')) : {
102
+ version: 8,
103
+ sources: {
104
+ osm: {
105
+ type: 'raster',
106
+ tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
107
+ tileSize: 256,
108
+ },
109
+ point: {
110
+ type: 'geojson',
111
+ data: {
112
+ type: 'FeatureCollection',
113
+ features: [
114
+ {
115
+ type: 'Feature',
116
+ properties: {},
117
+ geometry: {
118
+ type: 'Point',
119
+ coordinates: [140, 40],
120
+ },
121
+ },
122
+ ],
123
+ },
124
+ },
125
+ },
126
+ layers: [
127
+ {
128
+ id: 'osm',
129
+ type: 'raster',
130
+ source: 'osm',
131
+ minzoom: 0,
132
+ maxzoom: 22,
133
+ },
134
+ {
135
+ id: 'geojson',
136
+ type: 'circle',
137
+ source: 'point',
138
+ paint: {
139
+ 'circle-radius': 10,
140
+ 'circle-color': '#f00',
141
+ },
142
+ },
143
+ ],
144
+ };
145
+
146
+ editor.set(initialStyle);
147
+
148
+ maplibregl.addProtocol('post', async (params, abortController) => {
149
+ const imageUrl = params.url.replace('post://', '');
150
+ const style = editor.get()
151
+ const png = await fetch(imageUrl, {
152
+ method: 'POST',
153
+ headers: {
154
+ 'Content-Type': 'application/json',
155
+ },
156
+ body: JSON.stringify({ style }),
157
+ }).then((res) => res.arrayBuffer());
158
+ return { data: png };
159
+ });
160
+
161
+ const style = {
162
+ version: 8,
163
+ sources: {
164
+ chiitiler: {
165
+ type: 'raster',
166
+ tiles: ['post://' + window.location.origin + '/tiles/{z}/{x}/{y}.png'],
167
+ }
168
+ },
169
+ layers: [
170
+ {
171
+ id: 'chiitiler',
172
+ type: 'raster',
173
+ source: 'chiitiler',
174
+ minzoom: 0,
175
+ maxzoom: 22,
176
+ }
177
+ ],
178
+ };
179
+
180
+ const map = new maplibregl.Map({
181
+ hash: true,
182
+ container: 'map', // container id
183
+ style,
184
+ center: [0, 0], // starting position [lng, lat]
185
+ zoom: 1, // starting zoom
186
+ });
187
+
188
+ // reload button
189
+ function reloadStyle() {
190
+ map.setStyle({
191
+ version: 8,
192
+ sources: {},
193
+ layers: [],
194
+ });
195
+ map.setStyle(style);
196
+ }
197
+ </script>
198
+ </body>
199
+ </html>`);
200
+ }
201
+ export { getDebugPage, getEditorgPage };
@@ -0,0 +1,15 @@
1
+ import { Hono } from 'hono/quick';
2
+ import { type Cache } from '../cache/index.js';
3
+ type InitServerOptions = {
4
+ cache: Cache;
5
+ port: number;
6
+ debug: boolean;
7
+ };
8
+ type InitializedServer = {
9
+ app: Hono;
10
+ tiles: Hono;
11
+ clip: Hono;
12
+ start: () => void;
13
+ };
14
+ declare function initServer(options: InitServerOptions): InitializedServer;
15
+ export { initServer, type InitServerOptions };
@@ -0,0 +1,187 @@
1
+ import { Hono } from 'hono/quick';
2
+ import { serve } from '@hono/node-server';
3
+ import { validateStyleMin, } from '@maplibre/maplibre-gl-style-spec';
4
+ import { getDebugPage, getEditorgPage } from './debug.js';
5
+ import { getRenderedTileBuffer, getRenderedBboxBuffer, } from '../render/index.js';
6
+ function isValidStylejson(stylejson) {
7
+ return validateStyleMin(stylejson).length === 0;
8
+ }
9
+ function isValidXyz(x, y, z) {
10
+ if (x < 0 || y < 0 || z < 0)
11
+ return false;
12
+ if (x >= 2 ** z || y >= 2 ** z)
13
+ return false;
14
+ return true;
15
+ }
16
+ function isSupportedFormat(ext) {
17
+ return ['png', 'jpeg', 'jpg', 'webp'].includes(ext);
18
+ }
19
+ function initServer(options) {
20
+ const tiles = new Hono()
21
+ .get('/:z/:x/:y_ext', async (c) => {
22
+ const url = c.req.query('url');
23
+ if (url === undefined)
24
+ return c.body('url is required', 400);
25
+ // path params
26
+ const z = Number(c.req.param('z'));
27
+ const x = Number(c.req.param('x'));
28
+ let [_y, ext] = c.req.param('y_ext').split('.');
29
+ const y = Number(_y);
30
+ if (!isValidXyz(x, y, z))
31
+ return c.body('invalid xyz', 400);
32
+ if (!isSupportedFormat(ext))
33
+ return c.body('invalid format', 400);
34
+ // query params
35
+ const tileSize = Number(c.req.query('tileSize') ?? 512);
36
+ const quality = Number(c.req.query('quality') ?? 100);
37
+ const margin = Number(c.req.query('margin') ?? 0);
38
+ let buf;
39
+ try {
40
+ buf = await getRenderedTileBuffer({
41
+ stylejson: url,
42
+ z,
43
+ x,
44
+ y,
45
+ tileSize,
46
+ cache: options.cache,
47
+ margin,
48
+ ext,
49
+ quality,
50
+ });
51
+ }
52
+ catch (e) {
53
+ console.error(`render error: ${e}`);
54
+ return c.body('failed to render tile', 400);
55
+ }
56
+ c.header('Content-Type', `image/${ext}`);
57
+ return c.body(buf);
58
+ })
59
+ .post('/:z/:x/:y_ext', async (c) => {
60
+ // body
61
+ const { style } = await c.req.json();
62
+ if (!isValidStylejson(style))
63
+ return c.body('invalid stylejson', 400);
64
+ // path params
65
+ const z = Number(c.req.param('z'));
66
+ const x = Number(c.req.param('x'));
67
+ let [_y, ext] = c.req.param('y_ext').split('.');
68
+ const y = Number(_y);
69
+ if (!isValidXyz(x, y, z))
70
+ return c.body('invalid xyz', 400);
71
+ if (!isSupportedFormat(ext))
72
+ return c.body('invalid format', 400);
73
+ // query params
74
+ const tileSize = Number(c.req.query('tileSize') ?? 512);
75
+ const quality = Number(c.req.query('quality') ?? 100);
76
+ const margin = Number(c.req.query('margin') ?? 0);
77
+ let buf;
78
+ try {
79
+ buf = await getRenderedTileBuffer({
80
+ stylejson: style,
81
+ z,
82
+ x,
83
+ y,
84
+ tileSize,
85
+ cache: options.cache,
86
+ margin,
87
+ ext,
88
+ quality,
89
+ });
90
+ }
91
+ catch (e) {
92
+ console.error(`render error: ${e}`);
93
+ return c.body('failed to render tile', 400);
94
+ }
95
+ c.header('Content-Type', `image/${ext}`);
96
+ return c.body(buf);
97
+ });
98
+ const clip = new Hono()
99
+ .get('/:filename_ext', async (c) => {
100
+ // path params
101
+ const [filename, ext] = c.req.param('filename_ext').split('.');
102
+ if (filename !== 'clip')
103
+ return c.body('not found', 404);
104
+ if (!isSupportedFormat(ext))
105
+ return c.body('invalid format', 400);
106
+ // query params
107
+ const bbox = c.req.query('bbox'); // ?bbox=minx,miny,maxx,maxy
108
+ if (bbox === undefined)
109
+ return c.body('bbox is required', 400);
110
+ const [minx, miny, maxx, maxy] = bbox.split(',').map(Number);
111
+ if (minx >= maxx || miny >= maxy)
112
+ return c.body('invalid bbox', 400);
113
+ const url = c.req.query('url');
114
+ if (url === undefined)
115
+ return c.body('url is required', 400);
116
+ const quality = Number(c.req.query('quality') ?? 100);
117
+ const size = Number(c.req.query('size') ?? 1024);
118
+ try {
119
+ const buf = await getRenderedBboxBuffer({
120
+ stylejson: url,
121
+ bbox: [minx, miny, maxx, maxy],
122
+ size,
123
+ cache: options.cache,
124
+ ext,
125
+ quality,
126
+ });
127
+ c.header('Content-Type', `image/${ext}`);
128
+ return c.body(buf);
129
+ }
130
+ catch (e) {
131
+ console.error(`render error: ${e}`);
132
+ return c.body('failed to render bbox', 400);
133
+ }
134
+ })
135
+ .post('/:filename_ext', async (c) => {
136
+ // body
137
+ const { style } = await c.req.json();
138
+ if (!isValidStylejson(style))
139
+ return c.body('invalid stylejson', 400);
140
+ // path params
141
+ const [filename, ext] = c.req.param('filename_ext').split('.');
142
+ if (filename !== 'clip')
143
+ return c.body('not found', 404);
144
+ if (!isSupportedFormat(ext))
145
+ return c.body('invalid format', 400);
146
+ // query params
147
+ const bbox = c.req.query('bbox'); // ?bbox=minx,miny,maxx,maxy
148
+ if (bbox === undefined)
149
+ return c.body('bbox is required', 400);
150
+ const [minx, miny, maxx, maxy] = bbox.split(',').map(Number);
151
+ if (minx >= maxx || miny >= maxy)
152
+ return c.body('invalid bbox', 400);
153
+ const quality = Number(c.req.query('quality') ?? 100);
154
+ const size = Number(c.req.query('size') ?? 1024);
155
+ try {
156
+ const buf = await getRenderedBboxBuffer({
157
+ stylejson: style,
158
+ bbox: [minx, miny, maxx, maxy],
159
+ size,
160
+ cache: options.cache,
161
+ ext,
162
+ quality,
163
+ });
164
+ c.header('Content-Type', `image/${ext}`);
165
+ return c.body(buf);
166
+ }
167
+ catch (e) {
168
+ console.error(`render error: ${e}`);
169
+ return c.body('failed to render bbox', 400);
170
+ }
171
+ });
172
+ const hono = new Hono();
173
+ if (options.debug) {
174
+ hono.get('/debug', getDebugPage);
175
+ hono.get('/editor', getEditorgPage);
176
+ }
177
+ hono.get('/health', (c) => c.text('OK'));
178
+ hono.route('/tiles', tiles);
179
+ hono.route('/', clip);
180
+ return {
181
+ app: hono,
182
+ tiles,
183
+ clip,
184
+ start: () => serve({ port: options.port, fetch: hono.fetch }),
185
+ };
186
+ }
187
+ export { initServer };
@@ -0,0 +1,3 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ declare function getFilesystemSource(uri: string): Promise<Buffer | null>;
3
+ export { getFilesystemSource };
@@ -0,0 +1,15 @@
1
+ import fs from 'fs';
2
+ function getFilesystemSource(uri) {
3
+ return new Promise((resolve, _) => {
4
+ fs.readFile(uri.replace('file://', ''), (err, data) => {
5
+ if (err) {
6
+ console.error(`[ERROR]: ${err}`);
7
+ resolve(null);
8
+ }
9
+ else {
10
+ resolve(data);
11
+ }
12
+ });
13
+ });
14
+ }
15
+ export { getFilesystemSource };
@@ -0,0 +1,5 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /// <reference lib="dom" />
3
+ import { type Cache } from '../cache/index.js';
4
+ declare function getHttpSource(uri: string, cache?: Cache): Promise<Buffer | null>;
5
+ export { getHttpSource };
@@ -0,0 +1,25 @@
1
+ /// <reference lib="dom" />
2
+ // for using native fetch in TypeScript
3
+ import { noneCache } from '../cache/index.js';
4
+ async function getHttpSource(uri, cache = noneCache()) {
5
+ // use cache only for http(s) sources
6
+ const val = await cache.get(uri);
7
+ if (val !== undefined)
8
+ return val; // hit
9
+ // miss
10
+ try {
11
+ const res = await fetch(uri);
12
+ if (!res.ok) {
13
+ console.log(`failed to fetch ${uri}`);
14
+ return null;
15
+ }
16
+ const buf = Buffer.from(await res.arrayBuffer());
17
+ cache.set(uri, buf);
18
+ return buf;
19
+ }
20
+ catch (e) {
21
+ console.error(`[ERROR] ${e}`);
22
+ return null;
23
+ }
24
+ }
25
+ export { getHttpSource };
@@ -0,0 +1,10 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { Cache } from '../cache/index.js';
3
+ /**
4
+ * retrieve sources from the uri
5
+ * @param uri
6
+ * @param cache {Cache} - Cache Strategy. Affect only for http(s) sources.
7
+ * @returns
8
+ */
9
+ declare function getSource(uri: string, cache?: Cache): Promise<Buffer | null>;
10
+ export { getSource };
@@ -0,0 +1,27 @@
1
+ import { getFilesystemSource } from './fs.js';
2
+ import { getHttpSource } from './http.js';
3
+ import { getPmtilesSoruce } from './pmtiles.js';
4
+ import { getMbtilesSource } from './mbtiles.js';
5
+ import { getS3Source } from './s3.js';
6
+ import { noneCache } from '../cache/index.js';
7
+ /**
8
+ * retrieve sources from the uri
9
+ * @param uri
10
+ * @param cache {Cache} - Cache Strategy. Affect only for http(s) sources.
11
+ * @returns
12
+ */
13
+ async function getSource(uri, cache = noneCache()) {
14
+ let data = null;
15
+ if (uri.startsWith('http://') || uri.startsWith('https://'))
16
+ data = await getHttpSource(uri, cache);
17
+ else if (uri.startsWith('file://'))
18
+ data = await getFilesystemSource(uri);
19
+ else if (uri.startsWith('s3://'))
20
+ data = await getS3Source(uri);
21
+ else if (uri.startsWith('mbtiles://'))
22
+ data = await getMbtilesSource(uri);
23
+ else if (uri.startsWith('pmtiles://'))
24
+ data = await getPmtilesSoruce(uri, cache);
25
+ return data;
26
+ }
27
+ export { getSource };
@@ -0,0 +1,6 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /**
3
+ * uri = mbtiles://path/to/file.mbtiles/{z}/{x}/{y}
4
+ */
5
+ declare function getMbtilesSource(uri: string): Promise<Buffer | null>;
6
+ export { getMbtilesSource };
@@ -0,0 +1,39 @@
1
+ import Database from 'better-sqlite3';
2
+ import { unzip } from 'zlib';
3
+ import { LRUCache } from 'lru-cache';
4
+ const mbtilesCache = new LRUCache({
5
+ max: 20,
6
+ });
7
+ /**
8
+ * uri = mbtiles://path/to/file.mbtiles/{z}/{x}/{y}
9
+ */
10
+ async function getMbtilesSource(uri) {
11
+ const mbtilesFilepath = uri
12
+ .replace('mbtiles://', '')
13
+ .replace(/\/\d+\/\d+\/\d+$/, '');
14
+ let statement = mbtilesCache.get(mbtilesFilepath);
15
+ if (statement === undefined) {
16
+ const db = new Database(mbtilesFilepath, { readonly: true });
17
+ statement = db.prepare('SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?');
18
+ mbtilesCache.set(mbtilesFilepath, statement);
19
+ }
20
+ const [z, x, y] = uri
21
+ .replace(`mbtiles://${mbtilesFilepath}/`, '')
22
+ .split('/');
23
+ const ty = Math.pow(2, Number(z)) - 1 - Number(y);
24
+ const row = statement.get(z, x, ty);
25
+ if (!row)
26
+ return null;
27
+ const unzipped = await new Promise((resolve, _) => {
28
+ unzip(row.tile_data, (err, buffer) => {
29
+ if (err) {
30
+ console.error(`[ERROR]: ${err}`);
31
+ resolve(null);
32
+ }
33
+ else
34
+ resolve(buffer);
35
+ });
36
+ });
37
+ return unzipped;
38
+ }
39
+ export { getMbtilesSource };
@@ -0,0 +1,9 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { type Cache } from '../cache/index.js';
3
+ /**
4
+ * uri = pmtiles://path/to/file.pmtiles/{z}/{x}/{y}
5
+ * uri = pmtiles://http://url/to/file.pmtiles/{z}/{x}/{y}
6
+ * uri = pmtiles://s3://bucket/key/to/file.pmtiles/{z}/{x}/{y}
7
+ */
8
+ declare function getPmtilesSoruce(uri: string, cache?: Cache): Promise<Buffer | null>;
9
+ export { getPmtilesSoruce };
@@ -0,0 +1,106 @@
1
+ import * as fs from 'fs';
2
+ import { PMTiles } from 'pmtiles';
3
+ import { GetObjectCommand } from '@aws-sdk/client-s3';
4
+ import { LRUCache } from 'lru-cache';
5
+ import { getS3Client } from '../s3.js';
6
+ import { noneCache } from '../cache/index.js';
7
+ const pmtilesCache = new LRUCache({
8
+ max: 50,
9
+ });
10
+ class FilesystemSource {
11
+ filepath;
12
+ fileHandle;
13
+ constructor(filepath) {
14
+ this.filepath = filepath;
15
+ this.fileHandle = fs.promises.open(filepath, 'r');
16
+ }
17
+ getKey() {
18
+ return this.filepath;
19
+ }
20
+ async getBytes(offset, length) {
21
+ const buf = Buffer.alloc(length);
22
+ await (await this.fileHandle).read(buf, 0, length, offset);
23
+ return { data: buf.buffer };
24
+ }
25
+ }
26
+ class S3Source {
27
+ bucket;
28
+ key;
29
+ s3Client;
30
+ constructor(bucket, key) {
31
+ this.bucket = bucket;
32
+ this.key = key;
33
+ this.s3Client = getS3Client({
34
+ region: process.env.CHIITILER_S3_REGION ?? 'us-east1',
35
+ endpoint: process.env.CHIITILER_S3_ENDPOINT ?? null,
36
+ });
37
+ }
38
+ getKey() {
39
+ return `s3://${this.bucket}/${this.key}`;
40
+ }
41
+ async getBytes(offset, length) {
42
+ const cmd = new GetObjectCommand({
43
+ Bucket: this.bucket,
44
+ Key: this.key,
45
+ Range: `bytes=${offset}-${offset + length - 1}`,
46
+ });
47
+ try {
48
+ const obj = await this.s3Client.send(cmd);
49
+ if (obj.Body === undefined)
50
+ return { data: Buffer.alloc(0).buffer };
51
+ const buf = Buffer.from(await obj.Body.transformToByteArray());
52
+ return { data: buf.buffer };
53
+ }
54
+ catch (e) {
55
+ if (e.name !== 'NoSuchKey')
56
+ console.log(e);
57
+ return { data: Buffer.alloc(0).buffer };
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * uri = pmtiles://path/to/file.pmtiles/{z}/{x}/{y}
63
+ * uri = pmtiles://http://url/to/file.pmtiles/{z}/{x}/{y}
64
+ * uri = pmtiles://s3://bucket/key/to/file.pmtiles/{z}/{x}/{y}
65
+ */
66
+ async function getPmtilesSoruce(uri, cache = noneCache()) {
67
+ const pmtilesUri = uri
68
+ .replace('pmtiles://', '')
69
+ .replace(/\/\d+\/\d+\/\d+$/, '');
70
+ let pmtiles = pmtilesCache.get(pmtilesUri);
71
+ const isHttpSource = pmtilesUri.startsWith('http://') || pmtilesUri.startsWith('https://');
72
+ if (isHttpSource) {
73
+ const val = await cache.get(uri);
74
+ if (val !== undefined)
75
+ return val; // hit
76
+ if (pmtiles === undefined) {
77
+ pmtiles = new PMTiles(pmtilesUri);
78
+ pmtilesCache.set(pmtilesUri, pmtiles);
79
+ }
80
+ }
81
+ else if (pmtilesUri.startsWith('s3://')) {
82
+ if (pmtiles === undefined) {
83
+ const bucket = pmtilesUri.replace('s3://', '').split('/')[0];
84
+ const key = pmtilesUri.replace(`s3://${bucket}/`, '');
85
+ const s3Source = new S3Source(bucket, key);
86
+ pmtiles = new PMTiles(s3Source);
87
+ pmtilesCache.set(pmtilesUri, pmtiles);
88
+ }
89
+ }
90
+ else {
91
+ if (pmtiles === undefined) {
92
+ const fileSource = new FilesystemSource(pmtilesUri);
93
+ pmtiles = new PMTiles(fileSource);
94
+ pmtilesCache.set(pmtilesUri, pmtiles);
95
+ }
96
+ }
97
+ const [z, x, y] = uri.replace(`pmtiles://${pmtilesUri}/`, '').split('/');
98
+ const tile = await pmtiles.getZxy(Number(z), Number(x), Number(y));
99
+ if (!tile)
100
+ return null;
101
+ const buf = Buffer.from(tile.data);
102
+ if (isHttpSource)
103
+ cache.set(uri, buf);
104
+ return buf;
105
+ }
106
+ export { getPmtilesSoruce };
@@ -0,0 +1,3 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ declare function getS3Source(uri: string): Promise<Buffer | null>;
3
+ export { getS3Source };