styled-map-package 2.2.1 → 3.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.
- package/bin/smp-download.js +3 -3
- package/bin/smp-mbtiles.js +1 -1
- package/bin/smp-view.js +2 -2
- package/dist/download.cjs +101 -0
- package/dist/download.d.cts +65 -0
- package/dist/{lib/download.d.ts → download.d.ts} +22 -6
- package/dist/download.js +77 -0
- package/dist/from-mbtiles.cjs +91 -0
- package/dist/from-mbtiles.d.cts +17 -0
- package/dist/from-mbtiles.d.ts +17 -0
- package/dist/from-mbtiles.js +57 -0
- package/dist/index.cjs +49 -0
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +18 -0
- package/dist/reader-watch.cjs +135 -0
- package/dist/reader-watch.d.cts +24 -0
- package/dist/reader-watch.d.ts +24 -0
- package/dist/reader-watch.js +101 -0
- package/dist/reader.cjs +167 -0
- package/dist/reader.d.cts +62 -0
- package/dist/{lib/reader.d.ts → reader.d.ts} +16 -5
- package/dist/reader.js +138 -0
- package/dist/reporters.cjs +122 -0
- package/dist/reporters.d.cts +10 -0
- package/dist/{lib/reporters.d.ts → reporters.d.ts} +5 -2
- package/dist/reporters.js +88 -0
- package/dist/server.cjs +79 -0
- package/dist/server.d.cts +37 -0
- package/dist/server.d.ts +37 -0
- package/dist/server.js +45 -0
- package/dist/style-downloader.cjs +312 -0
- package/dist/style-downloader.d.cts +120 -0
- package/dist/{lib/style-downloader.d.ts → style-downloader.d.ts} +23 -13
- package/dist/style-downloader.js +288 -0
- package/dist/tile-downloader.cjs +158 -0
- package/dist/tile-downloader.d.cts +84 -0
- package/dist/{lib/tile-downloader.d.ts → tile-downloader.d.ts} +22 -10
- package/dist/tile-downloader.js +126 -0
- package/dist/{lib/writer.d.ts → types-B4Xn1F9K.d.cts} +74 -14
- package/dist/types-B4Xn1F9K.d.ts +189 -0
- package/dist/utils/errors.cjs +41 -0
- package/dist/utils/errors.d.cts +18 -0
- package/dist/{lib/utils → utils}/errors.d.ts +4 -2
- package/dist/utils/errors.js +16 -0
- package/dist/utils/fetch.cjs +96 -0
- package/dist/utils/fetch.d.cts +51 -0
- package/dist/{lib/utils → utils}/fetch.d.ts +10 -4
- package/dist/utils/fetch.js +62 -0
- package/dist/utils/file-formats.cjs +98 -0
- package/dist/utils/file-formats.d.cts +35 -0
- package/dist/{lib/utils → utils}/file-formats.d.ts +13 -3
- package/dist/utils/file-formats.js +62 -0
- package/dist/utils/geo.cjs +84 -0
- package/dist/utils/geo.d.cts +46 -0
- package/dist/{lib/utils → utils}/geo.d.ts +8 -6
- package/dist/utils/geo.js +56 -0
- package/dist/utils/mapbox.cjs +121 -0
- package/dist/utils/mapbox.d.cts +43 -0
- package/dist/utils/mapbox.d.ts +43 -0
- package/dist/utils/mapbox.js +91 -0
- package/dist/utils/misc.cjs +39 -0
- package/{lib/utils/misc.js → dist/utils/misc.d.cts} +5 -9
- package/dist/{lib/utils → utils}/misc.d.ts +5 -3
- package/dist/utils/misc.js +13 -0
- package/dist/utils/streams.cjs +130 -0
- package/dist/utils/streams.d.cts +73 -0
- package/dist/{lib/utils → utils}/streams.d.ts +14 -10
- package/dist/utils/streams.js +103 -0
- package/dist/utils/style.cjs +126 -0
- package/dist/utils/style.d.cts +69 -0
- package/dist/{lib/utils → utils}/style.d.ts +19 -9
- package/dist/utils/style.js +98 -0
- package/dist/utils/templates.cjs +114 -0
- package/dist/utils/templates.d.cts +78 -0
- package/dist/{lib/utils → utils}/templates.d.ts +24 -14
- package/dist/utils/templates.js +79 -0
- package/dist/writer.cjs +401 -0
- package/dist/writer.d.cts +7 -0
- package/dist/writer.d.ts +7 -0
- package/dist/writer.js +374 -0
- package/package.json +87 -33
- package/dist/lib/from-mbtiles.d.ts +0 -13
- package/dist/lib/index.d.ts +0 -10
- package/dist/lib/reader-watch.d.ts +0 -13
- package/dist/lib/server.d.ts +0 -15
- package/dist/lib/types.d.ts +0 -64
- package/dist/lib/utils/mapbox.d.ts +0 -41
- package/lib/download.js +0 -114
- package/lib/from-mbtiles.js +0 -83
- package/lib/index.js +0 -11
- package/lib/reader-watch.js +0 -133
- package/lib/reader.js +0 -165
- package/lib/reporters.js +0 -92
- package/lib/server.js +0 -81
- package/lib/style-downloader.js +0 -363
- package/lib/tile-downloader.js +0 -188
- package/lib/types.ts +0 -104
- package/lib/utils/errors.js +0 -24
- package/lib/utils/fetch.js +0 -100
- package/lib/utils/file-formats.js +0 -85
- package/lib/utils/geo.js +0 -87
- package/lib/utils/mapbox.js +0 -155
- package/lib/utils/streams.js +0 -165
- package/lib/utils/style.js +0 -174
- package/lib/utils/templates.js +0 -136
- package/lib/writer.js +0 -478
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
|
package/lib/utils/errors.js
DELETED
|
@@ -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
|
-
}
|
package/lib/utils/fetch.js
DELETED
|
@@ -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
|
-
}
|
package/lib/utils/mapbox.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
// from https://github.com/mapbox/mapbox-gl-js/blob/495a695/src/util/mapbox.js
|
|
3
|
-
|
|
4
|
-
export const API_URL = 'https://api.mapbox.com'
|
|
5
|
-
const HELP = 'See https://www.mapbox.com/api-documentation/#access-tokens'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {object} URLObject
|
|
9
|
-
* @property {string} protocol
|
|
10
|
-
* @property {string} authority
|
|
11
|
-
* @property {string} path
|
|
12
|
-
* @property {string[]} params
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {URLObject} urlObject
|
|
17
|
-
* @param {string} [accessToken]
|
|
18
|
-
*/
|
|
19
|
-
function makeAPIURL(urlObject, accessToken) {
|
|
20
|
-
const apiUrlObject = parseUrl(API_URL)
|
|
21
|
-
urlObject.protocol = apiUrlObject.protocol
|
|
22
|
-
urlObject.authority = apiUrlObject.authority
|
|
23
|
-
|
|
24
|
-
if (!accessToken) {
|
|
25
|
-
throw new Error(
|
|
26
|
-
`An API access token is required to use a Mapbox style. ${HELP}`,
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
if (accessToken[0] === 's') {
|
|
30
|
-
throw new Error(
|
|
31
|
-
`Use a public access token (pk.*) not a secret access token (sk.*). ${HELP}`,
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
urlObject.params.push(`access_token=${accessToken}`)
|
|
36
|
-
return formatUrl(urlObject)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** @param {string} url */
|
|
40
|
-
export function isMapboxURL(url) {
|
|
41
|
-
return url.indexOf('mapbox:') === 0
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @param {string} url
|
|
46
|
-
* @param {string} [accessToken]
|
|
47
|
-
*/
|
|
48
|
-
export function normalizeStyleURL(url, accessToken) {
|
|
49
|
-
if (!isMapboxURL(url)) return url
|
|
50
|
-
if (!accessToken) throw new Error('Mapbox styles require an access token')
|
|
51
|
-
const urlObject = parseUrl(url)
|
|
52
|
-
urlObject.path = `/styles/v1${urlObject.path}`
|
|
53
|
-
return makeAPIURL(urlObject, accessToken)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @param {string} url
|
|
58
|
-
* @param {string} [accessToken]
|
|
59
|
-
*/
|
|
60
|
-
export function normalizeGlyphsURL(url, accessToken) {
|
|
61
|
-
if (!isMapboxURL(url)) return url
|
|
62
|
-
if (!accessToken) throw new Error('Mapbox styles require an access token')
|
|
63
|
-
const urlObject = parseUrl(url)
|
|
64
|
-
urlObject.path = `/fonts/v1${urlObject.path}`
|
|
65
|
-
return makeAPIURL(urlObject, accessToken)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @param {string} url
|
|
70
|
-
* @param {string} [accessToken]
|
|
71
|
-
*/
|
|
72
|
-
export function normalizeSourceURL(url, accessToken) {
|
|
73
|
-
if (!isMapboxURL(url)) return url
|
|
74
|
-
if (!accessToken) throw new Error('Mapbox styles require an access token')
|
|
75
|
-
const urlObject = parseUrl(url)
|
|
76
|
-
urlObject.path = `/v4/${urlObject.authority}.json`
|
|
77
|
-
// TileJSON requests need a secure flag appended to their URLs so
|
|
78
|
-
// that the server knows to send SSL-ified resource references.
|
|
79
|
-
urlObject.params.push('secure')
|
|
80
|
-
return makeAPIURL(urlObject, accessToken)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @param {string} url
|
|
85
|
-
* @param {'' | '@2x'} format
|
|
86
|
-
* @param {'.png' | '.json'} extension
|
|
87
|
-
* @param {string} [accessToken]
|
|
88
|
-
*/
|
|
89
|
-
export function normalizeSpriteURL(url, format, extension, accessToken) {
|
|
90
|
-
const urlObject = parseUrl(url)
|
|
91
|
-
if (!isMapboxURL(url)) {
|
|
92
|
-
urlObject.path += `${format}${extension}`
|
|
93
|
-
return formatUrl(urlObject)
|
|
94
|
-
}
|
|
95
|
-
urlObject.path = `/styles/v1${urlObject.path}/sprite${format}${extension}`
|
|
96
|
-
return makeAPIURL(urlObject, accessToken)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const imageExtensionRe = /(\.(png|jpg)\d*)(?=$)/
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* @param {any} tileURL
|
|
103
|
-
* @param {string} sourceURL
|
|
104
|
-
* @param {256 | 512} [tileSize]
|
|
105
|
-
* @param {{ devicePixelRatio?: number; supportsWebp?: boolean; }} [opts]
|
|
106
|
-
*/
|
|
107
|
-
export function normalizeTileURL(
|
|
108
|
-
tileURL,
|
|
109
|
-
sourceURL,
|
|
110
|
-
tileSize,
|
|
111
|
-
{ devicePixelRatio = 1, supportsWebp = false } = {},
|
|
112
|
-
) {
|
|
113
|
-
if (!sourceURL || !isMapboxURL(sourceURL)) return tileURL
|
|
114
|
-
|
|
115
|
-
const urlObject = parseUrl(tileURL)
|
|
116
|
-
|
|
117
|
-
// The v4 mapbox tile API supports 512x512 image tiles only when @2x
|
|
118
|
-
// is appended to the tile URL. If `tileSize: 512` is specified for
|
|
119
|
-
// a Mapbox raster source force the @2x suffix even if a non hidpi device.
|
|
120
|
-
const suffix = devicePixelRatio >= 2 || tileSize === 512 ? '@2x' : ''
|
|
121
|
-
const extension = supportsWebp ? '.webp' : '$1'
|
|
122
|
-
urlObject.path = urlObject.path.replace(
|
|
123
|
-
imageExtensionRe,
|
|
124
|
-
`${suffix}${extension}`,
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
return formatUrl(urlObject)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const urlRe = /^(\w+):\/\/([^/?]*)(\/[^?]+)?\??(.+)?/
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* @param {string} url
|
|
134
|
-
* @returns {URLObject}
|
|
135
|
-
*/
|
|
136
|
-
function parseUrl(url) {
|
|
137
|
-
const parts = url.match(urlRe)
|
|
138
|
-
if (!parts) {
|
|
139
|
-
throw new Error('Unable to parse URL object')
|
|
140
|
-
}
|
|
141
|
-
return {
|
|
142
|
-
protocol: parts[1],
|
|
143
|
-
authority: parts[2],
|
|
144
|
-
path: parts[3] || '/',
|
|
145
|
-
params: parts[4] ? parts[4].split('&') : [],
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* @param {URLObject} obj
|
|
151
|
-
*/
|
|
152
|
-
function formatUrl(obj) {
|
|
153
|
-
const params = obj.params.length ? `?${obj.params.join('&')}` : ''
|
|
154
|
-
return `${obj.protocol}://${obj.authority}${obj.path}${params}`
|
|
155
|
-
}
|