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,7 @@
1
+ import { type Cache } from './index.js';
2
+ type fileCacheOptions = {
3
+ dir: string;
4
+ ttl: number;
5
+ };
6
+ declare const fileCache: (options: fileCacheOptions) => Cache;
7
+ export { fileCache };
@@ -0,0 +1,33 @@
1
+ import { Cache as FileSystemCache } from 'file-system-cache';
2
+ const fileCache = function (options) {
3
+ const cache = new FileSystemCache({
4
+ basePath: options.dir,
5
+ hash: 'sha1',
6
+ ttl: options.ttl,
7
+ });
8
+ return {
9
+ name: 'file',
10
+ set: async function (key, value) {
11
+ try {
12
+ await cache.set(key, value.toString('hex'));
13
+ }
14
+ catch (e) {
15
+ console.error(e);
16
+ }
17
+ },
18
+ get: async function (key) {
19
+ try {
20
+ const val = await cache.get(key, undefined);
21
+ if (val === undefined)
22
+ return undefined;
23
+ return Buffer.from(val, 'hex');
24
+ }
25
+ catch (e) {
26
+ console.error(e);
27
+ cache.remove(key);
28
+ return undefined;
29
+ }
30
+ },
31
+ };
32
+ };
33
+ export { fileCache };
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import { memoryCache } from './memory.js';
3
+ import { s3Cache } from './s3.js';
4
+ import { fileCache } from './file.js';
5
+ type Value = Buffer;
6
+ type Cache = {
7
+ name: string;
8
+ get: (key: string) => Promise<Value | undefined>;
9
+ set: (key: string, value: Value) => Promise<void>;
10
+ };
11
+ declare const noneCache: () => Cache;
12
+ export { noneCache, memoryCache, s3Cache, fileCache, type Value, type Cache };
@@ -0,0 +1,9 @@
1
+ import { memoryCache } from './memory.js';
2
+ import { s3Cache } from './s3.js';
3
+ import { fileCache } from './file.js';
4
+ const noneCache = () => ({
5
+ name: 'none',
6
+ get: async () => undefined,
7
+ set: async () => undefined,
8
+ });
9
+ export { noneCache, memoryCache, s3Cache, fileCache };
@@ -0,0 +1,7 @@
1
+ import { type Cache } from './index.js';
2
+ type MemoryCacheOptions = {
3
+ ttl: number;
4
+ maxItemCount: number;
5
+ };
6
+ declare const memoryCache: (options: MemoryCacheOptions) => Cache;
7
+ export { memoryCache };
@@ -0,0 +1,18 @@
1
+ import { LRUCache } from 'lru-cache';
2
+ const memoryCache = function (options) {
3
+ const MEMORY_CACHE = new LRUCache({
4
+ max: options.maxItemCount,
5
+ ttl: options.ttl * 1000,
6
+ });
7
+ return {
8
+ name: 'memory',
9
+ set: async function (key, value) {
10
+ MEMORY_CACHE.set(key, value);
11
+ },
12
+ get: async function (key) {
13
+ const item = MEMORY_CACHE.get(key);
14
+ return item;
15
+ },
16
+ };
17
+ };
18
+ export { memoryCache };
@@ -0,0 +1,8 @@
1
+ import { type Cache } from './index.js';
2
+ type S3CacheOptions = {
3
+ bucket: string;
4
+ region: string;
5
+ endpoint: string | null;
6
+ };
7
+ declare function s3Cache(options: S3CacheOptions): Cache;
8
+ export { s3Cache };
@@ -0,0 +1,65 @@
1
+ import { PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
2
+ import { getS3Client } from '../s3.js';
3
+ function s3Cache(options) {
4
+ const s3Client = getS3Client({
5
+ region: options.region,
6
+ endpoint: options.endpoint,
7
+ });
8
+ return {
9
+ name: 's3',
10
+ set: async function (key, value) {
11
+ try {
12
+ const cmd = new PutObjectCommand({
13
+ Bucket: options.bucket,
14
+ Key: escapeFileName(key),
15
+ Body: value,
16
+ });
17
+ await s3Client.send(cmd);
18
+ }
19
+ catch (e) {
20
+ console.log(`[error]: ${e}`);
21
+ }
22
+ },
23
+ get: async function (key) {
24
+ const cmd = new GetObjectCommand({
25
+ Bucket: options.bucket,
26
+ Key: escapeFileName(key),
27
+ });
28
+ try {
29
+ const obj = await s3Client.send(cmd);
30
+ if (obj.Body === undefined)
31
+ return undefined;
32
+ const buf = Buffer.from(await obj.Body.transformToByteArray());
33
+ return buf;
34
+ }
35
+ catch {
36
+ // miss or any error
37
+ return undefined;
38
+ }
39
+ },
40
+ };
41
+ }
42
+ function escapeFileName(url) {
43
+ return url
44
+ .replace(/\//g, '_') // replace slashes with underscores
45
+ .replace(/\?/g, '-') // replace question marks with dashes
46
+ .replace(/&/g, '-') // replace ampersands with dashes
47
+ .replace(/=/g, '-') // replace equals signs with dashes
48
+ .replace(/%/g, '-') // replace percent signs with dashes
49
+ .replace(/#/g, '-') // replace hash signs with dashes
50
+ .replace(/:/g, '-') // replace colons with dashes
51
+ .replace(/\+/g, '-') // replace plus signs with dashes
52
+ .replace(/ /g, '-') // replace spaces with dashes
53
+ .replace(/</g, '-') // replace less than signs with dashes
54
+ .replace(/>/g, '-') // replace greater than signs with dashes
55
+ .replace(/\*/g, '-') // replace asterisks with dashes
56
+ .replace(/\|/g, '-') // replace vertical bars with dashes
57
+ .replace(/"/g, '-') // replace double quotes with dashes
58
+ .replace(/'/g, '-') // replace single quotes with dashes
59
+ .replace(/\?/g, '-') // replace question marks with dashes
60
+ .replace(/\./g, '-') // replace dots with dashes
61
+ .replace(/,/g, '-') // replace commas with dashes
62
+ .replace(/;/g, '-') // replace semicolons with dashes
63
+ .replace(/\\/g, '-'); // replace backslashes with dashes
64
+ }
65
+ export { s3Cache };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function createProgram(): Command;
package/dist/cli.js ADDED
@@ -0,0 +1,101 @@
1
+ import process from 'node:process';
2
+ import { Command } from 'commander';
3
+ import { initServer } from './server/index.js';
4
+ import * as caches from './cache/index.js';
5
+ function parseCacheStrategy(method, options) {
6
+ // command-line option
7
+ if (method === 'memory')
8
+ return caches.memoryCache({
9
+ ttl: options.cacheTtl,
10
+ maxItemCount: options.memoryCacheMaxItemCount,
11
+ });
12
+ if (method === 'file')
13
+ return caches.fileCache({
14
+ dir: options.fileCacheDir,
15
+ ttl: options.cacheTtl,
16
+ });
17
+ if (method === 's3')
18
+ return caches.s3Cache({
19
+ bucket: options.s3CacheBucket,
20
+ region: options.s3Region,
21
+ endpoint: options.s3Endpoint,
22
+ });
23
+ // command-line is not specified -> try to read from env
24
+ const cacheEnv = process.env.CHIITILER_CACHE_METHOD;
25
+ if (cacheEnv === 'memory')
26
+ return caches.memoryCache({
27
+ ttl: Number(process.env.CHIITILER_CACHE_TTL_SEC ?? '3600'),
28
+ maxItemCount: Number(process.env.CHIITILER_MEMORYCACHE_MAXITEMCOUNT ?? '1000'),
29
+ });
30
+ if (cacheEnv === 'file')
31
+ return caches.fileCache({
32
+ dir: process.env.CHIITILER_FILECACHE_DIR ?? './.cache',
33
+ ttl: Number(process.env.CHIITILER_CACHE_TTL_SEC ?? '3600'),
34
+ });
35
+ if (cacheEnv === 's3')
36
+ return caches.s3Cache({
37
+ bucket: process.env.CHIITILER_S3CACHE_BUCKET ?? '',
38
+ region: process.env.CHIITILER_S3_REGION ?? 'us-east1',
39
+ endpoint: process.env.CHIITILER_S3_ENDPOINT ?? null,
40
+ });
41
+ // undefined or invalid
42
+ return caches.noneCache();
43
+ }
44
+ function parsePort(port) {
45
+ // command-line option
46
+ if (port !== undefined)
47
+ return Number(port);
48
+ // command-line is not specified -> try to read from env
49
+ const portEnv = process.env.CHIITILER_PORT;
50
+ if (portEnv !== undefined)
51
+ return Number(portEnv);
52
+ // undefined or invalid
53
+ return 3000;
54
+ }
55
+ function parseDebug(debug) {
56
+ // command-line option
57
+ if (debug)
58
+ return true;
59
+ // command-line is not specified or false -> try to read from env
60
+ const debugEnv = process.env.CHIITILER_DEBUG;
61
+ if (debugEnv !== undefined)
62
+ return debugEnv === 'true';
63
+ // undefined or invalid
64
+ return false;
65
+ }
66
+ export function createProgram() {
67
+ const program = new Command();
68
+ program
69
+ .command('tile-server')
70
+ .option('-c, --cache <type>', 'cache type', 'none')
71
+ .option('-ctl --cache-ttl', 'cache ttl', '3600')
72
+ .option('-mci --memory-cache-max-item-count', 'memory cache max item count', '1000')
73
+ .option('-fcd --file-cache-dir <dir>', 'file cache directory', './.cache')
74
+ .option('-s3r --s3-region <region-name>', 's3 bucket region for get/put', 'us-east1')
75
+ .option('-s3b --s3-cache-bucket <bucket-name>', 's3 cache bucket name', '')
76
+ .option('-s3e --s3-endpoint <url>', 's3 endpoint url', '')
77
+ .option('-p --port <port>', 'port number')
78
+ .option('-D --debug', 'debug mode')
79
+ .action((options) => {
80
+ const serverOptions = {
81
+ cache: parseCacheStrategy(options.cache, {
82
+ cacheTtl: Number(options.cacheTtl),
83
+ memoryCacheMaxItemCount: Number(options.memoryCacheMaxItemCount),
84
+ fileCacheDir: options.fileCacheDir,
85
+ s3CacheBucket: options.s3CacheBucket,
86
+ s3Region: options.s3Region,
87
+ s3Endpoint: options.s3Endpoint,
88
+ }),
89
+ port: parsePort(options.port),
90
+ debug: parseDebug(options.debug),
91
+ };
92
+ if (serverOptions.debug) {
93
+ console.log(`running server: http://localhost:${serverOptions.port}`);
94
+ console.log(`cache method: ${serverOptions.cache.name}`);
95
+ console.log(`debug page: http://localhost:${serverOptions.port}/debug`);
96
+ }
97
+ const { start } = initServer(serverOptions);
98
+ start();
99
+ });
100
+ return program;
101
+ }
@@ -0,0 +1 @@
1
+ export { initServer, type InitServerOptions } from './server/index.js';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { initServer } from './server/index.js';
package/dist/main.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/main.js ADDED
@@ -0,0 +1,22 @@
1
+ import cluster from 'node:cluster';
2
+ import { availableParallelism } from 'node:os';
3
+ import process from 'node:process';
4
+ import { createProgram } from './cli.js';
5
+ const program = createProgram();
6
+ const numProcesses = (() => {
7
+ if (process.env.CHIITILER_PROCESSES === undefined)
8
+ return 1;
9
+ const processesEnv = Number(process.env.CHIITILER_PROCESSES);
10
+ if (processesEnv === 0)
11
+ return availableParallelism();
12
+ else
13
+ return processesEnv;
14
+ })();
15
+ if (cluster.isPrimary && numProcesses !== 1) {
16
+ // Fork workers.
17
+ for (let i = 0; i < numProcesses; i++)
18
+ cluster.fork();
19
+ }
20
+ else {
21
+ program.parse(process.argv);
22
+ }
@@ -0,0 +1,25 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
3
+ import type { Cache } from '../cache/index.js';
4
+ type RenderTilePipelineOptions = {
5
+ stylejson: string | StyleSpecification;
6
+ z: number;
7
+ x: number;
8
+ y: number;
9
+ tileSize: number;
10
+ cache: Cache;
11
+ margin: number;
12
+ ext: SupportedFormat;
13
+ quality: number;
14
+ };
15
+ type SupportedFormat = 'png' | 'jpeg' | 'jpg' | 'webp';
16
+ declare function getRenderedTileBuffer({ stylejson, z, x, y, tileSize, cache, margin, ext, quality, }: RenderTilePipelineOptions): Promise<Buffer>;
17
+ declare function getRenderedBboxBuffer({ stylejson, bbox, size, cache, ext, quality, }: {
18
+ stylejson: string | StyleSpecification;
19
+ bbox: [number, number, number, number];
20
+ size: number;
21
+ cache: Cache;
22
+ ext: SupportedFormat;
23
+ quality: number;
24
+ }): Promise<Buffer>;
25
+ export { getRenderedTileBuffer, getRenderedBboxBuffer, type SupportedFormat };
@@ -0,0 +1,146 @@
1
+ import sharp from 'sharp';
2
+ // @ts-ignore
3
+ import SphericalMercator from '@mapbox/sphericalmercator';
4
+ const mercator = new SphericalMercator();
5
+ import { LRUCache } from 'lru-cache';
6
+ import { renderTile, render } from './rasterize.js';
7
+ import { getSource } from '../source/index.js';
8
+ const styleCache = new LRUCache({
9
+ max: 5,
10
+ });
11
+ async function loadStyle(stylejson, cache) {
12
+ let style;
13
+ if (typeof stylejson === 'string') {
14
+ // url
15
+ const cachedStyle = styleCache.get(stylejson);
16
+ if (cachedStyle !== undefined) {
17
+ // hit-cache
18
+ style = cachedStyle;
19
+ }
20
+ else {
21
+ const styleJsonBuf = await getSource(stylejson, cache);
22
+ if (styleJsonBuf === null) {
23
+ throw new Error('style not found');
24
+ }
25
+ style = JSON.parse(styleJsonBuf.toString());
26
+ styleCache.set(stylejson, style);
27
+ }
28
+ }
29
+ else {
30
+ // as stylejson object
31
+ style = stylejson;
32
+ }
33
+ return style;
34
+ }
35
+ async function getRenderedTileBuffer({ stylejson, z, x, y, tileSize, cache, margin, ext, quality, }) {
36
+ const style = await loadStyle(stylejson, cache);
37
+ let pixels;
38
+ pixels = await renderTile(style, z, x, y, {
39
+ tileSize,
40
+ cache,
41
+ margin,
42
+ });
43
+ // hack: tile-margin clip area
44
+ // maplibre-native won't render outer area of meractor
45
+ // so top-end and bottom-end clipped area is special
46
+ const isTopEnd = y === 0;
47
+ const isBottomEnd = y === 2 ** z - 1;
48
+ const topMargin = isTopEnd ? 0 : isBottomEnd ? margin : margin / 2;
49
+ let _sharp;
50
+ if (tileSize === 256 && z === 0) {
51
+ // hack: when tileSize=256, z=0
52
+ // pixlels will be 512x512 so we need to resize to 256x256
53
+ _sharp = sharp(pixels, {
54
+ raw: {
55
+ width: 512,
56
+ height: 512,
57
+ channels: 4,
58
+ },
59
+ }).resize(256, 256);
60
+ }
61
+ else if (margin === 0) {
62
+ _sharp = sharp(pixels, {
63
+ raw: {
64
+ width: tileSize,
65
+ height: tileSize,
66
+ channels: 4,
67
+ },
68
+ });
69
+ }
70
+ else {
71
+ _sharp = sharp(pixels, {
72
+ raw: {
73
+ width: tileSize + margin,
74
+ height: tileSize + margin,
75
+ channels: 4,
76
+ },
77
+ })
78
+ .extract({
79
+ left: margin / 2,
80
+ top: topMargin,
81
+ width: tileSize,
82
+ height: tileSize,
83
+ })
84
+ .resize(tileSize, tileSize);
85
+ }
86
+ let buf;
87
+ switch (ext) {
88
+ case 'png':
89
+ buf = await _sharp.png().toBuffer();
90
+ break;
91
+ case 'jpeg':
92
+ case 'jpg':
93
+ buf = await _sharp.jpeg({ quality }).toBuffer();
94
+ break;
95
+ case 'webp':
96
+ buf = await _sharp.webp({ quality, effort: 0 }).toBuffer();
97
+ break;
98
+ }
99
+ return buf;
100
+ }
101
+ const calcRenderingParams = (bbox, size) => {
102
+ // reference: https://github.com/maptiler/tileserver-gl/blob/cc4b8f7954069fd0e1db731ff07f5349f7b9c8cd/src/serve_rendered.js#L346
103
+ // very hacky and it might be wrong
104
+ let zoom = 25;
105
+ const minCorner = mercator.px([bbox[0], bbox[3]], zoom);
106
+ const maxCorner = mercator.px([bbox[2], bbox[1]], zoom);
107
+ const dx = maxCorner[0] - minCorner[0];
108
+ const dy = maxCorner[1] - minCorner[1];
109
+ zoom -= Math.max(Math.log(dx / size), Math.log(dy / size)) / Math.LN2;
110
+ zoom = Math.max(Math.log(size / 256) / Math.LN2, Math.min(25, zoom)) - 1;
111
+ const width = dx > dy ? size : Math.ceil((dx / dy) * size);
112
+ const height = dx > dy ? Math.ceil((dy / dx) * size) : size;
113
+ const mercCenter = [
114
+ (maxCorner[0] + minCorner[0]) / 2,
115
+ (maxCorner[1] + minCorner[1]) / 2,
116
+ ];
117
+ const center = mercator.ll(mercCenter, 25); // latlon
118
+ return { zoom, width, height, center };
119
+ };
120
+ async function getRenderedBboxBuffer({ stylejson, bbox, size, cache, ext, quality, }) {
121
+ const style = await loadStyle(stylejson, cache);
122
+ const { zoom, width, height, center } = calcRenderingParams(bbox, size);
123
+ const pixels = await render(style, {
124
+ zoom,
125
+ width,
126
+ height,
127
+ center,
128
+ }, cache, 'static');
129
+ let _sharp = sharp(pixels, {
130
+ raw: {
131
+ width,
132
+ height,
133
+ channels: 4,
134
+ },
135
+ });
136
+ switch (ext) {
137
+ case 'png':
138
+ return await _sharp.png().toBuffer();
139
+ case 'jpeg':
140
+ case 'jpg':
141
+ return await _sharp.jpeg({ quality }).toBuffer();
142
+ case 'webp':
143
+ return await _sharp.webp({ quality, effort: 0 }).toBuffer();
144
+ }
145
+ }
146
+ export { getRenderedTileBuffer, getRenderedBboxBuffer };
@@ -0,0 +1,6 @@
1
+ import mbgl from '@maplibre/maplibre-gl-native';
2
+ import { Pool } from 'lightning-pool';
3
+ import type { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
4
+ import type { Cache } from '../cache/index.js';
5
+ declare function getRenderPool(style: StyleSpecification, cache: Cache, mode: 'tile' | 'static'): Promise<Pool<mbgl.Map>>;
6
+ export { getRenderPool };
@@ -0,0 +1,75 @@
1
+ import * as path from 'path';
2
+ import mbgl from '@maplibre/maplibre-gl-native';
3
+ import { Pool } from 'lightning-pool';
4
+ import { LRUCache } from 'lru-cache';
5
+ import { getSource } from '../source/index.js';
6
+ const EMPTY_BUFFER = Buffer.alloc(0);
7
+ const TRANSPARENT_BUFFER = {
8
+ // 1x1 transparent images
9
+ png: Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4//8/AwAI/AL+p5qgoAAAAABJRU5ErkJggg==', 'base64'),
10
+ webp: Buffer.from('UklGRkAAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAIAAAAAAFZQOCAYAAAAMAEAnQEqAQABAAFAJiWkAANwAP789AAA', 'base64'),
11
+ jpeg: Buffer.from('/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAABAAEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigD//2Q=='),
12
+ };
13
+ function handleFileExt(uri) {
14
+ // extract extension only, take into account query string or hash
15
+ const basename = path.basename(uri).split(/[?#]/)[0];
16
+ const ext = basename.split('.').pop();
17
+ if (ext === undefined)
18
+ return null;
19
+ const l = ext.toLowerCase();
20
+ if (l === 'jpeg')
21
+ return 'jpg';
22
+ return l;
23
+ }
24
+ const mapPoolCache = new LRUCache({
25
+ max: 10,
26
+ dispose: (pool, key) => {
27
+ pool.close();
28
+ },
29
+ });
30
+ async function getRenderPool(style, cache, mode) {
31
+ const cacheKey = JSON.stringify(style);
32
+ const pool = mapPoolCache.get(cacheKey);
33
+ if (pool !== undefined)
34
+ return pool;
35
+ const newPool = new Pool({
36
+ create: () => {
37
+ const map = new mbgl.Map({
38
+ request: function (req, callback) {
39
+ const ext = handleFileExt(req.url);
40
+ getSource(req.url, cache)
41
+ .then((buf) => {
42
+ if (buf) {
43
+ callback(undefined, { data: buf });
44
+ }
45
+ else if (ext && TRANSPARENT_BUFFER[ext])
46
+ callback(undefined, {
47
+ data: TRANSPARENT_BUFFER[ext],
48
+ });
49
+ else
50
+ callback(undefined, { data: EMPTY_BUFFER });
51
+ })
52
+ .catch(() => {
53
+ if (ext && TRANSPARENT_BUFFER[ext])
54
+ callback(undefined, {
55
+ data: TRANSPARENT_BUFFER[ext],
56
+ });
57
+ else
58
+ callback(undefined, { data: EMPTY_BUFFER });
59
+ });
60
+ },
61
+ ratio: 1,
62
+ // @ts-ignore
63
+ mode,
64
+ });
65
+ map.load(style);
66
+ return map;
67
+ },
68
+ destroy: (map) => {
69
+ map.release();
70
+ },
71
+ });
72
+ mapPoolCache.set(cacheKey, newPool);
73
+ return newPool;
74
+ }
75
+ export { getRenderPool };
@@ -0,0 +1,10 @@
1
+ import type { RenderOptions } from '@maplibre/maplibre-gl-native';
2
+ import type { StyleSpecification } from '@maplibre/maplibre-gl-style-spec';
3
+ import type { Cache } from '../cache/index.js';
4
+ declare function render(style: StyleSpecification, renderOptions: RenderOptions, cache: Cache, mode: 'tile' | 'static'): Promise<Uint8Array>;
5
+ declare function renderTile(style: StyleSpecification, z: number, x: number, y: number, options: {
6
+ tileSize: number;
7
+ cache: Cache;
8
+ margin?: number;
9
+ }): Promise<Uint8Array>;
10
+ export { renderTile, render };
@@ -0,0 +1,66 @@
1
+ // @ts-ignore
2
+ import SphericalMercator from '@mapbox/sphericalmercator';
3
+ import { getRenderPool } from './pool.js';
4
+ function getTileCenter(z, x, y, tileSize = 256) {
5
+ const mercator = new SphericalMercator({
6
+ size: tileSize,
7
+ });
8
+ const px = tileSize / 2 + x * tileSize;
9
+ const py = tileSize / 2 + y * tileSize;
10
+ const tileCenter = mercator.ll([px, py], z);
11
+ return tileCenter;
12
+ }
13
+ async function render(style, renderOptions, cache, mode) {
14
+ const pool = await getRenderPool(style, cache, mode);
15
+ const worker = await pool.acquire();
16
+ const rendered = new Promise((resolve, reject) => {
17
+ worker.render(renderOptions, function (err, buffer) {
18
+ pool.release(worker);
19
+ if (err) {
20
+ reject(err);
21
+ return;
22
+ }
23
+ if (buffer === undefined) {
24
+ reject('buffer is undefined');
25
+ return;
26
+ }
27
+ resolve(buffer);
28
+ });
29
+ });
30
+ return rendered;
31
+ }
32
+ async function renderTile(style, z, x, y, options) {
33
+ /**
34
+ * zoom(renderingOptions): tileSize=256 -> z-1, 512 -> z, 1024 -> z+1...
35
+ * width, height(renderingOptions): equal to tileSize but:
36
+ * when zoom=0, entire globe is rendered in 512x512
37
+ * even when tilesize=256, entire globe is rendered in "512x512 at zoom=0"
38
+ * so we have to set 512 when tilesize=256 and zoom=0, and adjust ratio
39
+ */
40
+ const renderingParams = options.tileSize === 256 && z === 0
41
+ ? {
42
+ zoom: 0,
43
+ height: 512,
44
+ width: 512,
45
+ ratio: 0.5,
46
+ }
47
+ : {
48
+ // offset = 128 -> -1, 256 -> 0, 512 -> 1, 1024 -> 2...
49
+ zoom: z - 1 + Math.log2(options.tileSize / 256),
50
+ height: options.tileSize,
51
+ width: options.tileSize,
52
+ ratio: 1,
53
+ };
54
+ const tileMode = options.margin === 0 &&
55
+ (options.tileSize === 256 || options.tileSize === 512); // mode=tile supports only 256 and 512
56
+ const rendered = await render(style, {
57
+ zoom: renderingParams.zoom,
58
+ width: renderingParams.width + (options.margin ?? 0),
59
+ height: renderingParams.height + (options.margin ?? 0),
60
+ center: getTileCenter(z, x, y, options.tileSize),
61
+ bearing: 0,
62
+ pitch: 0,
63
+ }, options.cache, tileMode ? 'tile' : 'static');
64
+ return rendered;
65
+ }
66
+ export { renderTile, render };
package/dist/s3.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ declare const getS3Client: ({ region, endpoint, }: {
3
+ region: string;
4
+ endpoint: string | null;
5
+ }) => S3Client;
6
+ export { getS3Client };
package/dist/s3.js ADDED
@@ -0,0 +1,24 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ let s3Client; // singleton
3
+ const getS3Client = function ({ region, endpoint, }) {
4
+ if (s3Client !== undefined)
5
+ return s3Client;
6
+ let s3ClientConfig = {
7
+ region,
8
+ endpoint: endpoint ?? undefined,
9
+ };
10
+ if (process.env.NODE_ENV === 'development') {
11
+ s3ClientConfig = {
12
+ region,
13
+ credentials: {
14
+ accessKeyId: 'minioadmin',
15
+ secretAccessKey: 'minioadmin',
16
+ },
17
+ forcePathStyle: true,
18
+ endpoint: 'http://minio:9000',
19
+ };
20
+ }
21
+ s3Client = new S3Client(s3ClientConfig);
22
+ return s3Client;
23
+ };
24
+ export { getS3Client };