chiitiler 1.14.1 → 1.15.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/README.md CHANGED
@@ -87,9 +87,6 @@ docker run -p 3000:3000 -d \
87
87
  -e CHIITILER_S3CACHE_BUCKET=bucketname \
88
88
  -e CHIITILER_S3_REGION=ap-northeast-1 \
89
89
  ghcr.io/kanahiro/chiitiler
90
-
91
- # you also can pass options
92
- docker run -p 8000:8000 ghcr.io/kanahiro/chiitiler tile-server -p 8000 -c s3 -s3b bucketname -s3r ap-northeast-1
93
90
  ```
94
91
 
95
92
  #### Environment Variables
@@ -102,7 +99,7 @@ you can pass server options via environment variables
102
99
  | CHIITILER_PROCESSES | 1 | num of chiitiler processes. 0 means all-CPUs |
103
100
  | CHIITILER_DEBUG | false | debug mode |
104
101
  | CHIITILER_STREAM_MODE | false | stream mode |
105
- | CHIITILER_CACHE_METHOD | none | cache method, `none`, `memory`, `file` or `s3` |
102
+ | CHIITILER_CACHE_METHOD | none | cache method, `none`, `memory`, `file`, `s3` or `gcs` |
106
103
  | CHIITILER_CACHE_TTL_SEC | 3600 | cache ttl, effect to `memory` and `file` |
107
104
  | CHIITILER_MEMORYCACHE_MAXITEMCOUNT | 1000 | max items for memorycache |
108
105
  | CHIITILER_FILECACHE_DIR | .cache | filecache directory |
@@ -110,54 +107,31 @@ you can pass server options via environment variables
110
107
  | CHIITILER_S3_REGION | us-east1 | s3 bucket region for caching/fetching |
111
108
  | CHIITILER_S3_ENDPOINT | | s3 endpoint for caching/fetching |
112
109
  | CHIITILER_S3_FORCE_PATH_STYLE | false | force path style for s3, needed for minio |
110
+ | CHIITILER_GCS_CACHE_BUCKE | | gcs cache bucket name |
111
+ | CHIITILER_GCS_CACHE_PREFIX | | gcs cache prefix |
112
+ | CHIITILER_GCS_PROJECT_ID | | gcs project id |
113
+ | CHIITILER_GCS_KEY_FILENAME | | gcs key filename |
114
+ | CHIITILER_GCS_API_ENDPOINT | | gcs api endpoint |
113
115
 
114
- ### CLI (deprecated)
115
-
116
- - Node.js v18 or v20
116
+ ### debug page
117
117
 
118
- ```sh
119
- npm install
120
- npm run build
121
- node dist/main.js tile-server
122
- # running server: http://localhost:3000
123
-
124
- # develop
125
- npm run dev
126
- # running server: http://localhost:3000
127
- # debug page: http://localhost:3000/debug
128
- ```
118
+ - in debug mode, you can access:
119
+ - debug page: <http://localhost:3000/debug>
120
+ - You can pass style.json url: <http://localhost:3000/debug?url=https://tile.openstreetmap.jp/styles/osm-bright/style.json>
121
+ - editor page: <http://localhost:3000/editor>
129
122
 
130
- #### options
123
+ ## deployment
131
124
 
132
- ```sh
133
- node dist/main.js tile-server -p 8000 -c file -ctl 60 -fcd cachedir -D
134
- # -p: port number
135
- # -c: cache method
136
- # -ctl: cache ttl
137
- # -fcd: cache directory
138
- # -D: debug mode
139
-
140
- node dist/main.js tile-server -c memory -ctl 60 -mci 1000
141
- # -mci: max cache items
142
-
143
- node dist/main.js tile-server -c s3 -s3b chiitiler -s3r ap-northeast-1
144
- # -s3b: S3 bucket name for cache
145
- # -s3r: S3 bucket region
146
- # caution: TTL is not supported in S3 cache, please utilize S3 lifecycle policy
147
- ```
125
+ ### AWS CDK
148
126
 
149
- ### debug page
150
-
151
- - in debug mode, you can access:
152
- - debug page: <http://localhost:3000/debug>
153
- - You can pass style.json url: <http://localhost:3000/debug?url=https://tile.openstreetmap.jp/styles/osm-bright/style.json>
154
- - editor page: <http://localhost:3000/editor>
127
+ - you can deploy chiitiler with AWS CDK, check [cdk](./cdk)
155
128
 
156
129
  ## supported protocols in style.json
157
130
 
158
131
  - `http://` or `https://` protocol are used in Style Specification
