styled-map-package 2.2.1 → 4.0.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.
Files changed (107) hide show
  1. package/bin/smp-download.js +3 -3
  2. package/bin/smp-mbtiles.js +1 -1
  3. package/bin/smp-view.js +15 -4
  4. package/dist/download.cjs +101 -0
  5. package/dist/download.d.cts +65 -0
  6. package/dist/{lib/download.d.ts → download.d.ts} +22 -6
  7. package/dist/download.js +77 -0
  8. package/dist/from-mbtiles.cjs +91 -0
  9. package/dist/from-mbtiles.d.cts +17 -0
  10. package/dist/from-mbtiles.d.ts +17 -0
  11. package/dist/from-mbtiles.js +57 -0
  12. package/dist/index.cjs +49 -0
  13. package/dist/index.d.cts +27 -0
  14. package/dist/index.d.ts +27 -0
  15. package/dist/index.js +18 -0
  16. package/dist/reader-watch.cjs +135 -0
  17. package/dist/reader-watch.d.cts +24 -0
  18. package/dist/reader-watch.d.ts +24 -0
  19. package/dist/reader-watch.js +101 -0
  20. package/dist/reader.cjs +167 -0
  21. package/dist/reader.d.cts +62 -0
  22. package/dist/{lib/reader.d.ts → reader.d.ts} +16 -5
  23. package/dist/reader.js +138 -0
  24. package/dist/reporters.cjs +122 -0
  25. package/dist/reporters.d.cts +10 -0
  26. package/dist/{lib/reporters.d.ts → reporters.d.ts} +5 -2
  27. package/dist/reporters.js +88 -0
  28. package/dist/server.cjs +79 -0
  29. package/dist/server.d.cts +48 -0
  30. package/dist/server.d.ts +48 -0
  31. package/dist/server.js +55 -0
  32. package/dist/style-downloader.cjs +312 -0
  33. package/dist/style-downloader.d.cts +120 -0
  34. package/dist/{lib/style-downloader.d.ts → style-downloader.d.ts} +23 -13
  35. package/dist/style-downloader.js +288 -0
  36. package/dist/tile-downloader.cjs +158 -0
  37. package/dist/tile-downloader.d.cts +84 -0
  38. package/dist/{lib/tile-downloader.d.ts → tile-downloader.d.ts} +22 -10
  39. package/dist/tile-downloader.js +126 -0
  40. package/dist/{lib/writer.d.ts → types-B4Xn1F9K.d.cts} +74 -14
  41. package/dist/types-B4Xn1F9K.d.ts +189 -0
  42. package/dist/utils/errors.cjs +41 -0
  43. package/dist/utils/errors.d.cts +18 -0
  44. package/dist/{lib/utils → utils}/errors.d.ts +4 -2
  45. package/dist/utils/errors.js +16 -0
  46. package/dist/utils/fetch.cjs +96 -0
  47. package/dist/utils/fetch.d.cts +51 -0
  48. package/dist/{lib/utils → utils}/fetch.d.ts +10 -4
  49. package/dist/utils/fetch.js +62 -0
  50. package/dist/utils/file-formats.cjs +98 -0
  51. package/dist/utils/file-formats.d.cts +35 -0
  52. package/dist/{lib/utils → utils}/file-formats.d.ts +13 -3
  53. package/dist/utils/file-formats.js +62 -0
  54. package/dist/utils/geo.cjs +84 -0
  55. package/dist/utils/geo.d.cts +46 -0
  56. package/dist/{lib/utils → utils}/geo.d.ts +8 -6
  57. package/dist/utils/geo.js +56 -0
  58. package/dist/utils/mapbox.cjs +121 -0
  59. package/dist/utils/mapbox.d.cts +43 -0
  60. package/dist/utils/mapbox.d.ts +43 -0
  61. package/dist/utils/mapbox.js +91 -0
  62. package/dist/utils/misc.cjs +39 -0
  63. package/{lib/utils/misc.js → dist/utils/misc.d.cts} +5 -9
  64. package/dist/{lib/utils → utils}/misc.d.ts +5 -3
  65. package/dist/utils/misc.js +13 -0
  66. package/dist/utils/streams.cjs +130 -0
  67. package/dist/utils/streams.d.cts +73 -0
  68. package/dist/{lib/utils → utils}/streams.d.ts +14 -10
  69. package/dist/utils/streams.js +103 -0
  70. package/dist/utils/style.cjs +126 -0
  71. package/dist/utils/style.d.cts +69 -0
  72. package/dist/{lib/utils → utils}/style.d.ts +19 -9
  73. package/dist/utils/style.js +98 -0
  74. package/dist/utils/templates.cjs +114 -0
  75. package/dist/utils/templates.d.cts +78 -0
  76. package/dist/{lib/utils → utils}/templates.d.ts +24 -14
  77. package/dist/utils/templates.js +79 -0
  78. package/dist/writer.cjs +401 -0
  79. package/dist/writer.d.cts +7 -0
  80. package/dist/writer.d.ts +7 -0
  81. package/dist/writer.js +374 -0
  82. package/package.json +107 -41
  83. package/dist/lib/from-mbtiles.d.ts +0 -13
  84. package/dist/lib/index.d.ts +0 -10
  85. package/dist/lib/reader-watch.d.ts +0 -13
  86. package/dist/lib/server.d.ts +0 -15
  87. package/dist/lib/types.d.ts +0 -64
  88. package/dist/lib/utils/mapbox.d.ts +0 -41
  89. package/lib/download.js +0 -114
  90. package/lib/from-mbtiles.js +0 -83
  91. package/lib/index.js +0 -11
  92. package/lib/reader-watch.js +0 -133
  93. package/lib/reader.js +0 -165
  94. package/lib/reporters.js +0 -92
  95. package/lib/server.js +0 -81
  96. package/lib/style-downloader.js +0 -363
  97. package/lib/tile-downloader.js +0 -188
  98. package/lib/types.ts +0 -104
  99. package/lib/utils/errors.js +0 -24
  100. package/lib/utils/fetch.js +0 -100
  101. package/lib/utils/file-formats.js +0 -85
  102. package/lib/utils/geo.js +0 -87
  103. package/lib/utils/mapbox.js +0 -155
  104. package/lib/utils/streams.js +0 -165
  105. package/lib/utils/style.js +0 -174
  106. package/lib/utils/templates.js +0 -136
  107. package/lib/writer.js +0 -478
