diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml new file mode 100644 index 0000000..9dae0a7 --- /dev/null +++ b/.github/workflows/node.yml @@ -0,0 +1,30 @@ +name: Node.js Tests + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x, 22.x] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: npm ci + + - name: Run tests + run: npm test diff --git a/README.md b/README.md index aeefad2..508f625 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ A Styled Map Package (`.smp`) file is a Zip archive containing all the resources needed to serve a Maplibre vector styled map offline. This includes the style JSON, vector and raster tiles, glyphs (fonts), the sprite image, and the sprite metadata. +## Installation + +Install globally to use the `smp` command. + +```sh +npm install --global styled-map-package +``` + ## Usage Download an online map to a styled map package file, specifying the bounding box (west, south, east, north) and max zoom level. diff --git a/lib/download.js b/lib/download.js index 2890928..105c3c3 100644 --- a/lib/download.js +++ b/lib/download.js @@ -25,7 +25,7 @@ import StyleDownloader from './style-downloader.js' * @param {string} opts.styleUrl URL of the style to download * @param { (progress: DownloadProgress) => void } [opts.onprogress] Optional callback for reporting progress * @param {string} [opts.accessToken] - * @returns {import('stream').Readable} Readable stream of the output styled map file + * @returns {import('./types.js').DownloadStream} Readable stream of the output styled map file */ export default function download({ bbox, @@ -75,15 +75,11 @@ export default function download({ ;(async () => { const style = await downloader.getStyle() const writer = new Writer(style) + handleProgress({ style: { done: true } }) writer.outputStream.pipe(sizeCounter) writer.on('error', (err) => sizeCounter.destroy(err)) try { - for await (const [sourceId, source] of downloader.getSources()) { - writer.addSource(sourceId, source) - } - handleProgress({ style: { done: true } }) - for await (const spriteInfo of downloader.getSprites()) { await writer.addSprite(spriteInfo) handleProgress({ diff --git a/lib/reader.js b/lib/reader.js index 6f22ace..3f0cb3a 100644 --- a/lib/reader.js +++ b/lib/reader.js @@ -58,7 +58,7 @@ export default class Reader { * will be transformed to use the provided base URL. * * @param {string | null} [baseUrl] Base URL where you plan to serve the resources in this styled map package, e.g. `http://localhost:3000/maps/styleA` - * @returns {Promise} + * @returns {Promise} */ async getStyle(baseUrl = null) { const styleEntry = (await this.#entriesPromise).get(STYLE_FILE) @@ -66,7 +66,7 @@ export default class Reader { const stream = await styleEntry.openReadStream() const style = await json(stream) if (!validateStyle(style)) { - throw new Error('Invalid style') + throw new AggregateError(validateStyle.errors, 'Invalid style') } if (typeof style.glyphs === 'string') { style.glyphs = getUrl(style.glyphs, baseUrl) @@ -83,13 +83,9 @@ export default class Reader { source.tiles = source.tiles.map((tile) => getUrl(tile, baseUrl)) } } - const transformedStyleJSON = JSON.stringify(style) - return { - contentType: 'application/json; charset=utf-8', - contentLength: Buffer.byteLength(transformedStyleJSON, 'utf8'), - resourceType: 'style', - stream: intoStream(transformedStyleJSON), - } + // Hard to get this type-safe without a validation function. Instead we + // trust the Writer and the tests for now. + return /** @type {import('./types.js').SMPStyle} */ (style) } /** @@ -101,7 +97,15 @@ export default class Reader { */ async getResource(path) { if (path[0] === '/') path = path.slice(1) - if (path === STYLE_FILE) return this.getStyle() + if (path === STYLE_FILE) { + const styleJSON = JSON.stringify(await this.getStyle()) + return { + contentType: 'application/json; charset=utf-8', + contentLength: Buffer.byteLength(styleJSON, 'utf8'), + resourceType: 'style', + stream: intoStream(styleJSON), + } + } const entry = (await this.#entriesPromise).get(path) if (!entry) throw new Error(`File not found: ${path}`) const resourceType = getResourceType(path) @@ -141,6 +145,6 @@ function getUrl(smpUri, baseUrl) { if (!smpUri.startsWith(URI_BASE)) { throw new Error(`Invalid SMP URI: ${smpUri}`) } - if (!baseUrl) return smpUri + if (typeof baseUrl !== 'string') return smpUri return smpUri.replace(URI_BASE, baseUrl + '/') } diff --git a/lib/server.js b/lib/server.js index a73107e..7a40f8b 100644 --- a/lib/server.js +++ b/lib/server.js @@ -36,11 +36,11 @@ export default function (fastify, { filepath, lazy = false }, done) { reader = new Reader(filepath) } - fastify.get('/style.json', async (_request, reply) => { + fastify.get('/style.json', async () => { if (!reader) { reader = new Reader(filepath) } - return sendResource(reply, await reader.getStyle(fastify.listeningOrigin)) + return reader.getStyle(fastify.listeningOrigin) }) fastify.get('*', async (request, reply) => { diff --git a/lib/style-downloader.js b/lib/style-downloader.js index 5118402..71c6416 100644 --- a/lib/style-downloader.js +++ b/lib/style-downloader.js @@ -1,3 +1,4 @@ +import { check as checkGeoJson } from '@placemarkio/check-geojson' import { includeKeys } from 'filter-obj' import ky from 'ky' import Queue from 'yocto-queue' @@ -12,15 +13,19 @@ import { normalizeStyleURL, } from './utils/mapbox.js' import { clone, noop } from './utils/misc.js' -import { assertTileJSON, mapFontStacks } from './utils/style.js' +import { + assertTileJSON, + isInlinedSource, + mapFontStacks, + validateStyle, +} from './utils/style.js' -/** @import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec' */ -/** @import { TileSource } from './types.js' */ -/** @import { TileInfo, GlyphInfo, GlyphRange, TileFormat } from './writer.js' */ +/** @import { SourceSpecification, StyleSpecification } from '@maplibre/maplibre-gl-style-spec' */ +/** @import { TileInfo, GlyphInfo, GlyphRange } from './writer.js' */ /** @import { TileDownloadStats } from './tile-downloader.js' */ +/** @import { StyleInlinedSources, InlinedSource } from './types.js'*/ /** @typedef { import('ky').ResponsePromise & { body: ReadableStream } } ResponsePromise */ -/** @typedef { import('type-fest').SetRequired } TileSourceWithTiles */ /** @import { DownloadResponse } from './utils/fetch.js' */ /** @@ -38,7 +43,7 @@ export default class StyleDownloader { /** @type {null | string} */ #styleURL = null /** @type {null | StyleSpecification} */ - #style = null + #inputStyle = null /** @type {FetchQueue} */ #fetchQueue #mapboxAccessToken @@ -55,8 +60,10 @@ export default class StyleDownloader { this.#mapboxAccessToken = searchParams.get('access_token') || mapboxAccessToken this.#styleURL = normalizeStyleURL(style, this.#mapboxAccessToken) + } else if (validateStyle(style)) { + this.#inputStyle = clone(style) } else { - this.#style = clone(style) + throw new AggregateError(validateStyle.errors, 'Invalid style') } this.#fetchQueue = new FetchQueue(concurrency) } @@ -69,61 +76,82 @@ export default class StyleDownloader { } /** - * Download the style JSON for this style. + * Download the style JSON for this style and inline the sources * - * @returns {Promise} + * @returns {Promise} */ async getStyle() { - if (!this.#style && this.#styleURL) { - this.#style = /** @type {StyleSpecification} */ ( - await ky(this.#styleURL).json() - ) - } else if (!this.#style) { + if (!this.#inputStyle && this.#styleURL) { + const downloadedStyle = await ky(this.#styleURL).json() + if (!validateStyle(downloadedStyle)) { + throw new AggregateError( + validateStyle.errors, + 'Invalid style: ' + this.#styleURL, + ) + } + this.#inputStyle = downloadedStyle + } else if (!this.#inputStyle) { throw new Error('Unexpected state: no style or style URL provided') } - return this.#style + /** @type {{ [_:string]: InlinedSource }} */ + const inlinedSources = {} + for (const [sourceId, source] of Object.entries(this.#inputStyle.sources)) { + inlinedSources[sourceId] = await this.#getInlinedSource(source) + } + return { + ...this.#inputStyle, + sources: inlinedSources, + } } /** - * Download info about the sources referenced by this style. Will ignore/skip - * sources which are not raster or vector tiles. - * - * @returns {AsyncGenerator<[string, TileSourceWithTiles]>} + * @param {SourceSpecification} source + * @returns {Promise} */ - async *getSources() { - const style = await this.getStyle() - for (const sourceId in style.sources) { - let source = style.sources[sourceId] - if (source.type !== 'raster' && source.type !== 'vector') { - continue + async #getInlinedSource(source) { + if (isInlinedSource(source)) { + return source + } + if ( + source.type === 'raster' || + source.type === 'vector' || + source.type === 'raster-dem' + ) { + if (!source.url) { + throw new Error('Source is missing both url and tiles properties') } - if (!source.tiles) { - if (!source.url) continue - const sourceUrl = normalizeSourceURL( - source.url, - this.#mapboxAccessToken, - ) - const tilejson = await ky(sourceUrl).json() - assertTileJSON(tilejson) - Object.assign( - source, - includeKeys(tilejson, [ - 'bounds', - 'maxzoom', - 'minzoom', - 'tiles', - 'description', - 'attribution', - 'vector_layers', - ]), - ) + const sourceUrl = normalizeSourceURL(source.url, this.#mapboxAccessToken) + const tilejson = await ky(sourceUrl).json() + assertTileJSON(tilejson) + return { + ...source, + ...includeKeys(tilejson, [ + 'bounds', + 'maxzoom', + 'minzoom', + 'tiles', + 'description', + 'attribution', + 'vector_layers', + ]), + } + } else if (source.type === 'geojson') { + if (typeof source.data !== 'string') { + // Shouldn't get here because of the `isInlineSource()` check above, but + // Typescript can't fiture that out. + throw new Error('Unexpected data property for GeoJson source') + } + const geojsonUrl = normalizeSourceURL( + source.data, + this.#mapboxAccessToken, + ) + const data = checkGeoJson(await ky(geojsonUrl).text()) + return { + ...source, + data, } - yield [ - sourceId, - // @ts-expect-error - we mutate this to add tiles prop - source, - ] } + return source } /** @@ -265,7 +293,13 @@ export default class StyleDownloader { /** @type {ReturnType} */ const tiles = (async function* () { - for await (const [sourceId, source] of _this.getSources()) { + const inlinedStyle = await _this.getStyle() + for await (const [sourceId, source] of Object.entries( + inlinedStyle.sources, + )) { + if (source.type !== 'raster' && source.type !== 'vector') { + continue + } // Baseline stats for this source, used in the `onprogress` closure // below. Sorry for the hard-to-follow code! `onprogress` can be called // after we are already reading the next source, hence the need for a @@ -275,7 +309,7 @@ export default class StyleDownloader { tileUrls: source.tiles, bounds, maxzoom: Math.min(maxzoom, source.maxzoom || maxzoom), - minzoom: source.minzoom || 0, + minzoom: source.minzoom, sourceBounds: source.bounds, boundsBuffer: true, scheme: source.scheme, diff --git a/lib/tile-downloader.js b/lib/tile-downloader.js index b2c8955..e7cf8ec 100644 --- a/lib/tile-downloader.js +++ b/lib/tile-downloader.js @@ -154,24 +154,25 @@ export function downloadTiles({ /** * * @param {object} opts - * @param {import('./utils/geo.js').BBox} opts.bounds - * @param {import('./utils/geo.js').BBox} opts.sourceBounds - * @param {boolean} opts.boundsBuffer - * @param {number} opts.minzoom + * @param {import('./utils/geo.js').BBox} [opts.bounds] + * @param {import('./utils/geo.js').BBox} [opts.sourceBounds] + * @param {boolean} [opts.boundsBuffer] + * @param {number} [opts.minzoom] * @param {number} opts.maxzoom */ -function* tileIterator({ - bounds, - minzoom, +export function* tileIterator({ + bounds = [...MAX_BOUNDS], + minzoom = 0, maxzoom, sourceBounds, - boundsBuffer, + boundsBuffer = false, }) { const sm = new SphericalMercator({ size: 256 }) for (let z = minzoom; z <= maxzoom; z++) { - let { minX, minY, maxX, maxY } = sm.xyz(bounds, z) + // Cloning bounds passed to sm.xyz because no guarantee it won't mutate the array + let { minX, minY, maxX, maxY } = sm.xyz([...bounds], z) let sourceXYBounds = sourceBounds - ? sm.xyz(sourceBounds, z) + ? sm.xyz([...sourceBounds], z) : { minX, minY, maxX, maxY } const buffer = boundsBuffer ? 1 : 0 minX = Math.max(0, minX - buffer, sourceXYBounds.minX) diff --git a/lib/types.ts b/lib/types.ts index da20773..af375a4 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,31 +1,104 @@ import type { + SourceSpecification, StyleSpecification, ValidationError, + GeoJSONSourceSpecification, + VectorSourceSpecification, + RasterSourceSpecification, + RasterDEMSourceSpecification, } from '@maplibre/maplibre-gl-style-spec' +import type { GeoJSON, BBox } from 'geojson' +import type { Readable } from 'stream' +import type { Except, SetRequired, Simplify } from 'type-fest' -export type VectorTileSource = { - type: 'vector' - url?: string - tiles?: Array - bounds?: [number, number, number, number] - scheme?: 'xyz' | 'tms' - minzoom?: number - maxzoom?: number -} -export type RasterTileSource = { - type: 'raster' - url?: string - tiles?: Array - bounds?: [number, number, number, number] - minzoom?: number - maxzoom?: number - tileSize?: number - scheme?: 'xyz' | 'tms' +import { SUPPORTED_SOURCE_TYPES } from './writer.js' + +export type InputSource = Extract< + SourceSpecification, + { type: (typeof SUPPORTED_SOURCE_TYPES)[number] } +> +type TransformInlinedSource = + T extends GeoJSONSourceSpecification + ? OmitUnion & { data: GeoJSON } + : T extends + | VectorSourceSpecification + | RasterSourceSpecification + | RasterDEMSourceSpecification + ? SetRequired, 'tiles'> + : T +/** + * This is a slightly stricter version of SourceSpecification that requires + * sources to be inlined (e.g. no urls to TileJSON or GeoJSON files). + */ +export type InlinedSource = TransformInlinedSource +type SupportedInlinedSource = Extract< + InlinedSource, + { type: (typeof SUPPORTED_SOURCE_TYPES)[number] } +> +/** + * This is a slightly stricter version of StyleSpecification that requires + * sources to be inlined (e.g. no urls to TileJSON or GeoJSON files). + */ +export type StyleInlinedSources = Omit & { + sources: { + [_: string]: InlinedSource + } } -export type TileSource = VectorTileSource | RasterTileSource +export type SMPSource = TransformSMPInputSource +/** + * This is a slightly stricter version of StyleSpecification that is provided in + * a Styled Map Package. Tile sources must have tile URLs inlined (they cannot + * refer to a TileJSON url), and they must have bounds, minzoom, and maxzoom. + * GeoJSON sources must have inlined GeoJSON (not a URL to a GeoJSON file). + */ +export type SMPStyle = TransformSMPStyle + +export type TransformSMPInputSource = + T extends GeoJSONSourceSpecification + ? T & { data: { bbox: BBox } } + : T extends RasterSourceSpecification | VectorSourceSpecification + ? SetRequired + : T + +type TransformSMPStyle = Omit & { + metadata: { + 'smp:bounds': [number, number, number, number] + 'smp:maxzoom': 0 + 'smp:sourceFolders': { [_: string]: string } + } + sources: { + [_: string]: SMPSource + } +} export interface ValidateStyle { (style: unknown): style is StyleSpecification errors: Array } + +export interface DownloadStream extends Readable { + iterator( + ...args: Parameters + ): AsyncIterableIterator + [Symbol.asyncIterator](): AsyncIterableIterator +} + +export type RequiredUnion = T extends any ? Required : never +export type OmitUnion = T extends unknown + ? Omit + : never + +type SetRequiredIfPresent< + BaseType, + Keys extends keyof any, +> = BaseType extends unknown + ? Keys extends keyof BaseType + ? Simplify< + // Pick just the keys that are optional from the base type. + Except & + // Pick the keys that should be required from the base type and make them required. + Required> + > + : never + : never diff --git a/lib/utils/geo.js b/lib/utils/geo.js index a3a0d67..0eb7f59 100644 --- a/lib/utils/geo.js +++ b/lib/utils/geo.js @@ -2,7 +2,10 @@ const r2d = 180 / Math.PI -export const MAX_BOUNDS = /** @type {BBox} */ ([-180, -85.05, 180, 85.05]) +/** Spherical Mercator max bounds, rounded to 6 decimal places */ +export const MAX_BOUNDS = /** @type {BBox} */ ([ + -180, -85.051129, 180, 85.051129, +]) /** * @typedef {[number, number, number, number]} BBox diff --git a/lib/utils/style.js b/lib/utils/style.js index 56dd206..61310ed 100644 --- a/lib/utils/style.js +++ b/lib/utils/style.js @@ -151,3 +151,24 @@ export const validateStyle = return true } ) + +/** + * Check whether a source is already inlined (e.g. does not reference a TileJSON or GeoJSON url) + * + * @param {import('@maplibre/maplibre-gl-style-spec').SourceSpecification} source + * @returns {source is import('../types.js').InlinedSource} + */ +export function isInlinedSource(source) { + if (source.type === 'geojson') { + return typeof source.data === 'object' + } else if ( + source.type === 'vector' || + source.type === 'raster' || + source.type === 'raster-dem' + ) { + return 'tiles' in source + } else { + // Video and image sources are not strictly "inlined", but we treat them as such. + return true + } +} diff --git a/lib/utils/templates.js b/lib/utils/templates.js index d951db5..1d3f8b1 100644 --- a/lib/utils/templates.js +++ b/lib/utils/templates.js @@ -63,11 +63,7 @@ export function getContentType(path) { */ export function getTileFilename({ sourceId, z, x, y, format }) { const ext = '.' + format + (format === 'mvt' ? '.gz' : '') - return TILE_FILE.replace('{sourceId}', sourceId) - .replace('{z}', String(z)) - .replace('{x}', String(x)) - .replace('{y}', String(y)) - .replace('{ext}', ext) + return replaceVariables(TILE_FILE, { sourceId, z, x, y, ext }) } /** @@ -76,9 +72,11 @@ export function getTileFilename({ sourceId, z, x, y, format }) { * @param {{ id: string, pixelRatio: number, ext: '.json' | '.png'}} spriteInfo */ export function getSpriteFilename({ id, pixelRatio, ext }) { - return SPRITE_FILE.replace('{id}', id) - .replace('{pixelRatio}', getPixelRatioString(pixelRatio)) - .replace('{ext}', ext) + return replaceVariables(SPRITE_FILE, { + id, + pixelRatio: getPixelRatioString(pixelRatio), + ext, + }) } /** @@ -89,7 +87,7 @@ export function getSpriteFilename({ id, pixelRatio, ext }) { * @param {import("../writer.js").GlyphRange} options.range */ export function getGlyphFilename({ fontstack, range }) { - return GLYPH_FILE.replace('{fontstack}', fontstack).replace('{range}', range) + return replaceVariables(GLYPH_FILE, { fontstack, range }) } /** @@ -97,10 +95,7 @@ export function getGlyphFilename({ fontstack, range }) { */ export function getSpriteUri(id = 'default') { return ( - URI_BASE + - SPRITE_FILE.replace('{id}', id) - .replace('{pixelRatio}', '') - .replace('{ext}', '') + URI_BASE + replaceVariables(SPRITE_FILE, { id, pixelRatio: '', ext: '' }) ) } @@ -125,3 +120,17 @@ export function getTileUri({ sourceId, format }) { function getPixelRatioString(pixelRatio) { return pixelRatio === 1 ? '' : `@${pixelRatio}x` } + +/** + * Replaces variables in a string with values provided in an object. Variables + * in the string are denoted by curly braces, e.g., {variableName}. + * + * @param {string} template - The string containing variables wrapped in curly braces. + * @param {Record} variables - An object where the keys correspond to variable names and values correspond to the replacement values. + * @returns {string} The string with the variables replaced by their corresponding values. + */ +export function replaceVariables(template, variables) { + return template.replace(/{(.*?)}/g, (match, varName) => { + return varName in variables ? String(variables[varName]) : match + }) +} diff --git a/lib/writer.js b/lib/writer.js index 11c4e08..0d2d944 100644 --- a/lib/writer.js +++ b/lib/writer.js @@ -1,4 +1,5 @@ import { validateStyleMin, migrate } from '@maplibre/maplibre-gl-style-spec' +import { bbox } from '@turf/bbox' import archiver from 'archiver' import { EventEmitter } from 'events' import { excludeKeys } from 'filter-obj' @@ -8,7 +9,7 @@ import { PassThrough, pipeline } from 'readable-stream' import { Readable } from 'stream' import { getTileFormatFromStream } from './utils/file-formats.js' -import { tileToBBox, unionBBox } from './utils/geo.js' +import { MAX_BOUNDS, tileToBBox, unionBBox } from './utils/geo.js' import { clone } from './utils/misc.js' import { writeStreamFromAsync } from './utils/streams.js' import { replaceFontStacks } from './utils/style.js' @@ -28,7 +29,7 @@ import { /** @typedef {'png' | 'mvt' | 'jpg' | 'webp'} TileFormat */ /** * @typedef {object} SourceInfo - * @property {SetRequired} source + * @property {import('./types.js').SMPSource} source * @property {string} encodedSourceId * @property {TileFormat} [format] */ @@ -47,8 +48,13 @@ import { */ /** @import { StyleSpecification } from '@maplibre/maplibre-gl-style-spec' */ -/** @import { TileSource } from './types.js' */ -/** @import { SetRequired } from 'type-fest' */ +/** @import { InputSource, SMPSource } from './types.js' */ + +export const SUPPORTED_SOURCE_TYPES = /** @type {const} */ ([ + 'raster', + 'vector', + 'geojson', +]) /** * Write a styled map package to a stream. Stream `writer.outputStream` to a @@ -70,11 +76,10 @@ export default class Writer extends EventEmitter { #sources = new Map() /** @type {StyleSpecification} */ #style - /** @type {import('./utils/geo.js').BBox | undefined} */ - #bounds - #maxzoom = 0 #outputStream + static SUPPORTED_SOURCE_TYPES = SUPPORTED_SOURCE_TYPES + /** * @param {any} style A v7 or v8 MapLibre style. v7 styles will be migrated to * v8. (There are currently no typescript declarations for v7 styles, hence @@ -84,19 +89,33 @@ export default class Writer extends EventEmitter { */ constructor(style, { highWaterMark = 1024 * 1024 } = {}) { super() + if (!style || !('version' in style)) { + throw new Error('Invalid style') + } + if (style.version !== 7 && style.version !== 8) { + throw new Error(`Invalid style: Unsupported version v${style.version}`) + } + // Basic validation so migrate can work - more validation is done later + if (!Array.isArray(style.layers)) { + throw new Error('Invalid style: missing layers property') + } const styleCopy = clone(style) // This mutates the style, so we work on a clone migrate(styleCopy) - if (styleCopy.version !== 8) { - throw new Error(`Unsupported style version: ${styleCopy.version}`) - } const errors = validateStyleMin(styleCopy) if (errors.length) { - throw new Error(`Invalid style: ${errors.join(', ')}`) + throw new AggregateError(errors, 'Invalid style') } this.#style = styleCopy + for (const [sourceId, source] of Object.entries(this.#style.sources)) { + if (source.type !== 'geojson') continue + // Eagerly add GeoJSON sources - if they reference data via a URL and data + // is not added, these sources will be excluded from the resulting SMP + this.#addSource(sourceId, source) + } + this.#outputStream = new PassThrough({ highWaterMark }) pipeline(this.#archive, this.#outputStream, (err) => { if (err) this.emit('error', err) @@ -110,27 +129,94 @@ export default class Writer extends EventEmitter { return this.#outputStream } + #getBounds() { + /** @type {import('./utils/geo.js').BBox | undefined} */ + let bounds + let maxzoom = 0 + for (const { source } of this.#sources.values()) { + if (source.type === 'geojson') { + if (isEmptyFeatureCollection(source.data)) continue + // GeoJSON source always increases the bounds of the map + const bbox = get2DBBox(source.data.bbox) + bounds = bounds ? unionBBox([bounds, bbox]) : [...bbox] + } else { + // For raster and vector tile sources, a source with a higher max zoom + // overrides the bounds from lower zooms, because bounds from lower zoom + // tiles do not really reflect actual bounds (imagine a source of zoom 0 + // - a single tile covers the whole world) + if (source.maxzoom < maxzoom) continue + if (source.maxzoom === maxzoom) { + bounds = bounds ? unionBBox([bounds, source.bounds]) : source.bounds + } else { + bounds = source.bounds + maxzoom = source.maxzoom + } + } + } + return bounds + } + + #getMaxZoom() { + let maxzoom = 0 + for (const { source } of this.#sources.values()) { + const sourceMaxzoom = + // For GeoJSON sources, the maxzoom is 16 unless otherwise set + source.type === 'geojson' ? source.maxzoom || 16 : source.maxzoom + maxzoom = Math.max(maxzoom, sourceMaxzoom) + } + return maxzoom + } + /** * Add a source definition to the styled map package * * @param {string} sourceId - * @param {TileSource} source - * @returns {void} + * @param {InputSource} source + * @returns {SourceInfo} */ - addSource(sourceId, source) { - if (source.type !== 'raster' && source.type !== 'vector') { - throw new Error(`Unsupported source type: ${source['type']}`) + #addSource(sourceId, source) { + const encodedSourceId = encodeSourceId(this.#sources.size) + // Most of the body of this function is just to keep Typescript happy. + // Makes it more verbose, but makes it more type safe. + const tileSourceOverrides = { + minzoom: 0, + maxzoom: 0, + bounds: /** @type {import('./utils/geo.js').BBox} */ ([...MAX_BOUNDS]), + tiles: /** @type {string[]} */ ([]), } - if (this.#sources.has(sourceId)) { - throw new Error(`${sourceId} already added`) + /** @type {SMPSource} */ + let smpSource + switch (source.type) { + case 'raster': + case 'vector': + smpSource = { + ...excludeKeys(source, ['tiles', 'url']), + ...tileSourceOverrides, + } + break + case 'geojson': + smpSource = { + ...source, + maxzoom: 0, + data: + typeof source.data !== 'string' + ? // Add a bbox property to the GeoJSON data if it doesn't already have one + { ...source.data, bbox: source.data.bbox || bbox(source.data) } + : // If GeoJSON data is referenced by a URL, start with an empty FeatureCollection + { + type: 'FeatureCollection', + features: [], + bbox: [0, 0, 0, 0], + }, + } + break } - this.#sources.set(sourceId, { - source: { - ...excludeKeys(source, ['url', 'tiles']), - maxzoom: 0, - }, - encodedSourceId: encodeSourceId(this.#sources.size), - }) + const sourceInfo = { + source: smpSource, + encodedSourceId, + } + this.#sources.set(sourceId, sourceInfo) + return sourceInfo } /** @@ -140,12 +226,22 @@ export default class Writer extends EventEmitter { * @param {TileInfo} opts */ async addTile(tileData, { z, x, y, sourceId, format }) { - const sourceInfo = this.#sources.get(sourceId) + let sourceInfo = this.#sources.get(sourceId) if (!sourceInfo) { - throw new Error(`Source ${sourceId} must be added before adding tiles`) + const source = this.#style.sources[sourceId] + if (!source) { + throw new Error(`Source not referenced in style.json: ${sourceId}`) + } + if (source.type !== 'raster' && source.type !== 'vector') { + throw new Error(`Unsupported source type: ${source.type}`) + } + sourceInfo = this.#addSource(sourceId, source) } const { source, encodedSourceId } = sourceInfo - source.maxzoom = Math.max(source.maxzoom, z) + // Mainly to keep Typescript happy... + if (source.type !== 'raster' && source.type !== 'vector') { + throw new Error(`Unsupported source type: ${source.type}`) + } if (!format) { const tileDataStream = @@ -168,11 +264,11 @@ export default class Writer extends EventEmitter { const bbox = tileToBBox({ z, x, y }) // We calculate the bounds from the tiles at the max zoom level, because at // lower zooms the tile bbox is much larger than the actual bounding box - if (z > this.#maxzoom) { - this.#maxzoom = z - this.#bounds = bbox - } else if (z === this.#maxzoom) { - this.#bounds = this.#bounds ? unionBBox([this.#bounds, bbox]) : bbox + if (z > source.maxzoom) { + source.maxzoom = z + source.bounds = bbox + } else if (z === source.maxzoom) { + source.bounds = unionBBox([source.bounds, bbox]) } const name = getTileFilename({ sourceId: encodedSourceId, z, x, y, format }) @@ -199,11 +295,11 @@ export default class Writer extends EventEmitter { * @param {object} options * @param {Source} options.json * @param {Source} options.png - * @param {number} options.pixelRatio + * @param {number} [options.pixelRatio] * @param {string} [options.id='default'] * @returns {Promise} */ - async addSprite({ json, png, pixelRatio, id = 'default' }) { + async addSprite({ json, png, pixelRatio = 1, id = 'default' }) { this.#addedSpriteIds.add(id) const jsonName = getSpriteFilename({ id, pixelRatio, ext: '.json' }) const pngName = getSpriteFilename({ id, pixelRatio, ext: '.png' }) @@ -217,9 +313,7 @@ export default class Writer extends EventEmitter { * Add glyphs to the styled map package * * @param {Source} glyphData - * @param {object} options - * @param {string} options.font - * @param {GlyphRange} options.range + * @param {GlyphInfo} glyphInfo * @returns {Promise} */ addGlyphs(glyphData, { font: fontName, range }) { @@ -291,27 +385,38 @@ export default class Writer extends EventEmitter { }) } - // Add a tile URL (with custom schema) for each source - for (const sourceId of Object.keys(this.#style.sources)) { - const sourceInfo = this.#sources.get(sourceId) - if (!sourceInfo) { - // TODO: Handle unsupported source types + this.#style.sources = {} + for (const [sourceId, { source, encodedSourceId, format = 'mvt' }] of this + .#sources) { + if (source.type === 'geojson' && isEmptyFeatureCollection(source.data)) { + // Skip empty GeoJSON sources continue } - const { encodedSourceId, source, format = 'mvt' } = sourceInfo - source.tiles = [getTileUri({ sourceId: encodedSourceId, format })] this.#style.sources[sourceId] = source + if (!('tiles' in source)) continue + // Add a tile URL (with custom schema) for each tile source + source.tiles = [getTileUri({ sourceId: encodedSourceId, format })] } + this.#style.layers = this.#style.layers.filter( + (layer) => !('source' in layer) || !!this.#style.sources[layer.source], + ) + /** @type {Record} */ const metadata = this.#style.metadata || (this.#style.metadata = {}) - if (this.#bounds) { - metadata['smp:bounds'] = this.#bounds - const [w, s, e, n] = this.#bounds + const bounds = this.#getBounds() + if (bounds) { + metadata['smp:bounds'] = bounds + const [w, s, e, n] = bounds this.#style.center = [w + (e - w) / 2, s + (n - s) / 2] } - metadata['smp:maxzoom'] = this.#maxzoom - this.#style.zoom = Math.max(0, this.#maxzoom - 2) + metadata['smp:maxzoom'] = this.#getMaxZoom() + /** @type {Record} */ + metadata['smp:sourceFolders'] = {} + for (const [sourceId, { encodedSourceId }] of this.#sources) { + metadata['smp:sourceFolders'][sourceId] = encodedSourceId + } + this.#style.zoom = Math.max(0, this.#getMaxZoom() - 2) } /** @@ -356,3 +461,18 @@ function convertSource(source) { ? Buffer.from(source.buffer, source.byteOffset, source.length) : source } + +/** @param {import('geojson').GeoJSON} data */ +function isEmptyFeatureCollection(data) { + return data.type === 'FeatureCollection' && data.features.length === 0 +} + +/** + * Strictly a GeoJSON bounding box could be 3D, but we only support 2D bounding + * @param {import('geojson').BBox} bbox + * @returns {import('./utils/geo.js').BBox} + */ +function get2DBBox(bbox) { + if (bbox.length === 4) return bbox + return [bbox[0], bbox[1], bbox[3], bbox[4]] +} diff --git a/package-lock.json b/package-lock.json index a9ba1f1..525af4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@fastify/static": "^7.0.4", "@mapbox/sphericalmercator": "^1.2.0", "@maplibre/maplibre-gl-style-spec": "^20.3.1", + "@placemarkio/check-geojson": "^0.1.12", + "@turf/bbox": "^7.1.0", "@turf/helpers": "^7.1.0", "ansi-diff": "^1.2.0", "archiver": "^7.0.1", @@ -32,6 +34,7 @@ "pretty-bytes": "^6.1.1", "pretty-ms": "^9.1.0", "readable-stream": "^4.5.2", + "temp-dir": "^3.0.0", "yauzl-promise": "^4.0.0", "yocto-queue": "^1.1.1" }, @@ -40,21 +43,30 @@ }, "devDependencies": { "@eslint/js": "^9.9.1", + "@jsquash/jpeg": "^1.4.0", + "@jsquash/png": "^3.0.1", + "@stealthybox/jpg-stream": "^1.1.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/archiver": "^6.0.2", "@types/eslint": "^9.6.1", "@types/eslint__js": "^8.42.3", + "@types/geojson": "^7946.0.14", "@types/mapbox__sphericalmercator": "^1.2.3", "@types/node": "^20.16.3", "@types/readable-stream": "^4.0.15", "@types/yauzl-promise": "^4.0.1", "ava": "^6.1.3", + "block-stream2": "^2.1.0", "eslint": "^9.9.1", "globals": "^15.9.0", "husky": "^9.1.5", + "jpg-stream": "^1.1.2", "lint-staged": "^15.2.10", + "pixel-stream": "^1.0.3", "playwright": "^1.46.1", + "png-stream": "^1.0.5", "prettier": "^3.3.3", + "random-bytes-readable-stream": "^3.0.0", "type-fest": "^4.26.0", "typescript": "5.5.4" } @@ -730,6 +742,20 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsquash/jpeg": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jsquash/jpeg/-/jpeg-1.4.0.tgz", + "integrity": "sha512-I/uGQ5Gk3qOEQNufUcR9boZ0qH+eoXW7cp0mW9eNInlgjRXwEhepvfbnutQ+RM1dNeuIHoOM5YVCEK1y/ATipQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@jsquash/png": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@jsquash/png/-/png-3.0.1.tgz", + "integrity": "sha512-Bnvv93Y5LL92cuk2r2gpV+9JKuDo2/w7bOODw1iPxk8VARknky0sS1tSDgMosUdhNb4CdMlcCm3TMzTaqa3zZw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@lukeed/ms": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", @@ -1095,6 +1121,15 @@ "node": ">=14" } }, + "node_modules/@placemarkio/check-geojson": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@placemarkio/check-geojson/-/check-geojson-0.1.12.tgz", + "integrity": "sha512-sSNPtPDVB0oKwImi4NYg1LVE2QSCIqs/jIRmu8U4fQVWdRjlGy+C/n7AbNO2FycE9rVWtz256f33aMGzvKC7gg==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/@rollup/pluginutils": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", @@ -1132,6 +1167,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@stealthybox/jpg-stream": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@stealthybox/jpg-stream/-/jpg-stream-1.1.2.tgz", + "integrity": "sha512-PkPMXND6NwdJO+Dv8CaekJj2TOljEYjR1POfHPWHBY7aVn4KUAuuGw2vLXXNA10Yo/J4LMxUiZIFqJOHlVk5tQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "exif-reader": "^1.0.0", + "pixel-stream": "^1.0.3" + } + }, "node_modules/@trivago/prettier-plugin-sort-imports": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", @@ -1155,6 +1201,21 @@ } } }, + "node_modules/@turf/bbox": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.1.0.tgz", + "integrity": "sha512-PdWPz9tW86PD78vSZj2fiRaB8JhUHy6piSa/QXb83lucxPK+HTAdzlDQMTKj5okRCU8Ox/25IR2ep9T8NdopRA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.1.0", + "@turf/meta": "^7.1.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.6.2" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@turf/helpers": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.1.0.tgz", @@ -1167,6 +1228,19 @@ "url": "https://opencollective.com/turf" } }, + "node_modules/@turf/meta": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.1.0.tgz", + "integrity": "sha512-ZgGpWWiKz797Fe8lfRj7HKCkGR+nSJ/5aKXMyofCvLSc2PuYJs/qyyifDPWjASQQCzseJ7AlF2Pc/XQ/3XkkuA==", + "license": "MIT", + "dependencies": { + "@turf/helpers": "^7.1.0", + "@types/geojson": "^7946.0.10" + }, + "funding": { + "url": "https://opencollective.com/turf" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -1213,7 +1287,8 @@ "node_modules/@types/geojson": { "version": "7946.0.14", "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz", - "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==" + "integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg==", + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -1760,6 +1835,68 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "integrity": "sha512-njlCs8XLBIK7LCChTWfzWuIAxkpmmLXcL7/igCofFT1B039Sz0IPnAmosN5QaO22lU4qr8LcUz2ojUlE6pLkRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~1.0.26" + } + }, + "node_modules/bl/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/bl/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/block-stream2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/blueimp-md5": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", @@ -1819,6 +1956,16 @@ "node": ">=8.0.0" } }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/buffer-peek-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-peek-stream/-/buffer-peek-stream-1.1.0.tgz", @@ -2838,6 +2985,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/exif-reader": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/exif-reader/-/exif-reader-1.2.0.tgz", + "integrity": "sha512-C9jsLWxaUh9/gNwo9Ouf9jxP0vGn/2vkhu89wbPFyFfpfl8WPbwLrvrb7h9Q6oVvkyvA+e4lXgOllW+/bAk0RQ==", + "dev": true, + "license": "MIT" + }, "node_modules/extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -3919,6 +4073,17 @@ "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", "dev": true }, + "node_modules/jpg-stream": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jpg-stream/-/jpg-stream-1.1.2.tgz", + "integrity": "sha512-ZQI+CgQhQ6vc4d4fUlF7wXXhLV00Z6UvWh58Pg7b0073UP1dIQn0aEmJwWAg+r4Z4//6+ObCOzEJbYaaqG0b3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "exif-reader": "^1.0.0", + "pixel-stream": "^1.0.3" + } + }, "node_modules/js-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", @@ -5006,6 +5171,16 @@ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.0.tgz", "integrity": "sha512-/MyYDxttz7DfGMMHiysAsFE4qF+pQYAA8ziO/3NcRVrQ5fSk+Mns4QZA/oRPFzvcqNoVJXQNWNAsdwBXLUkQKw==" }, + "node_modules/pixel-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pixel-stream/-/pixel-stream-1.0.3.tgz", + "integrity": "sha512-Ea322PoBS7oIos6fsc9YivK21foZRGrt319cj8wouPNlXsD55AIZ2OZ2Vf+nwsrQQKWGbjLcVsoHCoy/Ee2KXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shallow-copy": "0.0.1" + } + }, "node_modules/playwright": { "version": "1.46.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", @@ -5051,6 +5226,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/png-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/png-stream/-/png-stream-1.0.5.tgz", + "integrity": "sha512-F+5Cqjuy0gToeq8iX6Skalu4UQo5VVkc4mdLvkZaOA21yiiFlblXULDT5MhlvtOlaGyL2uqHKIg89JuIVSxu1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^0.9.3", + "buffer-crc32": "^0.2.3", + "buffer-equal": "^0.0.1", + "pixel-stream": "^1.0.3" + } + }, + "node_modules/png-stream/node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5174,6 +5372,19 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, + "node_modules/random-bytes-readable-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/random-bytes-readable-stream/-/random-bytes-readable-stream-3.0.0.tgz", + "integrity": "sha512-GrDPlkikCTvAOClNgxbbZg2Xq84lmBqhCkkPuDh92vgsDfW9dvrp4kQpE7AOL/3B3j3BATkQocpnCp/bUBn9NQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", @@ -5499,6 +5710,13 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "node_modules/shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", + "dev": true, + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5982,7 +6200,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true, + "license": "MIT", "engines": { "node": ">=14.16" } @@ -7125,6 +7343,18 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "@jsquash/jpeg": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@jsquash/jpeg/-/jpeg-1.4.0.tgz", + "integrity": "sha512-I/uGQ5Gk3qOEQNufUcR9boZ0qH+eoXW7cp0mW9eNInlgjRXwEhepvfbnutQ+RM1dNeuIHoOM5YVCEK1y/ATipQ==", + "dev": true + }, + "@jsquash/png": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@jsquash/png/-/png-3.0.1.tgz", + "integrity": "sha512-Bnvv93Y5LL92cuk2r2gpV+9JKuDo2/w7bOODw1iPxk8VARknky0sS1tSDgMosUdhNb4CdMlcCm3TMzTaqa3zZw==", + "dev": true + }, "@lukeed/ms": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", @@ -7328,6 +7558,11 @@ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "optional": true }, + "@placemarkio/check-geojson": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/@placemarkio/check-geojson/-/check-geojson-0.1.12.tgz", + "integrity": "sha512-sSNPtPDVB0oKwImi4NYg1LVE2QSCIqs/jIRmu8U4fQVWdRjlGy+C/n7AbNO2FycE9rVWtz256f33aMGzvKC7gg==" + }, "@rollup/pluginutils": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", @@ -7352,6 +7587,16 @@ "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", "dev": true }, + "@stealthybox/jpg-stream": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@stealthybox/jpg-stream/-/jpg-stream-1.1.2.tgz", + "integrity": "sha512-PkPMXND6NwdJO+Dv8CaekJj2TOljEYjR1POfHPWHBY7aVn4KUAuuGw2vLXXNA10Yo/J4LMxUiZIFqJOHlVk5tQ==", + "dev": true, + "requires": { + "exif-reader": "^1.0.0", + "pixel-stream": "^1.0.3" + } + }, "@trivago/prettier-plugin-sort-imports": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", @@ -7366,6 +7611,17 @@ "lodash": "^4.17.21" } }, + "@turf/bbox": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-7.1.0.tgz", + "integrity": "sha512-PdWPz9tW86PD78vSZj2fiRaB8JhUHy6piSa/QXb83lucxPK+HTAdzlDQMTKj5okRCU8Ox/25IR2ep9T8NdopRA==", + "requires": { + "@turf/helpers": "^7.1.0", + "@turf/meta": "^7.1.0", + "@types/geojson": "^7946.0.10", + "tslib": "^2.6.2" + } + }, "@turf/helpers": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.1.0.tgz", @@ -7375,6 +7631,15 @@ "tslib": "^2.6.2" } }, + "@turf/meta": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-7.1.0.tgz", + "integrity": "sha512-ZgGpWWiKz797Fe8lfRj7HKCkGR+nSJ/5aKXMyofCvLSc2PuYJs/qyyifDPWjASQQCzseJ7AlF2Pc/XQ/3XkkuA==", + "requires": { + "@turf/helpers": "^7.1.0", + "@types/geojson": "^7946.0.10" + } + }, "@tybys/wasm-util": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", @@ -7845,6 +8110,63 @@ "file-uri-to-path": "1.0.0" } }, + "bl": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz", + "integrity": "sha512-njlCs8XLBIK7LCChTWfzWuIAxkpmmLXcL7/igCofFT1B039Sz0IPnAmosN5QaO22lU4qr8LcUz2ojUlE6pLkRQ==", + "dev": true, + "requires": { + "readable-stream": "~1.0.26" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + } + } + }, + "block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "dev": true, + "requires": { + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "blueimp-md5": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", @@ -7884,6 +8206,12 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==" }, + "buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true + }, "buffer-peek-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-peek-stream/-/buffer-peek-stream-1.1.0.tgz", @@ -8596,6 +8924,12 @@ } } }, + "exif-reader": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/exif-reader/-/exif-reader-1.2.0.tgz", + "integrity": "sha512-C9jsLWxaUh9/gNwo9Ouf9jxP0vGn/2vkhu89wbPFyFfpfl8WPbwLrvrb7h9Q6oVvkyvA+e4lXgOllW+/bAk0RQ==", + "dev": true + }, "extend-shallow": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", @@ -9353,6 +9687,16 @@ "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", "dev": true }, + "jpg-stream": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/jpg-stream/-/jpg-stream-1.1.2.tgz", + "integrity": "sha512-ZQI+CgQhQ6vc4d4fUlF7wXXhLV00Z6UvWh58Pg7b0073UP1dIQn0aEmJwWAg+r4Z4//6+ObCOzEJbYaaqG0b3w==", + "dev": true, + "requires": { + "exif-reader": "^1.0.0", + "pixel-stream": "^1.0.3" + } + }, "js-string-escape": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", @@ -10110,6 +10454,15 @@ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==" }, + "pixel-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pixel-stream/-/pixel-stream-1.0.3.tgz", + "integrity": "sha512-Ea322PoBS7oIos6fsc9YivK21foZRGrt319cj8wouPNlXsD55AIZ2OZ2Vf+nwsrQQKWGbjLcVsoHCoy/Ee2KXg==", + "dev": true, + "requires": { + "shallow-copy": "0.0.1" + } + }, "playwright": { "version": "1.46.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.46.1.tgz", @@ -10135,6 +10488,26 @@ "irregular-plurals": "^3.3.0" } }, + "png-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/png-stream/-/png-stream-1.0.5.tgz", + "integrity": "sha512-F+5Cqjuy0gToeq8iX6Skalu4UQo5VVkc4mdLvkZaOA21yiiFlblXULDT5MhlvtOlaGyL2uqHKIg89JuIVSxu1w==", + "dev": true, + "requires": { + "bl": "^0.9.3", + "buffer-crc32": "^0.2.3", + "buffer-equal": "^0.0.1", + "pixel-stream": "^1.0.3" + }, + "dependencies": { + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + } + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -10211,6 +10584,12 @@ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" }, + "random-bytes-readable-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/random-bytes-readable-stream/-/random-bytes-readable-stream-3.0.0.tgz", + "integrity": "sha512-GrDPlkikCTvAOClNgxbbZg2Xq84lmBqhCkkPuDh92vgsDfW9dvrp4kQpE7AOL/3B3j3BATkQocpnCp/bUBn9NQ==", + "dev": true + }, "readable-stream": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", @@ -10445,6 +10824,12 @@ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" }, + "shallow-copy": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", + "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10790,8 +11175,7 @@ "temp-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", - "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", - "dev": true + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==" }, "text-decoder": { "version": "1.1.1", diff --git a/package.json b/package.json index f8d2e0d..852e07d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "npm run lint && node --test", "prepare": "husky", "lint": "eslint ." }, @@ -27,6 +27,8 @@ "@fastify/static": "^7.0.4", "@mapbox/sphericalmercator": "^1.2.0", "@maplibre/maplibre-gl-style-spec": "^20.3.1", + "@placemarkio/check-geojson": "^0.1.12", + "@turf/bbox": "^7.1.0", "@turf/helpers": "^7.1.0", "ansi-diff": "^1.2.0", "archiver": "^7.0.1", @@ -47,26 +49,36 @@ "pretty-bytes": "^6.1.1", "pretty-ms": "^9.1.0", "readable-stream": "^4.5.2", + "temp-dir": "^3.0.0", "yauzl-promise": "^4.0.0", "yocto-queue": "^1.1.1" }, "devDependencies": { "@eslint/js": "^9.9.1", + "@jsquash/jpeg": "^1.4.0", + "@jsquash/png": "^3.0.1", + "@stealthybox/jpg-stream": "^1.1.2", "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/archiver": "^6.0.2", "@types/eslint": "^9.6.1", "@types/eslint__js": "^8.42.3", + "@types/geojson": "^7946.0.14", "@types/mapbox__sphericalmercator": "^1.2.3", "@types/node": "^20.16.3", "@types/readable-stream": "^4.0.15", "@types/yauzl-promise": "^4.0.1", "ava": "^6.1.3", + "block-stream2": "^2.1.0", "eslint": "^9.9.1", "globals": "^15.9.0", "husky": "^9.1.5", + "jpg-stream": "^1.1.2", "lint-staged": "^15.2.10", + "pixel-stream": "^1.0.3", "playwright": "^1.46.1", + "png-stream": "^1.0.5", "prettier": "^3.3.3", + "random-bytes-readable-stream": "^3.0.0", "type-fest": "^4.26.0", "typescript": "5.5.4" }, diff --git a/test/download-write-read.js b/test/download-write-read.js new file mode 100644 index 0000000..7292604 --- /dev/null +++ b/test/download-write-read.js @@ -0,0 +1,43 @@ +import { validateStyleMin } from '@maplibre/maplibre-gl-style-spec' +import tempDir from 'temp-dir' + +import assert from 'node:assert' +import { randomBytes } from 'node:crypto' +import fs from 'node:fs' +import fsPromises from 'node:fs/promises' +import path from 'node:path' +import { pipeline } from 'node:stream/promises' +import test from 'node:test' + +import { download, Reader } from '../lib/index.js' + +const TEST_MAP_STYLE = 'https://demotiles.maplibre.org/style.json' +const TEST_MAP_AREA = /** @type {const} */ ([5.956, 45.818, 10.492, 47.808]) // Switzerland + +/** @param {import('node:test').TestContext} t */ +function tempFile(t) { + const temporaryPath = path.join(tempDir, randomBytes(16).toString('hex')) + t.after(async () => { + await fsPromises.rm(temporaryPath, { + recursive: true, + force: true, + maxRetries: 2, + }) + }) + return temporaryPath +} + +test('Everything written can be read', async (t) => { + const smpFilePath = tempFile(t) + const smpReadStream = download({ + styleUrl: TEST_MAP_STYLE, + bbox: [...TEST_MAP_AREA], + maxzoom: 5, + }) + await pipeline(smpReadStream, fs.createWriteStream(smpFilePath)) + + const reader = new Reader(smpFilePath) + + const smpStyle = await reader.getStyle() + assert.deepEqual(validateStyleMin(smpStyle), [], 'Style is valid') +}) diff --git a/test/fixtures/invalid-styles/empty.json b/test/fixtures/invalid-styles/empty.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/test/fixtures/invalid-styles/empty.json @@ -0,0 +1 @@ +{} diff --git a/test/fixtures/invalid-styles/missing-source.json b/test/fixtures/invalid-styles/missing-source.json new file mode 100644 index 0000000..83437aa --- /dev/null +++ b/test/fixtures/invalid-styles/missing-source.json @@ -0,0 +1,10 @@ +{ + "version": 8, + "layers": [ + { + "id": "vector-source-missing", + "type": "line", + "source": "vector-source-id" + } + ] +} diff --git a/test/fixtures/invalid-styles/no-layers.json b/test/fixtures/invalid-styles/no-layers.json new file mode 100644 index 0000000..2198215 --- /dev/null +++ b/test/fixtures/invalid-styles/no-layers.json @@ -0,0 +1,4 @@ +{ + "version": 8, + "sources": {} +} diff --git a/test/fixtures/invalid-styles/no-sources.json b/test/fixtures/invalid-styles/no-sources.json new file mode 100644 index 0000000..857997f --- /dev/null +++ b/test/fixtures/invalid-styles/no-sources.json @@ -0,0 +1,4 @@ +{ + "version": 8, + "layers": [] +} diff --git a/test/fixtures/invalid-styles/null.json b/test/fixtures/invalid-styles/null.json new file mode 100644 index 0000000..19765bd --- /dev/null +++ b/test/fixtures/invalid-styles/null.json @@ -0,0 +1 @@ +null diff --git a/test/fixtures/invalid-styles/unsupported-version.json b/test/fixtures/invalid-styles/unsupported-version.json new file mode 100644 index 0000000..f7eddb1 --- /dev/null +++ b/test/fixtures/invalid-styles/unsupported-version.json @@ -0,0 +1,5 @@ +{ + "version": 6, + "layers": [], + "sources": {} +} diff --git a/test/fixtures/valid-styles/external-geojson.input.json b/test/fixtures/valid-styles/external-geojson.input.json new file mode 100644 index 0000000..8315de8 --- /dev/null +++ b/test/fixtures/valid-styles/external-geojson.input.json @@ -0,0 +1,66 @@ +{ + "name": "MapLibre", + "zoom": 0.8619833357855968, + "pitch": 0, + "center": [17.65431710431244, 32.954120326746775], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": ["all"], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "coastline", + "type": "line", + "paint": { + "line-blur": 0.5, + "line-color": "#198EC8", + "line-width": { + "stops": [ + [0, 2], + [6, 6], + [14, 9], + [22, 18] + ] + } + }, + "filter": ["all"], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 0, + "source-layer": "countries" + }, + { + "id": "crimea-fill", + "type": "fill", + "source": "crimea", + "paint": { + "fill-color": "#D6C7FF" + } + } + ], + "bearing": 0, + "sources": { + "crimea": { + "type": "geojson", + "data": "https://example.com/something.json" + }, + "maplibre": { + "url": "https://demotiles.maplibre.org/tiles/tiles.json", + "type": "vector" + } + }, + "version": 8 +} diff --git a/test/fixtures/valid-styles/external-geojson.output.json b/test/fixtures/valid-styles/external-geojson.output.json new file mode 100644 index 0000000..f8c342f --- /dev/null +++ b/test/fixtures/valid-styles/external-geojson.output.json @@ -0,0 +1,93 @@ +{ + "name": "MapLibre", + "zoom": 14, + "pitch": 0, + "center": [ + 0, + 0 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": [ + "all" + ], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "coastline", + "type": "line", + "paint": { + "line-blur": 0.5, + "line-color": "#198EC8", + "line-width": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 0, + 2, + 6, + 6, + 14, + 9, + 22, + 18 + ] + }, + "filter": [ + "all" + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 0, + "source-layer": "countries" + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "type": "vector", + "minzoom": 0, + "maxzoom": 0, + "bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "tiles": [ + "smp://maps.v1/s/1/{z}/{x}/{y}.mvt.gz" + ] + } + }, + "version": 8, + "metadata": { + "smp:bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "smp:maxzoom": 16, + "smp:sourceFolders": { + "crimea": "0", + "maplibre": "1" + } + } +} \ No newline at end of file diff --git a/test/fixtures/valid-styles/inline-geojson.input.json b/test/fixtures/valid-styles/inline-geojson.input.json new file mode 100644 index 0000000..9462a01 --- /dev/null +++ b/test/fixtures/valid-styles/inline-geojson.input.json @@ -0,0 +1,421 @@ +{ + "name": "MapLibre", + "zoom": 0.8619833357855968, + "pitch": 0, + "center": [17.65431710431244, 32.954120326746775], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": ["all"], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "crimea-fill", + "type": "fill", + "source": "crimea", + "paint": { + "fill-color": "#D6C7FF" + } + } + ], + "bearing": 0, + "sources": { + "crimea": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [34.00905273547181, 46.55925987559425], + [33.64325260204026, 46.34533545368038], + [33.628682598560204, 46.12569762665683], + [33.64292861730951, 46.10476396128129], + [33.648473474905984, 46.09033047763651], + [33.63876482059936, 46.077976784785335], + [33.62782672238245, 46.06747935719011], + [33.62911357645072, 46.05708111413949], + [33.642686868727424, 46.02192963417187], + [33.6429723910654, 46.01521185644708], + [33.636224138774026, 46.006705833212465], + [33.63052626465907, 45.99692992186792], + [33.63193836679693, 45.988472992911284], + [33.64276684834442, 45.984575360297384], + [33.646928693041986, 45.97981936210982], + [33.638745893564305, 45.96829769147004], + [33.61958133326394, 45.951176418494185], + [33.63181380398527, 45.9445404758078], + [33.638921676216, 45.94737012930554], + [33.64561542516918, 45.95403251372139], + [33.65666403976448, 45.95687114427736], + [33.6825817382811, 45.95878100879199], + [33.738791807037614, 45.94836945227263], + [33.758180142697, 45.94072970008301], + [33.77735917288169, 45.92923970233858], + [33.75927796793485, 45.92241179584471], + [33.72529865009221, 45.91587363154565], + [33.70875012326826, 45.91008760988058], + [33.69378857293381, 45.91480850795665], + [33.69092650243843, 45.89657370898402], + [33.693592356906805, 45.87271465766318], + [33.69226765972388, 45.86041392418218], + [33.6704813511748, 45.8584273836251], + [33.65936345808916, 45.85944682601249], + [33.653870582376726, 45.86425922279372], + [33.65107345584843, 45.87089907254003], + [33.63067378180233, 45.88040685247182], + [33.61945300059696, 45.88147266102649], + [33.60987421595539, 45.88048951126686], + [33.59906957603934, 45.877610457390375], + [33.57828877687868, 45.86810261756233], + [33.55357394560386, 45.84700625141778], + [33.530220674480375, 45.84221983655459], + [33.5192297395441, 45.84121682367507], + [33.50832088442496, 45.84313067048083], + [33.48901101848409, 45.85268298292175], + [33.482152996405716, 45.854578171799005], + [33.46719955896293, 45.849912739405056], + [33.42447496599681, 45.83075886348303], + [33.40940172404095, 45.82691953557702], + [33.37918350072067, 45.802867525073566], + [33.37362145339398, 45.79619281922518], + [33.33805543634864, 45.78577808972071], + [33.26498872665803, 45.75410774187094], + [33.22887541283427, 45.75131270772724], + [33.19548267281132, 45.7644887297206], + [33.1789202379222, 45.78010311364778], + [33.1688456078636, 45.78470227904205], + [33.161012432811674, 45.77921593899549], + [33.15951585299757, 45.76864464913777], + [33.165962301438725, 45.762685940125465], + [33.1750888126426, 45.759218220695715], + [33.181464829753, 45.75490447884948], + [33.17613930782352, 45.7437961960276], + [33.16369168844906, 45.735912015025065], + [32.93692665480876, 45.662114646778264], + [32.86839112407645, 45.63044340698664], + [32.83803944575723, 45.60834075026611], + [32.82702772424804, 45.59576101516498], + [32.82433467080986, 45.58705137380335], + [32.82563941622885, 45.579605763895614], + [32.82993674258438, 45.56978311819469], + [32.82851940940563, 45.56227808675749], + [32.813310142795274, 45.55930933158257], + [32.80213583657516, 45.560145780074464], + [32.78258622159436, 45.565158335073846], + [32.77333922465823, 45.56689313356526], + [32.758306734735356, 45.565030173463356], + [32.750177256846115, 45.55943726334968], + [32.74340732630185, 45.55261895849793], + [32.73524549539499, 45.54598788110354], + [32.72031700779701, 45.53735927760957], + [32.70536040418847, 45.53169142131733], + [32.68589438933773, 45.52663379187257], + [32.66370583186284, 45.52563107058867], + [32.64312077736798, 45.52188979044979], + [32.525284074162556, 45.45838108691365], + [32.49490411219156, 45.43524910229854], + [32.48107654411925, 45.408986638827514], + [32.48514589713025, 45.39458067125969], + [32.51256939517424, 45.34060655033625], + [32.535915460470335, 45.33777248012882], + [32.57027153843481, 45.32510892683359], + [32.590830644991826, 45.32038723212662], + [32.66380378113439, 45.320421746458976], + [32.67760722618917, 45.32609231279554], + [32.71316246802607, 45.353283572618125], + [32.72817188836078, 45.36074681043402], + [32.750518060251466, 45.36371725645313], + [32.89973931692998, 45.35412322462227], + [32.941197846443885, 45.34245505845169], + [32.97701667405008, 45.32596743563991], + [33.04296090827762, 45.2853982930032], + [33.05274355585479, 45.28154273654923], + [33.06850284417635, 45.27703461892352], + [33.07825272648239, 45.272210805127315], + [33.089426322403455, 45.25656353201492], + [33.09897492343546, 45.247820101667884], + [33.12384611720435, 45.238235755071685], + [33.15767197859745, 45.20755227709648], + [33.172959979330074, 45.19681657531794], + [33.21837666514142, 45.187878368659824], + [33.24017433636709, 45.180191106261134], + [33.248571989896675, 45.16588271012458], + [33.259649216030766, 45.155918961282026], + [33.28309785485047, 45.16064860772312], + [33.31767999550894, 45.17535522412791], + [33.35458473323109, 45.18598673360148], + [33.39725661527919, 45.18973663076909], + [33.41344561756824, 45.18490731877088], + [33.468468576977216, 45.149132412229676], + [33.537128652906205, 45.11719769268973], + [33.56161328289443, 45.094099022711475], + [33.57837628774928, 45.053145935448015], + [33.58247744978442, 45.027377243150454], + [33.5851414316958, 45.01816461606674], + [33.6031021265521, 44.993103583251695], + [33.605922209331794, 44.986905272229734], + [33.60843524291815, 44.97039962759274], + [33.61943161357851, 44.93184946652454], + [33.619484500808824, 44.90819321920554], + [33.61549738593425, 44.88894092276257], + [33.608561183117274, 44.871288478948514], + [33.59889474705494, 44.859790298912856], + [33.55904244709464, 44.850057575124595], + [33.54667558363471, 44.83724531175508], + [33.53701832136994, 44.81871953508235], + [33.5303157846202, 44.798338017069625], + [33.5249116915937, 44.78918633101301], + [33.51669091675143, 44.784809980590666], + [33.524785531609865, 44.77183212449111], + [33.5302902535075, 44.75724515985675], + [33.53710734694323, 44.73034290771247], + [33.54650992495621, 44.70989226909535], + [33.5481286806762, 44.699106546699085], + [33.543995566510915, 44.68230506537358], + [33.53580273994743, 44.6726082589706], + [33.52337411931097, 44.661863083605255], + [33.515320778874354, 44.6491266698327], + [33.516377841582795, 44.63464990118433], + [33.52466971637648, 44.62863961572572], + [33.557474298027785, 44.62473000923737], + [33.5710648827386, 44.620853511273225], + [33.55105839203679, 44.61506440493406], + [33.499905706797676, 44.61452599304897], + [33.48451102966331, 44.60992438254493], + [33.47658499621011, 44.60714391514574], + [33.46705078205747, 44.60616254193252], + [33.44476599234898, 44.607062134677875], + [33.4353466482458, 44.60509936890821], + [33.413591053005575, 44.593500212748125], + [33.40543527945235, 44.59055535193136], + [33.37510958624222, 44.58564691897425], + [33.37074452434078, 44.58851022190515], + [33.372237834990756, 44.576810695127364], + [33.37913003799301, 44.56412673079859], + [33.48759131590526, 44.51024086451031], + [33.50011215135888, 44.50041002882833], + [33.517917009115365, 44.49074142372788], + [33.53836387802215, 44.49164280212756], + [33.56041892763031, 44.4966411022441], + [33.57822378538677, 44.497542389459795], + [33.59062975079095, 44.48975808594983], + [33.619577003408466, 44.46229988129974], + [33.62635433636015, 44.45336293328907], + [33.63175322871038, 44.434828756313124], + [33.645537634715026, 44.42498521035591], + [33.721007257593925, 44.39946630464436], + [33.74168386660085, 44.39560878121904], + [33.80727466517129, 44.39454176175843], + [33.81841706002561, 44.39552670349164], + [33.83909366903248, 44.40143600575672], + [33.85149963444792, 44.40143600575945], + [33.91467816197718, 44.38387049706651], + [33.938111652185, 44.38083293528811], + [33.957065210440874, 44.38272116790142], + [34.06614966692763, 44.42019923628979], + [34.088893936836286, 44.42200415824283], + [34.10279321289039, 44.42487551014821], + [34.135933345669, 44.44163597968952], + [34.14696087047267, 44.44959070749778], + [34.16058918507403, 44.466287285335795], + [34.170123399227776, 44.48186111741296], + [34.182759104731986, 44.49267838558103], + [34.22923417224524, 44.49949719774551], + [34.24301857824986, 44.50744404277697], + [34.263903954150294, 44.53186886058606], + [34.26631622520165, 44.53555362837611], + [34.26631622520165, 44.54153064468656], + [34.27033667695244, 44.545378535987936], + [34.2757355693048, 44.54644280144541], + [34.285384653508004, 44.54562413743594], + [34.299973149863405, 44.54554227040174], + [34.32260254971496, 44.543577427039224], + [34.3308731933177, 44.54546040325087], + [34.340292537420794, 44.55798473830754], + [34.38042135640006, 44.631830317636684], + [34.41495238900856, 44.673669777529994], + [34.424193090575585, 44.68239452736094], + [34.42959198292681, 44.68884644523774], + [34.469399167794535, 44.730194532749294], + [34.47376422969597, 44.73011292571252], + [34.47376422969597, 44.72635887754967], + [34.475142670296464, 44.723502373339585], + [34.499724861011515, 44.74292382044041], + [34.532800295801195, 44.752620844929055], + [34.61217550038418, 44.76534519537847], + [34.65065696715081, 44.777088262503725], + [34.72084256772871, 44.811080759265764], + [34.756796893391225, 44.82094054159748], + [34.82646979041766, 44.81208604604609], + [34.84289620758207, 44.816893835303176], + [34.856910353686715, 44.82373813182468], + [34.889648317948144, 44.817871641692506], + [34.90733830566026, 44.820886440346584], + [34.922960632465504, 44.83050015059965], + [34.92950822531711, 44.83652826953224], + [34.94179932067178, 44.84019370922482], + [34.95282684547897, 44.841415470643284], + [34.98567967978991, 44.840275160795755], + [35.0053224583441, 44.83538786296728], + [35.017958163849414, 44.82219008824552], + [35.02703289780189, 44.80890779582285], + [35.037933245998005, 44.79869792240089], + [35.08073333784134, 44.793525442788905], + [35.1080207326404, 44.824553365795765], + [35.130368105574235, 44.86879838545747], + [35.15485200090768, 44.90071251697748], + [35.17111229780758, 44.90746386008772], + [35.21522068940149, 44.91421441031795], + [35.233163085981715, 44.925728224907715], + [35.25636688416236, 44.95896657181197], + [35.27300098099195, 44.96690119386028], + [35.29748487632534, 44.95605693543271], + [35.30496087491386, 44.96121482614441], + [35.315240372954605, 44.965711070514175], + [35.31935217217088, 44.96941359539801], + [35.36757236298112, 44.94362319076086], + [35.36103086422793, 44.97364475976596], + [35.362152264014156, 44.98593980935419], + [35.374674561627444, 44.997835734117416], + [35.389439658813274, 45.00180049366759], + [35.42270785247763, 45.00087540764923], + [35.43504325012745, 45.00470780964241], + [35.43504325012745, 45.011446929213974], + [35.40631957913584, 45.02015821022701], + [35.40089948016896, 45.025046135473445], + [35.39790908073891, 45.03482073400548], + [35.40052568024015, 45.042216617888045], + [35.40631957913584, 45.051328088783805], + [35.40744097892215, 45.06294640963205], + [35.41734667704213, 45.0708666385693], + [35.469304867139925, 45.10068964922732], + [35.5070260597534, 45.113341616151644], + [35.54758335202416, 45.12019982412133], + [35.59019654390909, 45.11993606213795], + [35.63411803553862, 45.11439677872579], + [35.70669729572677, 45.09480210570922], + [35.771782422456766, 45.06572995732262], + [35.78430472007, 45.057941041321754], + [35.81250040352472, 45.031852200991295], + [35.81941570220667, 45.021152336906454], + [35.82763930064016, 44.99895365027004], + [35.848198296721705, 44.99208088455586], + [35.916977483614176, 45.00172895661731], + [35.99360646900681, 44.997896355361604], + [36.00893226608571, 45.00926125333629], + [36.02539976723364, 45.03288661039673], + [36.047827762958946, 45.048074065419456], + [36.078666257082034, 45.03883000769565], + [36.079137312377895, 45.046610970582435], + [36.135020401727616, 45.02125162210126], + [36.2241716847341, 45.00751061631556], + [36.24398308095806, 45.011474706353084], + [36.24828178013877, 45.01649549321965], + [36.25332807917695, 45.03247980324494], + [36.25743987839326, 45.03842324279259], + [36.267158676549116, 45.043573724415154], + [36.2783726744118, 45.04555455542638], + [36.36740852558336, 45.04833265291825], + [36.44029951169139, 45.06787222615526], + [36.45375630913995, 45.07631970334319], + [36.455251508854985, 45.09202341204062], + [36.44142091149291, 45.10709638287736], + [36.41432041665814, 45.12872568311289], + [36.40852651776157, 45.149160473330085], + [36.409997342308856, 45.171615955386955], + [36.418312796420764, 45.23001671705953], + [36.42672329481775, 45.25186253492981], + [36.43756477765089, 45.27227491599612], + [36.4497132753354, 45.28542626329343], + [36.45905827355429, 45.28753019598713], + [36.4814862692796, 45.28845064200263], + [36.4909554290368, 45.29213135137758], + [36.49637552800283, 45.300940007322055], + [36.49394582846682, 45.305015191082816], + [36.48871262946426, 45.30935296803605], + [36.48460083024801, 45.315924724862185], + [36.489647129296515, 45.336413860372005], + [36.502169426909745, 45.34731734941451], + [36.52104632331191, 45.35033842661815], + [36.544281237819945, 45.34731734942025], + [36.57455903204905, 45.33601971904315], + [36.585399229982954, 45.333917585593355], + [36.59810088537549, 45.334837278577254], + [36.630808379142394, 45.34048649352954], + [36.637536777859964, 45.3511265071989], + [36.63099527910589, 45.3741073632589], + [36.61359545390113, 45.40895280985421], + [36.59845655678569, 45.421547717459106], + [36.58331765967199, 45.42731944465129], + [36.566309762912795, 45.42548305000767], + [36.54836736633254, 45.41210180010589], + [36.53285466928139, 45.4090840212946], + [36.51565987255873, 45.41957994832251], + [36.49117597722616, 45.44279525429408], + [36.47043008117939, 45.4458112314303], + [36.411182792482634, 45.43610707766504], + [36.391371396258705, 45.43991025572652], + [36.35959840231365, 45.45407156049933], + [36.33960010612526, 45.45695583486963], + [36.33025510790637, 45.454464879327446], + [36.32053630976225, 45.44856480887407], + [36.31156511147125, 45.4438443081136], + [36.29885591389362, 45.442795254299995], + [36.3072664122906, 45.46115087970253], + [36.30016421364425, 45.47320989503609], + [36.283717016779036, 45.476355300848866], + [36.267082919949445, 45.46704963343626], + [36.25213092279836, 45.46115087970253], + [36.13681364478941, 45.46219959214511], + [36.11700224855986, 45.45721803432335], + [36.097003952371466, 45.441483909606006], + [36.06952965760803, 45.43046741078453], + [36.0655449627526, 45.42553028973455], + [36.05134056545904, 45.39535242162091], + [36.022557970944945, 45.368441166003805], + [35.986486277818386, 45.362926059418186], + [35.94723728529826, 45.372380198658874], + [35.87220216002379, 45.404075760536614], + [35.85388596351393, 45.413916621802144], + [35.84715756479628, 45.426379251448395], + [35.8524047739447, 45.44386497541683], + [35.85950697259193, 45.45933624762881], + [35.857824872912545, 45.469953901705], + [35.83278027768503, 45.47087138287168], + [35.8167068807486, 45.46392436820739], + [35.80362388324218, 45.44963442058864], + [35.79469305616038, 45.42980210462429], + [35.791889556694684, 45.41209230278156], + [35.772265060435046, 45.39214572935421], + [35.767405661361295, 45.38873311015669], + [35.75189296431793, 45.386632934388984], + [35.7481549650407, 45.379938103368545], + [35.746846665290036, 45.369960021421576], + [35.74423006578874, 45.36076812520648], + [35.71619507113218, 45.34040932557082], + [35.69451467527287, 45.32989869277279], + [35.51720627467216, 45.29506847418358], + [35.48038698168983, 45.2979608697527], + [35.33194061536096, 45.371562726652314], + [35.04491375777232, 45.669545248704424], + [35.00230056589345, 45.7290693869553], + [34.70631294999043, 46.024929846739866], + [34.35868883309806, 46.106725558140795], + [34.00905273547181, 46.55925987559425] + ] + ] + } + } + } + }, + "version": 8 +} diff --git a/test/fixtures/valid-styles/inline-geojson.output.json b/test/fixtures/valid-styles/inline-geojson.output.json new file mode 100644 index 0000000..7913a6b --- /dev/null +++ b/test/fixtures/valid-styles/inline-geojson.output.json @@ -0,0 +1,1573 @@ +{ + "name": "MapLibre", + "zoom": 14, + "pitch": 0, + "center": [ + 34.559306660989606, + 45.47004640544118 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": [ + "all" + ], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "crimea-fill", + "type": "fill", + "source": "crimea", + "paint": { + "fill-color": "#D6C7FF" + } + } + ], + "bearing": 0, + "sources": { + "crimea": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 34.00905273547181, + 46.55925987559425 + ], + [ + 33.64325260204026, + 46.34533545368038 + ], + [ + 33.628682598560204, + 46.12569762665683 + ], + [ + 33.64292861730951, + 46.10476396128129 + ], + [ + 33.648473474905984, + 46.09033047763651 + ], + [ + 33.63876482059936, + 46.077976784785335 + ], + [ + 33.62782672238245, + 46.06747935719011 + ], + [ + 33.62911357645072, + 46.05708111413949 + ], + [ + 33.642686868727424, + 46.02192963417187 + ], + [ + 33.6429723910654, + 46.01521185644708 + ], + [ + 33.636224138774026, + 46.006705833212465 + ], + [ + 33.63052626465907, + 45.99692992186792 + ], + [ + 33.63193836679693, + 45.988472992911284 + ], + [ + 33.64276684834442, + 45.984575360297384 + ], + [ + 33.646928693041986, + 45.97981936210982 + ], + [ + 33.638745893564305, + 45.96829769147004 + ], + [ + 33.61958133326394, + 45.951176418494185 + ], + [ + 33.63181380398527, + 45.9445404758078 + ], + [ + 33.638921676216, + 45.94737012930554 + ], + [ + 33.64561542516918, + 45.95403251372139 + ], + [ + 33.65666403976448, + 45.95687114427736 + ], + [ + 33.6825817382811, + 45.95878100879199 + ], + [ + 33.738791807037614, + 45.94836945227263 + ], + [ + 33.758180142697, + 45.94072970008301 + ], + [ + 33.77735917288169, + 45.92923970233858 + ], + [ + 33.75927796793485, + 45.92241179584471 + ], + [ + 33.72529865009221, + 45.91587363154565 + ], + [ + 33.70875012326826, + 45.91008760988058 + ], + [ + 33.69378857293381, + 45.91480850795665 + ], + [ + 33.69092650243843, + 45.89657370898402 + ], + [ + 33.693592356906805, + 45.87271465766318 + ], + [ + 33.69226765972388, + 45.86041392418218 + ], + [ + 33.6704813511748, + 45.8584273836251 + ], + [ + 33.65936345808916, + 45.85944682601249 + ], + [ + 33.653870582376726, + 45.86425922279372 + ], + [ + 33.65107345584843, + 45.87089907254003 + ], + [ + 33.63067378180233, + 45.88040685247182 + ], + [ + 33.61945300059696, + 45.88147266102649 + ], + [ + 33.60987421595539, + 45.88048951126686 + ], + [ + 33.59906957603934, + 45.877610457390375 + ], + [ + 33.57828877687868, + 45.86810261756233 + ], + [ + 33.55357394560386, + 45.84700625141778 + ], + [ + 33.530220674480375, + 45.84221983655459 + ], + [ + 33.5192297395441, + 45.84121682367507 + ], + [ + 33.50832088442496, + 45.84313067048083 + ], + [ + 33.48901101848409, + 45.85268298292175 + ], + [ + 33.482152996405716, + 45.854578171799005 + ], + [ + 33.46719955896293, + 45.849912739405056 + ], + [ + 33.42447496599681, + 45.83075886348303 + ], + [ + 33.40940172404095, + 45.82691953557702 + ], + [ + 33.37918350072067, + 45.802867525073566 + ], + [ + 33.37362145339398, + 45.79619281922518 + ], + [ + 33.33805543634864, + 45.78577808972071 + ], + [ + 33.26498872665803, + 45.75410774187094 + ], + [ + 33.22887541283427, + 45.75131270772724 + ], + [ + 33.19548267281132, + 45.7644887297206 + ], + [ + 33.1789202379222, + 45.78010311364778 + ], + [ + 33.1688456078636, + 45.78470227904205 + ], + [ + 33.161012432811674, + 45.77921593899549 + ], + [ + 33.15951585299757, + 45.76864464913777 + ], + [ + 33.165962301438725, + 45.762685940125465 + ], + [ + 33.1750888126426, + 45.759218220695715 + ], + [ + 33.181464829753, + 45.75490447884948 + ], + [ + 33.17613930782352, + 45.7437961960276 + ], + [ + 33.16369168844906, + 45.735912015025065 + ], + [ + 32.93692665480876, + 45.662114646778264 + ], + [ + 32.86839112407645, + 45.63044340698664 + ], + [ + 32.83803944575723, + 45.60834075026611 + ], + [ + 32.82702772424804, + 45.59576101516498 + ], + [ + 32.82433467080986, + 45.58705137380335 + ], + [ + 32.82563941622885, + 45.579605763895614 + ], + [ + 32.82993674258438, + 45.56978311819469 + ], + [ + 32.82851940940563, + 45.56227808675749 + ], + [ + 32.813310142795274, + 45.55930933158257 + ], + [ + 32.80213583657516, + 45.560145780074464 + ], + [ + 32.78258622159436, + 45.565158335073846 + ], + [ + 32.77333922465823, + 45.56689313356526 + ], + [ + 32.758306734735356, + 45.565030173463356 + ], + [ + 32.750177256846115, + 45.55943726334968 + ], + [ + 32.74340732630185, + 45.55261895849793 + ], + [ + 32.73524549539499, + 45.54598788110354 + ], + [ + 32.72031700779701, + 45.53735927760957 + ], + [ + 32.70536040418847, + 45.53169142131733 + ], + [ + 32.68589438933773, + 45.52663379187257 + ], + [ + 32.66370583186284, + 45.52563107058867 + ], + [ + 32.64312077736798, + 45.52188979044979 + ], + [ + 32.525284074162556, + 45.45838108691365 + ], + [ + 32.49490411219156, + 45.43524910229854 + ], + [ + 32.48107654411925, + 45.408986638827514 + ], + [ + 32.48514589713025, + 45.39458067125969 + ], + [ + 32.51256939517424, + 45.34060655033625 + ], + [ + 32.535915460470335, + 45.33777248012882 + ], + [ + 32.57027153843481, + 45.32510892683359 + ], + [ + 32.590830644991826, + 45.32038723212662 + ], + [ + 32.66380378113439, + 45.320421746458976 + ], + [ + 32.67760722618917, + 45.32609231279554 + ], + [ + 32.71316246802607, + 45.353283572618125 + ], + [ + 32.72817188836078, + 45.36074681043402 + ], + [ + 32.750518060251466, + 45.36371725645313 + ], + [ + 32.89973931692998, + 45.35412322462227 + ], + [ + 32.941197846443885, + 45.34245505845169 + ], + [ + 32.97701667405008, + 45.32596743563991 + ], + [ + 33.04296090827762, + 45.2853982930032 + ], + [ + 33.05274355585479, + 45.28154273654923 + ], + [ + 33.06850284417635, + 45.27703461892352 + ], + [ + 33.07825272648239, + 45.272210805127315 + ], + [ + 33.089426322403455, + 45.25656353201492 + ], + [ + 33.09897492343546, + 45.247820101667884 + ], + [ + 33.12384611720435, + 45.238235755071685 + ], + [ + 33.15767197859745, + 45.20755227709648 + ], + [ + 33.172959979330074, + 45.19681657531794 + ], + [ + 33.21837666514142, + 45.187878368659824 + ], + [ + 33.24017433636709, + 45.180191106261134 + ], + [ + 33.248571989896675, + 45.16588271012458 + ], + [ + 33.259649216030766, + 45.155918961282026 + ], + [ + 33.28309785485047, + 45.16064860772312 + ], + [ + 33.31767999550894, + 45.17535522412791 + ], + [ + 33.35458473323109, + 45.18598673360148 + ], + [ + 33.39725661527919, + 45.18973663076909 + ], + [ + 33.41344561756824, + 45.18490731877088 + ], + [ + 33.468468576977216, + 45.149132412229676 + ], + [ + 33.537128652906205, + 45.11719769268973 + ], + [ + 33.56161328289443, + 45.094099022711475 + ], + [ + 33.57837628774928, + 45.053145935448015 + ], + [ + 33.58247744978442, + 45.027377243150454 + ], + [ + 33.5851414316958, + 45.01816461606674 + ], + [ + 33.6031021265521, + 44.993103583251695 + ], + [ + 33.605922209331794, + 44.986905272229734 + ], + [ + 33.60843524291815, + 44.97039962759274 + ], + [ + 33.61943161357851, + 44.93184946652454 + ], + [ + 33.619484500808824, + 44.90819321920554 + ], + [ + 33.61549738593425, + 44.88894092276257 + ], + [ + 33.608561183117274, + 44.871288478948514 + ], + [ + 33.59889474705494, + 44.859790298912856 + ], + [ + 33.55904244709464, + 44.850057575124595 + ], + [ + 33.54667558363471, + 44.83724531175508 + ], + [ + 33.53701832136994, + 44.81871953508235 + ], + [ + 33.5303157846202, + 44.798338017069625 + ], + [ + 33.5249116915937, + 44.78918633101301 + ], + [ + 33.51669091675143, + 44.784809980590666 + ], + [ + 33.524785531609865, + 44.77183212449111 + ], + [ + 33.5302902535075, + 44.75724515985675 + ], + [ + 33.53710734694323, + 44.73034290771247 + ], + [ + 33.54650992495621, + 44.70989226909535 + ], + [ + 33.5481286806762, + 44.699106546699085 + ], + [ + 33.543995566510915, + 44.68230506537358 + ], + [ + 33.53580273994743, + 44.6726082589706 + ], + [ + 33.52337411931097, + 44.661863083605255 + ], + [ + 33.515320778874354, + 44.6491266698327 + ], + [ + 33.516377841582795, + 44.63464990118433 + ], + [ + 33.52466971637648, + 44.62863961572572 + ], + [ + 33.557474298027785, + 44.62473000923737 + ], + [ + 33.5710648827386, + 44.620853511273225 + ], + [ + 33.55105839203679, + 44.61506440493406 + ], + [ + 33.499905706797676, + 44.61452599304897 + ], + [ + 33.48451102966331, + 44.60992438254493 + ], + [ + 33.47658499621011, + 44.60714391514574 + ], + [ + 33.46705078205747, + 44.60616254193252 + ], + [ + 33.44476599234898, + 44.607062134677875 + ], + [ + 33.4353466482458, + 44.60509936890821 + ], + [ + 33.413591053005575, + 44.593500212748125 + ], + [ + 33.40543527945235, + 44.59055535193136 + ], + [ + 33.37510958624222, + 44.58564691897425 + ], + [ + 33.37074452434078, + 44.58851022190515 + ], + [ + 33.372237834990756, + 44.576810695127364 + ], + [ + 33.37913003799301, + 44.56412673079859 + ], + [ + 33.48759131590526, + 44.51024086451031 + ], + [ + 33.50011215135888, + 44.50041002882833 + ], + [ + 33.517917009115365, + 44.49074142372788 + ], + [ + 33.53836387802215, + 44.49164280212756 + ], + [ + 33.56041892763031, + 44.4966411022441 + ], + [ + 33.57822378538677, + 44.497542389459795 + ], + [ + 33.59062975079095, + 44.48975808594983 + ], + [ + 33.619577003408466, + 44.46229988129974 + ], + [ + 33.62635433636015, + 44.45336293328907 + ], + [ + 33.63175322871038, + 44.434828756313124 + ], + [ + 33.645537634715026, + 44.42498521035591 + ], + [ + 33.721007257593925, + 44.39946630464436 + ], + [ + 33.74168386660085, + 44.39560878121904 + ], + [ + 33.80727466517129, + 44.39454176175843 + ], + [ + 33.81841706002561, + 44.39552670349164 + ], + [ + 33.83909366903248, + 44.40143600575672 + ], + [ + 33.85149963444792, + 44.40143600575945 + ], + [ + 33.91467816197718, + 44.38387049706651 + ], + [ + 33.938111652185, + 44.38083293528811 + ], + [ + 33.957065210440874, + 44.38272116790142 + ], + [ + 34.06614966692763, + 44.42019923628979 + ], + [ + 34.088893936836286, + 44.42200415824283 + ], + [ + 34.10279321289039, + 44.42487551014821 + ], + [ + 34.135933345669, + 44.44163597968952 + ], + [ + 34.14696087047267, + 44.44959070749778 + ], + [ + 34.16058918507403, + 44.466287285335795 + ], + [ + 34.170123399227776, + 44.48186111741296 + ], + [ + 34.182759104731986, + 44.49267838558103 + ], + [ + 34.22923417224524, + 44.49949719774551 + ], + [ + 34.24301857824986, + 44.50744404277697 + ], + [ + 34.263903954150294, + 44.53186886058606 + ], + [ + 34.26631622520165, + 44.53555362837611 + ], + [ + 34.26631622520165, + 44.54153064468656 + ], + [ + 34.27033667695244, + 44.545378535987936 + ], + [ + 34.2757355693048, + 44.54644280144541 + ], + [ + 34.285384653508004, + 44.54562413743594 + ], + [ + 34.299973149863405, + 44.54554227040174 + ], + [ + 34.32260254971496, + 44.543577427039224 + ], + [ + 34.3308731933177, + 44.54546040325087 + ], + [ + 34.340292537420794, + 44.55798473830754 + ], + [ + 34.38042135640006, + 44.631830317636684 + ], + [ + 34.41495238900856, + 44.673669777529994 + ], + [ + 34.424193090575585, + 44.68239452736094 + ], + [ + 34.42959198292681, + 44.68884644523774 + ], + [ + 34.469399167794535, + 44.730194532749294 + ], + [ + 34.47376422969597, + 44.73011292571252 + ], + [ + 34.47376422969597, + 44.72635887754967 + ], + [ + 34.475142670296464, + 44.723502373339585 + ], + [ + 34.499724861011515, + 44.74292382044041 + ], + [ + 34.532800295801195, + 44.752620844929055 + ], + [ + 34.61217550038418, + 44.76534519537847 + ], + [ + 34.65065696715081, + 44.777088262503725 + ], + [ + 34.72084256772871, + 44.811080759265764 + ], + [ + 34.756796893391225, + 44.82094054159748 + ], + [ + 34.82646979041766, + 44.81208604604609 + ], + [ + 34.84289620758207, + 44.816893835303176 + ], + [ + 34.856910353686715, + 44.82373813182468 + ], + [ + 34.889648317948144, + 44.817871641692506 + ], + [ + 34.90733830566026, + 44.820886440346584 + ], + [ + 34.922960632465504, + 44.83050015059965 + ], + [ + 34.92950822531711, + 44.83652826953224 + ], + [ + 34.94179932067178, + 44.84019370922482 + ], + [ + 34.95282684547897, + 44.841415470643284 + ], + [ + 34.98567967978991, + 44.840275160795755 + ], + [ + 35.0053224583441, + 44.83538786296728 + ], + [ + 35.017958163849414, + 44.82219008824552 + ], + [ + 35.02703289780189, + 44.80890779582285 + ], + [ + 35.037933245998005, + 44.79869792240089 + ], + [ + 35.08073333784134, + 44.793525442788905 + ], + [ + 35.1080207326404, + 44.824553365795765 + ], + [ + 35.130368105574235, + 44.86879838545747 + ], + [ + 35.15485200090768, + 44.90071251697748 + ], + [ + 35.17111229780758, + 44.90746386008772 + ], + [ + 35.21522068940149, + 44.91421441031795 + ], + [ + 35.233163085981715, + 44.925728224907715 + ], + [ + 35.25636688416236, + 44.95896657181197 + ], + [ + 35.27300098099195, + 44.96690119386028 + ], + [ + 35.29748487632534, + 44.95605693543271 + ], + [ + 35.30496087491386, + 44.96121482614441 + ], + [ + 35.315240372954605, + 44.965711070514175 + ], + [ + 35.31935217217088, + 44.96941359539801 + ], + [ + 35.36757236298112, + 44.94362319076086 + ], + [ + 35.36103086422793, + 44.97364475976596 + ], + [ + 35.362152264014156, + 44.98593980935419 + ], + [ + 35.374674561627444, + 44.997835734117416 + ], + [ + 35.389439658813274, + 45.00180049366759 + ], + [ + 35.42270785247763, + 45.00087540764923 + ], + [ + 35.43504325012745, + 45.00470780964241 + ], + [ + 35.43504325012745, + 45.011446929213974 + ], + [ + 35.40631957913584, + 45.02015821022701 + ], + [ + 35.40089948016896, + 45.025046135473445 + ], + [ + 35.39790908073891, + 45.03482073400548 + ], + [ + 35.40052568024015, + 45.042216617888045 + ], + [ + 35.40631957913584, + 45.051328088783805 + ], + [ + 35.40744097892215, + 45.06294640963205 + ], + [ + 35.41734667704213, + 45.0708666385693 + ], + [ + 35.469304867139925, + 45.10068964922732 + ], + [ + 35.5070260597534, + 45.113341616151644 + ], + [ + 35.54758335202416, + 45.12019982412133 + ], + [ + 35.59019654390909, + 45.11993606213795 + ], + [ + 35.63411803553862, + 45.11439677872579 + ], + [ + 35.70669729572677, + 45.09480210570922 + ], + [ + 35.771782422456766, + 45.06572995732262 + ], + [ + 35.78430472007, + 45.057941041321754 + ], + [ + 35.81250040352472, + 45.031852200991295 + ], + [ + 35.81941570220667, + 45.021152336906454 + ], + [ + 35.82763930064016, + 44.99895365027004 + ], + [ + 35.848198296721705, + 44.99208088455586 + ], + [ + 35.916977483614176, + 45.00172895661731 + ], + [ + 35.99360646900681, + 44.997896355361604 + ], + [ + 36.00893226608571, + 45.00926125333629 + ], + [ + 36.02539976723364, + 45.03288661039673 + ], + [ + 36.047827762958946, + 45.048074065419456 + ], + [ + 36.078666257082034, + 45.03883000769565 + ], + [ + 36.079137312377895, + 45.046610970582435 + ], + [ + 36.135020401727616, + 45.02125162210126 + ], + [ + 36.2241716847341, + 45.00751061631556 + ], + [ + 36.24398308095806, + 45.011474706353084 + ], + [ + 36.24828178013877, + 45.01649549321965 + ], + [ + 36.25332807917695, + 45.03247980324494 + ], + [ + 36.25743987839326, + 45.03842324279259 + ], + [ + 36.267158676549116, + 45.043573724415154 + ], + [ + 36.2783726744118, + 45.04555455542638 + ], + [ + 36.36740852558336, + 45.04833265291825 + ], + [ + 36.44029951169139, + 45.06787222615526 + ], + [ + 36.45375630913995, + 45.07631970334319 + ], + [ + 36.455251508854985, + 45.09202341204062 + ], + [ + 36.44142091149291, + 45.10709638287736 + ], + [ + 36.41432041665814, + 45.12872568311289 + ], + [ + 36.40852651776157, + 45.149160473330085 + ], + [ + 36.409997342308856, + 45.171615955386955 + ], + [ + 36.418312796420764, + 45.23001671705953 + ], + [ + 36.42672329481775, + 45.25186253492981 + ], + [ + 36.43756477765089, + 45.27227491599612 + ], + [ + 36.4497132753354, + 45.28542626329343 + ], + [ + 36.45905827355429, + 45.28753019598713 + ], + [ + 36.4814862692796, + 45.28845064200263 + ], + [ + 36.4909554290368, + 45.29213135137758 + ], + [ + 36.49637552800283, + 45.300940007322055 + ], + [ + 36.49394582846682, + 45.305015191082816 + ], + [ + 36.48871262946426, + 45.30935296803605 + ], + [ + 36.48460083024801, + 45.315924724862185 + ], + [ + 36.489647129296515, + 45.336413860372005 + ], + [ + 36.502169426909745, + 45.34731734941451 + ], + [ + 36.52104632331191, + 45.35033842661815 + ], + [ + 36.544281237819945, + 45.34731734942025 + ], + [ + 36.57455903204905, + 45.33601971904315 + ], + [ + 36.585399229982954, + 45.333917585593355 + ], + [ + 36.59810088537549, + 45.334837278577254 + ], + [ + 36.630808379142394, + 45.34048649352954 + ], + [ + 36.637536777859964, + 45.3511265071989 + ], + [ + 36.63099527910589, + 45.3741073632589 + ], + [ + 36.61359545390113, + 45.40895280985421 + ], + [ + 36.59845655678569, + 45.421547717459106 + ], + [ + 36.58331765967199, + 45.42731944465129 + ], + [ + 36.566309762912795, + 45.42548305000767 + ], + [ + 36.54836736633254, + 45.41210180010589 + ], + [ + 36.53285466928139, + 45.4090840212946 + ], + [ + 36.51565987255873, + 45.41957994832251 + ], + [ + 36.49117597722616, + 45.44279525429408 + ], + [ + 36.47043008117939, + 45.4458112314303 + ], + [ + 36.411182792482634, + 45.43610707766504 + ], + [ + 36.391371396258705, + 45.43991025572652 + ], + [ + 36.35959840231365, + 45.45407156049933 + ], + [ + 36.33960010612526, + 45.45695583486963 + ], + [ + 36.33025510790637, + 45.454464879327446 + ], + [ + 36.32053630976225, + 45.44856480887407 + ], + [ + 36.31156511147125, + 45.4438443081136 + ], + [ + 36.29885591389362, + 45.442795254299995 + ], + [ + 36.3072664122906, + 45.46115087970253 + ], + [ + 36.30016421364425, + 45.47320989503609 + ], + [ + 36.283717016779036, + 45.476355300848866 + ], + [ + 36.267082919949445, + 45.46704963343626 + ], + [ + 36.25213092279836, + 45.46115087970253 + ], + [ + 36.13681364478941, + 45.46219959214511 + ], + [ + 36.11700224855986, + 45.45721803432335 + ], + [ + 36.097003952371466, + 45.441483909606006 + ], + [ + 36.06952965760803, + 45.43046741078453 + ], + [ + 36.0655449627526, + 45.42553028973455 + ], + [ + 36.05134056545904, + 45.39535242162091 + ], + [ + 36.022557970944945, + 45.368441166003805 + ], + [ + 35.986486277818386, + 45.362926059418186 + ], + [ + 35.94723728529826, + 45.372380198658874 + ], + [ + 35.87220216002379, + 45.404075760536614 + ], + [ + 35.85388596351393, + 45.413916621802144 + ], + [ + 35.84715756479628, + 45.426379251448395 + ], + [ + 35.8524047739447, + 45.44386497541683 + ], + [ + 35.85950697259193, + 45.45933624762881 + ], + [ + 35.857824872912545, + 45.469953901705 + ], + [ + 35.83278027768503, + 45.47087138287168 + ], + [ + 35.8167068807486, + 45.46392436820739 + ], + [ + 35.80362388324218, + 45.44963442058864 + ], + [ + 35.79469305616038, + 45.42980210462429 + ], + [ + 35.791889556694684, + 45.41209230278156 + ], + [ + 35.772265060435046, + 45.39214572935421 + ], + [ + 35.767405661361295, + 45.38873311015669 + ], + [ + 35.75189296431793, + 45.386632934388984 + ], + [ + 35.7481549650407, + 45.379938103368545 + ], + [ + 35.746846665290036, + 45.369960021421576 + ], + [ + 35.74423006578874, + 45.36076812520648 + ], + [ + 35.71619507113218, + 45.34040932557082 + ], + [ + 35.69451467527287, + 45.32989869277279 + ], + [ + 35.51720627467216, + 45.29506847418358 + ], + [ + 35.48038698168983, + 45.2979608697527 + ], + [ + 35.33194061536096, + 45.371562726652314 + ], + [ + 35.04491375777232, + 45.669545248704424 + ], + [ + 35.00230056589345, + 45.7290693869553 + ], + [ + 34.70631294999043, + 46.024929846739866 + ], + [ + 34.35868883309806, + 46.106725558140795 + ], + [ + 34.00905273547181, + 46.55925987559425 + ] + ] + ] + }, + "bbox": [ + 32.48107654411925, + 44.38083293528811, + 36.637536777859964, + 46.55925987559425 + ] + }, + "maxzoom": 0 + } + }, + "version": 8, + "metadata": { + "smp:bounds": [ + 32.48107654411925, + 44.38083293528811, + 36.637536777859964, + 46.55925987559425 + ], + "smp:maxzoom": 16, + "smp:sourceFolders": { + "crimea": "0" + } + } +} \ No newline at end of file diff --git a/test/fixtures/valid-styles/maplibre-demotiles.input.json b/test/fixtures/valid-styles/maplibre-demotiles.input.json new file mode 100644 index 0000000..a589b6f --- /dev/null +++ b/test/fixtures/valid-styles/maplibre-demotiles.input.json @@ -0,0 +1,831 @@ +{ + "id": "43f36e14-e3f5-43c1-84c0-50a9c80dc5c7", + "name": "MapLibre", + "zoom": 0.8619833357855968, + "pitch": 0, + "center": [17.65431710431244, 32.954120326746775], + "glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": ["all"], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "coastline", + "type": "line", + "paint": { + "line-blur": 0.5, + "line-color": "#198EC8", + "line-width": { + "stops": [ + [0, 2], + [6, 6], + [14, 9], + [22, 18] + ] + } + }, + "filter": ["all"], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 0, + "source-layer": "countries" + }, + { + "id": "countries-fill", + "type": "fill", + "paint": { + "fill-color": [ + "match", + ["get", "ADM0_A3"], + [ + "ARM", + "ATG", + "AUS", + "BTN", + "CAN", + "COG", + "CZE", + "GHA", + "GIN", + "HTI", + "ISL", + "JOR", + "KHM", + "KOR", + "LVA", + "MLT", + "MNE", + "MOZ", + "PER", + "SAH", + "SGP", + "SLV", + "SOM", + "TJK", + "TUV", + "UKR", + "WSM" + ], + "#D6C7FF", + [ + "AZE", + "BGD", + "CHL", + "CMR", + "CSI", + "DEU", + "DJI", + "GUY", + "HUN", + "IOA", + "JAM", + "LBN", + "LBY", + "LSO", + "MDG", + "MKD", + "MNG", + "MRT", + "NIU", + "NZL", + "PCN", + "PYF", + "SAU", + "SHN", + "STP", + "TTO", + "UGA", + "UZB", + "ZMB" + ], + "#EBCA8A", + [ + "AGO", + "ASM", + "ATF", + "BDI", + "BFA", + "BGR", + "BLZ", + "BRA", + "CHN", + "CRI", + "ESP", + "HKG", + "HRV", + "IDN", + "IRN", + "ISR", + "KNA", + "LBR", + "LCA", + "MAC", + "MUS", + "NOR", + "PLW", + "POL", + "PRI", + "SDN", + "TUN", + "UMI", + "USA", + "USG", + "VIR", + "VUT" + ], + "#C1E599", + [ + "ARE", + "ARG", + "BHS", + "CIV", + "CLP", + "DMA", + "ETH", + "GAB", + "GRD", + "HMD", + "IND", + "IOT", + "IRL", + "IRQ", + "ITA", + "KOS", + "LUX", + "MEX", + "NAM", + "NER", + "PHL", + "PRT", + "RUS", + "SEN", + "SUR", + "TZA", + "VAT" + ], + "#E7E58F", + [ + "AUT", + "BEL", + "BHR", + "BMU", + "BRB", + "CYN", + "DZA", + "EST", + "FLK", + "GMB", + "GUM", + "HND", + "JEY", + "KGZ", + "LIE", + "MAF", + "MDA", + "NGA", + "NRU", + "SLB", + "SOL", + "SRB", + "SWZ", + "THA", + "TUR", + "VEN", + "VGB" + ], + "#98DDA1", + [ + "AIA", + "BIH", + "BLM", + "BRN", + "CAF", + "CHE", + "COM", + "CPV", + "CUB", + "ECU", + "ESB", + "FSM", + "GAZ", + "GBR", + "GEO", + "KEN", + "LTU", + "MAR", + "MCO", + "MDV", + "NFK", + "NPL", + "PNG", + "PRY", + "QAT", + "SLE", + "SPM", + "SYC", + "TCA", + "TKM", + "TLS", + "VNM", + "WEB", + "WSB", + "YEM", + "ZWE" + ], + "#83D5F4", + [ + "ABW", + "ALB", + "AND", + "ATC", + "BOL", + "COD", + "CUW", + "CYM", + "CYP", + "EGY", + "FJI", + "GGY", + "IMN", + "KAB", + "KAZ", + "KWT", + "LAO", + "MLI", + "MNP", + "MSR", + "MYS", + "NIC", + "NLD", + "PAK", + "PAN", + "PRK", + "ROU", + "SGS", + "SVN", + "SWE", + "TGO", + "TWN", + "VCT", + "ZAF" + ], + "#B1BBF9", + ["ATA", "GRL"], + "#FFFFFF", + "#EAB38F" + ] + }, + "filter": ["all"], + "layout": { + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "source-layer": "countries" + }, + { + "id": "countries-boundary", + "type": "line", + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "stops": [ + [1, 1], + [6, 2], + [14, 6], + [22, 12] + ] + }, + "line-opacity": { + "stops": [ + [3, 0.5], + [6, 1] + ] + } + }, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "source-layer": "countries" + }, + { + "id": "geolines", + "type": "line", + "paint": { + "line-color": "#1077B0", + "line-opacity": 1, + "line-dasharray": [3, 3] + }, + "filter": ["all", ["!=", "name", "International Date Line"]], + "layout": { + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "source-layer": "geolines" + }, + { + "id": "geolines-label", + "type": "symbol", + "paint": { + "text-color": "#1077B0", + "text-halo-blur": 1, + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": 1 + }, + "filter": ["all", ["!=", "name", "International Date Line"]], + "layout": { + "text-font": ["Open Sans Semibold"], + "text-size": { + "stops": [ + [2, 12], + [6, 16] + ] + }, + "text-field": "{name}", + "visibility": "visible", + "symbol-placement": "line" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 1, + "source-layer": "geolines" + }, + { + "id": "countries-label", + "type": "symbol", + "paint": { + "text-color": "rgba(8, 37, 77, 1)", + "text-halo-blur": { + "stops": [ + [2, 0.2], + [6, 0] + ] + }, + "text-halo-color": "rgba(255, 255, 255, 1)", + "text-halo-width": { + "stops": [ + [2, 1], + [6, 1.6] + ] + } + }, + "filter": ["all"], + "layout": { + "text-font": ["Open Sans Semibold"], + "text-size": { + "stops": [ + [2, 10], + [4, 12], + [6, 16] + ] + }, + "text-field": { + "stops": [ + [2, "{ABBREV}"], + [4, "{NAME}"] + ] + }, + "visibility": "visible", + "text-max-width": 10, + "text-transform": { + "stops": [ + [0, "uppercase"], + [2, "none"] + ] + } + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 2, + "source-layer": "centroids" + }, + { + "id": "crimea-fill", + "type": "fill", + "source": "crimea", + "paint": { + "fill-color": "#D6C7FF" + } + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "url": "https://demotiles.maplibre.org/tiles/tiles.json", + "type": "vector" + }, + "crimea": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [34.00905273547181, 46.55925987559425], + [33.64325260204026, 46.34533545368038], + [33.628682598560204, 46.12569762665683], + [33.64292861730951, 46.10476396128129], + [33.648473474905984, 46.09033047763651], + [33.63876482059936, 46.077976784785335], + [33.62782672238245, 46.06747935719011], + [33.62911357645072, 46.05708111413949], + [33.642686868727424, 46.02192963417187], + [33.6429723910654, 46.01521185644708], + [33.636224138774026, 46.006705833212465], + [33.63052626465907, 45.99692992186792], + [33.63193836679693, 45.988472992911284], + [33.64276684834442, 45.984575360297384], + [33.646928693041986, 45.97981936210982], + [33.638745893564305, 45.96829769147004], + [33.61958133326394, 45.951176418494185], + [33.63181380398527, 45.9445404758078], + [33.638921676216, 45.94737012930554], + [33.64561542516918, 45.95403251372139], + [33.65666403976448, 45.95687114427736], + [33.6825817382811, 45.95878100879199], + [33.738791807037614, 45.94836945227263], + [33.758180142697, 45.94072970008301], + [33.77735917288169, 45.92923970233858], + [33.75927796793485, 45.92241179584471], + [33.72529865009221, 45.91587363154565], + [33.70875012326826, 45.91008760988058], + [33.69378857293381, 45.91480850795665], + [33.69092650243843, 45.89657370898402], + [33.693592356906805, 45.87271465766318], + [33.69226765972388, 45.86041392418218], + [33.6704813511748, 45.8584273836251], + [33.65936345808916, 45.85944682601249], + [33.653870582376726, 45.86425922279372], + [33.65107345584843, 45.87089907254003], + [33.63067378180233, 45.88040685247182], + [33.61945300059696, 45.88147266102649], + [33.60987421595539, 45.88048951126686], + [33.59906957603934, 45.877610457390375], + [33.57828877687868, 45.86810261756233], + [33.55357394560386, 45.84700625141778], + [33.530220674480375, 45.84221983655459], + [33.5192297395441, 45.84121682367507], + [33.50832088442496, 45.84313067048083], + [33.48901101848409, 45.85268298292175], + [33.482152996405716, 45.854578171799005], + [33.46719955896293, 45.849912739405056], + [33.42447496599681, 45.83075886348303], + [33.40940172404095, 45.82691953557702], + [33.37918350072067, 45.802867525073566], + [33.37362145339398, 45.79619281922518], + [33.33805543634864, 45.78577808972071], + [33.26498872665803, 45.75410774187094], + [33.22887541283427, 45.75131270772724], + [33.19548267281132, 45.7644887297206], + [33.1789202379222, 45.78010311364778], + [33.1688456078636, 45.78470227904205], + [33.161012432811674, 45.77921593899549], + [33.15951585299757, 45.76864464913777], + [33.165962301438725, 45.762685940125465], + [33.1750888126426, 45.759218220695715], + [33.181464829753, 45.75490447884948], + [33.17613930782352, 45.7437961960276], + [33.16369168844906, 45.735912015025065], + [32.93692665480876, 45.662114646778264], + [32.86839112407645, 45.63044340698664], + [32.83803944575723, 45.60834075026611], + [32.82702772424804, 45.59576101516498], + [32.82433467080986, 45.58705137380335], + [32.82563941622885, 45.579605763895614], + [32.82993674258438, 45.56978311819469], + [32.82851940940563, 45.56227808675749], + [32.813310142795274, 45.55930933158257], + [32.80213583657516, 45.560145780074464], + [32.78258622159436, 45.565158335073846], + [32.77333922465823, 45.56689313356526], + [32.758306734735356, 45.565030173463356], + [32.750177256846115, 45.55943726334968], + [32.74340732630185, 45.55261895849793], + [32.73524549539499, 45.54598788110354], + [32.72031700779701, 45.53735927760957], + [32.70536040418847, 45.53169142131733], + [32.68589438933773, 45.52663379187257], + [32.66370583186284, 45.52563107058867], + [32.64312077736798, 45.52188979044979], + [32.525284074162556, 45.45838108691365], + [32.49490411219156, 45.43524910229854], + [32.48107654411925, 45.408986638827514], + [32.48514589713025, 45.39458067125969], + [32.51256939517424, 45.34060655033625], + [32.535915460470335, 45.33777248012882], + [32.57027153843481, 45.32510892683359], + [32.590830644991826, 45.32038723212662], + [32.66380378113439, 45.320421746458976], + [32.67760722618917, 45.32609231279554], + [32.71316246802607, 45.353283572618125], + [32.72817188836078, 45.36074681043402], + [32.750518060251466, 45.36371725645313], + [32.89973931692998, 45.35412322462227], + [32.941197846443885, 45.34245505845169], + [32.97701667405008, 45.32596743563991], + [33.04296090827762, 45.2853982930032], + [33.05274355585479, 45.28154273654923], + [33.06850284417635, 45.27703461892352], + [33.07825272648239, 45.272210805127315], + [33.089426322403455, 45.25656353201492], + [33.09897492343546, 45.247820101667884], + [33.12384611720435, 45.238235755071685], + [33.15767197859745, 45.20755227709648], + [33.172959979330074, 45.19681657531794], + [33.21837666514142, 45.187878368659824], + [33.24017433636709, 45.180191106261134], + [33.248571989896675, 45.16588271012458], + [33.259649216030766, 45.155918961282026], + [33.28309785485047, 45.16064860772312], + [33.31767999550894, 45.17535522412791], + [33.35458473323109, 45.18598673360148], + [33.39725661527919, 45.18973663076909], + [33.41344561756824, 45.18490731877088], + [33.468468576977216, 45.149132412229676], + [33.537128652906205, 45.11719769268973], + [33.56161328289443, 45.094099022711475], + [33.57837628774928, 45.053145935448015], + [33.58247744978442, 45.027377243150454], + [33.5851414316958, 45.01816461606674], + [33.6031021265521, 44.993103583251695], + [33.605922209331794, 44.986905272229734], + [33.60843524291815, 44.97039962759274], + [33.61943161357851, 44.93184946652454], + [33.619484500808824, 44.90819321920554], + [33.61549738593425, 44.88894092276257], + [33.608561183117274, 44.871288478948514], + [33.59889474705494, 44.859790298912856], + [33.55904244709464, 44.850057575124595], + [33.54667558363471, 44.83724531175508], + [33.53701832136994, 44.81871953508235], + [33.5303157846202, 44.798338017069625], + [33.5249116915937, 44.78918633101301], + [33.51669091675143, 44.784809980590666], + [33.524785531609865, 44.77183212449111], + [33.5302902535075, 44.75724515985675], + [33.53710734694323, 44.73034290771247], + [33.54650992495621, 44.70989226909535], + [33.5481286806762, 44.699106546699085], + [33.543995566510915, 44.68230506537358], + [33.53580273994743, 44.6726082589706], + [33.52337411931097, 44.661863083605255], + [33.515320778874354, 44.6491266698327], + [33.516377841582795, 44.63464990118433], + [33.52466971637648, 44.62863961572572], + [33.557474298027785, 44.62473000923737], + [33.5710648827386, 44.620853511273225], + [33.55105839203679, 44.61506440493406], + [33.499905706797676, 44.61452599304897], + [33.48451102966331, 44.60992438254493], + [33.47658499621011, 44.60714391514574], + [33.46705078205747, 44.60616254193252], + [33.44476599234898, 44.607062134677875], + [33.4353466482458, 44.60509936890821], + [33.413591053005575, 44.593500212748125], + [33.40543527945235, 44.59055535193136], + [33.37510958624222, 44.58564691897425], + [33.37074452434078, 44.58851022190515], + [33.372237834990756, 44.576810695127364], + [33.37913003799301, 44.56412673079859], + [33.48759131590526, 44.51024086451031], + [33.50011215135888, 44.50041002882833], + [33.517917009115365, 44.49074142372788], + [33.53836387802215, 44.49164280212756], + [33.56041892763031, 44.4966411022441], + [33.57822378538677, 44.497542389459795], + [33.59062975079095, 44.48975808594983], + [33.619577003408466, 44.46229988129974], + [33.62635433636015, 44.45336293328907], + [33.63175322871038, 44.434828756313124], + [33.645537634715026, 44.42498521035591], + [33.721007257593925, 44.39946630464436], + [33.74168386660085, 44.39560878121904], + [33.80727466517129, 44.39454176175843], + [33.81841706002561, 44.39552670349164], + [33.83909366903248, 44.40143600575672], + [33.85149963444792, 44.40143600575945], + [33.91467816197718, 44.38387049706651], + [33.938111652185, 44.38083293528811], + [33.957065210440874, 44.38272116790142], + [34.06614966692763, 44.42019923628979], + [34.088893936836286, 44.42200415824283], + [34.10279321289039, 44.42487551014821], + [34.135933345669, 44.44163597968952], + [34.14696087047267, 44.44959070749778], + [34.16058918507403, 44.466287285335795], + [34.170123399227776, 44.48186111741296], + [34.182759104731986, 44.49267838558103], + [34.22923417224524, 44.49949719774551], + [34.24301857824986, 44.50744404277697], + [34.263903954150294, 44.53186886058606], + [34.26631622520165, 44.53555362837611], + [34.26631622520165, 44.54153064468656], + [34.27033667695244, 44.545378535987936], + [34.2757355693048, 44.54644280144541], + [34.285384653508004, 44.54562413743594], + [34.299973149863405, 44.54554227040174], + [34.32260254971496, 44.543577427039224], + [34.3308731933177, 44.54546040325087], + [34.340292537420794, 44.55798473830754], + [34.38042135640006, 44.631830317636684], + [34.41495238900856, 44.673669777529994], + [34.424193090575585, 44.68239452736094], + [34.42959198292681, 44.68884644523774], + [34.469399167794535, 44.730194532749294], + [34.47376422969597, 44.73011292571252], + [34.47376422969597, 44.72635887754967], + [34.475142670296464, 44.723502373339585], + [34.499724861011515, 44.74292382044041], + [34.532800295801195, 44.752620844929055], + [34.61217550038418, 44.76534519537847], + [34.65065696715081, 44.777088262503725], + [34.72084256772871, 44.811080759265764], + [34.756796893391225, 44.82094054159748], + [34.82646979041766, 44.81208604604609], + [34.84289620758207, 44.816893835303176], + [34.856910353686715, 44.82373813182468], + [34.889648317948144, 44.817871641692506], + [34.90733830566026, 44.820886440346584], + [34.922960632465504, 44.83050015059965], + [34.92950822531711, 44.83652826953224], + [34.94179932067178, 44.84019370922482], + [34.95282684547897, 44.841415470643284], + [34.98567967978991, 44.840275160795755], + [35.0053224583441, 44.83538786296728], + [35.017958163849414, 44.82219008824552], + [35.02703289780189, 44.80890779582285], + [35.037933245998005, 44.79869792240089], + [35.08073333784134, 44.793525442788905], + [35.1080207326404, 44.824553365795765], + [35.130368105574235, 44.86879838545747], + [35.15485200090768, 44.90071251697748], + [35.17111229780758, 44.90746386008772], + [35.21522068940149, 44.91421441031795], + [35.233163085981715, 44.925728224907715], + [35.25636688416236, 44.95896657181197], + [35.27300098099195, 44.96690119386028], + [35.29748487632534, 44.95605693543271], + [35.30496087491386, 44.96121482614441], + [35.315240372954605, 44.965711070514175], + [35.31935217217088, 44.96941359539801], + [35.36757236298112, 44.94362319076086], + [35.36103086422793, 44.97364475976596], + [35.362152264014156, 44.98593980935419], + [35.374674561627444, 44.997835734117416], + [35.389439658813274, 45.00180049366759], + [35.42270785247763, 45.00087540764923], + [35.43504325012745, 45.00470780964241], + [35.43504325012745, 45.011446929213974], + [35.40631957913584, 45.02015821022701], + [35.40089948016896, 45.025046135473445], + [35.39790908073891, 45.03482073400548], + [35.40052568024015, 45.042216617888045], + [35.40631957913584, 45.051328088783805], + [35.40744097892215, 45.06294640963205], + [35.41734667704213, 45.0708666385693], + [35.469304867139925, 45.10068964922732], + [35.5070260597534, 45.113341616151644], + [35.54758335202416, 45.12019982412133], + [35.59019654390909, 45.11993606213795], + [35.63411803553862, 45.11439677872579], + [35.70669729572677, 45.09480210570922], + [35.771782422456766, 45.06572995732262], + [35.78430472007, 45.057941041321754], + [35.81250040352472, 45.031852200991295], + [35.81941570220667, 45.021152336906454], + [35.82763930064016, 44.99895365027004], + [35.848198296721705, 44.99208088455586], + [35.916977483614176, 45.00172895661731], + [35.99360646900681, 44.997896355361604], + [36.00893226608571, 45.00926125333629], + [36.02539976723364, 45.03288661039673], + [36.047827762958946, 45.048074065419456], + [36.078666257082034, 45.03883000769565], + [36.079137312377895, 45.046610970582435], + [36.135020401727616, 45.02125162210126], + [36.2241716847341, 45.00751061631556], + [36.24398308095806, 45.011474706353084], + [36.24828178013877, 45.01649549321965], + [36.25332807917695, 45.03247980324494], + [36.25743987839326, 45.03842324279259], + [36.267158676549116, 45.043573724415154], + [36.2783726744118, 45.04555455542638], + [36.36740852558336, 45.04833265291825], + [36.44029951169139, 45.06787222615526], + [36.45375630913995, 45.07631970334319], + [36.455251508854985, 45.09202341204062], + [36.44142091149291, 45.10709638287736], + [36.41432041665814, 45.12872568311289], + [36.40852651776157, 45.149160473330085], + [36.409997342308856, 45.171615955386955], + [36.418312796420764, 45.23001671705953], + [36.42672329481775, 45.25186253492981], + [36.43756477765089, 45.27227491599612], + [36.4497132753354, 45.28542626329343], + [36.45905827355429, 45.28753019598713], + [36.4814862692796, 45.28845064200263], + [36.4909554290368, 45.29213135137758], + [36.49637552800283, 45.300940007322055], + [36.49394582846682, 45.305015191082816], + [36.48871262946426, 45.30935296803605], + [36.48460083024801, 45.315924724862185], + [36.489647129296515, 45.336413860372005], + [36.502169426909745, 45.34731734941451], + [36.52104632331191, 45.35033842661815], + [36.544281237819945, 45.34731734942025], + [36.57455903204905, 45.33601971904315], + [36.585399229982954, 45.333917585593355], + [36.59810088537549, 45.334837278577254], + [36.630808379142394, 45.34048649352954], + [36.637536777859964, 45.3511265071989], + [36.63099527910589, 45.3741073632589], + [36.61359545390113, 45.40895280985421], + [36.59845655678569, 45.421547717459106], + [36.58331765967199, 45.42731944465129], + [36.566309762912795, 45.42548305000767], + [36.54836736633254, 45.41210180010589], + [36.53285466928139, 45.4090840212946], + [36.51565987255873, 45.41957994832251], + [36.49117597722616, 45.44279525429408], + [36.47043008117939, 45.4458112314303], + [36.411182792482634, 45.43610707766504], + [36.391371396258705, 45.43991025572652], + [36.35959840231365, 45.45407156049933], + [36.33960010612526, 45.45695583486963], + [36.33025510790637, 45.454464879327446], + [36.32053630976225, 45.44856480887407], + [36.31156511147125, 45.4438443081136], + [36.29885591389362, 45.442795254299995], + [36.3072664122906, 45.46115087970253], + [36.30016421364425, 45.47320989503609], + [36.283717016779036, 45.476355300848866], + [36.267082919949445, 45.46704963343626], + [36.25213092279836, 45.46115087970253], + [36.13681364478941, 45.46219959214511], + [36.11700224855986, 45.45721803432335], + [36.097003952371466, 45.441483909606006], + [36.06952965760803, 45.43046741078453], + [36.0655449627526, 45.42553028973455], + [36.05134056545904, 45.39535242162091], + [36.022557970944945, 45.368441166003805], + [35.986486277818386, 45.362926059418186], + [35.94723728529826, 45.372380198658874], + [35.87220216002379, 45.404075760536614], + [35.85388596351393, 45.413916621802144], + [35.84715756479628, 45.426379251448395], + [35.8524047739447, 45.44386497541683], + [35.85950697259193, 45.45933624762881], + [35.857824872912545, 45.469953901705], + [35.83278027768503, 45.47087138287168], + [35.8167068807486, 45.46392436820739], + [35.80362388324218, 45.44963442058864], + [35.79469305616038, 45.42980210462429], + [35.791889556694684, 45.41209230278156], + [35.772265060435046, 45.39214572935421], + [35.767405661361295, 45.38873311015669], + [35.75189296431793, 45.386632934388984], + [35.7481549650407, 45.379938103368545], + [35.746846665290036, 45.369960021421576], + [35.74423006578874, 45.36076812520648], + [35.71619507113218, 45.34040932557082], + [35.69451467527287, 45.32989869277279], + [35.51720627467216, 45.29506847418358], + [35.48038698168983, 45.2979608697527], + [35.33194061536096, 45.371562726652314], + [35.04491375777232, 45.669545248704424], + [35.00230056589345, 45.7290693869553], + [34.70631294999043, 46.024929846739866], + [34.35868883309806, 46.106725558140795], + [34.00905273547181, 46.55925987559425] + ] + ] + } + } + } + }, + "version": 8, + "metadata": { + "maptiler:copyright": "This style was generated on MapTiler Cloud. Usage is governed by the license terms in https://github.com/maplibre/demotiles/blob/gh-pages/LICENSE", + "openmaptiles:version": "3.x" + } +} diff --git a/test/fixtures/valid-styles/maplibre-unlabelled.input.json b/test/fixtures/valid-styles/maplibre-unlabelled.input.json new file mode 100644 index 0000000..6c1de18 --- /dev/null +++ b/test/fixtures/valid-styles/maplibre-unlabelled.input.json @@ -0,0 +1,496 @@ +{ + "name": "MapLibre", + "zoom": 0.8619833357855968, + "pitch": 0, + "center": [17.65431710431244, 32.954120326746775], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": ["all"], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "coastline", + "type": "line", + "paint": { + "line-blur": 0.5, + "line-color": "#198EC8", + "line-width": { + "stops": [ + [0, 2], + [6, 6], + [14, 9], + [22, 18] + ] + } + }, + "filter": ["all"], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 0, + "source-layer": "countries" + }, + { + "id": "countries-boundary", + "type": "line", + "paint": { + "line-color": "rgba(255, 255, 255, 1)", + "line-width": { + "stops": [ + [1, 1], + [6, 2], + [14, 6], + [22, 12] + ] + }, + "line-opacity": { + "stops": [ + [3, 0.5], + [6, 1] + ] + } + }, + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "source-layer": "countries" + }, + { + "id": "geolines", + "type": "line", + "paint": { + "line-color": "#1077B0", + "line-opacity": 1, + "line-dasharray": [3, 3] + }, + "filter": ["all", ["!=", "name", "International Date Line"]], + "layout": { + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "source-layer": "geolines" + }, + { + "id": "crimea-fill", + "type": "fill", + "source": "crimea", + "paint": { + "fill-color": "#D6C7FF" + } + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "url": "https://demotiles.maplibre.org/tiles/tiles.json", + "type": "vector" + }, + "crimea": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [34.00905273547181, 46.55925987559425], + [33.64325260204026, 46.34533545368038], + [33.628682598560204, 46.12569762665683], + [33.64292861730951, 46.10476396128129], + [33.648473474905984, 46.09033047763651], + [33.63876482059936, 46.077976784785335], + [33.62782672238245, 46.06747935719011], + [33.62911357645072, 46.05708111413949], + [33.642686868727424, 46.02192963417187], + [33.6429723910654, 46.01521185644708], + [33.636224138774026, 46.006705833212465], + [33.63052626465907, 45.99692992186792], + [33.63193836679693, 45.988472992911284], + [33.64276684834442, 45.984575360297384], + [33.646928693041986, 45.97981936210982], + [33.638745893564305, 45.96829769147004], + [33.61958133326394, 45.951176418494185], + [33.63181380398527, 45.9445404758078], + [33.638921676216, 45.94737012930554], + [33.64561542516918, 45.95403251372139], + [33.65666403976448, 45.95687114427736], + [33.6825817382811, 45.95878100879199], + [33.738791807037614, 45.94836945227263], + [33.758180142697, 45.94072970008301], + [33.77735917288169, 45.92923970233858], + [33.75927796793485, 45.92241179584471], + [33.72529865009221, 45.91587363154565], + [33.70875012326826, 45.91008760988058], + [33.69378857293381, 45.91480850795665], + [33.69092650243843, 45.89657370898402], + [33.693592356906805, 45.87271465766318], + [33.69226765972388, 45.86041392418218], + [33.6704813511748, 45.8584273836251], + [33.65936345808916, 45.85944682601249], + [33.653870582376726, 45.86425922279372], + [33.65107345584843, 45.87089907254003], + [33.63067378180233, 45.88040685247182], + [33.61945300059696, 45.88147266102649], + [33.60987421595539, 45.88048951126686], + [33.59906957603934, 45.877610457390375], + [33.57828877687868, 45.86810261756233], + [33.55357394560386, 45.84700625141778], + [33.530220674480375, 45.84221983655459], + [33.5192297395441, 45.84121682367507], + [33.50832088442496, 45.84313067048083], + [33.48901101848409, 45.85268298292175], + [33.482152996405716, 45.854578171799005], + [33.46719955896293, 45.849912739405056], + [33.42447496599681, 45.83075886348303], + [33.40940172404095, 45.82691953557702], + [33.37918350072067, 45.802867525073566], + [33.37362145339398, 45.79619281922518], + [33.33805543634864, 45.78577808972071], + [33.26498872665803, 45.75410774187094], + [33.22887541283427, 45.75131270772724], + [33.19548267281132, 45.7644887297206], + [33.1789202379222, 45.78010311364778], + [33.1688456078636, 45.78470227904205], + [33.161012432811674, 45.77921593899549], + [33.15951585299757, 45.76864464913777], + [33.165962301438725, 45.762685940125465], + [33.1750888126426, 45.759218220695715], + [33.181464829753, 45.75490447884948], + [33.17613930782352, 45.7437961960276], + [33.16369168844906, 45.735912015025065], + [32.93692665480876, 45.662114646778264], + [32.86839112407645, 45.63044340698664], + [32.83803944575723, 45.60834075026611], + [32.82702772424804, 45.59576101516498], + [32.82433467080986, 45.58705137380335], + [32.82563941622885, 45.579605763895614], + [32.82993674258438, 45.56978311819469], + [32.82851940940563, 45.56227808675749], + [32.813310142795274, 45.55930933158257], + [32.80213583657516, 45.560145780074464], + [32.78258622159436, 45.565158335073846], + [32.77333922465823, 45.56689313356526], + [32.758306734735356, 45.565030173463356], + [32.750177256846115, 45.55943726334968], + [32.74340732630185, 45.55261895849793], + [32.73524549539499, 45.54598788110354], + [32.72031700779701, 45.53735927760957], + [32.70536040418847, 45.53169142131733], + [32.68589438933773, 45.52663379187257], + [32.66370583186284, 45.52563107058867], + [32.64312077736798, 45.52188979044979], + [32.525284074162556, 45.45838108691365], + [32.49490411219156, 45.43524910229854], + [32.48107654411925, 45.408986638827514], + [32.48514589713025, 45.39458067125969], + [32.51256939517424, 45.34060655033625], + [32.535915460470335, 45.33777248012882], + [32.57027153843481, 45.32510892683359], + [32.590830644991826, 45.32038723212662], + [32.66380378113439, 45.320421746458976], + [32.67760722618917, 45.32609231279554], + [32.71316246802607, 45.353283572618125], + [32.72817188836078, 45.36074681043402], + [32.750518060251466, 45.36371725645313], + [32.89973931692998, 45.35412322462227], + [32.941197846443885, 45.34245505845169], + [32.97701667405008, 45.32596743563991], + [33.04296090827762, 45.2853982930032], + [33.05274355585479, 45.28154273654923], + [33.06850284417635, 45.27703461892352], + [33.07825272648239, 45.272210805127315], + [33.089426322403455, 45.25656353201492], + [33.09897492343546, 45.247820101667884], + [33.12384611720435, 45.238235755071685], + [33.15767197859745, 45.20755227709648], + [33.172959979330074, 45.19681657531794], + [33.21837666514142, 45.187878368659824], + [33.24017433636709, 45.180191106261134], + [33.248571989896675, 45.16588271012458], + [33.259649216030766, 45.155918961282026], + [33.28309785485047, 45.16064860772312], + [33.31767999550894, 45.17535522412791], + [33.35458473323109, 45.18598673360148], + [33.39725661527919, 45.18973663076909], + [33.41344561756824, 45.18490731877088], + [33.468468576977216, 45.149132412229676], + [33.537128652906205, 45.11719769268973], + [33.56161328289443, 45.094099022711475], + [33.57837628774928, 45.053145935448015], + [33.58247744978442, 45.027377243150454], + [33.5851414316958, 45.01816461606674], + [33.6031021265521, 44.993103583251695], + [33.605922209331794, 44.986905272229734], + [33.60843524291815, 44.97039962759274], + [33.61943161357851, 44.93184946652454], + [33.619484500808824, 44.90819321920554], + [33.61549738593425, 44.88894092276257], + [33.608561183117274, 44.871288478948514], + [33.59889474705494, 44.859790298912856], + [33.55904244709464, 44.850057575124595], + [33.54667558363471, 44.83724531175508], + [33.53701832136994, 44.81871953508235], + [33.5303157846202, 44.798338017069625], + [33.5249116915937, 44.78918633101301], + [33.51669091675143, 44.784809980590666], + [33.524785531609865, 44.77183212449111], + [33.5302902535075, 44.75724515985675], + [33.53710734694323, 44.73034290771247], + [33.54650992495621, 44.70989226909535], + [33.5481286806762, 44.699106546699085], + [33.543995566510915, 44.68230506537358], + [33.53580273994743, 44.6726082589706], + [33.52337411931097, 44.661863083605255], + [33.515320778874354, 44.6491266698327], + [33.516377841582795, 44.63464990118433], + [33.52466971637648, 44.62863961572572], + [33.557474298027785, 44.62473000923737], + [33.5710648827386, 44.620853511273225], + [33.55105839203679, 44.61506440493406], + [33.499905706797676, 44.61452599304897], + [33.48451102966331, 44.60992438254493], + [33.47658499621011, 44.60714391514574], + [33.46705078205747, 44.60616254193252], + [33.44476599234898, 44.607062134677875], + [33.4353466482458, 44.60509936890821], + [33.413591053005575, 44.593500212748125], + [33.40543527945235, 44.59055535193136], + [33.37510958624222, 44.58564691897425], + [33.37074452434078, 44.58851022190515], + [33.372237834990756, 44.576810695127364], + [33.37913003799301, 44.56412673079859], + [33.48759131590526, 44.51024086451031], + [33.50011215135888, 44.50041002882833], + [33.517917009115365, 44.49074142372788], + [33.53836387802215, 44.49164280212756], + [33.56041892763031, 44.4966411022441], + [33.57822378538677, 44.497542389459795], + [33.59062975079095, 44.48975808594983], + [33.619577003408466, 44.46229988129974], + [33.62635433636015, 44.45336293328907], + [33.63175322871038, 44.434828756313124], + [33.645537634715026, 44.42498521035591], + [33.721007257593925, 44.39946630464436], + [33.74168386660085, 44.39560878121904], + [33.80727466517129, 44.39454176175843], + [33.81841706002561, 44.39552670349164], + [33.83909366903248, 44.40143600575672], + [33.85149963444792, 44.40143600575945], + [33.91467816197718, 44.38387049706651], + [33.938111652185, 44.38083293528811], + [33.957065210440874, 44.38272116790142], + [34.06614966692763, 44.42019923628979], + [34.088893936836286, 44.42200415824283], + [34.10279321289039, 44.42487551014821], + [34.135933345669, 44.44163597968952], + [34.14696087047267, 44.44959070749778], + [34.16058918507403, 44.466287285335795], + [34.170123399227776, 44.48186111741296], + [34.182759104731986, 44.49267838558103], + [34.22923417224524, 44.49949719774551], + [34.24301857824986, 44.50744404277697], + [34.263903954150294, 44.53186886058606], + [34.26631622520165, 44.53555362837611], + [34.26631622520165, 44.54153064468656], + [34.27033667695244, 44.545378535987936], + [34.2757355693048, 44.54644280144541], + [34.285384653508004, 44.54562413743594], + [34.299973149863405, 44.54554227040174], + [34.32260254971496, 44.543577427039224], + [34.3308731933177, 44.54546040325087], + [34.340292537420794, 44.55798473830754], + [34.38042135640006, 44.631830317636684], + [34.41495238900856, 44.673669777529994], + [34.424193090575585, 44.68239452736094], + [34.42959198292681, 44.68884644523774], + [34.469399167794535, 44.730194532749294], + [34.47376422969597, 44.73011292571252], + [34.47376422969597, 44.72635887754967], + [34.475142670296464, 44.723502373339585], + [34.499724861011515, 44.74292382044041], + [34.532800295801195, 44.752620844929055], + [34.61217550038418, 44.76534519537847], + [34.65065696715081, 44.777088262503725], + [34.72084256772871, 44.811080759265764], + [34.756796893391225, 44.82094054159748], + [34.82646979041766, 44.81208604604609], + [34.84289620758207, 44.816893835303176], + [34.856910353686715, 44.82373813182468], + [34.889648317948144, 44.817871641692506], + [34.90733830566026, 44.820886440346584], + [34.922960632465504, 44.83050015059965], + [34.92950822531711, 44.83652826953224], + [34.94179932067178, 44.84019370922482], + [34.95282684547897, 44.841415470643284], + [34.98567967978991, 44.840275160795755], + [35.0053224583441, 44.83538786296728], + [35.017958163849414, 44.82219008824552], + [35.02703289780189, 44.80890779582285], + [35.037933245998005, 44.79869792240089], + [35.08073333784134, 44.793525442788905], + [35.1080207326404, 44.824553365795765], + [35.130368105574235, 44.86879838545747], + [35.15485200090768, 44.90071251697748], + [35.17111229780758, 44.90746386008772], + [35.21522068940149, 44.91421441031795], + [35.233163085981715, 44.925728224907715], + [35.25636688416236, 44.95896657181197], + [35.27300098099195, 44.96690119386028], + [35.29748487632534, 44.95605693543271], + [35.30496087491386, 44.96121482614441], + [35.315240372954605, 44.965711070514175], + [35.31935217217088, 44.96941359539801], + [35.36757236298112, 44.94362319076086], + [35.36103086422793, 44.97364475976596], + [35.362152264014156, 44.98593980935419], + [35.374674561627444, 44.997835734117416], + [35.389439658813274, 45.00180049366759], + [35.42270785247763, 45.00087540764923], + [35.43504325012745, 45.00470780964241], + [35.43504325012745, 45.011446929213974], + [35.40631957913584, 45.02015821022701], + [35.40089948016896, 45.025046135473445], + [35.39790908073891, 45.03482073400548], + [35.40052568024015, 45.042216617888045], + [35.40631957913584, 45.051328088783805], + [35.40744097892215, 45.06294640963205], + [35.41734667704213, 45.0708666385693], + [35.469304867139925, 45.10068964922732], + [35.5070260597534, 45.113341616151644], + [35.54758335202416, 45.12019982412133], + [35.59019654390909, 45.11993606213795], + [35.63411803553862, 45.11439677872579], + [35.70669729572677, 45.09480210570922], + [35.771782422456766, 45.06572995732262], + [35.78430472007, 45.057941041321754], + [35.81250040352472, 45.031852200991295], + [35.81941570220667, 45.021152336906454], + [35.82763930064016, 44.99895365027004], + [35.848198296721705, 44.99208088455586], + [35.916977483614176, 45.00172895661731], + [35.99360646900681, 44.997896355361604], + [36.00893226608571, 45.00926125333629], + [36.02539976723364, 45.03288661039673], + [36.047827762958946, 45.048074065419456], + [36.078666257082034, 45.03883000769565], + [36.079137312377895, 45.046610970582435], + [36.135020401727616, 45.02125162210126], + [36.2241716847341, 45.00751061631556], + [36.24398308095806, 45.011474706353084], + [36.24828178013877, 45.01649549321965], + [36.25332807917695, 45.03247980324494], + [36.25743987839326, 45.03842324279259], + [36.267158676549116, 45.043573724415154], + [36.2783726744118, 45.04555455542638], + [36.36740852558336, 45.04833265291825], + [36.44029951169139, 45.06787222615526], + [36.45375630913995, 45.07631970334319], + [36.455251508854985, 45.09202341204062], + [36.44142091149291, 45.10709638287736], + [36.41432041665814, 45.12872568311289], + [36.40852651776157, 45.149160473330085], + [36.409997342308856, 45.171615955386955], + [36.418312796420764, 45.23001671705953], + [36.42672329481775, 45.25186253492981], + [36.43756477765089, 45.27227491599612], + [36.4497132753354, 45.28542626329343], + [36.45905827355429, 45.28753019598713], + [36.4814862692796, 45.28845064200263], + [36.4909554290368, 45.29213135137758], + [36.49637552800283, 45.300940007322055], + [36.49394582846682, 45.305015191082816], + [36.48871262946426, 45.30935296803605], + [36.48460083024801, 45.315924724862185], + [36.489647129296515, 45.336413860372005], + [36.502169426909745, 45.34731734941451], + [36.52104632331191, 45.35033842661815], + [36.544281237819945, 45.34731734942025], + [36.57455903204905, 45.33601971904315], + [36.585399229982954, 45.333917585593355], + [36.59810088537549, 45.334837278577254], + [36.630808379142394, 45.34048649352954], + [36.637536777859964, 45.3511265071989], + [36.63099527910589, 45.3741073632589], + [36.61359545390113, 45.40895280985421], + [36.59845655678569, 45.421547717459106], + [36.58331765967199, 45.42731944465129], + [36.566309762912795, 45.42548305000767], + [36.54836736633254, 45.41210180010589], + [36.53285466928139, 45.4090840212946], + [36.51565987255873, 45.41957994832251], + [36.49117597722616, 45.44279525429408], + [36.47043008117939, 45.4458112314303], + [36.411182792482634, 45.43610707766504], + [36.391371396258705, 45.43991025572652], + [36.35959840231365, 45.45407156049933], + [36.33960010612526, 45.45695583486963], + [36.33025510790637, 45.454464879327446], + [36.32053630976225, 45.44856480887407], + [36.31156511147125, 45.4438443081136], + [36.29885591389362, 45.442795254299995], + [36.3072664122906, 45.46115087970253], + [36.30016421364425, 45.47320989503609], + [36.283717016779036, 45.476355300848866], + [36.267082919949445, 45.46704963343626], + [36.25213092279836, 45.46115087970253], + [36.13681364478941, 45.46219959214511], + [36.11700224855986, 45.45721803432335], + [36.097003952371466, 45.441483909606006], + [36.06952965760803, 45.43046741078453], + [36.0655449627526, 45.42553028973455], + [36.05134056545904, 45.39535242162091], + [36.022557970944945, 45.368441166003805], + [35.986486277818386, 45.362926059418186], + [35.94723728529826, 45.372380198658874], + [35.87220216002379, 45.404075760536614], + [35.85388596351393, 45.413916621802144], + [35.84715756479628, 45.426379251448395], + [35.8524047739447, 45.44386497541683], + [35.85950697259193, 45.45933624762881], + [35.857824872912545, 45.469953901705], + [35.83278027768503, 45.47087138287168], + [35.8167068807486, 45.46392436820739], + [35.80362388324218, 45.44963442058864], + [35.79469305616038, 45.42980210462429], + [35.791889556694684, 45.41209230278156], + [35.772265060435046, 45.39214572935421], + [35.767405661361295, 45.38873311015669], + [35.75189296431793, 45.386632934388984], + [35.7481549650407, 45.379938103368545], + [35.746846665290036, 45.369960021421576], + [35.74423006578874, 45.36076812520648], + [35.71619507113218, 45.34040932557082], + [35.69451467527287, 45.32989869277279], + [35.51720627467216, 45.29506847418358], + [35.48038698168983, 45.2979608697527], + [35.33194061536096, 45.371562726652314], + [35.04491375777232, 45.669545248704424], + [35.00230056589345, 45.7290693869553], + [34.70631294999043, 46.024929846739866], + [34.35868883309806, 46.106725558140795], + [34.00905273547181, 46.55925987559425] + ] + ] + } + } + } + }, + "version": 8 +} diff --git a/test/fixtures/valid-styles/maplibre-unlabelled.output.json b/test/fixtures/valid-styles/maplibre-unlabelled.output.json new file mode 100644 index 0000000..7913a6b --- /dev/null +++ b/test/fixtures/valid-styles/maplibre-unlabelled.output.json @@ -0,0 +1,1573 @@ +{ + "name": "MapLibre", + "zoom": 14, + "pitch": 0, + "center": [ + 34.559306660989606, + 45.47004640544118 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": [ + "all" + ], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "crimea-fill", + "type": "fill", + "source": "crimea", + "paint": { + "fill-color": "#D6C7FF" + } + } + ], + "bearing": 0, + "sources": { + "crimea": { + "type": "geojson", + "data": { + "type": "Feature", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 34.00905273547181, + 46.55925987559425 + ], + [ + 33.64325260204026, + 46.34533545368038 + ], + [ + 33.628682598560204, + 46.12569762665683 + ], + [ + 33.64292861730951, + 46.10476396128129 + ], + [ + 33.648473474905984, + 46.09033047763651 + ], + [ + 33.63876482059936, + 46.077976784785335 + ], + [ + 33.62782672238245, + 46.06747935719011 + ], + [ + 33.62911357645072, + 46.05708111413949 + ], + [ + 33.642686868727424, + 46.02192963417187 + ], + [ + 33.6429723910654, + 46.01521185644708 + ], + [ + 33.636224138774026, + 46.006705833212465 + ], + [ + 33.63052626465907, + 45.99692992186792 + ], + [ + 33.63193836679693, + 45.988472992911284 + ], + [ + 33.64276684834442, + 45.984575360297384 + ], + [ + 33.646928693041986, + 45.97981936210982 + ], + [ + 33.638745893564305, + 45.96829769147004 + ], + [ + 33.61958133326394, + 45.951176418494185 + ], + [ + 33.63181380398527, + 45.9445404758078 + ], + [ + 33.638921676216, + 45.94737012930554 + ], + [ + 33.64561542516918, + 45.95403251372139 + ], + [ + 33.65666403976448, + 45.95687114427736 + ], + [ + 33.6825817382811, + 45.95878100879199 + ], + [ + 33.738791807037614, + 45.94836945227263 + ], + [ + 33.758180142697, + 45.94072970008301 + ], + [ + 33.77735917288169, + 45.92923970233858 + ], + [ + 33.75927796793485, + 45.92241179584471 + ], + [ + 33.72529865009221, + 45.91587363154565 + ], + [ + 33.70875012326826, + 45.91008760988058 + ], + [ + 33.69378857293381, + 45.91480850795665 + ], + [ + 33.69092650243843, + 45.89657370898402 + ], + [ + 33.693592356906805, + 45.87271465766318 + ], + [ + 33.69226765972388, + 45.86041392418218 + ], + [ + 33.6704813511748, + 45.8584273836251 + ], + [ + 33.65936345808916, + 45.85944682601249 + ], + [ + 33.653870582376726, + 45.86425922279372 + ], + [ + 33.65107345584843, + 45.87089907254003 + ], + [ + 33.63067378180233, + 45.88040685247182 + ], + [ + 33.61945300059696, + 45.88147266102649 + ], + [ + 33.60987421595539, + 45.88048951126686 + ], + [ + 33.59906957603934, + 45.877610457390375 + ], + [ + 33.57828877687868, + 45.86810261756233 + ], + [ + 33.55357394560386, + 45.84700625141778 + ], + [ + 33.530220674480375, + 45.84221983655459 + ], + [ + 33.5192297395441, + 45.84121682367507 + ], + [ + 33.50832088442496, + 45.84313067048083 + ], + [ + 33.48901101848409, + 45.85268298292175 + ], + [ + 33.482152996405716, + 45.854578171799005 + ], + [ + 33.46719955896293, + 45.849912739405056 + ], + [ + 33.42447496599681, + 45.83075886348303 + ], + [ + 33.40940172404095, + 45.82691953557702 + ], + [ + 33.37918350072067, + 45.802867525073566 + ], + [ + 33.37362145339398, + 45.79619281922518 + ], + [ + 33.33805543634864, + 45.78577808972071 + ], + [ + 33.26498872665803, + 45.75410774187094 + ], + [ + 33.22887541283427, + 45.75131270772724 + ], + [ + 33.19548267281132, + 45.7644887297206 + ], + [ + 33.1789202379222, + 45.78010311364778 + ], + [ + 33.1688456078636, + 45.78470227904205 + ], + [ + 33.161012432811674, + 45.77921593899549 + ], + [ + 33.15951585299757, + 45.76864464913777 + ], + [ + 33.165962301438725, + 45.762685940125465 + ], + [ + 33.1750888126426, + 45.759218220695715 + ], + [ + 33.181464829753, + 45.75490447884948 + ], + [ + 33.17613930782352, + 45.7437961960276 + ], + [ + 33.16369168844906, + 45.735912015025065 + ], + [ + 32.93692665480876, + 45.662114646778264 + ], + [ + 32.86839112407645, + 45.63044340698664 + ], + [ + 32.83803944575723, + 45.60834075026611 + ], + [ + 32.82702772424804, + 45.59576101516498 + ], + [ + 32.82433467080986, + 45.58705137380335 + ], + [ + 32.82563941622885, + 45.579605763895614 + ], + [ + 32.82993674258438, + 45.56978311819469 + ], + [ + 32.82851940940563, + 45.56227808675749 + ], + [ + 32.813310142795274, + 45.55930933158257 + ], + [ + 32.80213583657516, + 45.560145780074464 + ], + [ + 32.78258622159436, + 45.565158335073846 + ], + [ + 32.77333922465823, + 45.56689313356526 + ], + [ + 32.758306734735356, + 45.565030173463356 + ], + [ + 32.750177256846115, + 45.55943726334968 + ], + [ + 32.74340732630185, + 45.55261895849793 + ], + [ + 32.73524549539499, + 45.54598788110354 + ], + [ + 32.72031700779701, + 45.53735927760957 + ], + [ + 32.70536040418847, + 45.53169142131733 + ], + [ + 32.68589438933773, + 45.52663379187257 + ], + [ + 32.66370583186284, + 45.52563107058867 + ], + [ + 32.64312077736798, + 45.52188979044979 + ], + [ + 32.525284074162556, + 45.45838108691365 + ], + [ + 32.49490411219156, + 45.43524910229854 + ], + [ + 32.48107654411925, + 45.408986638827514 + ], + [ + 32.48514589713025, + 45.39458067125969 + ], + [ + 32.51256939517424, + 45.34060655033625 + ], + [ + 32.535915460470335, + 45.33777248012882 + ], + [ + 32.57027153843481, + 45.32510892683359 + ], + [ + 32.590830644991826, + 45.32038723212662 + ], + [ + 32.66380378113439, + 45.320421746458976 + ], + [ + 32.67760722618917, + 45.32609231279554 + ], + [ + 32.71316246802607, + 45.353283572618125 + ], + [ + 32.72817188836078, + 45.36074681043402 + ], + [ + 32.750518060251466, + 45.36371725645313 + ], + [ + 32.89973931692998, + 45.35412322462227 + ], + [ + 32.941197846443885, + 45.34245505845169 + ], + [ + 32.97701667405008, + 45.32596743563991 + ], + [ + 33.04296090827762, + 45.2853982930032 + ], + [ + 33.05274355585479, + 45.28154273654923 + ], + [ + 33.06850284417635, + 45.27703461892352 + ], + [ + 33.07825272648239, + 45.272210805127315 + ], + [ + 33.089426322403455, + 45.25656353201492 + ], + [ + 33.09897492343546, + 45.247820101667884 + ], + [ + 33.12384611720435, + 45.238235755071685 + ], + [ + 33.15767197859745, + 45.20755227709648 + ], + [ + 33.172959979330074, + 45.19681657531794 + ], + [ + 33.21837666514142, + 45.187878368659824 + ], + [ + 33.24017433636709, + 45.180191106261134 + ], + [ + 33.248571989896675, + 45.16588271012458 + ], + [ + 33.259649216030766, + 45.155918961282026 + ], + [ + 33.28309785485047, + 45.16064860772312 + ], + [ + 33.31767999550894, + 45.17535522412791 + ], + [ + 33.35458473323109, + 45.18598673360148 + ], + [ + 33.39725661527919, + 45.18973663076909 + ], + [ + 33.41344561756824, + 45.18490731877088 + ], + [ + 33.468468576977216, + 45.149132412229676 + ], + [ + 33.537128652906205, + 45.11719769268973 + ], + [ + 33.56161328289443, + 45.094099022711475 + ], + [ + 33.57837628774928, + 45.053145935448015 + ], + [ + 33.58247744978442, + 45.027377243150454 + ], + [ + 33.5851414316958, + 45.01816461606674 + ], + [ + 33.6031021265521, + 44.993103583251695 + ], + [ + 33.605922209331794, + 44.986905272229734 + ], + [ + 33.60843524291815, + 44.97039962759274 + ], + [ + 33.61943161357851, + 44.93184946652454 + ], + [ + 33.619484500808824, + 44.90819321920554 + ], + [ + 33.61549738593425, + 44.88894092276257 + ], + [ + 33.608561183117274, + 44.871288478948514 + ], + [ + 33.59889474705494, + 44.859790298912856 + ], + [ + 33.55904244709464, + 44.850057575124595 + ], + [ + 33.54667558363471, + 44.83724531175508 + ], + [ + 33.53701832136994, + 44.81871953508235 + ], + [ + 33.5303157846202, + 44.798338017069625 + ], + [ + 33.5249116915937, + 44.78918633101301 + ], + [ + 33.51669091675143, + 44.784809980590666 + ], + [ + 33.524785531609865, + 44.77183212449111 + ], + [ + 33.5302902535075, + 44.75724515985675 + ], + [ + 33.53710734694323, + 44.73034290771247 + ], + [ + 33.54650992495621, + 44.70989226909535 + ], + [ + 33.5481286806762, + 44.699106546699085 + ], + [ + 33.543995566510915, + 44.68230506537358 + ], + [ + 33.53580273994743, + 44.6726082589706 + ], + [ + 33.52337411931097, + 44.661863083605255 + ], + [ + 33.515320778874354, + 44.6491266698327 + ], + [ + 33.516377841582795, + 44.63464990118433 + ], + [ + 33.52466971637648, + 44.62863961572572 + ], + [ + 33.557474298027785, + 44.62473000923737 + ], + [ + 33.5710648827386, + 44.620853511273225 + ], + [ + 33.55105839203679, + 44.61506440493406 + ], + [ + 33.499905706797676, + 44.61452599304897 + ], + [ + 33.48451102966331, + 44.60992438254493 + ], + [ + 33.47658499621011, + 44.60714391514574 + ], + [ + 33.46705078205747, + 44.60616254193252 + ], + [ + 33.44476599234898, + 44.607062134677875 + ], + [ + 33.4353466482458, + 44.60509936890821 + ], + [ + 33.413591053005575, + 44.593500212748125 + ], + [ + 33.40543527945235, + 44.59055535193136 + ], + [ + 33.37510958624222, + 44.58564691897425 + ], + [ + 33.37074452434078, + 44.58851022190515 + ], + [ + 33.372237834990756, + 44.576810695127364 + ], + [ + 33.37913003799301, + 44.56412673079859 + ], + [ + 33.48759131590526, + 44.51024086451031 + ], + [ + 33.50011215135888, + 44.50041002882833 + ], + [ + 33.517917009115365, + 44.49074142372788 + ], + [ + 33.53836387802215, + 44.49164280212756 + ], + [ + 33.56041892763031, + 44.4966411022441 + ], + [ + 33.57822378538677, + 44.497542389459795 + ], + [ + 33.59062975079095, + 44.48975808594983 + ], + [ + 33.619577003408466, + 44.46229988129974 + ], + [ + 33.62635433636015, + 44.45336293328907 + ], + [ + 33.63175322871038, + 44.434828756313124 + ], + [ + 33.645537634715026, + 44.42498521035591 + ], + [ + 33.721007257593925, + 44.39946630464436 + ], + [ + 33.74168386660085, + 44.39560878121904 + ], + [ + 33.80727466517129, + 44.39454176175843 + ], + [ + 33.81841706002561, + 44.39552670349164 + ], + [ + 33.83909366903248, + 44.40143600575672 + ], + [ + 33.85149963444792, + 44.40143600575945 + ], + [ + 33.91467816197718, + 44.38387049706651 + ], + [ + 33.938111652185, + 44.38083293528811 + ], + [ + 33.957065210440874, + 44.38272116790142 + ], + [ + 34.06614966692763, + 44.42019923628979 + ], + [ + 34.088893936836286, + 44.42200415824283 + ], + [ + 34.10279321289039, + 44.42487551014821 + ], + [ + 34.135933345669, + 44.44163597968952 + ], + [ + 34.14696087047267, + 44.44959070749778 + ], + [ + 34.16058918507403, + 44.466287285335795 + ], + [ + 34.170123399227776, + 44.48186111741296 + ], + [ + 34.182759104731986, + 44.49267838558103 + ], + [ + 34.22923417224524, + 44.49949719774551 + ], + [ + 34.24301857824986, + 44.50744404277697 + ], + [ + 34.263903954150294, + 44.53186886058606 + ], + [ + 34.26631622520165, + 44.53555362837611 + ], + [ + 34.26631622520165, + 44.54153064468656 + ], + [ + 34.27033667695244, + 44.545378535987936 + ], + [ + 34.2757355693048, + 44.54644280144541 + ], + [ + 34.285384653508004, + 44.54562413743594 + ], + [ + 34.299973149863405, + 44.54554227040174 + ], + [ + 34.32260254971496, + 44.543577427039224 + ], + [ + 34.3308731933177, + 44.54546040325087 + ], + [ + 34.340292537420794, + 44.55798473830754 + ], + [ + 34.38042135640006, + 44.631830317636684 + ], + [ + 34.41495238900856, + 44.673669777529994 + ], + [ + 34.424193090575585, + 44.68239452736094 + ], + [ + 34.42959198292681, + 44.68884644523774 + ], + [ + 34.469399167794535, + 44.730194532749294 + ], + [ + 34.47376422969597, + 44.73011292571252 + ], + [ + 34.47376422969597, + 44.72635887754967 + ], + [ + 34.475142670296464, + 44.723502373339585 + ], + [ + 34.499724861011515, + 44.74292382044041 + ], + [ + 34.532800295801195, + 44.752620844929055 + ], + [ + 34.61217550038418, + 44.76534519537847 + ], + [ + 34.65065696715081, + 44.777088262503725 + ], + [ + 34.72084256772871, + 44.811080759265764 + ], + [ + 34.756796893391225, + 44.82094054159748 + ], + [ + 34.82646979041766, + 44.81208604604609 + ], + [ + 34.84289620758207, + 44.816893835303176 + ], + [ + 34.856910353686715, + 44.82373813182468 + ], + [ + 34.889648317948144, + 44.817871641692506 + ], + [ + 34.90733830566026, + 44.820886440346584 + ], + [ + 34.922960632465504, + 44.83050015059965 + ], + [ + 34.92950822531711, + 44.83652826953224 + ], + [ + 34.94179932067178, + 44.84019370922482 + ], + [ + 34.95282684547897, + 44.841415470643284 + ], + [ + 34.98567967978991, + 44.840275160795755 + ], + [ + 35.0053224583441, + 44.83538786296728 + ], + [ + 35.017958163849414, + 44.82219008824552 + ], + [ + 35.02703289780189, + 44.80890779582285 + ], + [ + 35.037933245998005, + 44.79869792240089 + ], + [ + 35.08073333784134, + 44.793525442788905 + ], + [ + 35.1080207326404, + 44.824553365795765 + ], + [ + 35.130368105574235, + 44.86879838545747 + ], + [ + 35.15485200090768, + 44.90071251697748 + ], + [ + 35.17111229780758, + 44.90746386008772 + ], + [ + 35.21522068940149, + 44.91421441031795 + ], + [ + 35.233163085981715, + 44.925728224907715 + ], + [ + 35.25636688416236, + 44.95896657181197 + ], + [ + 35.27300098099195, + 44.96690119386028 + ], + [ + 35.29748487632534, + 44.95605693543271 + ], + [ + 35.30496087491386, + 44.96121482614441 + ], + [ + 35.315240372954605, + 44.965711070514175 + ], + [ + 35.31935217217088, + 44.96941359539801 + ], + [ + 35.36757236298112, + 44.94362319076086 + ], + [ + 35.36103086422793, + 44.97364475976596 + ], + [ + 35.362152264014156, + 44.98593980935419 + ], + [ + 35.374674561627444, + 44.997835734117416 + ], + [ + 35.389439658813274, + 45.00180049366759 + ], + [ + 35.42270785247763, + 45.00087540764923 + ], + [ + 35.43504325012745, + 45.00470780964241 + ], + [ + 35.43504325012745, + 45.011446929213974 + ], + [ + 35.40631957913584, + 45.02015821022701 + ], + [ + 35.40089948016896, + 45.025046135473445 + ], + [ + 35.39790908073891, + 45.03482073400548 + ], + [ + 35.40052568024015, + 45.042216617888045 + ], + [ + 35.40631957913584, + 45.051328088783805 + ], + [ + 35.40744097892215, + 45.06294640963205 + ], + [ + 35.41734667704213, + 45.0708666385693 + ], + [ + 35.469304867139925, + 45.10068964922732 + ], + [ + 35.5070260597534, + 45.113341616151644 + ], + [ + 35.54758335202416, + 45.12019982412133 + ], + [ + 35.59019654390909, + 45.11993606213795 + ], + [ + 35.63411803553862, + 45.11439677872579 + ], + [ + 35.70669729572677, + 45.09480210570922 + ], + [ + 35.771782422456766, + 45.06572995732262 + ], + [ + 35.78430472007, + 45.057941041321754 + ], + [ + 35.81250040352472, + 45.031852200991295 + ], + [ + 35.81941570220667, + 45.021152336906454 + ], + [ + 35.82763930064016, + 44.99895365027004 + ], + [ + 35.848198296721705, + 44.99208088455586 + ], + [ + 35.916977483614176, + 45.00172895661731 + ], + [ + 35.99360646900681, + 44.997896355361604 + ], + [ + 36.00893226608571, + 45.00926125333629 + ], + [ + 36.02539976723364, + 45.03288661039673 + ], + [ + 36.047827762958946, + 45.048074065419456 + ], + [ + 36.078666257082034, + 45.03883000769565 + ], + [ + 36.079137312377895, + 45.046610970582435 + ], + [ + 36.135020401727616, + 45.02125162210126 + ], + [ + 36.2241716847341, + 45.00751061631556 + ], + [ + 36.24398308095806, + 45.011474706353084 + ], + [ + 36.24828178013877, + 45.01649549321965 + ], + [ + 36.25332807917695, + 45.03247980324494 + ], + [ + 36.25743987839326, + 45.03842324279259 + ], + [ + 36.267158676549116, + 45.043573724415154 + ], + [ + 36.2783726744118, + 45.04555455542638 + ], + [ + 36.36740852558336, + 45.04833265291825 + ], + [ + 36.44029951169139, + 45.06787222615526 + ], + [ + 36.45375630913995, + 45.07631970334319 + ], + [ + 36.455251508854985, + 45.09202341204062 + ], + [ + 36.44142091149291, + 45.10709638287736 + ], + [ + 36.41432041665814, + 45.12872568311289 + ], + [ + 36.40852651776157, + 45.149160473330085 + ], + [ + 36.409997342308856, + 45.171615955386955 + ], + [ + 36.418312796420764, + 45.23001671705953 + ], + [ + 36.42672329481775, + 45.25186253492981 + ], + [ + 36.43756477765089, + 45.27227491599612 + ], + [ + 36.4497132753354, + 45.28542626329343 + ], + [ + 36.45905827355429, + 45.28753019598713 + ], + [ + 36.4814862692796, + 45.28845064200263 + ], + [ + 36.4909554290368, + 45.29213135137758 + ], + [ + 36.49637552800283, + 45.300940007322055 + ], + [ + 36.49394582846682, + 45.305015191082816 + ], + [ + 36.48871262946426, + 45.30935296803605 + ], + [ + 36.48460083024801, + 45.315924724862185 + ], + [ + 36.489647129296515, + 45.336413860372005 + ], + [ + 36.502169426909745, + 45.34731734941451 + ], + [ + 36.52104632331191, + 45.35033842661815 + ], + [ + 36.544281237819945, + 45.34731734942025 + ], + [ + 36.57455903204905, + 45.33601971904315 + ], + [ + 36.585399229982954, + 45.333917585593355 + ], + [ + 36.59810088537549, + 45.334837278577254 + ], + [ + 36.630808379142394, + 45.34048649352954 + ], + [ + 36.637536777859964, + 45.3511265071989 + ], + [ + 36.63099527910589, + 45.3741073632589 + ], + [ + 36.61359545390113, + 45.40895280985421 + ], + [ + 36.59845655678569, + 45.421547717459106 + ], + [ + 36.58331765967199, + 45.42731944465129 + ], + [ + 36.566309762912795, + 45.42548305000767 + ], + [ + 36.54836736633254, + 45.41210180010589 + ], + [ + 36.53285466928139, + 45.4090840212946 + ], + [ + 36.51565987255873, + 45.41957994832251 + ], + [ + 36.49117597722616, + 45.44279525429408 + ], + [ + 36.47043008117939, + 45.4458112314303 + ], + [ + 36.411182792482634, + 45.43610707766504 + ], + [ + 36.391371396258705, + 45.43991025572652 + ], + [ + 36.35959840231365, + 45.45407156049933 + ], + [ + 36.33960010612526, + 45.45695583486963 + ], + [ + 36.33025510790637, + 45.454464879327446 + ], + [ + 36.32053630976225, + 45.44856480887407 + ], + [ + 36.31156511147125, + 45.4438443081136 + ], + [ + 36.29885591389362, + 45.442795254299995 + ], + [ + 36.3072664122906, + 45.46115087970253 + ], + [ + 36.30016421364425, + 45.47320989503609 + ], + [ + 36.283717016779036, + 45.476355300848866 + ], + [ + 36.267082919949445, + 45.46704963343626 + ], + [ + 36.25213092279836, + 45.46115087970253 + ], + [ + 36.13681364478941, + 45.46219959214511 + ], + [ + 36.11700224855986, + 45.45721803432335 + ], + [ + 36.097003952371466, + 45.441483909606006 + ], + [ + 36.06952965760803, + 45.43046741078453 + ], + [ + 36.0655449627526, + 45.42553028973455 + ], + [ + 36.05134056545904, + 45.39535242162091 + ], + [ + 36.022557970944945, + 45.368441166003805 + ], + [ + 35.986486277818386, + 45.362926059418186 + ], + [ + 35.94723728529826, + 45.372380198658874 + ], + [ + 35.87220216002379, + 45.404075760536614 + ], + [ + 35.85388596351393, + 45.413916621802144 + ], + [ + 35.84715756479628, + 45.426379251448395 + ], + [ + 35.8524047739447, + 45.44386497541683 + ], + [ + 35.85950697259193, + 45.45933624762881 + ], + [ + 35.857824872912545, + 45.469953901705 + ], + [ + 35.83278027768503, + 45.47087138287168 + ], + [ + 35.8167068807486, + 45.46392436820739 + ], + [ + 35.80362388324218, + 45.44963442058864 + ], + [ + 35.79469305616038, + 45.42980210462429 + ], + [ + 35.791889556694684, + 45.41209230278156 + ], + [ + 35.772265060435046, + 45.39214572935421 + ], + [ + 35.767405661361295, + 45.38873311015669 + ], + [ + 35.75189296431793, + 45.386632934388984 + ], + [ + 35.7481549650407, + 45.379938103368545 + ], + [ + 35.746846665290036, + 45.369960021421576 + ], + [ + 35.74423006578874, + 45.36076812520648 + ], + [ + 35.71619507113218, + 45.34040932557082 + ], + [ + 35.69451467527287, + 45.32989869277279 + ], + [ + 35.51720627467216, + 45.29506847418358 + ], + [ + 35.48038698168983, + 45.2979608697527 + ], + [ + 35.33194061536096, + 45.371562726652314 + ], + [ + 35.04491375777232, + 45.669545248704424 + ], + [ + 35.00230056589345, + 45.7290693869553 + ], + [ + 34.70631294999043, + 46.024929846739866 + ], + [ + 34.35868883309806, + 46.106725558140795 + ], + [ + 34.00905273547181, + 46.55925987559425 + ] + ] + ] + }, + "bbox": [ + 32.48107654411925, + 44.38083293528811, + 36.637536777859964, + 46.55925987559425 + ] + }, + "maxzoom": 0 + } + }, + "version": 8, + "metadata": { + "smp:bounds": [ + 32.48107654411925, + 44.38083293528811, + 36.637536777859964, + 46.55925987559425 + ], + "smp:maxzoom": 16, + "smp:sourceFolders": { + "crimea": "0" + } + } +} \ No newline at end of file diff --git a/test/fixtures/valid-styles/minimal-labelled.input.json b/test/fixtures/valid-styles/minimal-labelled.input.json new file mode 100644 index 0000000..4bb2484 --- /dev/null +++ b/test/fixtures/valid-styles/minimal-labelled.input.json @@ -0,0 +1,37 @@ +{ + "name": "MapLibre", + "zoom": 0.8619833357855968, + "pitch": 0, + "center": [17.65431710431244, 32.954120326746775], + "glyphs": "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf", + "layers": [ + { + "id": "countries-label", + "type": "symbol", + "paint": { + "text-color": "rgba(8, 37, 77, 1)", + "text-halo-color": "rgba(255, 255, 255, 1)" + }, + "filter": ["all"], + "layout": { + "text-font": ["Open Sans Semibold"], + "text-size": 12, + "text-field": "{NAME}", + "visibility": "visible", + "text-max-width": 10 + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 2, + "source-layer": "centroids" + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "url": "https://demotiles.maplibre.org/tiles/tiles.json", + "type": "vector" + } + }, + "version": 8 +} diff --git a/test/fixtures/valid-styles/minimal-labelled.output.json b/test/fixtures/valid-styles/minimal-labelled.output.json new file mode 100644 index 0000000..0087e84 --- /dev/null +++ b/test/fixtures/valid-styles/minimal-labelled.output.json @@ -0,0 +1,72 @@ +{ + "name": "MapLibre", + "zoom": 0, + "pitch": 0, + "center": [ + 0, + 0 + ], + "glyphs": "smp://maps.v1/fonts/{fontstack}/{range}.pbf.gz", + "layers": [ + { + "id": "countries-label", + "type": "symbol", + "paint": { + "text-color": "rgba(8, 37, 77, 1)", + "text-halo-color": "rgba(255, 255, 255, 1)" + }, + "filter": [ + "all" + ], + "layout": { + "text-font": [ + "Open Sans Semibold" + ], + "text-size": 12, + "text-field": [ + "to-string", + [ + "get", + "NAME" + ] + ], + "visibility": "visible", + "text-max-width": 10 + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 2, + "source-layer": "centroids" + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "type": "vector", + "minzoom": 0, + "maxzoom": 0, + "bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "tiles": [ + "smp://maps.v1/s/0/{z}/{x}/{y}.mvt.gz" + ] + } + }, + "version": 8, + "metadata": { + "smp:bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "smp:maxzoom": 0, + "smp:sourceFolders": { + "maplibre": "0" + } + } +} \ No newline at end of file diff --git a/test/fixtures/valid-styles/minimal-sprites.input.json b/test/fixtures/valid-styles/minimal-sprites.input.json new file mode 100644 index 0000000..b67f8f7 --- /dev/null +++ b/test/fixtures/valid-styles/minimal-sprites.input.json @@ -0,0 +1,37 @@ +{ + "version": 8, + "name": "OSM Bright", + "sources": { + "openmaptiles": { + "type": "vector", + "url": "https://demotiles.maplibre.org/tiles-omt/tiles.json" + } + }, + "sprite": "https://demotiles.maplibre.org/styles/osm-bright-gl-style/sprite", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#f8f4f0" + } + }, + { + "id": "poi-level-1", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "filter": [ + "all", + ["==", "$type", "Point"], + ["<=", "rank", 14], + ["has", "name"], + ["any", ["!has", "level"], ["==", "level", 0]] + ], + "layout": { + "icon-image": "{class}_11" + } + } + ] +} diff --git a/test/fixtures/valid-styles/minimal-sprites.output.json b/test/fixtures/valid-styles/minimal-sprites.output.json new file mode 100644 index 0000000..8967fcc --- /dev/null +++ b/test/fixtures/valid-styles/minimal-sprites.output.json @@ -0,0 +1,58 @@ +{ + "version": 8, + "name": "OSM Bright", + "sources": { + "openmaptiles": { + "type": "vector", + "minzoom": 0, + "maxzoom": 0, + "bounds": [-180, -85.051129, 180, 85.051129], + "tiles": ["smp://maps.v1/s/0/{z}/{x}/{y}.mvt.gz"] + } + }, + "sprite": "smp://maps.v1/sprites/default/sprite", + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#f8f4f0" + } + }, + { + "id": "poi-level-1", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "filter": [ + "all", + ["==", ["geometry-type"], "Point"], + ["<=", ["get", "rank"], 14], + ["has", "name"], + [ + "any", + ["!", ["has", "level"]], + [ + "case", + ["==", ["typeof", ["get", "level"]], "number"], + ["==", ["get", "level"], 0], + false + ] + ] + ], + "layout": { + "icon-image": ["concat", ["get", "class"], "_11"] + } + } + ], + "metadata": { + "smp:bounds": [-180, -85.051129, 180, 85.051129], + "smp:maxzoom": 0, + "smp:sourceFolders": { + "openmaptiles": "0" + } + }, + "center": [0, 0], + "zoom": 0 +} diff --git a/test/fixtures/valid-styles/minimal.input.json b/test/fixtures/valid-styles/minimal.input.json new file mode 100644 index 0000000..6ad7ae3 --- /dev/null +++ b/test/fixtures/valid-styles/minimal.input.json @@ -0,0 +1,54 @@ +{ + "name": "MapLibre", + "zoom": 0.8619833357855968, + "pitch": 0, + "center": [17.65431710431244, 32.954120326746775], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": ["all"], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "coastline", + "type": "line", + "paint": { + "line-blur": 0.5, + "line-color": "#198EC8", + "line-width": { + "stops": [ + [0, 2], + [6, 6], + [14, 9], + [22, 18] + ] + } + }, + "filter": ["all"], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 0, + "source-layer": "countries" + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "url": "https://demotiles.maplibre.org/tiles/tiles.json", + "type": "vector" + } + }, + "version": 8 +} diff --git a/test/fixtures/valid-styles/minimal.output.json b/test/fixtures/valid-styles/minimal.output.json new file mode 100644 index 0000000..4796f67 --- /dev/null +++ b/test/fixtures/valid-styles/minimal.output.json @@ -0,0 +1,92 @@ +{ + "name": "MapLibre", + "zoom": 3, + "pitch": 0, + "center": [ + 56.25, + 10.53212169788165 + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#D8F2FF" + }, + "filter": [ + "all" + ], + "layout": { + "visibility": "visible" + }, + "maxzoom": 24 + }, + { + "id": "coastline", + "type": "line", + "paint": { + "line-blur": 0.5, + "line-color": "#198EC8", + "line-width": [ + "interpolate", + [ + "linear" + ], + [ + "zoom" + ], + 0, + 2, + 6, + 6, + 14, + 9, + 22, + 18 + ] + }, + "filter": [ + "all" + ], + "layout": { + "line-cap": "round", + "line-join": "round", + "visibility": "visible" + }, + "source": "maplibre", + "maxzoom": 24, + "minzoom": 0, + "source-layer": "countries" + } + ], + "bearing": 0, + "sources": { + "maplibre": { + "type": "vector", + "minzoom": 0, + "maxzoom": 5, + "bounds": [ + -45, + -55.776573018667676, + 157.5, + 76.84081641443098 + ], + "tiles": [ + "smp://maps.v1/s/0/{z}/{x}/{y}.mvt.gz" + ] + } + }, + "version": 8, + "metadata": { + "smp:bounds": [ + -45, + -55.776573018667676, + 157.5, + 76.84081641443098 + ], + "smp:maxzoom": 5, + "smp:sourceFolders": { + "maplibre": "0" + } + } +} \ No newline at end of file diff --git a/test/fixtures/valid-styles/multiple-sprites.input.json b/test/fixtures/valid-styles/multiple-sprites.input.json new file mode 100644 index 0000000..4d5e4ca --- /dev/null +++ b/test/fixtures/valid-styles/multiple-sprites.input.json @@ -0,0 +1,46 @@ +{ + "version": 8, + "name": "OSM Bright", + "sources": { + "openmaptiles": { + "type": "vector", + "url": "https://demotiles.maplibre.org/tiles-omt/tiles.json" + } + }, + "sprite": [ + { + "id": "roadsigns", + "url": "https://example.com/myroadsigns" + }, + { + "id": "default", + "url": "https://example2.com/anotherurl" + } + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#f8f4f0" + } + }, + { + "id": "poi-level-1", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "filter": [ + "all", + ["==", "$type", "Point"], + ["<=", "rank", 14], + ["has", "name"], + ["any", ["!has", "level"], ["==", "level", 0]] + ], + "layout": { + "icon-image": "{class}_11" + } + } + ] +} diff --git a/test/fixtures/valid-styles/multiple-sprites.output.json b/test/fixtures/valid-styles/multiple-sprites.output.json new file mode 100644 index 0000000..c3487b0 --- /dev/null +++ b/test/fixtures/valid-styles/multiple-sprites.output.json @@ -0,0 +1,128 @@ +{ + "version": 8, + "name": "OSM Bright", + "sources": { + "openmaptiles": { + "type": "vector", + "minzoom": 0, + "maxzoom": 0, + "bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "tiles": [ + "/s/0/{z}/{x}/{y}.mvt.gz" + ] + } + }, + "sprite": [ + { + "id": "roadsigns", + "url": "/sprites/roadsigns/sprite" + }, + { + "id": "default", + "url": "/sprites/default/sprite" + } + ], + "layers": [ + { + "id": "background", + "type": "background", + "paint": { + "background-color": "#f8f4f0" + } + }, + { + "id": "poi-level-1", + "type": "symbol", + "source": "openmaptiles", + "source-layer": "poi", + "minzoom": 14, + "filter": [ + "all", + [ + "==", + [ + "geometry-type" + ], + "Point" + ], + [ + "<=", + [ + "get", + "rank" + ], + 14 + ], + [ + "has", + "name" + ], + [ + "any", + [ + "!", + [ + "has", + "level" + ] + ], + [ + "case", + [ + "==", + [ + "typeof", + [ + "get", + "level" + ] + ], + "number" + ], + [ + "==", + [ + "get", + "level" + ], + 0 + ], + false + ] + ] + ], + "layout": { + "icon-image": [ + "concat", + [ + "get", + "class" + ], + "_11" + ] + } + } + ], + "metadata": { + "smp:bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "smp:maxzoom": 0, + "smp:sourceFolders": { + "openmaptiles": "0" + } + }, + "center": [ + 0, + 0 + ], + "zoom": 0 +} \ No newline at end of file diff --git a/test/fixtures/valid-styles/raster-sources.input.json b/test/fixtures/valid-styles/raster-sources.input.json new file mode 100644 index 0000000..8894781 --- /dev/null +++ b/test/fixtures/valid-styles/raster-sources.input.json @@ -0,0 +1,33 @@ +{ + "version": 8, + "sources": { + "jpg-tiles": { + "type": "raster", + "tiles": ["https://example1.com/{z}/{x}/{y}.jpg"], + "tileSize": 256 + }, + "png-tiles": { + "type": "raster", + "tiles": ["https://example2.com/{z}/{x}/{y}.png"], + "tileSize": 256 + } + }, + "layers": [ + { + "id": "jpg-tiles", + "type": "raster", + "source": "jpg-tiles", + "minzoom": 0, + "maxzoom": 22 + }, + { + "id": "png-tiles", + "type": "raster", + "source": "png-tiles", + "minzoom": 0, + "maxzoom": 22 + } + ], + "center": [-74.5, 40], + "zoom": 2 +} diff --git a/test/fixtures/valid-styles/raster-sources.output.json b/test/fixtures/valid-styles/raster-sources.output.json new file mode 100644 index 0000000..e5cdf6f --- /dev/null +++ b/test/fixtures/valid-styles/raster-sources.output.json @@ -0,0 +1,69 @@ +{ + "version": 8, + "sources": { + "png-tiles": { + "type": "raster", + "tileSize": 256, + "minzoom": 0, + "maxzoom": 0, + "bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "tiles": [ + "smp://maps.v1/s/0/{z}/{x}/{y}.png" + ] + }, + "jpg-tiles": { + "type": "raster", + "tileSize": 256, + "minzoom": 0, + "maxzoom": 0, + "bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "tiles": [ + "smp://maps.v1/s/1/{z}/{x}/{y}.jpg" + ] + } + }, + "layers": [ + { + "id": "jpg-tiles", + "type": "raster", + "source": "jpg-tiles", + "minzoom": 0, + "maxzoom": 22 + }, + { + "id": "png-tiles", + "type": "raster", + "source": "png-tiles", + "minzoom": 0, + "maxzoom": 22 + } + ], + "center": [ + 0, + 0 + ], + "zoom": 0, + "metadata": { + "smp:bounds": [ + -180, + -85.051129, + 180, + 85.051129 + ], + "smp:maxzoom": 0, + "smp:sourceFolders": { + "png-tiles": "0", + "jpg-tiles": "1" + } + } +} \ No newline at end of file diff --git a/test/utils/assert-bbox-equal.js b/test/utils/assert-bbox-equal.js new file mode 100644 index 0000000..ab7e596 --- /dev/null +++ b/test/utils/assert-bbox-equal.js @@ -0,0 +1,19 @@ +import assert from 'node:assert/strict' + +const PRECISION = 1e-6 + +/** + * Assert that two bounding boxes are equal within 6 decimal places. + * @param {number[]} actual + * @param {number[]} expected + * @param {string} msg + */ +export function assertBboxEqual(actual, expected, msg) { + assert.equal(actual.length, expected.length, `${msg}: length`) + for (let i = 0; i < actual.length; i++) { + assert( + Math.abs(actual[i] - expected[i]) < PRECISION, + `${msg}:\n ${actual[i]} - ${expected[i]} > ${PRECISION}`, + ) + } +} diff --git a/test/utils/digest-stream.js b/test/utils/digest-stream.js new file mode 100644 index 0000000..4875ccd --- /dev/null +++ b/test/utils/digest-stream.js @@ -0,0 +1,36 @@ +import { createHash } from 'node:crypto' +import { Transform } from 'node:stream' + +/** + * A passthrough stream that calculates a digest of the data passing through it. + */ +export class DigestStream extends Transform { + #hash + /** @param {string} algorithm */ + constructor(algorithm) { + super() + this.#hash = createHash(algorithm) + } + /** + * @param {*} chunk + * @param {BufferEncoding} encoding + * @param {import('node:stream').TransformCallback} callback + */ + _transform(chunk, encoding, callback) { + this.#hash.update(chunk) + callback(null, chunk) + } + /** + * Calculates the digest of all of the data passed through the stream. If + * encoding is provided a string will be returned; otherwise a Buffer is + * returned. + * + * The stream can not be used again after the `digest()` method has been + * called. Multiple calls will cause an error to be thrown. + * + * @param {import('node:crypto').BinaryToTextEncoding} [encoding] + */ + digest(encoding = 'binary') { + return this.#hash.digest(encoding) + } +} diff --git a/test/utils/image-streams.js b/test/utils/image-streams.js new file mode 100644 index 0000000..783796e --- /dev/null +++ b/test/utils/image-streams.js @@ -0,0 +1,30 @@ +// @ts-ignore +import JPEGEncoder from '@stealthybox/jpg-stream/encoder.js' +// @ts-ignore +import BlockStream from 'block-stream2' +// @ts-ignore +import PNGEncoder from 'png-stream/encoder.js' +import randomBytesReadableStream from 'random-bytes-readable-stream' + +/** + * Create a random-noise image stream with the given dimensions, either PNG or + * JPEG. + * + * @param {object} options + * @param {number} options.width + * @param {number} options.height + * @param {'png' | 'jpg'} options.format + * @returns + */ +export function randomImageStream({ width, height, format }) { + const encoder = + format === 'jpg' + ? new JPEGEncoder(width, height, { colorSpace: 'rgb', quality: 30 }) + : new PNGEncoder(width, height, { colorSpace: 'rgb' }) + return ( + randomBytesReadableStream({ size: width * height * 3 }) + // JPEG Encoder requires one line at a time + .pipe(new BlockStream({ size: width * 3 })) + .pipe(encoder) + ) +} diff --git a/test/utils/reader-helper.js b/test/utils/reader-helper.js new file mode 100644 index 0000000..908b57c --- /dev/null +++ b/test/utils/reader-helper.js @@ -0,0 +1,72 @@ +import { once } from 'node:events' + +import { replaceVariables } from '../../lib/utils/templates.js' +import { DigestStream } from './digest-stream.js' + +/** @import { Reader } from '../../lib/index.js' */ +/** + * A helper class for reading resources from a styled map package. + */ +export class ReaderHelper { + #reader + /** @type {Awaited> | undefined} */ + #style + /** @param {Reader} reader */ + constructor(reader) { + this.#reader = reader + } + + /** @param {string} path */ + async #digest(path) { + const resource = await this.#reader.getResource(path) + const digestStream = new DigestStream('md5') + resource.stream.pipe(digestStream).resume() + await once(digestStream, 'finish') + return digestStream.digest('hex') + } + + /** + * @param {{ z: number, x: number, y: number, sourceId: string }} opts + */ + async getTileHash({ z, x, y, sourceId }) { + const style = this.#style || (this.#style = await this.#reader.getStyle('')) + const source = style.sources[sourceId] + if (!source || !('tiles' in source) || !source.tiles) { + throw new Error(`Source not found: ${sourceId}`) + } + const tilePath = replaceVariables(source.tiles[0], { z, x, y }) + return this.#digest(tilePath) + } + + /** + * @param {import('../../lib/writer.js').GlyphInfo} glyphInfo + */ + async getGlyphHash({ font, range }) { + const style = this.#style || (this.#style = await this.#reader.getStyle('')) + if (typeof style.glyphs !== 'string') { + throw new Error('No glyphs defined in style') + } + const glyphPath = replaceVariables(style.glyphs, { fontstack: font, range }) + return this.#digest(glyphPath) + } + + /** @param {{ id?: string, pixelRatio?: 1 | 2 | 3, ext: 'json' | 'png'}} opts */ + async getSpriteHash({ id, pixelRatio = 1, ext }) { + const style = this.#style || (this.#style = await this.#reader.getStyle('')) + if (!style.sprite) { + throw new Error('No sprites defined in style') + } + const pixelRatioString = pixelRatio === 1 ? '' : `@${pixelRatio}x` + let spritePath + if (typeof style.sprite === 'string') { + spritePath = style.sprite + pixelRatioString + '.' + ext + } else { + const sprite = style.sprite.find((s) => s.id === (id || 'default')) + if (!sprite) { + throw new Error(`Sprite not found: ${id}`) + } + spritePath = sprite.url + pixelRatioString + '.' + ext + } + return this.#digest(spritePath) + } +} diff --git a/test/write-read.js b/test/write-read.js new file mode 100644 index 0000000..d6b0bd9 --- /dev/null +++ b/test/write-read.js @@ -0,0 +1,620 @@ +import SphericalMercator from '@mapbox/sphericalmercator' +import { bbox as turfBbox } from '@turf/bbox' +import randomStream from 'random-bytes-readable-stream' +import { fromBuffer as zipFromBuffer } from 'yauzl-promise' + +import assert from 'node:assert/strict' +import fs from 'node:fs/promises' +import { + buffer as streamToBuffer, + json as streamToJson, +} from 'node:stream/consumers' +import { test } from 'node:test' + +import { Reader, Writer } from '../lib/index.js' +import { tileIterator } from '../lib/tile-downloader.js' +import { unionBBox } from '../lib/utils/geo.js' +import { assertBboxEqual } from './utils/assert-bbox-equal.js' +import { DigestStream } from './utils/digest-stream.js' +import { randomImageStream } from './utils/image-streams.js' +import { ReaderHelper } from './utils/reader-helper.js' + +/** @import { BBox } from '../lib/utils/geo.js' */ + +/** @param {string | URL} filePath */ +async function readJson(filePath) { + return JSON.parse(await fs.readFile(filePath, 'utf8')) +} + +const updateSnapshots = !!process.env.UPDATE_SNAPSHOTS + +test('Invalid styles', async (t) => { + const fixturesDir = new URL('./fixtures/invalid-styles/', import.meta.url) + const fixtures = await fs.readdir(fixturesDir) + for (const fixture of fixtures) { + await t.test(fixture, async () => { + const stylePath = new URL(fixture, fixturesDir) + const style = await readJson(stylePath) + await assert.rejects( + async () => { + new Writer(style) + }, + { + message: /Invalid style/, + }, + `Expected ${fixture} to throw an error`, + ) + }) + } +}) + +test('Minimal write & read', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/minimal.input.json', + import.meta.url, + ) + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + const sm = new SphericalMercator() + + const bounds = /** @type {BBox} */ ([-40.6, -50.6, 151.6, 76.0]) + const sourceId = 'maplibre' + const maxzoom = 5 + const { minX, minY, maxX, maxY } = sm.xyz(bounds, maxzoom) + const expectedOutputBounds = unionBBox([ + sm.bbox(minX, minY, maxzoom), + sm.bbox(maxX, maxY, maxzoom), + ]) + + const tileHashes = new Map() + for (const { x, y, z } of tileIterator({ maxzoom: 5, bounds })) { + const stream = randomStream({ size: random(2048, 4096) }).pipe( + new DigestStream('md5'), + ) + await writer.addTile(stream, { x, y, z, sourceId, format: 'mvt' }) + const tileId = `${z}/${x}/${y}` + tileHashes.set(tileId, await stream.digest('hex')) + } + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + const readerHelper = new ReaderHelper(reader) + + const styleOut = await reader.getStyle() + compareAndSnapshotStyle({ styleInUrl, styleOut }) + + assertBboxEqual( + // @ts-expect-error + styleOut.sources[sourceId].bounds, + expectedOutputBounds, + 'Source has correct bounds added', + ) + assertBboxEqual( + styleOut.metadata['smp:bounds'], + expectedOutputBounds, + 'Style has correct bounds metadata added', + ) + + for (const { x, y, z } of tileIterator({ maxzoom: 5, bounds })) { + const hash = await readerHelper.getTileHash({ x, y, z, sourceId }) + assert.equal( + hash, + tileHashes.get(`${z}/${x}/${y}`), + `Tile ${z}/${x}/${y} is the same`, + ) + } +}) + +test('Inline GeoJSON is not removed from style', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/inline-geojson.input.json', + import.meta.url, + ) + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + + const styleOut = await reader.getStyle() + await compareAndSnapshotStyle({ styleInUrl, styleOut }) + + assert.equal(styleOut.sources.crimea.type, 'geojson') + const { bbox, ...geoJsonOut } = styleOut.sources.crimea.data + assert.deepEqual( + geoJsonOut, + styleIn.sources.crimea.data, + 'GeoJSON is the same', + ) + const expectedBbox = turfBbox(styleIn.sources.crimea.data) + assertBboxEqual(bbox, expectedBbox, 'GeoJSON has correct bbox added') + assertBboxEqual( + styleOut.metadata['smp:bounds'], + expectedBbox, + 'Style has correct bounds metadata added', + ) + assert.equal( + styleOut.metadata['smp:maxzoom'], + 16, + 'Style has correct maxzoom metadata added for GeoJSON', + ) +}) + +test('Un-added source is stripped from output', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/maplibre-unlabelled.input.json', + import.meta.url, + ) + + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + assert('maplibre' in styleIn.sources, 'input style contains maplibre source') + const styleInGeoJsonSourceEntry = Object.entries(styleIn.sources).find( + ([, source]) => source.type === 'geojson', + ) + assert(styleInGeoJsonSourceEntry, 'input style contains geojson source') + assert( + styleIn.layers.filter((l) => 'source' in l && l.source === 'maplibre') + .length > 0, + 'input style contains layers with maplibre source', + ) + const writer = new Writer(styleIn) + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + + const styleOut = await reader.getStyle() + await compareAndSnapshotStyle({ styleInUrl, styleOut }) + assert.deepEqual( + Object.keys(styleOut.sources), + [styleInGeoJsonSourceEntry[0]], + 'output style only contains geojson source', + ) + assert(styleOut.layers.length > 0, 'output style contains layers') + assert.equal( + styleOut.layers.filter((l) => 'source' in l && l.source === 'maplibre') + .length, + 0, + 'output style does not contain layers with maplibre source', + ) +}) + +test('Glyphs can be written and read', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/minimal-labelled.input.json', + import.meta.url, + ) + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + assert(typeof styleIn.glyphs === 'string', 'input style has glyphs URL') + const font = 'Open Sans Semibold' + + // Need to add at least one tile for the source + await writer.addTile(randomStream({ size: 1024 }), { + x: 0, + y: 0, + z: 0, + sourceId: 'maplibre', + format: 'mvt', + }) + + /** @type {Map} */ + const glyphHashes = new Map() + for (const range of glyphRanges()) { + const stream = randomStream({ size: random(256, 1024) }).pipe( + new DigestStream('md5'), + ) + await writer.addGlyphs(stream, { range, font }) + glyphHashes.set(range, await stream.digest('hex')) + } + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + const readerHelper = new ReaderHelper(reader) + + const styleOut = await reader.getStyle() + await compareAndSnapshotStyle({ styleInUrl, styleOut }) + + for (const range of glyphRanges()) { + const hash = await readerHelper.getGlyphHash({ range, font }) + assert.equal( + hash, + glyphHashes.get(range), + `Glyphs for ${range} are the same`, + ) + } +}) + +test('Missing glyphs throws an error', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/minimal-labelled.input.json', + import.meta.url, + ) + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + assert(typeof styleIn.glyphs === 'string', 'input style has glyphs URL') + + // Need to add at least one tile for the source + await writer.addTile(randomStream({ size: 1024 }), { + x: 0, + y: 0, + z: 0, + sourceId: 'maplibre', + format: 'mvt', + }) + + await assert.rejects(async () => writer.finish(), { + message: /Missing fonts/, + }) +}) + +test('Finishing writer with no sources throws and error', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/minimal.input.json', + import.meta.url, + ) + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + await assert.rejects(async () => writer.finish(), { + message: /Missing sources/, + }) +}) + +test('External GeoJSON & layers that use it are excluded if not added', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/external-geojson.input.json', + import.meta.url, + ) + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + assert( + 'crimea' in styleIn.sources && styleIn.sources.crimea.type === 'geojson', + 'input style contains crimea geojson source', + ) + assert.equal( + typeof styleIn.sources.crimea.data, + 'string', + 'geojson source is external (data is URL)', + ) + assert( + styleIn.layers.find((l) => 'source' in l && l.source === 'crimea'), + 'input style contains layers with crimea source', + ) + + // Need to add at least one tile for the source + await writer.addTile(randomStream({ size: 1024 }), { + x: 0, + y: 0, + z: 0, + sourceId: 'maplibre', + format: 'mvt', + }) + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + + const styleOut = await reader.getStyle() + await compareAndSnapshotStyle({ styleInUrl, styleOut }) + + assert( + !('crimea' in styleOut.sources), + 'output style does not contain crimea geojson source', + ) + assert( + !styleOut.layers.find((l) => 'source' in l && l.source === 'crimea'), + 'output style does not contain layers with crimea source', + ) +}) + +test('Missing sprites throws an error', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/minimal-sprites.input.json', + import.meta.url, + ) + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + assert(typeof styleIn.sprite === 'string', 'input style has sprite URL') + + // Need to add at least one tile for the source + await writer.addTile(randomStream({ size: 1024 }), { + x: 0, + y: 0, + z: 0, + sourceId: 'openmaptiles', + format: 'mvt', + }) + + await assert.rejects(async () => writer.finish(), { + message: /Missing sprite/, + }) +}) + +test('Can write and read sprites', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/minimal-sprites.input.json', + import.meta.url, + ) + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + assert(typeof styleIn.sprite === 'string', 'input style has sprite URL') + + // Need to add at least one tile for the source + await writer.addTile(randomStream({ size: 1024 }), { + x: 0, + y: 0, + z: 0, + sourceId: 'openmaptiles', + format: 'mvt', + }) + + const sprite1xImageStream = randomStream({ size: random(1024, 2048) }).pipe( + new DigestStream('md5'), + ) + const sprite2xImageStream = randomStream({ size: random(1024, 2048) }).pipe( + new DigestStream('md5'), + ) + const spriteLayoutIn = { + airfield_11: { + height: 17, + pixelRatio: 1, + width: 17, + x: 21, + y: 0, + }, + } + await writer.addSprite({ + png: sprite1xImageStream, + json: JSON.stringify(spriteLayoutIn), + }) + const sprite1xImageHash = await sprite1xImageStream.digest('hex') + await writer.addSprite({ + png: sprite2xImageStream, + json: JSON.stringify(spriteLayoutIn), + pixelRatio: 2, + }) + const sprite2xImageHash = await sprite2xImageStream.digest('hex') + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + const readerHelper = new ReaderHelper(reader) + + const styleOut = await reader.getStyle('') + await compareAndSnapshotStyle({ styleInUrl, styleOut }) + + const sprite1xImageHashOut = await readerHelper.getSpriteHash({ ext: 'png' }) + const sprite2xImageHashOut = await readerHelper.getSpriteHash({ + ext: 'png', + pixelRatio: 2, + }) + const spriteJsonResource = await reader.getResource(styleOut.sprite + '.json') + assert.equal( + spriteJsonResource.contentType, + 'application/json; charset=utf-8', + ) + const spriteLayoutOut = await streamToJson(spriteJsonResource.stream) + + assert.equal( + sprite1xImageHashOut, + sprite1xImageHash, + 'Sprite image is the same', + ) + assert.equal( + sprite2xImageHashOut, + sprite2xImageHash, + 'Sprite @2x image is the same', + ) + assert.deepEqual(spriteLayoutOut, spriteLayoutIn, 'Sprite layout is the same') +}) + +test('Can write and read style with multiple sprites', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/multiple-sprites.input.json', + import.meta.url, + ) + /** @type {import('@maplibre/maplibre-gl-style-spec').StyleSpecification} */ + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + assert(Array.isArray(styleIn.sprite), 'input style has array of sprites') + + // Need to add at least one tile for the source + await writer.addTile(randomStream({ size: 1024 }), { + x: 0, + y: 0, + z: 0, + sourceId: 'openmaptiles', + format: 'mvt', + }) + + const spriteRoadsignsImageStream = randomStream({ + size: random(1024, 2048), + }).pipe(new DigestStream('md5')) + const spriteDefaultImageStream = randomStream({ + size: random(1024, 2048), + }).pipe(new DigestStream('md5')) + const spriteRoadsignsLayoutIn = { + airfield_11: { + height: 17, + pixelRatio: 1, + width: 17, + x: 21, + y: 0, + }, + } + const spriteDefaultLayoutIn = { + other_sprite: { + height: 17, + pixelRatio: 1, + width: 17, + x: 21, + y: 0, + }, + } + await writer.addSprite({ + png: spriteDefaultImageStream, + json: JSON.stringify(spriteDefaultLayoutIn), + }) + const spriteDefaultImageHash = await spriteDefaultImageStream.digest('hex') + await writer.addSprite({ + id: 'roadsigns', + png: spriteRoadsignsImageStream, + json: JSON.stringify(spriteRoadsignsLayoutIn), + }) + const spriteRoadsignsImageHash = + await spriteRoadsignsImageStream.digest('hex') + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + const readerHelper = new ReaderHelper(reader) + + const styleOut = await reader.getStyle('') + await compareAndSnapshotStyle({ styleInUrl, styleOut }) + + const spriteDefaultImageHashOut = await readerHelper.getSpriteHash({ + ext: 'png', + }) + const spriteRoadsignsImageHashOut = await readerHelper.getSpriteHash({ + ext: 'png', + id: 'roadsigns', + }) + // @ts-expect-error + const defaultUrl = styleOut.sprite.find((s) => s.id === 'default').url + // @ts-expect-error + const roadsignsUrl = styleOut.sprite.find((s) => s.id === 'roadsigns').url + const defaultJsonResource = await reader.getResource(defaultUrl + '.json') + const roadsignsJsonResource = await reader.getResource(roadsignsUrl + '.json') + const defaultLayoutOut = await streamToJson(defaultJsonResource.stream) + const roadsignsLayoutOut = await streamToJson(roadsignsJsonResource.stream) + + assert.equal( + spriteDefaultImageHashOut, + spriteDefaultImageHash, + 'Sprite image is the same', + ) + assert.equal( + spriteRoadsignsImageHashOut, + spriteRoadsignsImageHash, + 'Sprite @2x image is the same', + ) + assert.deepEqual( + defaultLayoutOut, + spriteDefaultLayoutIn, + 'Sprite layout is the same', + ) + assert.deepEqual( + roadsignsLayoutOut, + spriteRoadsignsLayoutIn, + 'Sprite layout is the same', + ) +}) + +test('Raster tiles write and read', async () => { + const styleInUrl = new URL( + './fixtures/valid-styles/raster-sources.input.json', + import.meta.url, + ) + const styleIn = await readJson(styleInUrl) + const writer = new Writer(styleIn) + + const pngStream = randomImageStream({ + width: 256, + height: 256, + format: 'png', + }).pipe(new DigestStream('md5')) + const jpgStream = randomImageStream({ + width: 256, + height: 256, + format: 'jpg', + }).pipe(new DigestStream('md5')) + const pngTileId = { x: 0, y: 0, z: 0, sourceId: 'png-tiles' } + const jpgTileId = { x: 0, y: 0, z: 0, sourceId: 'jpg-tiles' } + await writer.addTile(pngStream, { ...pngTileId, format: 'png' }) + await writer.addTile(jpgStream, { ...jpgTileId, format: 'jpg' }) + const pngTileHash = await pngStream.digest('hex') + const jpgTileHash = await jpgStream.digest('hex') + + writer.finish() + + const smp = await streamToBuffer(writer.outputStream) + const reader = new Reader(await zipFromBuffer(smp)) + const readerHelper = new ReaderHelper(reader) + + const styleOut = await reader.getStyle() + compareAndSnapshotStyle({ styleInUrl, styleOut }) + + const pngTileHashOut = await readerHelper.getTileHash(pngTileId) + const jpgTileHashOut = await readerHelper.getTileHash(jpgTileId) + + assert.equal(pngTileHashOut, pngTileHash, 'PNG tile is the same') + assert.equal(jpgTileHashOut, jpgTileHash, 'JPG tile is the same') +}) + +/** + * + * @param {number} min + * @param {number} max + * @returns + */ +function random(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min +} + +/** + * @param {{ styleInUrl: URL, styleOut: import('../lib/types.js').SMPStyle }} opts + */ +async function compareAndSnapshotStyle({ styleInUrl, styleOut }) { + const snapshotUrl = new URL( + styleInUrl.pathname.replace(/(\.input)?\.json$/, '.output.json'), + import.meta.url, + ) + if (styleInUrl.pathname === snapshotUrl.pathname) { + throw new Error('Snapshot URL is the same as input') + } + if (updateSnapshots) { + await fs.writeFile(snapshotUrl, JSON.stringify(styleOut, null, 2)) + } else { + try { + const expected = await readJson(snapshotUrl) + assert.deepEqual(styleOut, expected) + } catch (e) { + if (e instanceof Error && 'code' in e && e.code === 'ENOENT') { + await fs.writeFile(snapshotUrl, JSON.stringify(styleOut, null, 2)) + } + } + } +} + +/** + * + * @param {number} max + * @returns {Generator<`${number}-${number}`>} + */ +function* glyphRanges(max = Math.pow(2, 16)) { + for (let i = 0; i < max; i += 256) { + yield `${i}-${i + 255}` + } +}