chiitiler 1.14.2 → 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
@@ -99,7 +99,7 @@ you can pass server options via environment variables
99
99
  | CHIITILER_PROCESSES | 1 | num of chiitiler processes. 0 means all-CPUs |
100
100
  | CHIITILER_DEBUG | false | debug mode |
101
101
  | CHIITILER_STREAM_MODE | false | stream mode |
102
- | CHIITILER_CACHE_METHOD | none | cache method, `none`, `memory`, `file` or `s3` |
102
+ | CHIITILER_CACHE_METHOD | none | cache method, `none`, `memory`, `file`, `s3` or `gcs` |
103
103
  | CHIITILER_CACHE_TTL_SEC | 3600 | cache ttl, effect to `memory` and `file` |
104
104
  | CHIITILER_MEMORYCACHE_MAXITEMCOUNT | 1000 | max items for memorycache |
105
105
  | CHIITILER_FILECACHE_DIR | .cache | filecache directory |
@@ -107,6 +107,11 @@ you can pass server options via environment variables
107
107
  | CHIITILER_S3_REGION | us-east1 | s3 bucket region for caching/fetching |
108
108
  | CHIITILER_S3_ENDPOINT | | s3 endpoint for caching/fetching |
109
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 |
110
115
 
111
116
  ### debug page
112
117
 
@@ -115,11 +120,18 @@ you can pass server options via environment variables
115
120
  - You can pass style.json url: <http://localhost:3000/debug?url=https://tile.openstreetmap.jp/styles/osm-bright/style.json>
116
121
  - editor page: <http://localhost:3000/editor>
117
122
 
123
+ ## deployment
124
+
125
+ ### AWS CDK
126
+
127
+ - you can deploy chiitiler with AWS CDK, check [cdk](./cdk)
128
+
118
129
  ## supported protocols in style.json
119
130
 
120
131
  - `http://` or `https://` protocol are used in Style Specification
121
132
  - In addition, chiitiler supports following protocols:
122
133
  - `s3://` for S3 bucket
134
+ - `gs://` for Google Cloud Storage bucket
123
135
  - `file://` for file system
124
136
  - `mbtiles://` for MBTIles files
125
137
  - `pmtiles://` for PMTiles, remote or local or s3
@@ -162,6 +174,13 @@ you can pass server options via environment variables
162
174
  ],
163
175
  "maxzoom": 6
164
176
  },
177
+ "gcs": {
178
+ "type": "vector",
179
+ "tiles": [
180
+ "gs://tiles/{z}/{x}/{y}.pbf"
181
+ ],
182
+ "maxzoom": 6
183
+ },
165
184
  "cog": {
166
185
  "type": "raster",
167
186
  "tiles": [
@@ -211,6 +230,26 @@ you can pass server options via environment variables
211
230
  "circle-color": "green"
212
231
  }
213
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
+ },
214
253
  {
215
254
  "id": "cog",
216
255
  "source": "cog",
@@ -252,6 +291,11 @@ const s3Cache = ChiitilerCache.s3Cache({
252
291
  });
253
292
  // credentials are loaded from environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY
254
293
 
294
+ const gcsCache = ChiitilerCache.gcsCache({
295
+ bucket: 'chiitiler',
296
+ });
297
+ // credentials are loaded from environment variables: GOOGLE_APPLICATION_CREDENTIALS
298
+
255
299
  const tileBuf = await getRenderedTileBuffer({
256
300
  stylejson: 'https://example.com/style.json', // or StyleSpecification object
257
301
  z: 0,
@@ -278,4 +322,5 @@ const bboxBuf = await getRenderedBboxBuffer({
278
322
 
279
323
  ## development
280
324
 
281
- - 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 };
@@ -3,6 +3,7 @@ import { getHttpSource } from './http.js';
3
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,6 +20,8 @@ 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://'))
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "chiitiler",
4
- "version": "1.14.2",
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",
@@ -33,6 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@aws-sdk/client-s3": "^3.418.0",
36
+ "@google-cloud/storage": "^7.15.2",
36
37
  "@hono/node-server": "^1.12.0",
37
38
  "@mapbox/sphericalmercator": "^1.2.0",
38
39
  "@mapbox/tilebelt": "^1.0.2",