@@ -1,188 +0,0 @@
1
- import SphericalMercator from '@mapbox/sphericalmercator'
2
- import Queue from 'yocto-queue'
3
- import zlib from 'zlib'
4
-
5
- import { FetchQueue } from './utils/fetch.js'
6
- import {
7
- getFormatFromMimeType,
8
- getTileFormatFromStream,
9
- } from './utils/file-formats.js'
10
- import { getTileUrl, MAX_BOUNDS } from './utils/geo.js'
11
- import { noop } from './utils/misc.js'
12
-
13
- /** @typedef {Omit<import('./writer.js').TileInfo, 'sourceId'>} TileInfo */
14
- /**
15
- * @typedef {object} TileDownloadStats
16
- * @property {number} total
17
- * @property {number} downloaded
18
- * @property {number} skipped
19
- * @property {number} totalBytes
20
- */
21
-
22
- /**
23
- * Download tiles from a list of tile URLs within a bounding box and zoom range.
24
- * Returns an async generator of tile data as readable streams and tile info objects.
25
- *
26
- * @param {object} opts
27
- * @param {string[]} opts.tileUrls Array of tile URL templates. Use `{x}`, `{y}`, `{z}` placeholders, and optional `{scheme}` placeholder which can be `xyz` or `tms`.
28
- * @param {import('./utils/geo.js').BBox} opts.bounds Bounding box of the area to download
29
- * @param {number} opts.maxzoom Maximum zoom level to download
30
- * @param {(progress: TileDownloadStats) => void} [opts.onprogress] Callback to report download progress
31
- * @param {boolean} [opts.trackErrors=false] Include errors in the returned array of skipped tiles - this has memory overhead so should only be used for debugging.
32
- * @param {import('./utils/geo.js').BBox} [opts.sourceBounds=MAX_BOUNDS] Bounding box of source data.
33
- * @param {boolean} [opts.boundsBuffer=false] Buffer the bounds by one tile at each zoom level to ensure no tiles are missed at the edges. With this set to false, in most instances the map will appear incomplete when viewed because the downloaded tiles at lower zoom levels will not cover the map view area.
34
- * @param {number} [opts.minzoom=0] Minimum zoom level to download (for most cases this should be left as `0` - the size overhead is minimal, because each zoom level has 4x as many tiles)
35
- * @param {number} [opts.concurrency=8] Number of concurrent downloads (ignored if `fetchQueue` is provided)
36
- * @param {FetchQueue} [opts.fetchQueue=new FetchQueue(concurrency)] Optional fetch queue to use for downloading tiles
37
- * @param {'xyz' | 'tms'} [opts.scheme='xyz'] Tile scheme to use for tile URLs
38
- * @returns {AsyncGenerator<[import('stream').Readable, TileInfo]> & { readonly skipped: Array<TileInfo & { error?: Error }>, readonly stats: TileDownloadStats }}
39
- */
40
- export function downloadTiles({
41
- tileUrls,
42
- bounds,
43
- maxzoom,
44
- onprogress = noop,
45
- trackErrors = false,
46
- sourceBounds = MAX_BOUNDS,
47
- boundsBuffer = false,
48
- minzoom = 0,
49
- concurrency = 8,
50
- fetchQueue = new FetchQueue(concurrency),
51
- scheme = 'xyz',
52
- }) {
53
- /** @type {Array<TileInfo & { error?: Error }>} */
54
- const skipped = []
55
- let completed = 0
56
- /** @type {TileDownloadStats} */
57
- let stats = {
58
- total: 0,
59
- downloaded: 0,
60
- skipped: 0,
61
- totalBytes: 0,
62
- }
63
- /** @type {import('./utils/streams.js').ProgressCallback} */
64
- function onDownloadProgress({ chunkBytes }) {
65
- stats.totalBytes += chunkBytes
66
- onprogress(stats)
67
- }
68
- /**
69
- *
70
- * @param {Error} error
71
- * @param {TileInfo} tileInfo
72
- */
73
- function onDownloadError(error, tileInfo) {
74
- if (trackErrors) {
75
- skipped.push({ ...tileInfo, error })
76
- } else {
77
- skipped.push(tileInfo)
78
- }
79
- onprogress(stats)
80
- }
81
- function onDownloadComplete() {
82
- stats.downloaded = ++completed - skipped.length
83
- stats.skipped = skipped.length
84
- onprogress(stats)
85
- }
86
-
87
- /** @type {ReturnType<downloadTiles>} */
88
- const tiles = (async function* () {
89
- /** @type {Queue<[Promise<void | import('./utils/fetch.js').DownloadResponse>, TileInfo]>} */
90
- const queue = new Queue()
91
- const tiles = tileIterator({
92
- bounds,
93
- minzoom,
94
- maxzoom,
95
- sourceBounds,
96
- boundsBuffer,
97
- })
98
- for (const { x, y, z } of tiles) {
99
- const tileURL = getTileUrl(tileUrls, { x, y, z, scheme })
100
- const tileInfo = { z, x, y }
101
- const result = fetchQueue
102
- .fetch(tileURL, { onprogress: onDownloadProgress })
103
- // We handle error here rather than below to avoid uncaught errors
104
- .catch((err) => onDownloadError(err, tileInfo))
105
- queue.enqueue([result, tileInfo])
106
- }
107
-
108
- stats.total = queue.size
109
- if (onprogress) onprogress(stats)
110
-
111
- for (const [result, tileInfo] of queue) {
112
- // We handle any error above and add to `skipped`
113
- const downloadResponse = await result.catch(noop)
114
- if (!downloadResponse) continue
115
- let { body, mimeType } = downloadResponse
116
- body.on('end', onDownloadComplete)
117
- body.on('error', (err) => onDownloadError(err, tileInfo))
118
- /** @type {import('./writer.js').TileFormat} */
119
- let format
120
- if (mimeType) {
121
- format = getFormatFromMimeType(mimeType)
122
- } else {
123
- ;[format, body] = await getTileFormatFromStream(body)
124
- }
125
-
126
- let stream = body
127
- if (format === 'mvt') {
128
- // MVT tiles are always gzipped. Unfortunately we can't stop fetch from
129
- // ungzipping the data during download, so we need to re-gzip it.
130
- const gzipStream = zlib.createGzip()
131
- stream = body.pipe(gzipStream)
132
- stream.on('error', (err) => gzipStream.destroy(err))
133
- }
134
-
135
- yield [stream, { ...tileInfo, format }]
136
- }
137
- })()
138
-
139
- Object.defineProperty(tiles, 'skipped', {
140
- get() {
141
- return skipped
142
- },
143
- })
144
-
145
- Object.defineProperty(tiles, 'stats', {
146
- get() {
147
- return stats
148
- },
149
- })
150
-
151
- return tiles
152
- }
153
-
154
- /**
155
- *
156
- * @param {object} opts
157
- * @param {import('./utils/geo.js').BBox} [opts.bounds]
158
- * @param {import('./utils/geo.js').BBox} [opts.sourceBounds]
159
- * @param {boolean} [opts.boundsBuffer]
160
- * @param {number} [opts.minzoom]
161
- * @param {number} opts.maxzoom
162
- */
163
- export function* tileIterator({
164
- bounds = [...MAX_BOUNDS],
165
- minzoom = 0,
166
- maxzoom,
167
- sourceBounds,
168
- boundsBuffer = false,
169
- }) {
170
- const sm = new SphericalMercator({ size: 256 })
171
- for (let z = minzoom; z <= maxzoom; z++) {
172
- // Cloning bounds passed to sm.xyz because no guarantee it won't mutate the array
173
- let { minX, minY, maxX, maxY } = sm.xyz([...bounds], z)
174
- let sourceXYBounds = sourceBounds
175
- ? sm.xyz([...sourceBounds], z)
176
- : { minX, minY, maxX, maxY }
177
- const buffer = boundsBuffer ? 1 : 0
178
- minX = Math.max(0, minX - buffer, sourceXYBounds.minX)
179
- minY = Math.max(0, minY - buffer, sourceXYBounds.minY)
180
- maxX = Math.min(Math.pow(2, z) - 1, maxX + buffer, sourceXYBounds.maxX)
181
- maxY = Math.min(Math.pow(2, z) - 1, maxY + buffer, sourceXYBounds.maxY)
182
- for (let x = minX; x <= maxX; x++) {
183
- for (let y = minY; y <= maxY; y++) {
184
- yield { x, y, z }
185
- }
186
- }
187
- }
188
- }
package/lib/types.ts DELETED
@@ -1,104 +0,0 @@
1
- import type {
2
- SourceSpecification,
3
- StyleSpecification,
4
- ValidationError,
5
- GeoJSONSourceSpecification,
6
- VectorSourceSpecification,
7
- RasterSourceSpecification,
8
- RasterDEMSourceSpecification,
9
- } from '@maplibre/maplibre-gl-style-spec'
10
- import type { GeoJSON, BBox } from 'geojson'
11
- import type { Readable } from 'stream'
12
- import type { Except, SetRequired, Simplify } from 'type-fest'
13
-
14
- import { SUPPORTED_SOURCE_TYPES } from './writer.js'
15
-
16
- export type InputSource = Extract<
17
- SourceSpecification,
18
- { type: (typeof SUPPORTED_SOURCE_TYPES)[number] }
19
- >
20
- type TransformInlinedSource<T extends SourceSpecification> =
21
- T extends GeoJSONSourceSpecification
22
- ? OmitUnion<T, 'data'> & { data: GeoJSON }
23
- : T extends
24
- | VectorSourceSpecification
25
- | RasterSourceSpecification
26
- | RasterDEMSourceSpecification
27
- ? SetRequired<OmitUnion<T, 'url'>, 'tiles'>
28
- : T
29
- /**
30
- * This is a slightly stricter version of SourceSpecification that requires
31
- * sources to be inlined (e.g. no urls to TileJSON or GeoJSON files).
32
- */
33
- export type InlinedSource = TransformInlinedSource<SourceSpecification>
34
- type SupportedInlinedSource = Extract<
35
- InlinedSource,
36
- { type: (typeof SUPPORTED_SOURCE_TYPES)[number] }
37
- >
38
- /**
39
- * This is a slightly stricter version of StyleSpecification that requires
40
- * sources to be inlined (e.g. no urls to TileJSON or GeoJSON files).
41
- */
42
- export type StyleInlinedSources = Omit<StyleSpecification, 'sources'> & {
43
- sources: {
44
- [_: string]: InlinedSource
45
- }
46
- }
47
-
48
- export type SMPSource = TransformSMPInputSource<SupportedInlinedSource>
49
- /**
50
- * This is a slightly stricter version of StyleSpecification that is provided in
51
- * a Styled Map Package. Tile sources must have tile URLs inlined (they cannot
52
- * refer to a TileJSON url), and they must have bounds, minzoom, and maxzoom.
53
- * GeoJSON sources must have inlined GeoJSON (not a URL to a GeoJSON file).
54
- */
55
- export type SMPStyle = TransformSMPStyle<StyleSpecification>
56
-
57
- export type TransformSMPInputSource<T extends SupportedInlinedSource> =
58
- T extends GeoJSONSourceSpecification
59
- ? T & { data: { bbox: BBox } }
60
- : T extends RasterSourceSpecification | VectorSourceSpecification
61
- ? SetRequired<T, 'bounds' | 'minzoom' | 'maxzoom'>
62
- : T
63
-
64
- type TransformSMPStyle<T extends StyleSpecification> = Omit<T, 'sources'> & {
65
- metadata: {
66
- 'smp:bounds': [number, number, number, number]
67
- 'smp:maxzoom': 0
68
- 'smp:sourceFolders': { [_: string]: string }
69
- }
70
- sources: {
71
- [_: string]: SMPSource
72
- }
73
- }
74
-
75
- export interface ValidateStyle {
76
- (style: unknown): style is StyleSpecification
77
- errors: Array<ValidationError>
78
- }
79
-
80
- export interface DownloadStream extends Readable {
81
- iterator(
82
- ...args: Parameters<Readable['iterator']>
83
- ): AsyncIterableIterator<Buffer>
84
- [Symbol.asyncIterator](): AsyncIterableIterator<Buffer>
85
- }
86
-
87
- export type RequiredUnion<T> = T extends any ? Required<T> : never
88
- export type OmitUnion<T, K extends keyof any> = T extends unknown
89
- ? Omit<T, K>
90
- : never
91
-
92
- type SetRequiredIfPresent<
93
- BaseType,
94
- Keys extends keyof any,
95
- > = BaseType extends unknown
96
- ? Keys extends keyof BaseType
97
- ? Simplify<
98
- // Pick just the keys that are optional from the base type.
99
- Except<BaseType, Keys> &
100
- // Pick the keys that should be required from the base type and make them required.
101
- Required<Pick<BaseType, Keys>>
102
- >
103
- : never
104
- : never
@@ -1,24 +0,0 @@
1
- export class ENOENT extends Error {
2
- code = 'ENOENT'
3
- /** @param {string} path */
4
- constructor(path) {
5
- const message = `ENOENT: no such file or directory, open '${path}'`
6
- super(message)
7
- this.path = path
8
- }
9
- }
10
-
11
- /**
12
- * Returns true if the error if because a file is not found. On Windows, some
13
- * operations like fs.watch() throw an EPERM error rather than ENOENT.
14
- *
15
- * @param {unknown} error
16
- * @returns {error is Error & { code: 'ENOENT' | 'EPERM' }}
17
- */
18
- export function isFileNotThereError(error) {
19
- return (
20
- error instanceof Error &&
21
- 'code' in error &&
22
- (error.code === 'ENOENT' || error.code === 'EPERM')
23
- )
24
- }
@@ -1,100 +0,0 @@
1
- import ky from 'ky'
2
- import { pEvent } from 'p-event'
3
- import pLimit from 'p-limit'
4
-
5
- import { fromWebReadableStream, ProgressStream } from './streams.js'
6
-
7
- /**
8
- * @typedef {object} DownloadResponse
9
- * @property {import('stream').Readable} body Node ReadableStream of the response body
10
- * @property {string | null} mimeType Content mime-type (from http content-type header)
11
- * @property {number | null} contentLength Content length in bytes (from http content-length header)
12
- */
13
-
14
- /**
15
- * A wrapper for fetch that limits the number of concurrent downloads.
16
- */
17
- export class FetchQueue {
18
- /** @type {import('p-limit').LimitFunction} */
19
- #limit
20
- /** @param {number} concurrency */
21
- constructor(concurrency) {
22
- this.#limit = pLimit(concurrency)
23
- }
24
-
25
- get activeCount() {
26
- return this.#limit.activeCount
27
- }
28
-
29
- /**
30
- * Fetch a URL, limiting the number of concurrent downloads. Resolves with a
31
- * `DownloadResponse`, which is a parsed from the Fetch `Response` objects,
32
- * with `body` as a Node readable stream, and the MIME type and content length
33
- * of the response.
34
- *
35
- * NB: The response body stream must be consumed to the end, otherwise the
36
- * queue will never be emptied.
37
- *
38
- * @param {string} url
39
- * @param {{ onprogress?: import('./streams.js').ProgressCallback }} opts
40
- * @returns {Promise<DownloadResponse>}
41
- */
42
- fetch(url, { onprogress } = {}) {
43
- // This is wrapped like this so that pLimit limits concurrent `fetchStream`
44
- // calls, which only resolve when the body is completely downloaded, but
45
- // this method will return a response as soon as it is available. NB: If the
46
- // body of a response is never "consumed" (e.g. by reading it to the end),
47
- // the fetchStream function will never resolve, and the limit will never be
48
- // released.
49
- return new Promise((resolveResponse, rejectResponse) => {
50
- this.#limit(fetchStream, {
51
- url,
52
- onresponse: resolveResponse,
53
- onerror: rejectResponse,
54
- onprogress,
55
- })
56
- })
57
- }
58
- }
59
-
60
- /**
61
- * This will resolve when the download is complete, regardless of success or
62
- * failure, but a readable stream is available before download via the
63
- * onReadStream param. This strange function signature is used for limiting the
64
- * number of simultaneous downloads, but still being able to expose the Response
65
- * as soon as it is available. This is implmented this way to avoid creating
66
- * unnecessary closures, which is important here because we can have thousands
67
- * of tile requests.
68
- *
69
- * @param {object} opts
70
- * @param {string} opts.url
71
- * @param {(response: DownloadResponse) => void} opts.onresponse
72
- * @param {(err: Error) => void} opts.onerror
73
- * @param {import('./streams.js').ProgressCallback} [opts.onprogress]
74
- * @returns {Promise<void>}
75
- */
76
- async function fetchStream({ url, onresponse, onerror, onprogress }) {
77
- try {
78
- const response = await ky(url, { retry: 3 })
79
- if (!response.body) {
80
- throw new Error('No body in response')
81
- }
82
- const body = fromWebReadableStream(response.body)
83
- const contentType = response.headers.get('content-type')
84
- const mimeType =
85
- typeof contentType === 'string' ? contentType.split(';')[0] : null
86
- const contentLengthHeader = response.headers.get('content-length')
87
- const contentLength =
88
- contentLengthHeader === null ? null : parseInt(contentLengthHeader, 10)
89
- onresponse({
90
- body: onprogress ? body.pipe(new ProgressStream({ onprogress })) : body,
91
- mimeType,
92
- contentLength,
93
- })
94
- // Wait for the read stream to end before resolving this function, so that
95
- // we limit concurrent downloads
96
- await pEvent(body, 'end')
97
- } catch (err) {
98
- onerror(err instanceof Error ? err : new Error('Unknown error'))
99
- }
100
- }
@@ -1,85 +0,0 @@
1
- import BufferPeerStream from 'buffer-peek-stream'
2
-
3
- import { hasOwn } from './misc.js'
4
-
5
- const peek = BufferPeerStream.promise
6
-
7
- const MAGIC_BYTES = /** @type {const} */ ({
8
- png: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a],
9
- jpg: [0xff, 0xd8, 0xff],
10
- // eslint-disable-next-line no-sparse-arrays
11
- webp: [0x52, 0x49, 0x46, 0x46, , , , , 0x57, 0x45, 0x42, 0x50],
12
- // Include the compression-type byte, which is always 0x08 (DEFLATE) for gzip
13
- gz: [0x1f, 0x8b, 0x08],
14
- })
15
-
16
- const MIME_TYPES = /** @type {const} */ ({
17
- 'image/png': 'png',
18
- 'image/jpeg': 'jpg',
19
- 'image/webp': 'webp',
20
- })
21
-
22
- /** @type {Map<number, keyof typeof MAGIC_BYTES>} */
23
- const magicByteMap = new Map()
24
- for (const [ext, bytes] of Object.entries(MAGIC_BYTES)) {
25
- magicByteMap.set(
26
- bytes[0],
27
- // @ts-ignore
28
- ext,
29
- )
30
- }
31
- /**
32
- * For a given buffer, determine the tile format based on the magic bytes.
33
- * Will throw for unknown file types.
34
- * Smaller and faster version of magic-bytes.js due to the limited use case.
35
- *
36
- * @param {Buffer | Uint8Array} buf
37
- * @returns {import("../writer.js").TileFormat}
38
- */
39
- export function getTileFormatFromBuffer(buf) {
40
- const ext = magicByteMap.get(buf[0])
41
- if (!ext) {
42
- throw new Error('Unknown file type')
43
- }
44
- const sig = MAGIC_BYTES[ext]
45
- for (let i = 1; i < sig.length; i++) {
46
- if (typeof sig[i] !== 'undefined' && sig[i] !== buf[i]) {
47
- throw new Error('Unknown file type')
48
- }
49
- }
50
- if (ext === 'gz') {
51
- // Gzipped tiles are always MVT
52
- return 'mvt'
53
- }
54
- return ext
55
- }
56
-
57
- /**
58
- * Determine the tile format from either a readable stream, buffer or Uint8Array
59
- * from the magic bytes at the start of the file. Used if data is served without
60
- * a content-type header.
61
- *
62
- * @param {import('stream').Readable} tileData
63
- * @returns {Promise<[import("../writer.js").TileFormat, import('stream').Readable]>}
64
- */
65
- export async function getTileFormatFromStream(tileData) {
66
- // NB: The buffer-peek-stream library uses the peeked bytes as the high
67
- // water mark for the transform stream, so we set this to the default high
68
- // water mark of 16KB
69
- const [peekedData, outputStream] = await peek(tileData, 16 * 1024)
70
- tileData = outputStream
71
- const format = getTileFormatFromBuffer(peekedData)
72
- return [format, outputStream]
73
- }
74
-
75
- /**
76
- * Get the tile format from a MIME type. Throws for unsupported types.
77
- *
78
- * @param {string} mimeType
79
- * @returns {import("../writer.js").TileFormat}
80
- */
81
- export function getFormatFromMimeType(mimeType) {
82
- if (mimeType.startsWith('application/')) return 'mvt'
83
- if (hasOwn(MIME_TYPES, mimeType)) return MIME_TYPES[mimeType]
84
- throw new Error('Unsupported MIME type ' + mimeType)
85
- }
package/lib/utils/geo.js DELETED
@@ -1,87 +0,0 @@
1
- // Adapted from https://github.com/mapbox/tilebelt
2
-
3
- const r2d = 180 / Math.PI
4
-
5
- /** Spherical Mercator max bounds, rounded to 6 decimal places */
6
- export const MAX_BOUNDS = /** @type {BBox} */ ([
7
- -180, -85.051129, 180, 85.051129,
8
- ])
9
-
10
- /**
11
- * @typedef {[number, number, number, number]} BBox
12
- */
13
-
14
- /**
15
- * Return the bounding box for the given tile.
16
- *
17
- * @param {{ x: number, y: number, z: number }} tile
18
- * @returns {BBox} Bounding Box [w, s, e, n]
19
- */
20
- export function tileToBBox({ x, y, z }) {
21
- const e = tile2lon({ x: x + 1, z })
22
- const w = tile2lon({ x, z })
23
- const s = tile2lat({ y: y + 1, z })
24
- const n = tile2lat({ y, z })
25
- return [w, s, e, n]
26
- }
27
-
28
- /**
29
- * @param {{ x: number, y: number, z: number }} tile
30
- */
31
- export function getQuadkey({ x, y, z }) {
32
- let quadkey = ''
33
- let mask
34
- for (let i = z; i > 0; i--) {
35
- mask = 1 << (i - 1)
36
- quadkey += (x & mask ? 1 : 0) + (y & mask ? 2 : 0)
37
- }
38
- return quadkey
39
- }
40
-
41
- /**
42
- * From an array of tile URL templates, get the URL for the given tile.
43
- *
44
- * @param {string[]} urls
45
- * @param {{ x: number, y: number, z: number, scheme?: 'xyz' | 'tms' }} opts
46
- */
47
- export function getTileUrl(urls, { x, y, z, scheme = 'xyz' }) {
48
- const bboxEspg3857 = tileToBBox({ x, y: Math.pow(2, z) - y - 1, z })
49
- const quadkey = getQuadkey({ x, y, z })
50
-
51
- return urls[(x + y) % urls.length]
52
- .replace('{prefix}', (x % 16).toString(16) + (y % 16).toString(16))
53
- .replace(/{z}/g, String(z))
54
- .replace(/{x}/g, String(x))
55
- .replace(/{y}/g, String(scheme === 'tms' ? Math.pow(2, z) - y - 1 : y))
56
- .replace('{quadkey}', quadkey)
57
- .replace('{bbox-epsg-3857}', bboxEspg3857.join(','))
58
- }
59
-
60
- /**
61
- * Returns a bbox that is the smallest bounding box that contains all the input bboxes.
62
- *
63
- * @param {[BBox, ...BBox[]]} bboxes
64
- * @returns {BBox} Bounding Box [w, s, e, n]
65
- */
66
- export function unionBBox(bboxes) {
67
- let [w, s, e, n] = bboxes[0]
68
- for (let i = 1; i < bboxes.length; i++) {
69
- const [w1, s1, e1, n1] = bboxes[i]
70
- w = Math.min(w, w1)
71
- s = Math.min(s, s1)
72
- e = Math.max(e, e1)
73
- n = Math.max(n, n1)
74
- }
75
- return [w, s, e, n]
76
- }
77
-
78
- /** @param {{ x: number, z: number }} opts */
79
- function tile2lon({ x, z }) {
80
- return (x / Math.pow(2, z)) * 360 - 180
81
- }
82
-
83
- /** @param {{ y: number, z: number }} opts */
84
- function tile2lat({ y, z }) {
85
- const n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z)
86
- return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))
87
- }