@versatiles/container 1.0.0 → 1.0.1

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
@@ -1,29 +1,5 @@
1
1
  A client library for [VersaTiles containers](https://github.com/versatiles-org/versatiles-spec).
2
2
 
3
- # Table of Content
4
-
5
- * [Install](#install)
6
- * [Usage Example](#usage-example)
7
- * [API](#api)
8
- * [Class: VersaTiles](#class_versatiles)
9
- * [constructor: new VersaTiles(source, options)](#constructor-new-versatilessource-options)
10
- * [async getHeader()](#async-getheader)
11
- * [async getMetadata()](#async-getmetadata)
12
- * [async getTileFormat()](#async-gettileformat)
13
- * [async getBlockIndex()](#async-getblockindex)
14
- * [async getTileIndex(block)](#async-gettileindexblock)
15
- * [async getTile(z, x, y)](#async-gettilez-x-y)
16
- * [async getTileUncompressed(z, x, y)](#async-gettileuncompressedz-x-y)
17
- * [Interface: Block](#interface_block)
18
- * [Interface: Header](#interface_header)
19
- * [Interface: Options](#interface_options)
20
- * [Interface: TileIndex](#interface_tileindex)
21
- * [Type: Compression](#type_compression)
22
- * [Type: Format](#type_format)
23
- * [Type: Reader](#type_reader)
24
- * [License](#license)
25
- * [Future work](#future-work)
26
-
27
3
  # Install
28
4
 
29
5
  `npm i versatiles`
@@ -42,199 +18,175 @@ fs.writeFileSync('tile.' + header.tile_format, tile);
42
18
 
43
19
  # API
44
20
 
45
- ## Class: `VersaTiles`<a id="class_versatiles"></a>
21
+ <!--- This chapter is generated automatically --->
22
+
23
+ <details>
24
+
25
+ <summary><h2>Class: <code>VersaTiles</code><a id="class_versatiles"></a></h2></summary>
46
26
 
47
27
  The `VersaTiles` class is a wrapper around a `.versatiles` container file. It provides methods\
48
- to access tile data, metadata, and other properties within the container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L27">\[src]</a></sup>
28
+ to access tile data, metadata, and other properties within the container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/index.ts#L27">\[src]</a></sup>
29
+
30
+ <details>
49
31
 
50
- ### constructor: `new VersaTiles(source, options)`
32
+ <summary><h3>Constructor: <code>new VersaTiles&#40;source, options&#41;</code></h3></summary>
51
33
 
52
- Constructs a new instance of the VersaTiles class. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L50">\[src]</a></sup>
34
+ Constructs a new instance of the VersaTiles class. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/index.ts#L50">\[src]</a></sup>
53
35
 
54
36
  **Parameters:**
55
37
 
56
- * `source: string | `[`Reader`](#type_reader)\
38
+ * <code>source: string | [Reader](#type_reader)</code>\
57
39
  The data source for the tiles. This can be a URL starting with `http://` or `https://`,
58
40
  a path to a local file, or a custom `Reader` function that reads data chunks based on offset and length.
59
- * `options: `[`Options`](#interface_options) (optional)\
41
+ * <code>options: [Options](#interface_options)</code> (optional)\
60
42
  Optional settings that configure tile handling.
61
43
 
62
- **Returns:** [`VersaTiles`](#class_versatiles)
63
- **Methods**
44
+ **Returns:** <code>[VersaTiles](#class_versatiles)</code>
64
45
 
65
- ### `async getHeader()`
46
+ </details>
66
47
 
67
- Asynchronously retrieves the header information from the `.versatiles` container.\
68
- This method is primarily for internal use. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L75">\[src]</a></sup>
48
+ <details>
69
49
 
70
- **Returns:** [`Header`](#interface_header)
71
-
72
- ### `async getMetadata()`
73
-
74
- Asynchronously retrieves the metadata associated with the `.versatiles` container.\
75
- Metadata typically includes information about `vector_layers` for vector tiles.
76
- If the container does not include metadata, this method returns `null`. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L133">\[src]</a></sup>
50
+ <summary><h3>Method: <code>getHeader&#40;&#41;</code></h3></summary>
77
51
 
78
- **Returns:** `null | object`
52
+ Asynchronously retrieves the header information from the `.versatiles` container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/index.ts#L74">\[src]</a></sup>
79
53
 
80
- ### `async getTileFormat()`
54
+ **Returns:** <code>Promise<[Header](#interface_header)></code>
81
55
 
82
- Asynchronously determines the tile format, such as "png" or "pbf", based on the header information. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L153">\[src]</a></sup>
56
+ </details>
83
57
 
84
- **Returns:** [`Format`](#type_format)
58
+ <details>
85
59
 
86
- ### `async getBlockIndex()`
60
+ <summary><h3>Method: <code>getMetadata&#40;&#41;</code></h3></summary>
87
61
 
88
- Asynchronously retrieves a mapping of tile block indices. The map's keys are formatted as "{z},{x},{y}".\
89
- This method is for internal use to manage tile lookup within the container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L165">\[src]</a></sup>
90
-
91
- **Returns:** `Map<string,`[`Block`](#interface_block)`>`
92
-
93
- ### `async getTileIndex(block)`
94
-
95
- Asynchronously retrieves the tile index for a specified block. This is an internal method used to\
96
- maintain a lookup for every tile within a block. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L244">\[src]</a></sup>
62
+ Asynchronously retrieves the metadata associated with the `.versatiles` container.\
63
+ Metadata typically includes information about `vector_layers` for vector tiles.
64
+ If the container does not include metadata, this method returns `null`. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/index.ts#L132">\[src]</a></sup>
97
65
 
98
- **Parameters:**
66
+ **Returns:** <code>Promise\<null | object></code>
99
67
 
100
- * `block: `[`Block`](#interface_block)\
101
- The block for which to retrieve the tile index.
68
+ </details>
102
69
 
103
- **Returns:** [`TileIndex`](#interface_tileindex)
70
+ <details>
104
71
 
105
- ### `async getTile(z, x, y)`
72
+ <summary><h3>Method: <code>getTile&#40;z, x, y&#41;</code></h3></summary>
106
73
 
107
74
  Asynchronously retrieves a specific tile's data as a Buffer. If the tile data is compressed as\
108
75
  defined in the container header, the returned Buffer will contain the compressed data.
109
76
  To obtain uncompressed data, use the `getTileUncompressed` method.
110
- If the specified tile does not exist, the method returns `null`. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L274">\[src]</a></sup>
77
+ If the specified tile does not exist, the method returns `null`. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/index.ts#L158">\[src]</a></sup>
111
78
 
112
79
  **Parameters:**
113
80
 
114
- * `z: number`\
81
+ * <code>z: number</code>\
115
82
  The zoom level of the tile.
116
- * `x: number`\
83
+ * <code>x: number</code>\
117
84
  The x coordinate of the tile within its zoom level.
118
- * `y: number`\
85
+ * <code>y: number</code>\
119
86
  The y coordinate of the tile within its zoom level.
120
87
 
121
- **Returns:** `null | Buffer`
88
+ **Returns:** <code>Promise\<null | Buffer></code>
89
+
90
+ </details>
122
91
 
123
- ### `async getTileUncompressed(z, x, y)`
92
+ <details>
93
+
94
+ <summary><h3>Method: <code>getTileUncompressed&#40;z, x, y&#41;</code></h3></summary>
124
95
 
125
96
  Asynchronously retrieves a specific tile's uncompressed data as a Buffer. This method first\
126
97
  retrieves the compressed tile data using `getTile` and then decompresses it based on the
127
98
  compression setting in the container header.
128
- If the specified tile does not exist, the method returns `null`. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/index.ts#L322">\[src]</a></sup>
99
+ If the specified tile does not exist, the method returns `null`. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/index.ts#L206">\[src]</a></sup>
129
100
 
130
101
  **Parameters:**
131
102
 
132
- * `z: number`\
103
+ * <code>z: number</code>\
133
104
  The zoom level of the tile.
134
- * `x: number`\
105
+ * <code>x: number</code>\
135
106
  The x coordinate of the tile within its zoom level.
136
- * `y: number`\
107
+ * <code>y: number</code>\
137
108
  The y coordinate of the tile within its zoom level.
138
109
 
139
- **Returns:** `null | Buffer`
140
-
141
- ## Interface: `Block`<a id="interface_block"></a>
142
-
143
- Interface for a block of tiles including necessary metadata.\
144
- For more details, refer to [spec v02](https://github.com/versatiles-org/versatiles-spec/blob/main/v02/readme.md#block_index). <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L85">\[src]</a></sup>
145
- **Properties**
146
-
147
- * `level: number`\
148
- The zoom level for this block.
149
- * `column: number`\
150
- The column position of this block at the current zoom level.
151
- * `row: number`\
152
- The row position of this block at the current zoom level.
153
- * `colMin: number`\
154
- Minimum column index for tiles stored in this block (range: 0-255).
155
- * `rowMin: number`\
156
- Minimum row index for tiles stored in this block (range: 0-255).
157
- * `colMax: number`\
158
- Maximum column index for tiles stored in this block (range: 0-255).
159
- * `rowMax: number`\
160
- Maximum row index for tiles stored in this block (range: 0-255).
161
- * `blockOffset: number`\
162
- Byte position where this block starts in the file container.
163
- * `tileIndexOffset: number`\
164
- Byte position where the tile index starts within the container.
165
- * `tileIndexLength: number`\
166
- Length of the tile index in bytes.
167
- * `tileCount: number`\
168
- The number of tiles contained in this block.
169
- * `tileIndex: `[`TileIndex`](#interface_tileindex) (optional)\
170
- Optional tile index if it has been fetched.
171
-
172
- ## Interface: `Header`<a id="interface_header"></a>
173
-
174
- Interface for the metadata header of a `*.Versatiles` container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L52">\[src]</a></sup>
175
- **Properties**
176
-
177
- * `magic: string`\
110
+ **Returns:** <code>Promise\<null | Buffer></code>
111
+
112
+ </details>
113
+
114
+ </details>
115
+
116
+ <details>
117
+
118
+ <summary><h2>Interface: <code>Header</code><a id="interface_header"></a></h2></summary>
119
+
120
+ Interface for the metadata header of a `*.Versatiles` container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/interfaces.ts#L56">\[src]</a></sup>
121
+
122
+ * <code>magic: string</code>\
178
123
  Identifier for the container format, usually "versatiles\_v02".
179
- * `version: string`\
124
+ * <code>version: string</code>\
180
125
  Version of the container format, typically "v02".
181
- * `tileFormat: `[`Format`](#type_format)\
126
+ * <code>tileFormat: [Format](#type_format)</code>\
182
127
  The format used for storing tiles.
183
- * `tileCompression: `[`Compression`](#type_compression)\
128
+ * <code>tileCompression: [Compression](#type_compression)</code>\
184
129
  The type of compression applied to tiles.
185
- * `zoomMin: number`\
130
+ * <code>zoomMin: number</code>\
186
131
  The minimum zoom level.
187
- * `zoomMax: number`\
132
+ * <code>zoomMax: number</code>\
188
133
  The maximum zoom level.
189
- * `bbox: [number, number, number, number]`\
134
+ * <code>bbox: \[number, number, number, number]</code>\
190
135
  Bounding box coordinates as \[lon\_min, lat\_min, lon\_max, lat\_max].
191
- * `metaOffset: number`\
136
+ * <code>metaOffset: number</code>\
192
137
  The byte offset for metadata within the container.
193
- * `metaLength: number`\
138
+ * <code>metaLength: number</code>\
194
139
  The byte size of the metadata. A value of 0 means no metadata.
195
- * `blockIndexOffset: number`\
140
+ * <code>blockIndexOffset: number</code>\
196
141
  The byte offset for the block index within the container.
197
- * `blockIndexLength: number`\
142
+ * <code>blockIndexLength: number</code>\
198
143
  The byte size of the block index. A value of 0 indicates no tiles in the container.
199
144
 
200
- ## Interface: `Options`<a id="interface_options"></a>
145
+ </details>
201
146
 
202
- Interface for defining the options available for reading a container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L119">\[src]</a></sup>
203
- **Properties**
147
+ <details>
204
148
 
205
- * `tms: boolean`\
206
- If set to true, uses the [TMS (Tile Map Service) tile ordering](https://wiki.openstreetmap.org/wiki/TMS) where y=0 is the southernmost point.
149
+ <summary><h2>Interface: <code>Options</code><a id="interface_options"></a></h2></summary>
207
150
 
208
- ## Interface: `TileIndex`<a id="interface_tileindex"></a>
151
+ Interface for defining the options available for reading a container. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/interfaces.ts#L123">\[src]</a></sup>
209
152
 
210
- Interface for the index structure used for tiles within a block. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L108">\[src]</a></sup>
211
- **Properties**
153
+ * <code>tms: boolean</code>\
154
+ If set to true, uses the [TMS (Tile Map Service) tile ordering](https://wiki.openstreetmap.org/wiki/TMS) where y=0 is the southernmost point.
155
+
156
+ </details>
212
157
 
213
- * `offsets: Float64Array`\
214
- Array indicating the start byte positions of tiles within the block.
215
- * `lengths: Float64Array`\
216
- Array specifying the byte lengths of the tiles. A length of 0 means the tile is not stored.
158
+ <details>
217
159
 
218
- ## Type: `Compression`<a id="type_compression"></a>
160
+ <summary><h2>Type: <code>Compression</code><a id="type_compression"></a></h2></summary>
219
161
 
220
162
  Supported compression.\
221
- `null` signifies that the data is uncompressed. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L5">\[src]</a></sup>
163
+ `null` signifies that the data is uncompressed. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/interfaces.ts#L5">\[src]</a></sup>
164
+
165
+ **Type:** <code>"br" | "gzip" | null</code>
166
+
167
+ </details>
168
+
169
+ <details>
170
+
171
+ <summary><h2>Type: <code>Format</code><a id="type_format"></a></h2></summary>
172
+
173
+ Supported tile formats. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/interfaces.ts#L11">\[src]</a></sup>
222
174
 
223
- **Type:** `"br" | "gzip" | null`
175
+ **Type:** <code>"avif" | "bin" | "geojson" | "jpeg" | "json" | "pbf" | "png" | "svg" | "topojson" | "webp" | null</code>
224
176
 
225
- ## Type: `Format`<a id="type_format"></a>
177
+ </details>
226
178
 
227
- Supported tile formats. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L11">\[src]</a></sup>
179
+ <details>
228
180
 
229
- **Type:** `"avif" | "bin" | "geojson" | "jpeg" | "json" | "pbf" | "png" | "svg" | "topojson" | "webp" | null`
181
+ <summary><h2>Type: <code>Reader</code><a id="type_reader"></a></h2></summary>
230
182
 
231
- ## Type: `Reader`<a id="type_reader"></a>
183
+ Type definition for reading content from a VersaTiles container.
232
184
 
233
- Type definition for an asynchronous function to read a container's content.
185
+ This is useful for implementing new container readers, e.g. reading over other network protocols. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/8921e07/versatiles-container/src/interfaces.ts#L37">\[src]</a></sup>
234
186
 
235
- This can be useful for accessing data stored in various storage mediums. <sup><a href="https://github.com/versatiles-org/node-versatiles/blob/575beea/container/src/interfaces.ts#L33">\[src]</a></sup>
187
+ **Type:** <code>(position: number, length: number) => Promise<Buffer></code>
236
188
 
237
- **Type:** `(position: number, length: number) => Promise<Buffer>`
189
+ </details>
238
190
 
239
191
  # License
240
192
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import type { Block, Format, Header, Options, Reader, TileIndex } from './interfaces.js';
3
- export type { Block, Compression, Format, Header, Options, Reader, TileIndex } from './interfaces.js';
2
+ import type { Block, Header, Options, Reader, TileIndex } from './interfaces.js';
3
+ export type { Compression, Format, Header, Options, Reader } from './interfaces.js';
4
4
  /**
5
5
  * The `VersaTiles` class is a wrapper around a `.versatiles` container file. It provides methods
6
6
  * to access tile data, metadata, and other properties within the container.
@@ -18,7 +18,6 @@ export declare class VersaTiles {
18
18
  constructor(source: Reader | string, options?: Options);
19
19
  /**
20
20
  * Asynchronously retrieves the header information from the `.versatiles` container.
21
- * This method is primarily for internal use.
22
21
  *
23
22
  * @returns A promise that resolves with the header object.
24
23
  * @throws Will throw an error if the container does not start with expected magic bytes indicating a valid format.
@@ -32,29 +31,6 @@ export declare class VersaTiles {
32
31
  * @returns A promise that resolves with an object representing the metadata, or null if no metadata is present.
33
32
  */
34
33
  getMetadata(): Promise<object | null>;
35
- /**
36
- * Asynchronously determines the tile format, such as "png" or "pbf", based on the header information.
37
- *
38
- * @returns A promise that resolves with the tile format string, or null if the format is undefined.
39
- */
40
- getTileFormat(): Promise<Format | null>;
41
- /**
42
- * Asynchronously retrieves a mapping of tile block indices. The map's keys are formatted as "{z},{x},{y}".
43
- * This method is for internal use to manage tile lookup within the container.
44
- *
45
- * @returns A promise that resolves with the block index map.
46
- * @internal
47
- */
48
- getBlockIndex(): Promise<Map<string, Block>>;
49
- /**
50
- * Asynchronously retrieves the tile index for a specified block. This is an internal method used to
51
- * maintain a lookup for every tile within a block.
52
- *
53
- * @param block - The block for which to retrieve the tile index.
54
- * @returns A promise that resolves with the tile index.
55
- * @internal
56
- */
57
- getTileIndex(block: Block): Promise<TileIndex>;
58
34
  /**
59
35
  * Asynchronously retrieves a specific tile's data as a Buffer. If the tile data is compressed as
60
36
  * defined in the container header, the returned Buffer will contain the compressed data.
@@ -79,4 +55,30 @@ export declare class VersaTiles {
79
55
  * @returns A promise that resolves with the uncompressed tile data as a Buffer, or null if the tile cannot be found.
80
56
  */
81
57
  getTileUncompressed(z: number, x: number, y: number): Promise<Buffer | null>;
58
+ /**
59
+ * Asynchronously retrieves a mapping of tile block indices. The map's keys are formatted as "{z},{x},{y}".
60
+ * This method is for internal use to manage tile lookup within the container.
61
+ *
62
+ * @returns A promise that resolves with the block index map.
63
+ * @protected
64
+ */
65
+ protected getBlockIndex(): Promise<Map<string, Block>>;
66
+ /**
67
+ * Asynchronously retrieves the tile index for a specified block. This is an internal method used to
68
+ * maintain a lookup for every tile within a block.
69
+ *
70
+ * @param block - The block for which to retrieve the tile index.
71
+ * @returns A promise that resolves with the tile index.
72
+ * @protected
73
+ */
74
+ protected getTileIndex(block: Block): Promise<TileIndex>;
75
+ /**
76
+ * A protected method to read a chunk of data from the source based on the specified offset and length.
77
+ *
78
+ * @param offset - The offset from the start of the source data to begin reading.
79
+ * @param length - The number of bytes to read from the source.
80
+ * @returns A promise that resolves with the read data as a Buffer.
81
+ * @protected
82
+ */
83
+ protected read(offset: number, length: number): Promise<Buffer>;
82
84
  }
package/dist/index.js CHANGED
@@ -53,7 +53,6 @@ export class VersaTiles {
53
53
  }
54
54
  /**
55
55
  * Asynchronously retrieves the header information from the `.versatiles` container.
56
- * This method is primarily for internal use.
57
56
  *
58
57
  * @returns A promise that resolves with the header object.
59
58
  * @throws Will throw an error if the container does not start with expected magic bytes indicating a valid format.
@@ -62,7 +61,7 @@ export class VersaTiles {
62
61
  // deliver if known
63
62
  if (this.#header)
64
63
  return this.#header;
65
- const data = await this.#read(0, 66);
64
+ const data = await this.read(0, 66);
66
65
  // check magic bytes
67
66
  if (!/^versatiles_v0[12]$/.test(data.toString('utf8', 0, 14))) {
68
67
  throw new Error('Invalid Container');
@@ -119,33 +118,86 @@ export class VersaTiles {
119
118
  this.#metadata = null;
120
119
  }
121
120
  else {
122
- let buffer = await this.#read(header.metaOffset, header.metaLength);
121
+ let buffer = await this.read(header.metaOffset, header.metaLength);
123
122
  buffer = await this.#decompress(buffer, header.tileCompression);
124
123
  this.#metadata = JSON.parse(buffer.toString());
125
124
  }
126
125
  return this.#metadata;
127
126
  }
128
127
  /**
129
- * Asynchronously determines the tile format, such as "png" or "pbf", based on the header information.
128
+ * Asynchronously retrieves a specific tile's data as a Buffer. If the tile data is compressed as
129
+ * defined in the container header, the returned Buffer will contain the compressed data.
130
+ * To obtain uncompressed data, use the `getTileUncompressed` method.
131
+ * If the specified tile does not exist, the method returns `null`.
132
+ *
133
+ * @param z - The zoom level of the tile.
134
+ * @param x - The x coordinate of the tile within its zoom level.
135
+ * @param y - The y coordinate of the tile within its zoom level.
136
+ * @returns A promise that resolves with the tile data as a Buffer, or null if the tile cannot be found.
137
+ */
138
+ async getTile(z, x, y) {
139
+ // when y index is inverted
140
+ if (this.#options.tms)
141
+ y = Math.pow(2, z) - y - 1;
142
+ // ensure block index is loaded
143
+ const blockIndex = await this.getBlockIndex();
144
+ // block xy
145
+ const bx = x >> 8;
146
+ const by = y >> 8;
147
+ // tile xy (within block)
148
+ const tx = x & 0xFF;
149
+ const ty = y & 0xFF;
150
+ // check if block containing tile is within bounds
151
+ const blockKey = `${z},${bx},${by}`;
152
+ const block = blockIndex.get(blockKey);
153
+ if (!block)
154
+ return null;
155
+ // check if block contains tile
156
+ if (tx < block.colMin || tx > block.colMax)
157
+ return null;
158
+ if (ty < block.rowMin || ty > block.rowMax)
159
+ return null;
160
+ // calculate sequential tile number
161
+ const j = (ty - block.rowMin) * (block.colMax - block.colMin + 1) + (tx - block.colMin);
162
+ // get tile index
163
+ const tileIndex = await this.getTileIndex(block);
164
+ const offset = tileIndex.offsets[j];
165
+ const length = tileIndex.lengths[j];
166
+ if (length === 0)
167
+ return null;
168
+ return this.read(offset, length);
169
+ }
170
+ /**
171
+ * Asynchronously retrieves a specific tile's uncompressed data as a Buffer. This method first
172
+ * retrieves the compressed tile data using `getTile` and then decompresses it based on the
173
+ * compression setting in the container header.
174
+ * If the specified tile does not exist, the method returns `null`.
130
175
  *
131
- * @returns A promise that resolves with the tile format string, or null if the format is undefined.
176
+ * @param z - The zoom level of the tile.
177
+ * @param x - The x coordinate of the tile within its zoom level.
178
+ * @param y - The y coordinate of the tile within its zoom level.
179
+ * @returns A promise that resolves with the uncompressed tile data as a Buffer, or null if the tile cannot be found.
132
180
  */
133
- async getTileFormat() {
134
- return (await this.getHeader()).tileFormat;
181
+ async getTileUncompressed(z, x, y) {
182
+ const tile = await this.getTile(z, x, y);
183
+ if (!tile)
184
+ return null;
185
+ const header = await this.getHeader();
186
+ return this.#decompress(tile, header.tileCompression);
135
187
  }
136
188
  /**
137
189
  * Asynchronously retrieves a mapping of tile block indices. The map's keys are formatted as "{z},{x},{y}".
138
190
  * This method is for internal use to manage tile lookup within the container.
139
191
  *
140
192
  * @returns A promise that resolves with the block index map.
141
- * @internal
193
+ * @protected
142
194
  */
143
195
  async getBlockIndex() {
144
196
  if (this.#blockIndex)
145
197
  return this.#blockIndex;
146
198
  const header = await this.getHeader();
147
199
  // read block_index buffer
148
- let data = await this.#read(header.blockIndexOffset, header.blockIndexLength);
200
+ let data = await this.read(header.blockIndexOffset, header.blockIndexLength);
149
201
  data = await this.#decompress(data, 'br');
150
202
  // read index from buffer
151
203
  const blocks = [];
@@ -199,12 +251,12 @@ export class VersaTiles {
199
251
  *
200
252
  * @param block - The block for which to retrieve the tile index.
201
253
  * @returns A promise that resolves with the tile index.
202
- * @internal
254
+ * @protected
203
255
  */
204
256
  async getTileIndex(block) {
205
257
  if (block.tileIndex)
206
258
  return block.tileIndex;
207
- let buffer = await this.#read(block.tileIndexOffset, block.tileIndexLength);
259
+ let buffer = await this.read(block.tileIndexOffset, block.tileIndexLength);
208
260
  buffer = await this.#decompress(buffer, 'br');
209
261
  const offsets = new Float64Array(block.tileCount);
210
262
  const lengths = new Float64Array(block.tileCount);
@@ -216,75 +268,14 @@ export class VersaTiles {
216
268
  return block.tileIndex;
217
269
  }
218
270
  /**
219
- * Asynchronously retrieves a specific tile's data as a Buffer. If the tile data is compressed as
220
- * defined in the container header, the returned Buffer will contain the compressed data.
221
- * To obtain uncompressed data, use the `getTileUncompressed` method.
222
- * If the specified tile does not exist, the method returns `null`.
223
- *
224
- * @param z - The zoom level of the tile.
225
- * @param x - The x coordinate of the tile within its zoom level.
226
- * @param y - The y coordinate of the tile within its zoom level.
227
- * @returns A promise that resolves with the tile data as a Buffer, or null if the tile cannot be found.
228
- */
229
- async getTile(z, x, y) {
230
- // when y index is inverted
231
- if (this.#options.tms)
232
- y = Math.pow(2, z) - y - 1;
233
- // ensure block index is loaded
234
- const blockIndex = await this.getBlockIndex();
235
- // block xy
236
- const bx = x >> 8;
237
- const by = y >> 8;
238
- // tile xy (within block)
239
- const tx = x & 0xFF;
240
- const ty = y & 0xFF;
241
- // check if block containing tile is within bounds
242
- const blockKey = `${z},${bx},${by}`;
243
- const block = blockIndex.get(blockKey);
244
- if (!block)
245
- return null;
246
- // check if block contains tile
247
- if (tx < block.colMin || tx > block.colMax)
248
- return null;
249
- if (ty < block.rowMin || ty > block.rowMax)
250
- return null;
251
- // calculate sequential tile number
252
- const j = (ty - block.rowMin) * (block.colMax - block.colMin + 1) + (tx - block.colMin);
253
- // get tile index
254
- const tileIndex = await this.getTileIndex(block);
255
- const offset = tileIndex.offsets[j];
256
- const length = tileIndex.lengths[j];
257
- if (length === 0)
258
- return null;
259
- return this.#read(offset, length);
260
- }
261
- /**
262
- * Asynchronously retrieves a specific tile's uncompressed data as a Buffer. This method first
263
- * retrieves the compressed tile data using `getTile` and then decompresses it based on the
264
- * compression setting in the container header.
265
- * If the specified tile does not exist, the method returns `null`.
266
- *
267
- * @param z - The zoom level of the tile.
268
- * @param x - The x coordinate of the tile within its zoom level.
269
- * @param y - The y coordinate of the tile within its zoom level.
270
- * @returns A promise that resolves with the uncompressed tile data as a Buffer, or null if the tile cannot be found.
271
- */
272
- async getTileUncompressed(z, x, y) {
273
- const tile = await this.getTile(z, x, y);
274
- if (!tile)
275
- return null;
276
- const header = await this.getHeader();
277
- return this.#decompress(tile, header.tileCompression);
278
- }
279
- /**
280
- * A private method to read a chunk of data from the source based on the specified offset and length.
271
+ * A protected method to read a chunk of data from the source based on the specified offset and length.
281
272
  *
282
273
  * @param offset - The offset from the start of the source data to begin reading.
283
274
  * @param length - The number of bytes to read from the source.
284
275
  * @returns A promise that resolves with the read data as a Buffer.
285
- * @private
276
+ * @protected
286
277
  */
287
- async #read(offset, length) {
278
+ async read(offset, length) {
288
279
  if (length === 0)
289
280
  return Buffer.allocUnsafe(0);
290
281
  return this.#reader(offset, length);
@@ -17,12 +17,16 @@ export type Format = 'avif' | 'bin' | 'geojson' | 'jpeg' | 'json' | 'pbf' | 'png
17
17
  */
18
18
  export type Decompressor = (data: Buffer, compression: Compression) => Promise<Buffer>;
19
19
  /**
20
- * Type definition for an asynchronous function to read a container's content.
20
+ * Type definition for reading content from a VersaTiles container.
21
+ *
22
+ * This is useful for implementing new container readers, e.g. reading over other network protocols.
21
23
  *
22
- * This can be useful for accessing data stored in various storage mediums.
23
24
  * @param {number} position - The byte offset at which to start reading.
24
25
  * @param {number} length - The number of bytes to read.
25
- * @returns {Promise<Buffer>} - The read data.
26
+ * @returns {Promise<Buffer>} A promise that resolves with the data read as a Buffer.
27
+ * @throws {RangeError} If `position` is less than 0 or if `length` is less than 0.
28
+ * @throws {RangeError} If the sum of `position` and `length` exceeds the size of the content (filesize).
29
+ * @throws {Error} If there is any filesystem or network error such as the content not being accessible or readable.
26
30
  */
27
31
  export type Reader = (position: number, length: number) => Promise<Buffer>;
28
32
  /**
@@ -1,4 +1,4 @@
1
- import fs from 'fs';
1
+ import fs from 'node:fs';
2
2
  /**
3
3
  * Creates a file reader function for reading chunks of data from a file. This reader abstracts
4
4
  * the file reading process and provides a simple async interface to read data from the specified file.
@@ -18,6 +18,7 @@ import fs from 'fs';
18
18
  */
19
19
  export default function getFileReader(filename) {
20
20
  const fd = fs.openSync(filename, 'r');
21
+ const { size } = fs.statSync(filename);
21
22
  /**
22
23
  * Reads a chunk of data from the file at the specified position and length.
23
24
  * It creates a buffer of the specified `length`, seeks to the `position` in the file,
@@ -37,6 +38,15 @@ export default function getFileReader(filename) {
37
38
  * promise will be rejected with an error.
38
39
  */
39
40
  return async function read(position, length) {
41
+ if (position < 0) {
42
+ throw new RangeError(`Invalid read position: ${position}. The read position must be a non-negative integer.`);
43
+ }
44
+ if (length < 0) {
45
+ throw new RangeError(`Invalid read length: ${length}. The read length must be a non-negative integer.`);
46
+ }
47
+ if (position + length > size) {
48
+ throw new RangeError(`Read range out of bounds: The requested range ends at position ${position + length}, which exceeds the file's limit of ${size} bytes.`);
49
+ }
40
50
  return new Promise((resolve, reject) => {
41
51
  fs.read(fd, {
42
52
  buffer: Buffer.alloc(length),
@@ -27,6 +27,12 @@ export default function getHTTPReader(url) {
27
27
  * the promise is rejected with an error.
28
28
  */
29
29
  return async function read(position, length) {
30
+ if (position < 0) {
31
+ throw new RangeError(`Invalid read position: ${position}. The read position must be a non-negative integer.`);
32
+ }
33
+ if (length < 0) {
34
+ throw new RangeError(`Invalid read length: ${length}. The read length must be a non-negative integer.`);
35
+ }
30
36
  const headers = {
31
37
  // eslint-disable-next-line @typescript-eslint/naming-convention
32
38
  'user-agent': 'Mozilla/5.0 (compatible; versatiles; +https://www.npmjs.com/package/versatiles)',
@@ -61,9 +67,24 @@ export default function getHTTPReader(url) {
61
67
  })
62
68
  .end();
63
69
  });
70
+ const contentRange = message.headers['content-range'];
71
+ if (contentRange == null)
72
+ throw Error('The response header does not contain "content-range"');
73
+ const parts = /^bytes (\d+)\-(\d+)\/(\d+)/i.exec(contentRange);
74
+ if (parts == null)
75
+ throw Error('"content-range" in response header is malformed');
76
+ if (position !== parseInt(parts[1], 10))
77
+ throw Error(`requestet position (${position}) and returned offset (${parts[1]}) must be equal`);
78
+ if (position + length > parseInt(parts[3], 10)) {
79
+ throw new RangeError(`Read range out of bounds: The requested range ends at position ${position + length}, which exceeds the file's limit of ${parts[3]} bytes.`);
80
+ }
81
+ const returnedLength = parseInt(parts[2], 10) + 1 - position;
82
+ if (length !== returnedLength) {
83
+ throw new Error(`Returned length (${returnedLength}) is not requested length (${length}).`);
84
+ }
64
85
  if ((message.statusCode == null) || Math.floor(message.statusCode / 100) !== 2) {
65
86
  message.destroy();
66
- throw new Error(`Server responded with status code: ${message.statusCode}`);
87
+ throw new Error(`Server responded with status code: ${message.statusCode} `);
67
88
  }
68
89
  /**
69
90
  * Collects and concatenates response data chunks into a buffer.
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@versatiles/container",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A client library for reading VersaTiles containers",
5
5
  "scripts": {
6
- "check": "npm run lint && npm run build && npm run test",
7
6
  "build": "rm -rf dist && tsc -p tsconfig.build.json",
8
- "doc": "npm run -w tsdoc2readme update_readme container/src/index.ts '# API'",
9
- "lint": "eslint .",
10
- "test": "jest"
7
+ "check": "cd ..; npm run check",
8
+ "doc": "npx vrt ts2md src/index.ts tsconfig.build.json | npx vrt insertmd README.md '# API' true",
9
+ "lint": "cd ..; npm run lint",
10
+ "prepublish": "npm run build && npm run doc",
11
+ "test": "cd ..; npm run test"
11
12
  },
12
13
  "files": [
13
14
  "dist/**/*.js",
@@ -46,7 +47,8 @@
46
47
  ],
47
48
  "devDependencies": {
48
49
  "@types/node": "^20.9.0",
49
- "jest": "^29.7.0",
50
- "typescript": "^5.2.2"
50
+ "tsx": "^4.1.0",
51
+ "typescript": "^5.2.2",
52
+ "@versatiles/release-tool": "^1.0.0"
51
53
  }
52
54
  }