159
132
  - In addition, chiitiler supports following protocols:
160
133
  - `s3://` for S3 bucket
134
+ - `gs://` for Google Cloud Storage bucket
161
135
  - `file://` for file system
162
136
  - `mbtiles://` for MBTIles files
163
137
  - `pmtiles://` for PMTiles, remote or local or s3
@@ -200,6 +174,13 @@ node dist/main.js tile-server -c s3 -s3b chiitiler -s3r ap-northeast-1
200
174
  ],
201
175
  "maxzoom": 6
202
176
  },
177
+ "gcs": {
178
+ "type": "vector",
179
+ "tiles": [
180
+ "gs://tiles/{z}/{x}/{y}.pbf"
181
+ ],
182
+ "maxzoom": 6
183
+ },
203
184
  "cog": {
204
185
  "type": "raster",
205
186
  "tiles": [
@@ -249,6 +230,26 @@ node dist/main.js tile-server -c s3 -s3b chiitiler -s3r ap-northeast-1
249
230
  "circle-color": "green"
250
231
  }
251
232
  },
233
+ {
234
+ "id": "pmtiles-s3",
235
+ "source": "pmtiles-s3",
236
+ "source-layer": "P2921",
237
+ "type": "circle",
238
+ "paint": {
239
+ "circle-radius": 3,
240
+ "circle-color": "purple"
241
+ }
242
+ },
243
+ {
244
+ "id": "gcs",
245
+ "source": "gcs",
246
+ "source-layer": "P2921",
247
+ "type": "circle",
248
+ "paint": {
249
+ "circle-radius": 3,
250
+ "circle-color": "purple"
251
+ }
252
+ },
252
253
  {
253
254
  "id": "cog",
254
255
  "source": "cog",
@@ -290,6 +291,11 @@ const s3Cache = ChiitilerCache.s3Cache({
290
291
  });
291
292
  // credentials are loaded from environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
292
293
 
294
+ const gcsCache = ChiitilerCache.gcsCache({
295
+ bucket: 'chiitiler',
296
+ });
297
+ // credentials are loaded from environment variables: GOOGLE_APPLICATION_CREDENTIALS
298
+
293
299
  const tileBuf = await getRenderedTileBuffer({
294
300
  stylejson: 'https://example.com/style.json', // or StyleSpecification object
295
301
  z: 0,
@@ -316,4 +322,5 @@ const bboxBuf = await getRenderedBboxBuffer({
316
322
 
317
323
  ## development
318
324
 
319
- - run `docker compose up`
325
+ - To debug S3, run `docker compose up`
326
+ - To debug GCS, change `CHIITILER_CACHE_METHOD` to `gcs` in `docker-compose.yml`, and then run `docker compose up`
@@ -0,0 +1,10 @@
1
+ import { type Cache } from './index.js';
2
+ type GCSCacheOptions = {
3
+ bucket: string;
4
+ prefix?: string;
5
+ projectId?: string;
6
+ keyFilename?: string;
7
+ apiEndpoint?: string;
8
+ };
9
+ declare function gcsCache(options: GCSCacheOptions): Cache;
10
+ export { gcsCache };
@@ -0,0 +1,66 @@
1
+ import { getStorageClient } from '../gcs.js';
2
+ function gcsCache(options) {
3
+ if (options.prefix?.endsWith('/')) {
4
+ options.prefix = options.prefix.slice(0, -1);
5
+ }
6
+ const storageClient = getStorageClient({
7
+ projectId: options.projectId,
8
+ keyFilename: options.keyFilename,
9
+ apiEndpoint: options.apiEndpoint,
10
+ });
11
+ const bucket = storageClient.bucket(options.bucket);
12
+ function bucketFile(key) {
13
+ const name = escapeFileName(key);
14
+ const nameWithPrefix = options.prefix ? `${options.prefix}/${name}` : name;
15
+ return bucket.file(nameWithPrefix);
16
+ }
17
+ return {
18
+ name: 'gcs',
19
+ set: async function (key, value) {
20
+ const file = bucketFile(key);
21
+ try {
22
+ await file.save(value, {
23
+ resumable: false,
24
+ });
25
+ }
26
+ catch (e) {
27
+ console.log(`[error]: ${e}`);
28
+ }
29
+ },
30
+ get: async function (key) {
31
+ const file = bucketFile(key);
32
+ try {
33
+ const [content] = await file.download();
34
+ return Buffer.from(content);
35
+ }
36
+ catch {
37
+ // miss or any error
38
+ return undefined;
39
+ }
40
+ },
41
+ };
42
+ }
43
+ function escapeFileName(url) {
44
+ return url
45
+ .replace(/\//g, '_') // replace slashes with underscores
46
+ .replace(/\?/g, '-') // replace question marks with dashes
47
+ .replace(/&/g, '-') // replace ampersands with dashes
48
+ .replace(/=/g, '-') // replace equals signs with dashes
49
+ .replace(/%/g, '-') // replace percent signs with dashes
50
+ .replace(/#/g, '-') // replace hash signs with dashes
51
+ .replace(/:/g, '-') // replace colons with dashes
52
+ .replace(/\+/g, '-') // replace plus signs with dashes
53
+ .replace(/ /g, '-') // replace spaces with dashes
54
+ .replace(/</g, '-') // replace less than signs with dashes
55
+ .replace(/>/g, '-') // replace greater than signs with dashes
56
+ .replace(/\*/g, '-') // replace asterisks with dashes
57
+ .replace(/\|/g, '-') // replace vertical bars with dashes
58
+ .replace(/"/g, '-') // replace double quotes with dashes
59
+ .replace(/'/g, '-') // replace single quotes with dashes
60
+ .replace(/\?/g, '-') // replace question marks with dashes
61
+ .replace(/\./g, '-') // replace dots with dashes
62
+ .replace(/,/g, '-') // replace commas with dashes
63
+ .replace(/;/g, '-') // replace semicolons with dashes
64
+ .replace(/\\/g, '-'); // replace backslashes with dashes
65
+ }
66
+ export { gcsCache };
@@ -1,5 +1,6 @@
1
1
  import { memoryCache } from './memory.js';
2
2
  import { s3Cache } from './s3.js';
3
+ import { gcsCache } from './gcs.js';
3
4
  import { fileCache } from './file.js';
4
5
  type Value = Buffer;
5
6
  type Cache = {
@@ -8,4 +9,4 @@ type Cache = {
8
9
  set: (key: string, value: Value) => Promise<void>;
9
10
  };
10
11
  declare const noneCache: () => Cache;
11
- export { noneCache, memoryCache, s3Cache, fileCache, type Value, type Cache };
12
+ export { noneCache, memoryCache, s3Cache, gcsCache, fileCache, type Value, type Cache };
@@ -1,9 +1,10 @@
1
1
  import { memoryCache } from './memory.js';
2
2
  import { s3Cache } from './s3.js';
3
+ import { gcsCache } from './gcs.js';
3
4
  import { fileCache } from './file.js';
4
5
  const noneCache = () => ({
5
6
  name: 'none',
6
7
  get: async () => undefined,
7
8
  set: async () => undefined,
8
9
  });
9
- export { noneCache, memoryCache, s3Cache, fileCache };
10
+ export { noneCache, memoryCache, s3Cache, gcsCache, fileCache };
package/dist/cli.js CHANGED
@@ -21,6 +21,14 @@ function parseCacheStrategy(method, options) {
21
21
  endpoint: options.s3Endpoint,
22
22
  forcePathStyle: options.s3ForcePathStyle,
23
23
  });
24
+ if (method === 'gcs')
25
+ return caches.gcsCache({
26
+ bucket: options.gcsCacheBucket,
27
+ projectId: options.gcsProjectId,
28
+ keyFilename: options.gcsKeyFilename,
29
+ prefix: options.gcsCachePrefix,
30
+ apiEndpoint: options.gcsApiEndpoint,
31
+ });
24
32
  // command-line is not specified -> try to read from env
25
33
  const cacheEnv = process.env.CHIITILER_CACHE_METHOD;
26
34
  if (cacheEnv === 'memory')
@@ -40,6 +48,14 @@ function parseCacheStrategy(method, options) {
40
48
  endpoint: process.env.CHIITILER_S3_ENDPOINT,
41
49
  forcePathStyle: process.env.CHIITILER_S3_FORCE_PATH_STYLE === 'true',
42
50
  });
51
+ if (cacheEnv === 'gcs')
52
+ return caches.gcsCache({
53
+ bucket: process.env.CHIITILER_GCS_CACHE_BUCKET ?? '',
54
+ prefix: process.env.CHIITILER_GCS_CACHE_PREFIX,
55
+ projectId: process.env.CHIITILER_GCS_PROJECT_ID,
56
+ keyFilename: process.env.CHIITILER_GCS_KEY_FILENAME,
57
+ apiEndpoint: process.env.CHIITILER_GCS_API_ENDPOINT,
58
+ });
43
59
  // undefined or invalid
44
60
  return caches.noneCache();
45
61
  }
@@ -88,6 +104,11 @@ export function createProgram() {
88
104
  .option('-s3b --s3-cache-bucket <bucket-name>', 's3 cache bucket name', '')
89
105
  .option('-s3e --s3-endpoint <url>', 's3 endpoint url', '')
90
106
  .option('-3p --s3-force-path-style', 's3 force path style', '')
107
+ .option('-gcsb --gcs-cache-bucket <bucket-name>', 'gcs cache bucket name', '')
108
+ .option('-gcsp --gcs-project-id <project-id>', 'gcs project id', '')
109
+ .option('-gcsk --gcs-key-filename <key-filename>', 'gcs key filename', '')
110
+ .option('-gcsp --gcs-cache-prefix <prefix>', 'gcs cache prefix', '')
111
+ .option('-gcse --gcs-api-endpoint <api-endpoint>', 'gcs api endpoint', '')
91
112
  .option('-p --port <port>', 'port number')
92
113
  .option('-r --stream', 'stream mode')
93
114
  .option('-D --debug', 'debug mode')
@@ -101,6 +122,11 @@ export function createProgram() {
101
122
  s3Region: options.s3Region,
102
123
  s3Endpoint: options.s3Endpoint,
103
124
  s3ForcePathStyle: options.s3ForcePathStyle === 'true',
125
+ gcsCacheBucket: options.gcsCacheBucket,
126
+ gcsCachePrefix: options.gcsCachePrefix,
127
+ gcsProjectId: options.gcsProjectId,
128
+ gcsKeyFilename: options.gcsKeyFilename,
129
+ gcsApiEndpoint: options.gcsApiEndpoint,
104
130
  }),
105
131
  port: parsePort(options.port),
106
132
  debug: parseDebug(options.debug),
package/dist/gcs.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { Storage } from '@google-cloud/storage';
2
+ declare const getStorageClient: ({ projectId, keyFilename, apiEndpoint }: {
3
+ projectId?: string;
4
+ keyFilename?: string;
5
+ apiEndpoint?: string;
6
+ }) => Storage;
7
+ export { getStorageClient };
package/dist/gcs.js ADDED
@@ -0,0 +1,14 @@
1
+ import { Storage } from '@google-cloud/storage';
2
+ let storageClient; // singleton
3
+ const getStorageClient = function ({ projectId, keyFilename, apiEndpoint }) {
4
+ if (storageClient !== undefined)
5
+ return storageClient;
6
+ const storageOptions = {
7
+ projectId,
8
+ keyFilename,
9
+ apiEndpoint
10
+ };
11
+ storageClient = new Storage(storageOptions);
12
+ return storageClient;
13
+ };
14
+ export { getStorageClient };
@@ -0,0 +1,2 @@
1
+ declare function getGCSSource(uri: string): Promise<Buffer<ArrayBufferLike> | null>;
2
+ export { getGCSSource };
@@ -0,0 +1,21 @@
1
+ import { getStorageClient } from '../gcs.js';
2
+ async function getGCSSource(uri) {
3
+ const storageClient = getStorageClient({
4
+ projectId: process.env.CHIITILER_GCS_PROJECT_ID,
5
+ keyFilename: process.env.CHIITILER_GCS_KEY_FILENAME,
6
+ apiEndpoint: process.env.CHIITILER_GCS_API_ENDPOINT,
7
+ });
8
+ const bucket = uri.replace('gs://', '').split('/')[0];
9
+ const path = uri.replace(`gs://${bucket}/`, '');
10
+ try {
11
+ const file = storageClient.bucket(bucket).file(path);
12
+ const [buffer] = await file.download();
13
+ return buffer;
14
+ }
15
+ catch (e) {
16
+ if (e.code !== 404)
17
+ console.log(e);
18
+ return null;
19
+ }
20
+ }
21
+ export { getGCSSource };
@@ -1,8 +1,9 @@
1
1
  import { getFilesystemSource } from './fs.js';
2
2
  import { getHttpSource } from './http.js';
3
- import { getPmtilesSoruce } from './pmtiles.js';
3
+ import { getPmtilesSource } from './pmtiles.js';
4
4
  import { getMbtilesSource } from './mbtiles.js';
5
5
  import { getS3Source } from './s3.js';
6
+ import { getGCSSource } from './gcs.js';
6
7
  import { getCogSource } from './cog.js';
7
8
  import { noneCache } from '../cache/index.js';
8
9
  /**
@@ -19,10 +20,12 @@ async function getSource(uri, cache = noneCache()) {
19
20
  data = await getFilesystemSource(uri);
20
21
  else if (uri.startsWith('s3://'))
21
22
  data = await getS3Source(uri);
23
+ else if (uri.startsWith('gs://'))
24
+ data = await getGCSSource(uri);
22
25
  else if (uri.startsWith('mbtiles://'))
23
26
  data = await getMbtilesSource(uri);
24
27
  else if (uri.startsWith('pmtiles://'))
25
- data = await getPmtilesSoruce(uri, cache);
28
+ data = await getPmtilesSource(uri, cache);
26
29
  else if (uri.startsWith('cog://'))
27
30
  data = await getCogSource(uri);
28
31
  else
@@ -4,5 +4,5 @@ import { type Cache } from '../cache/index.js';
4
4
  * uri = pmtiles://http://url/to/file.pmtiles/{z}/{x}/{y}
5
5
  * uri = pmtiles://s3://bucket/key/to/file.pmtiles/{z}/{x}/{y}
6
6
  */
7
- declare function getPmtilesSoruce(uri: string, cache?: Cache): Promise<Buffer | null>;
8
- export { getPmtilesSoruce };
7
+ declare function getPmtilesSource(uri: string, cache?: Cache): Promise<Buffer | null>;
8
+ export { getPmtilesSource };
@@ -72,7 +72,7 @@ class S3Source {
72
72
  * uri = pmtiles://http://url/to/file.pmtiles/{z}/{x}/{y}
73
73
  * uri = pmtiles://s3://bucket/key/to/file.pmtiles/{z}/{x}/{y}
74
74
  */
75
- async function getPmtilesSoruce(uri, cache = noneCache()) {
75
+ async function getPmtilesSource(uri, cache = noneCache()) {
76
76
  const pmtilesUri = uri
77
77
  .replace('pmtiles://', '')
78
78
  .replace(/\/\d+\/\d+\/\d+$/, '');
@@ -112,4 +112,4 @@ async function getPmtilesSoruce(uri, cache = noneCache()) {
112
112
  cache.set(uri, buf);
113
113
  return buf;
114
114
  }
115
- export { getPmtilesSoruce };
115
+ export { getPmtilesSource };
@@ -1,2 +1,2 @@
1
- declare function getS3Source(uri: string): Promise<Buffer<Uint8Array<ArrayBufferLike>> | null>;
1
+ declare function getS3Source(uri: string): Promise<Buffer<ArrayBuffer> | null>;
2
2
  export { getS3Source };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "chiitiler",
4
- "version": "1.14.1",
4
+ "version": "1.15.0",
5
5
  "description": "Tiny map rendering server for MapLibre Style Spec",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -26,15 +26,18 @@
26
26
  "@vitest/coverage-v8": "^1.6.0",
27
27
  "esbuild": "^0.25.0",
28
28
  "image-size": "^1.1.1",
29
- "maplibre-gl": "^3.3.1",
30
29
  "ts-node": "^10.9.1",
31
30
  "tsx": "^4.7.1",
32
31
  "typescript": "^5.2.2",
33
- "vitest": "^1.6.0",
32
+ "vitest": "^1.6.0"
33
+ },
34
+ "dependencies": {
34
35
  "@aws-sdk/client-s3": "^3.418.0",
36
+ "@google-cloud/storage": "^7.15.2",
35
37
  "@hono/node-server": "^1.12.0",
36
38
  "@mapbox/sphericalmercator": "^1.2.0",
37
39
  "@mapbox/tilebelt": "^1.0.2",
40
+ "@maplibre/maplibre-gl-native": "^5.4.0",
38
41
  "@maplibre/maplibre-gl-style-spec": "^20.2.0",
39
42
  "better-sqlite3": "^9.6.0",
40
43
  "commander": "^11.0.0",
@@ -43,10 +46,8 @@
43
46
  "hono": "^4.5.4",
44
47
  "lightning-pool": "^4.2.2",
45
48
  "lru-cache": "^11.0.0",
46
- "pmtiles": "^3.0.5"
47
- },
48
- "dependencies": {
49
- "@maplibre/maplibre-gl-native": "^5.4.0",
49
+ "maplibre-gl": "^3.3.1",
50
+ "pmtiles": "^3.0.5",
50
51
  "sharp": "^0.32.5"
51
52
  }
52
53
  }