import * as _maplibre_maplibre_gl_style_spec from '@maplibre/maplibre-gl-style-spec'; import { PropertyValueSpecification, StyleSpecification } from '@maplibre/maplibre-gl-style-spec'; export { SpriteSpecification, StyleSpecification } from '@maplibre/maplibre-gl-style-spec'; /** Represents the structure of a vector layer in a TileJSON specification. */ interface VectorLayer { id: string; fields: Record; description?: string; minzoom?: number; maxzoom?: number; } /** Basic structure for TileJSON specification, applicable to both raster and vector types. */ interface TileJSONSpecificationRaster { tilejson?: '3.0.0'; tiles: string[]; attribution?: string; bounds?: [number, number, number, number]; center?: [number, number] | [number, number, number]; data?: string[]; description?: string; fillzoom?: number; grids?: string[]; legend?: string; maxzoom?: number; minzoom?: number; name?: string; scheme?: 'tms' | 'xyz'; template?: string; version?: string; tile_schema?: string; tile_size?: number; encoding?: 'terrarium' | 'mapbox' | 'custom'; } /** Structure for TileJSON specification of vector type, specifying vector-specific properties. */ interface TileJSONSpecificationVector extends TileJSONSpecificationRaster { vector_layers: VectorLayer[]; } /** Represents a TileJSON specification, which can be either raster or vector. */ type TileJSONSpecification = TileJSONSpecificationRaster | TileJSONSpecificationVector; /** * Validate an object against the TileJSON specification, throwing a descriptive error naming the * offending field if it does not conform. * * Use {@link isTileJSONSpecification} when you want a boolean instead. These were one function * whose `spec is T` signature promised a predicate but which threw for every invalid input, so * `if (isTileJSONSpecification(x))` blew up rather than branching. * * **npm only.** Not exported by the browser bundle (`versatiles-style.js`) — see the * `versatiles-style.js` module for what that carries. */ declare function assertTileJSONSpecification(spec: unknown): asserts spec is TileJSONSpecification; /** * Whether an object adheres to the TileJSON specification. Never throws. * * **npm only.** Not exported by the browser bundle (`versatiles-style.js`) — see the * `versatiles-style.js` module for what that carries. */ declare function isTileJSONSpecification(spec: unknown): spec is TileJSONSpecification; /** * Validate an object as a *raster* TileJSON — a TileJSON with no `vector_layers`. * Throws the same descriptive errors as {@link assertTileJSONSpecification}. * * **npm only.** Not exported by the browser bundle (`versatiles-style.js`) — see the * `versatiles-style.js` module for what that carries. */ declare function assertRasterTileJSONSpecification(spec: unknown): asserts spec is TileJSONSpecificationRaster; /** * Whether an object is a raster TileJSON (no `vector_layers`). Never throws. * * **npm only.** Not exported by the browser bundle (`versatiles-style.js`) — see the * `versatiles-style.js` module for what that carries. */ declare function isRasterTileJSONSpecification(spec: unknown): spec is TileJSONSpecificationRaster; /** * The language codes a tileset carries, from its TileJSON. * * Both naming conventions are scanned, as a schema-neutral static must: * Shortbread and OpenMapTiles spell a translation `name_de`, Protomaps spells it `name:de`, and * OpenMapTiles carries both — `name_de`/`name_en`/`name_int` alongside the full `name:xx` set. A caller * asking which languages a tileset offers wants the union, not whichever spelling the style happens to * prefer. */ declare function getLanguages(tileJSON: TileJSONSpecification): string[]; /** * A theme name. Each of the five palettes is a light theme and has a dark theme of its own, named * with a `-dark` suffix. * * The names after those are lookalikes: themes whose colour scheme resembles another project's map. * A lookalike has a dark theme only where that map has one — `positrino` does, `protozoa` does not, `fnord` is itself * dark and has no light partner — so ask `isDarkPalette`, not the suffix, whether a theme is dark. */ type Palette = 'colorful' | 'colorful-dark' | 'natural' | 'natural-dark' | 'muted' | 'muted-dark' | 'gray' | 'gray-dark' | 'toner' | 'toner-dark' | 'positrino' | 'positrino-dark' | 'fnord' | 'protocol' | 'protocol-dark' | 'protostar' | 'protostar-dark' | 'protozoa' | 'classic' | 'googol' | 'freedom' | 'macbob' | 'mactaylor' | 'bingo'; type ThemeOptions = Palette; type ResolvedTheme = Palette; declare function isDarkMode(): boolean; type ResolvedColors = Required; type HillshadeOptions = boolean | { exaggeration?: number; shadowColor?: string; highlightColor?: string; /** Only with `light: false`: lit, the hillshade uses a method that draws no accent colour. */ accentColor?: string; }; /** Whether the shading is fixed to the screen is `light.fixedToScreen`, shared with 3D buildings. */ type ResolvedHillshade = false | { exaggeration: number; shadowColor: string; highlightColor: string; accentColor: string; }; /** * 3D terrain: the map's surface raised by the elevation source. MapLibre does not light that surface, * so it shows no relief from above and `light` does not shade it; add `features.hillshade` for that. */ type TerrainOptions = boolean | { exaggeration?: number; }; type ResolvedTerrain = false | { exaggeration: number; }; type SatelliteFeaturesOptions = { terrain?: TerrainOptions; hillshade?: HillshadeOptions; }; type OsmFeaturesOptions = { terrain?: TerrainOptions; hillshade?: HillshadeOptions; landcover?: boolean; buildings?: 'flat' | 'extruded'; }; type ResolvedSatelliteFeatures = { terrain: ResolvedTerrain; hillshade: ResolvedHillshade; }; type ResolvedOsmFeatures = { terrain: ResolvedTerrain; hillshade: ResolvedHillshade; landcover: boolean; buildings: 'flat' | 'extruded'; }; /** Size and spacing of icons: POI and transit-stop icons, motorway shields and road markings. */ type IconOptions = { /** Multiplies each icon's own size. Default `1`. */ scale?: number; /** * How far apart icons keep. Along a line (oneway arrows) it multiplies the repeat distance; at a point * each step above 1 adds 14 px of collision padding. Default `1`. */ spacing?: number; }; type ResolvedIcon = Required; /** * One thing wrong with an options object, as data rather than as English. * * `path` is where it sits, written the way the caller wrote it and relative to the options object * itself (`colors.wood`, `text.places.font`) — so a UI can mark the offending line. `message` says what * is wrong with that one node, without the path prefixed, since a caller that wants a sentence can * build one and a caller that wants to mark a control cannot unbuild one. `suggestion` is the v6 key to * use instead, where `v5-hints.ts` knows of one. */ type OptionIssue = { path: string; message: string; suggestion?: string; }; /** * How a line is drawn, for the groups that are one: borders and paths. Takes the place of the plain * `boolean | number` those groups also accept — `state: 0.5` and `state: { opacity: 0.5 }` say the * same thing. */ type LineStyle = { /** As the plain value: `false` hides the line, a number in (0, 1] is its opacity. Default `true`. */ opacity?: boolean | number; /** * `true` draws the line in the style's own dash pattern for it, `false` draws it solid, and a list * is a dash pattern of your own — the lengths of dashes and gaps in turn, in multiples of the line * width, as MapLibre's `line-dasharray` takes them. The default depends on the line: a country * border is solid, the other five are dashed. */ dashed?: boolean | number[]; /** * Multiplies the line's width, at every zoom: `0.5` draws it half as wide, `2` twice. The dash * follows, since a pattern is in multiples of the width, and so does the casing under a border. * Default `1`. */ width?: number; }; type ResolvedLineStyle = Required; type LayerGroupOptions = { land?: boolean | number | { forest?: boolean | number; vegetation?: boolean | number; rock?: boolean | number; wetland?: boolean | number; sand?: boolean | number; glacier?: boolean | number; agriculture?: boolean | number; urban?: boolean | number; }; water?: boolean | number | { ocean?: boolean | number; rivers?: boolean | number; lakes?: boolean | number; piers?: boolean | number; }; roads?: boolean | number | { motorways?: boolean | number; highways?: boolean | number; streets?: boolean | number | { residential?: boolean | number; service?: boolean | number; pedestrian?: boolean | number; track?: boolean | number; bus?: boolean | number; }; paths?: boolean | number | LineStyle; footway?: boolean | number | LineStyle; steps?: boolean | number | LineStyle; }; transit?: boolean | number | { rail?: boolean | number; aerialways?: boolean | number; ferries?: boolean | number; stops?: boolean | number; }; buildings?: boolean | number; sites?: boolean | number; airport?: boolean | number; pois?: boolean | number; boundaries?: boolean | number | { country?: boolean | number | LineStyle; state?: boolean | number | LineStyle; disputed?: boolean | number | LineStyle; }; markings?: boolean | number; labels?: boolean | number | { boundaries?: boolean | number | { countries?: boolean | number; states?: boolean | number; }; places?: boolean | number | { capitals?: boolean | number; statecapitals?: boolean | number; cities?: boolean | number; towns?: boolean | number; villages?: boolean | number; hamlets?: boolean | number; districts?: boolean | number; }; streets?: boolean | number | { names?: boolean | number; refs?: boolean | number; exits?: boolean | number; }; water?: boolean | number | { lakes?: boolean | number; rivers?: boolean | number; }; addresses?: boolean | number; }; icons?: boolean | number; }; type ResolvedLayerGroups = { land: { forest: boolean | number; vegetation: boolean | number; rock: boolean | number; wetland: boolean | number; sand: boolean | number; glacier: boolean | number; agriculture: boolean | number; urban: boolean | number; }; water: { ocean: boolean | number; rivers: boolean | number; lakes: boolean | number; piers: boolean | number; }; roads: { motorways: boolean | number; highways: boolean | number; streets: { residential: boolean | number; service: boolean | number; pedestrian: boolean | number; track: boolean | number; bus: boolean | number; }; paths: ResolvedLineStyle; footway: ResolvedLineStyle; steps: ResolvedLineStyle; }; transit: { rail: boolean | number; aerialways: boolean | number; ferries: boolean | number; stops: boolean | number; }; buildings: boolean | number; sites: boolean | number; airport: boolean | number; pois: boolean | number; boundaries: { country: ResolvedLineStyle; state: ResolvedLineStyle; disputed: ResolvedLineStyle; }; markings: boolean | number; labels: { boundaries: { countries: boolean | number; states: boolean | number; }; places: { capitals: boolean | number; statecapitals: boolean | number; cities: boolean | number; towns: boolean | number; villages: boolean | number; hamlets: boolean | number; districts: boolean | number; }; streets: { names: boolean | number; refs: boolean | number; exits: boolean | number; }; water: { lakes: boolean | number; rivers: boolean | number; }; addresses: boolean | number; }; icons: boolean | number; }; /** * The one light of the map: it shades the terrain (`features.hillshade`), lights 3D buildings * (`features.buildings: 'extruded'`) and, on a globe, decides which side of the atmosphere glows * (`sky.atmosphereBlend`). Set once, all three are lit from the same side. `features.terrain` alone * is not shaded: MapLibre never lights the 3D terrain surface, so only the hillshade shows the light. * * On by default: unset or `true` uses the defaults below, an object overrides individual values. * `false` writes no `light`, and hillshade and buildings fall back to MapLibre's own lights, which * disagree. The style's root `light` is written only where 3D buildings or a visible atmosphere read * it; the hillshade carries the light in its own paint properties. */ type LightOptions = boolean | { /** * Where the light comes from, in degrees clockwise: 0 = north, 90 = east (with `fixedToScreen`: * 0 = top, 90 = right). Default `315`, the north-west — the top left while the map points north, * the cartographic convention, under which relief never reads inverted. Any angle is accepted and * wrapped: `-45` is `315`. */ direction?: number; /** * The light's height above the horizon, in degrees, 0–90. Default `45`. A low light deepens the * hillshade's shadows, a high one flattens the relief. With `features.hillshade`, `light` switches * the hillshade to MapLibre's `combined` method, the one that takes the altitude. */ altitude?: number; /** * Whether the light is fixed to the screen. Default `false`: the light turns with the map, so * rotating the map leaves the shading as it is. `true` keeps `direction` on screen however the map * is rotated, but MapLibre then relights the hillshade on every frame of a rotation, which is slow. * The globe's atmosphere (`sky.atmosphereBlend` > 0) is always fixed to the screen; it glows from * `direction` on screen, which matches the terrain while the map points north. */ fixedToScreen?: boolean; /** The light's colour, on 3D buildings only. Default `#ffffff`. */ color?: string; /** The light's intensity on 3D buildings, 0–1. Default `0.5`. */ intensity?: number; }; /** `false` when there is no light, otherwise every value — so it can be fed back in as options. */ type ResolvedLight = false | Required>; /** * Map projection. * * Web Mercator grossly exaggerates area away from the equator — Greenland reads as the size of * Africa — and that distortion is worst at exactly the low zooms where the whole world is visible. * `globe` is correct there, and MapLibre transitions back to Mercator as you zoom in, so the cost is * confined to the zooms where Mercator is wrong (issue #129). * * Requires MapLibre GL JS 5.0+. Renderers without projection support — MapLibre GL JS 4.x, and the * native/server renderers, which are Mercator-only — ignore the property and draw Mercator, which is * the pre-v6 behaviour. Note the style spec accepts *any* string here, so an unsupported value * validates cleanly and then silently does nothing. */ type ProjectionOptions = 'globe' | 'mercator' | 'vertical-perspective'; type ResolvedProjection = ProjectionOptions; type RecolorOptions = { invertBrightness?: boolean; rotateHue?: number; saturate?: number; tint?: { color?: string; amount?: number; }; gamma?: number; contrast?: number; brightness?: number; blend?: { color?: string; amount?: number; }; }; type ResolvedRecolor = { invertBrightness: boolean; rotateHue: number; saturate: number; tint: { color: string; amount: number; }; gamma: number; contrast: number; brightness: number; blend: { color: string; amount: number; }; }; type SatelliteRasterOptions = { opacity?: number; hueRotate?: number; brightnessMin?: number; brightnessMax?: number; saturation?: number; contrast?: number; }; type ResolvedSatelliteRaster = Required; /** * Atmosphere above the horizon. `true` (the default) uses the defaults below, `false` omits the * style's `sky` block entirely, an object overrides individual values — the same * `boolean | object` shape as `features.terrain` and `features.hillshade`. * * MapLibre only renders the sky when the horizon is in frame — in globe projection, or in Mercator * once the pitch passes ~65° (measured; nothing shows at MapLibre's default `maxPitch` of 60). A * flat 2D map therefore carries paint properties it never draws; `sky: false` drops them. */ type SkyOptions = { /** Default `#ffffff`. */ fogColor?: string; /** Default `#ffffff`. */ horizonColor?: string; /** Default: the palette's `colors.water` — see `resolveSky`. */ skyColor?: string; /** * Default `0`: no haze, so the globe's edge stays crisp. The haze is lit from `light`: on a globe it * glows on the side of the screen the light comes from — unless `light.fixedToScreen`, only while the * map points north. */ atmosphereBlend?: PropertyValueSpecification; /** Default `0.5`. */ fogGroundBlend?: PropertyValueSpecification; /** Default `0.8`. */ horizonFogBlend?: PropertyValueSpecification; /** Default `0.8`. */ skyHorizonBlend?: PropertyValueSpecification; }; /** * `false` when the sky is off. Otherwise every sky value but `skyColor`, which is set only when the * caller set it. */ type ResolvedSky = false | (Required> & Pick); type SpriteEntries = string | { id: string; url: string; }[]; /** * A glyph name, as the glyph server publishes it: `'noto_sans_regular'`, `'fira_sans_semibold_italic'`, * … — or any name a custom server uses. Not checked: `osm()` does no I/O, so it cannot know which * faces a server has. `fetchFontFaces()` asks the server. */ type FontName = string; /** Capitalization of a label's text. */ type TextTransform = 'none' | 'uppercase' | 'lowercase'; /** * How labels that follow a line — street, river and motorway names — sit in a tilted map: lying on * the ground (`map`, MapLibre's own behaviour for line labels) or standing up facing the viewer * (`viewport`). Point labels, such as places and POIs, face the viewer either way. */ type PitchAlignment = 'map' | 'viewport'; /** * How a node of the text tree sets its labels. Every property is optional: a topic takes each one from * the nearest node above it that sets it, else from the style's default for that topic. */ type LabelStyle = { /** A glyph name, e.g. `'noto_sans_regular'`. */ font?: FontName; /** Multiplies the layer's own text size. Default `1`. */ scale?: number; /** * How far apart labels keep. Along a line it multiplies the repeat distance; at a point each step above * 1 adds 14 px of collision padding. Default `1`. */ spacing?: number; /** Wrap width in ems. Default `10`. */ maxWidth?: number; /** Line height in ems. Default `1.2`. */ lineHeight?: number; /** Letter spacing in ems. Default `0`. */ letterSpacing?: number; /** Capitalization. Default `'none'`, `'uppercase'` for boundaries, hamlets and districts. */ transform?: TextTransform; /** Halo width in px. Default `2`; `1` for motorway exits, `0.1` for motorway refs, `0.5` for POIs, `0` for addresses. */ haloWidth?: number; /** Halo blur in px. Default `1`; `0.5` for POIs, `0` for addresses. */ haloBlur?: number; }; type ResolvedLabelStyle = Required; /** * `text`: the label language, and a tree of label topics whose every node — the root, a group, a topic — * takes the same `LabelStyle` properties. `language`, `languageStrict` and `pitchAlignment` apply to * every label and are set on the root only. */ type TextOptions = LabelStyle & { /** `'local'` (each feature's own name), `'user'` (the browser language), or a code such as `'de'`. Default `'local'`. */ language?: string; /** Omit labels that have no name in `language`, instead of falling back to the local name. Default `false`. */ languageStrict?: boolean; /** Line labels in a tilted map. Default `'map'`. */ pitchAlignment?: PitchAlignment; boundaries?: LabelStyle & { countries?: LabelStyle; states?: LabelStyle; }; places?: LabelStyle & { capitals?: LabelStyle; statecapitals?: LabelStyle; cities?: LabelStyle; towns?: LabelStyle; villages?: LabelStyle; hamlets?: LabelStyle; districts?: LabelStyle; }; streets?: LabelStyle & { names?: LabelStyle; refs?: LabelStyle; exits?: LabelStyle; }; water?: LabelStyle & { lakes?: LabelStyle; rivers?: LabelStyle; }; /** Names drawn with POI icons (`general`) and transit stop icons (`transit`). */ pois?: LabelStyle & { general?: LabelStyle; transit?: LabelStyle; }; addresses?: LabelStyle; }; /** Every topic with every `LabelStyle` property. Valid as input, where it builds the same style. */ type ResolvedText = { language: string; languageStrict: boolean; pitchAlignment: PitchAlignment; boundaries: { countries: ResolvedLabelStyle; states: ResolvedLabelStyle; }; places: { capitals: ResolvedLabelStyle; statecapitals: ResolvedLabelStyle; cities: ResolvedLabelStyle; towns: ResolvedLabelStyle; villages: ResolvedLabelStyle; hamlets: ResolvedLabelStyle; districts: ResolvedLabelStyle; }; streets: { names: ResolvedLabelStyle; refs: ResolvedLabelStyle; exits: ResolvedLabelStyle; }; water: { lakes: ResolvedLabelStyle; rivers: ResolvedLabelStyle; }; pois: { general: ResolvedLabelStyle; transit: ResolvedLabelStyle; }; addresses: ResolvedLabelStyle; }; /** * The text topics, by group. The groups and their children are the label groups of `layers.labels`, * plus `pois`, whose two topics are the names drawn with POI and transit-stop icons. */ declare const TEXT_GROUPS: { readonly boundaries: readonly ["countries", "states"]; readonly places: readonly ["capitals", "statecapitals", "cities", "towns", "villages", "hamlets", "districts"]; readonly streets: readonly ["names", "refs", "exits"]; readonly water: readonly ["lakes", "rivers"]; readonly pois: readonly ["general", "transit"]; }; type Groups = typeof TEXT_GROUPS; type GroupName = keyof Groups; /** A leaf of the text tree: `'water.rivers'`, `'pois.transit'`, …, or `'addresses'`. */ type TextTopic = { [G in GroupName]: `${G}.${Groups[G][number]}`; }[GroupName] | 'addresses'; /** * The language labels are drawn in: `'user'` becomes the browser's language (`'de'` for `de-AT`), or * `'local'` where there is no browser. Every other value is returned as it is. */ declare function labelLanguage(language: string): string; /** * A tile source: a URL string, or a TileJSON document the caller already holds. * * A string is a TileJSON URL unless it contains a `{z}` placeholder, in which case it * is a raw tile template. Pass an object (see `fetchTileJSON`) when the style must be * self-contained at build time, or when the source needs fields MapLibre cannot infer. */ type TileSource = string | TileJSONSpecification; /** A `fetch`-compatible function, used to download TileJSON documents. */ type FetchLike = typeof fetch; type OsmUrlsOptions = { base?: string; osm?: TileSource; elevation?: TileSource; glyphsPattern?: string; sprite?: SpriteEntries; }; type SatelliteUrlsOptions = OsmUrlsOptions & { satellite?: TileSource; }; type ResolvedOsmUrls = { osm: TileSource; elevation: TileSource; glyphsPattern: string; sprite: SpriteEntries; }; type ResolvedSatelliteUrls = ResolvedOsmUrls & { satellite: TileSource; }; /** * What `validateOptions` gives back: the resolved options, or everything wrong with them. * * A discriminated union rather than `{ issues, options? }`, so a caller that checks `ok` gets the * resolved options without a second narrowing step, and one that forgets cannot read an `options` that * was never produced. */ type ValidationResult = { ok: true; issues: readonly []; options: T; } | { ok: false; issues: readonly OptionIssue[]; }; type OsmOverlayOptions = { theme?: ThemeOptions; layers?: boolean | number | LayerGroupOptions; text?: TextOptions; icon?: IconOptions; colors?: ColorsOptions; recolor?: RecolorOptions; }; type ResolvedOsmOverlay = { theme: ResolvedTheme; layers: ResolvedLayerGroups; text: ResolvedText; icon: ResolvedIcon; colors: ResolvedColors; recolor: ResolvedRecolor; }; type OsmOptions = OsmOverlayOptions & { urls?: OsmUrlsOptions; features?: OsmFeaturesOptions; light?: LightOptions; sky?: boolean | SkyOptions; projection?: ProjectionOptions; }; type ResolvedOsm = ResolvedOsmOverlay & { urls: ResolvedOsmUrls; features: ResolvedOsmFeatures; light: ResolvedLight; sky: ResolvedSky; projection: ResolvedProjection; }; declare function resolveOsm(options?: OsmOptions): ResolvedOsm; /** * The overlay's options: everything `osm()` takes for its cartography, plus what only makes sense over * imagery. */ type SatelliteOverlayOptions = OsmOverlayOptions & { /** * Dim borders and motorways with `line-layer-opacity` instead of `line-opacity`. Default `false`. * * `line-opacity` applies to every feature on its own, so where a line crosses itself — a border * winding along a river at low zoom, a motorway's two carriageways — the overlap is blended twice * and shows as a brighter spot. `line-layer-opacity` composites the finished layer once, so the * line reads evenly. It costs an extra offscreen pass per layer, so it is applied to the three * layers that overlap themselves most: `boundary-country`, `boundary-state` and `street-motorway`. * * **Only for MapLibre GL JS 6.0 or newer.** MapLibre Native (Android, iOS) does not implement the * property and drops every layer that carries it — the map then has no borders or motorways * (maplibre-native#4298). Leave it off for any style a native app may load. */ layerOpacity?: boolean; }; type ResolvedSatelliteOverlay = ResolvedOsmOverlay & { layerOpacity: boolean; }; type SatelliteOptions = { urls?: SatelliteUrlsOptions; /** * The OSM vector overlay over the imagery. `true` (the default) uses the overlay's own * defaults, `false` disables it, an object configures it — the same shape as `features.terrain` * and `features.hillshade`. */ osmOverlay?: boolean | SatelliteOverlayOptions; raster?: SatelliteRasterOptions; features?: SatelliteFeaturesOptions; light?: LightOptions; sky?: boolean | SkyOptions; projection?: ProjectionOptions; }; type ResolvedSatellite = { urls: ResolvedSatelliteUrls; features: ResolvedSatelliteFeatures; light: ResolvedLight; sky: ResolvedSky; projection: ResolvedProjection; osmOverlay: false | ResolvedSatelliteOverlay; raster: ResolvedSatelliteRaster; }; declare function resolveSatellite(options?: SatelliteOptions): ResolvedSatellite; /** * The six colour spaces the library understands, and the channel metadata every other colour module * reads off them. * * One table, six spaces, three channels each. Parsing (what `100%` means in this channel), serialising * (which channels are angles) and output (what to clamp) all consult this table instead of repeating the * rules per space — which is what makes a sixth space cost a table row rather than a class. * * Ranges follow CSS Color 4 where it has an opinion (§4.2 alpha, §7 HSL, §9.4 OKLab/OKLCh): the sRGB and * percentage channels are bounded, hues wrap, and OKLab's `a`/`b` stay open at both ends while OKLCh's * chroma is only closed at the bottom. * * The ranges describe what can be shown, not what a `Color` may hold. Construction only wraps hues and * removes `NaN` (`normalize`); the ranges are applied where a colour enters or leaves — parsing and output * (`clampToRange`). In between, an out-of-range value is a legitimate intermediate result: a contrast * that pushes a light grey past white and a brightness that brings it back must end at a light grey, not * at a grey that was clipped on the way. MapLibre's raster shader works the same way. * * `hsv` is not a CSS space. It stays because `randomColor` is built on it and it was public API in v6; * it parses and serialises as `hsv()`, which is ours, not CSS. */ /** A colour space this library can hold a colour in. */ type Space = 'srgb' | 'hsl' | 'hwb' | 'hsv' | 'oklab' | 'oklch'; /** The three numbers of a colour, in the order its space names them. Alpha is carried separately. */ type Coords = readonly [number, number, number]; /** * Colour operations that are about more than one colour, or about the edge of what a screen can show: * perceptual distance, mixing, WCAG contrast, and gamut mapping. */ /** * How to get from one hue to another (CSS Color 4 §13.5). * * Two hues sit on a circle, so there are always two ways round; which one is wanted is a design * decision, not a mathematical one. `shorter` takes the short arc — red to blue through magenta — * while `longer` takes the other, through yellow and green. */ type HueMethod = 'shorter' | 'longer' | 'increasing' | 'decreasing'; interface MixOptions { /** Where to interpolate. Default `oklab`, as CSS uses — it is the space that mixes without grey middles. */ space?: Space; /** How to travel between the two hues, in a polar space. Default `shorter`. */ hue?: HueMethod; } /** A colour as the operations here pass it around. */ interface ColorValue { readonly space: Space; readonly coords: Coords; readonly alpha: number; } /** * A colour: a space, three coordinates in it, and an alpha. * * Immutable — every method returns a new instance, and every field is `readonly`. * * Values are held as the maths gives them, including sRGB channels outside 0–255 or an HSL saturation * above 100%, so a chain of transforms loses nothing in between. They are clamped where a colour enters * (parsing) and where it leaves (`asString`, `asHex`, `toCSS`, `luminance`); see `space.ts`. * * One class rather than v5's three. There, `RGB`, `HSL` and `HSV` each carried their own storage, and * the thirteen transforms lived on `RGB` with the other two inheriting and converting, which had two * consequences worth naming because they are the reason this exists: * * - **A fourth space was unaffordable.** Every space meant another class and another set of * conversions. Here a space is a row in `SPACES` and two functions in `convert.ts`. * - **The class a transform returned decided the output format.** `invertLuminosity()` produced an * `HSL`, whose `asString()` wrote `hsl(…)`, so four `marking-*` layers in every generated style were * `hsl()` while the other 370-odd colours were `rgb()` — not a decision anyone made. Here every * transform returns a `Color`, and how a colour is written is decided only when it is written. * * The transforms keep v5's maths exactly, so adopting this moves no generated colour. Perceptual * behaviour is opt-in, per call, through `mix(other, t, { space: 'oklab' })`. */ /** Channel values of a colour in one space, named as that space names them, plus its alpha. */ type Channels = Record; declare class Color implements ColorValue { readonly space: Space; readonly coords: Coords; readonly alpha: number; private constructor(); /** A colour from coordinates already in `space`. */ static from(space: Space, coords: Coords, alpha?: number): Color; /** * Reads a colour string — hex, `rgb()`, `hsl()`, `hwb()`, `hsv()`, `oklab()`, `oklch()` or * `transparent`. A `Color` is returned unchanged, so this is safe to call on an unknown value. * * @throws {ColorParseError} on anything it cannot read, naming what was wrong. */ static parse(input: string | Color): Color; /** sRGB, channels 0–255. */ static srgb(r: number, g: number, b: number, alpha?: number): Color; /** Hue 0–360, saturation and lightness 0–100. */ static hsl(h: number, s: number, l: number, alpha?: number): Color; /** Hue 0–360, whiteness and blackness 0–100. */ static hwb(h: number, w: number, b: number, alpha?: number): Color; /** Hue 0–360, saturation and value 0–100. Not a CSS space; kept for `randomColor`. */ static hsv(h: number, s: number, v: number, alpha?: number): Color; /** Lightness 0–1, opponent axes roughly ±0.4. */ static oklab(l: number, a: number, b: number, alpha?: number): Color; /** Lightness 0–1, chroma from 0, hue 0–360. */ static oklch(l: number, c: number, h: number, alpha?: number): Color; /** Two colours mixed, `t` of the way across. Interpolates in OKLab unless told otherwise. */ static mix(from: Color, to: Color, t?: number, options?: MixOptions): Color; /** The same colour, held in `space`. Nothing is clamped on the way, so wide colours survive. */ to(space: Space): Color; private channels; /** `{ r, g, b, alpha }`, channels 0–255. */ get srgb(): Channels; /** `{ h, s, l, alpha }`. */ get hsl(): Channels; /** `{ h, w, b, alpha }`. */ get hwb(): Channels; /** `{ h, s, v, alpha }`. */ get hsv(): Channels; /** `{ l, a, b, alpha }`. */ get oklab(): Channels; /** `{ l, c, h, alpha }`. */ get oklch(): Channels; /** This colour's coordinates and alpha, in its own space. */ asArray(): [number, number, number, number]; /** * A copy with some channels replaced, named as this colour's space names them, plus `alpha`. * * `color.to('oklch').with({ l: 0.8 })` — lighten without touching hue or chroma. * * @throws {Error} naming the channels this space does have, if given one it does not. */ with(changes: Partial): Color; /** A copy with its coordinates rounded — integers for sRGB, `digits` decimals elsewhere. */ round(digits?: number): Color; /** * Returns this colour. * * A `Color` is frozen and every method returns a new instance, so there is nothing a copy could * protect against. It stays because v5 had it, and because the duck-typed clone convention is * what a generic deep-copy would look for. */ clone(): Color; /** Whether a screen can show this colour without help. */ inGamut(): boolean; /** The nearest colour a screen can show, holding lightness and hue (CSS Color 4 §14.2.1). */ toGamut(): Color; /** `#RRGGBB`, or `#RRGGBBAA` when not opaque. Uppercase. */ asHex(): string; /** * `rgb(r,g,b)` or `rgba(r,g,b,a)` — the only syntax a MapLibre style may contain. * * Whatever space this colour is held in, it is converted and, if need be, gamut-mapped on the way * out, because MapLibre's native parser reads neither `oklch()` nor CSS's space-separated `rgb()` * and renders what it cannot read as nothing at all. */ asString(): string; /** CSS syntax, in this colour's own space or another: `oklch(0.7 0.15 45)`. Not for style output. */ toCSS(space?: Space, precision?: number): string; toString(): string; toJSON(): string; /** WCAG 2.1 relative luminance, 0 for black and 1 for white. */ luminance(): number; /** WCAG 2.1 contrast ratio against another colour, 1 to 21. Alpha is ignored. */ contrastRatio(other: Color): number; /** Perceptual distance in OKLab. Below 0.02 — one JND — is a difference nobody can see. */ deltaEOK(other: Color): number; /** Whether this colour's hue channel carries information, or is grey reading rounding noise. */ hasPowerlessHue(): boolean; /** Mixes toward `other`, `t` of the way, in OKLab unless told otherwise. */ mix(other: Color, t?: number, options?: MixOptions): Color; /** Reduces alpha proportionally: `a → a · (1 − value)`. */ fade(value: number): Color; /** The same colour, fully opaque. */ opaque(): Color; /** `top` composited over this colour, source-over. */ over(top: Color): Color; /** * Recoloured with `top`'s hue and saturation, keeping this colour's lightness — the HSL "Color" * blend mode — then blended back by `top`'s alpha, which reads as the strength. */ colorize(top: Color): Color; /** * Linear interpolation toward `other` in sRGB. * * Unlike v5, alpha is interpolated too: `blend(1, x)` now returns `x`, alpha included, rather than * `x`'s channels wearing this colour's alpha. */ blend(value: number, other: Color): Color; /** * Shifts this colour's hue toward `other`'s, keeping its own lightness and saturation. * * A colour with no hue to give — white, black or any grey — leaves this one alone. v5 read such a * colour's hue as a real 0°, so tinting toward white turned everything red. */ tint(value: number, other: Color): Color; /** * Per-channel gamma: `c → 255 · (c/255)^value`. Below 1 brightens midtones, above 1 darkens them. * * Mirrored for a channel below 0, which a colour held out of range can have, and which would * otherwise become `NaN`. */ gamma(value: number): Color; /** Flips every channel: `c → 255 − c`. Flips the hue too — for dark mode use `invertLuminosity`. */ invert(): Color; /** Scales each channel around mid-grey. 0 collapses to `#808080`; above 1 increases contrast. */ contrast(value: number): Color; /** Adds `value · 255` to every channel: negative darkens, positive brightens, ±1 reaches black or white. */ brightness(value: number): Color; /** Toward white. Theme-absolute: use `blend(ratio, bg)` for something that follows the palette. */ lighten(ratio: number): Color; /** Toward black. Theme-absolute: use `blend(ratio, fg)` for something that follows the palette. */ darken(ratio: number): Color; /** Flips lightness while keeping hue and saturation — the dark-mode inversion. */ invertLuminosity(): Color; /** Rotates the hue by `offset` degrees. */ rotateHue(offset: number): Color; /** Scales HSL saturation by `1 + ratio`. */ saturate(ratio: number): Color; /** Replaces the hue, keeping HSV saturation and value. */ setHue(value: number): Color; } /** * Reading a colour written by hand. * * One parser for all six spaces, replacing v5's three (`Color.parse`, `RGB.parse`, `HSL.parse`), which * disagreed with each other: `hsl(-120,50%,50%)` was a colour to one and an error to another, and * `rgb(100%,0%,0%)` was silently read as `rgb(100,0,0)` because the input was stripped of every * character outside `[0-9a-z.#,()]` before matching — taking `%` and `-` with it. * * The grammar is CSS Color 4's, minus the parts that only make sense inside a stylesheet engine and with * two deliberate relaxations, because these inputs are written by hand rather than generated: * * - **Commas or spaces, anywhere.** CSS gives `rgb()` and `hsl()` a legacy comma form and gives * `hwb()`, `oklab()` and `oklch()` no comma form at all. Here every function takes either. * - **Bare numbers where CSS's legacy form demands a percentage.** `hsl(120, 50, 50)` is an error in a * browser and a colour here, reading 50 as 50%, exactly as the modern space-separated form does. * * Both relaxations accept more than CSS, never less, and never change what a valid CSS colour means. * * Not supported, each with an error that says so by name rather than a generic parse failure: named * colours (a 148-entry table costs more than it gives when hex is right there), `calc()`, `color-mix()`, * relative colour syntax, `color()`, `light-dark()`, `currentColor`, system colours, and `none` * components. `transparent` is supported — it is one keyword, not a table. */ /** Thrown for every input this parser rejects. Carries the caller's original string, never a sanitised one. */ declare class ColorParseError extends Error { readonly input: string; constructor(input: string, reason: string); } interface RandomColorOptions { /** Same seed, same colour. Omit for a colour that differs on every call. */ seed?: number | string; /** A hue in degrees, or a colour name from the dictionary (`'red'`, `'blue'`, `'monochrome'`, …). */ hue?: number | string; opacity?: number; luminosity?: number | string; /** `'strong'`, `'weak'`, or a saturation in 0–100. */ saturation?: number | string; } declare function randomColor(options?: RandomColorOptions): Color; /** * A tree mirroring `LayerGroupOptions`, with the layer IDs each group controls at the leaves. * Branch nodes (e.g. `roads.streets`) hold their sub-groups rather than IDs. */ type LayerGroupMap = { [key: string]: string[] | LayerGroupMap; }; /** The text tree — `boundaries.countries`, `pois.transit`, … — with the text layer IDs each topic sets. */ type TextGroupMap = LayerGroupMap; /** The smallest `OsmOptions` that builds the same style as `options`. */ declare function minimizeOsmOptions(options?: OsmOptions): OsmOptions; /** * The colour slots a palette fills: their names, their order, and the option type that mirrors them. * * A leaf on purpose — it imports nothing — so anything may read the key list without dragging the * resolver, and its `checkKeys` dependency, along with it. `options/parts/v5-hints.ts` is why: it needs * the list to map v5 colour names onto v6 ones, and while the list lived beside `resolveColors` that * single import closed a cycle (`keys → v5-hints → colors → keys`), survived only because `v5-hints` * built its tables lazily. * * It sits in `themes/` rather than `options/` because the order is load-bearing here: `tables.ts` stores * each derived palette as bare values in exactly this sequence, and `getPaletteColors` zips them back * together against this array. `options` re-exports both names from `parts/colors.ts`, so callers still * import them from the options surface; the dependency runs options → themes, never back. */ type ColorsOptions = { background?: string; land?: string; water?: string; glacier?: string; natureWood?: string; natureGrass?: string; naturePark?: string; natureAgriculture?: string; natureSand?: string; natureRock?: string; natureWetland?: string; natureLeisure?: string; areaResidential?: string; areaCommercial?: string; areaIndustrial?: string; areaWaste?: string; areaBurial?: string; siteConstruction?: string; siteEducation?: string; siteHospital?: string; siteDanger?: string; sitePrison?: string; siteParking?: string; siteSports?: string; building?: string; buildingBg?: string; roadStreet?: string; roadStreetBg?: string; roadMotorway?: string; roadMotorwayBg?: string; roadTrunk?: string; roadTrunkBg?: string; roadSecondary?: string; roadSecondaryBg?: string; transitRail?: string; transitSubway?: string; transitCycle?: string; transitFoot?: string; boundary?: string; boundaryDisputed?: string; label?: string; labelHalo?: string; labelShield?: string; labelSymbol?: string; labelPoi?: string; labelHousenumber?: string; /** Lake, sea and river names — a darkened water tone, so they read as water, not as places. */ labelWater?: string; }; /** * The colour table of a palette. * * `colorful` is the hand-written reference (`colorful.ts`); the other nine are derived from it and * stored in `tables.ts` as one string each — the 47 colours of `colorOptionsKeys`, in that order, * comma-separated and without their `#`. Writing them as objects cost 5.4 KB in the browser bundle for * nothing but the same 47 identifiers repeated nine times; this form is a tenth of that. * * Decoding is cheap and the result is not cached: every caller here — `resolveColors`, the palette * search in `deriveOptions` — copies or reshapes the table anyway, so a shared frozen object would buy * nothing and risk being mutated by one of them. * * Every palette therefore returns a **fresh** object, `colorful` included. It used to hand back the * `COLORFUL` constant itself, which is public API through `osm.colors` — so a caller editing the * returned palette (exactly what a style editor does) silently repainted every later `osm()` in the * process, and every derived table with it, since all nine are generated from this one. */ declare function getPaletteColors(palette: Palette): ResolvedColors; /** * Whether a palette is a dark theme, whose derived colours blend toward black instead of white. * * Not the same as a `-dark` suffix: `'fnord'` is dark and has no light partner. * * **npm only** — not in the browser bundle. */ declare function isDarkPalette(palette: Palette): boolean; /** * What `guessStyle` needs from a schema in order to recognise its tileset and build a style for it. * * Each schema function carries one as a static, so the schema owns both halves: which source-layers * identify its tiles, and how its own `urls` option names the vector source. `guessStyle` never learns * either — it just asks. * * Deliberately its own module rather than living in `guess-style.ts`: `osm` must carry a descriptor and * `guessStyle` must read `osm`'s, so declaring the type in either file makes the two mutually * dependent. TypeScript resolves such a cycle by widening the inferred statics to `any`, silently — it * cost a whole file's type-checking before this module existed. */ type SchemaDescriptor = { /** * Which schema this builds. For the built-in schemas it is the name `guessSchema` reports * (`'shortbread'`, `'openmaptiles'`, `'protomaps'`), which is how `guessStyle` finds the builder for a * detected tileset. Any other id marks a caller's own schema, recognised by `sourceLayers` alone. */ readonly id: string; /** Source-layer ids this schema's tileset carries, for detection. */ readonly sourceLayers: readonly string[]; /** Build a style for a tileset already detected as this schema. */ build: (source: string | TileJSONSpecification, urls: SchemaUrls) => StyleSpecification; }; /** The subset of the URL options a guessed style can carry over: everything but the tile source. */ type SchemaUrls = Pick | undefined; /** A schema function as `guessStyle` sees it — `osm`, `omt`, or another schema's builder. */ type SchemaBuilder = { readonly tileset: SchemaDescriptor; }; /** Which builder a recorded set of options belongs to. */ type StyleBuilder = 'osm' | 'satellite' | 'omt' | 'protomaps'; /** * The metadata keys that record what a style was built from. * * Namespaced, because `metadata` is a free-for-all in the style spec and other tooling writes there too. */ declare const METADATA_BUILDER_KEY = "versatiles:builder"; declare const METADATA_OPTIONS_KEY = "versatiles:options"; declare const METADATA_VERSION_KEY = "versatiles:metadataVersion"; /** * The shape of the recorded options, not the package version. A reader branches on this; bump it when * the meaning of what `styleMetadata` writes changes. */ declare const METADATA_VERSION = 1; /** What `readStyleOptions` recovered from a style. */ interface StyleOptionsRecord { builder: StyleBuilder; /** The options as they were recorded — feed them back into the matching builder. */ options: Record; version: number; } /** * Metadata recording the options a style was built from, so a tool can later read a finished style.json * back into the options that made it. * * **Opt-in, and deliberately not written by the builders themselves.** `osm()` and friends guarantee that * `osm(minimizeOptions(x))` builds the *identical* style to `osm(x)`; minimising drops representations * that resolve differently but render the same (a tint at amount 0, the `icons` alias), so baking the * options into the style would make that guarantee false and `src/api/minimize.test.ts` fail. It would * also add a copy of every option to every style built by every consumer, most of whom never read it back. * * So the caller that wants a round-trip asks for one — pass the *minimised* options, which is what such a * caller already holds: * * ```ts * const options = osm.minimizeOptions(myOptions); * const style = { ...osm(myOptions), metadata: styleMetadata('osm', options) }; * ``` * * Pass options only — never a resolved tree containing `urls`, whose `osm` field may be a whole * pre-fetched TileJSON, and which pins the style to the host that built it. URLs are environment, not * style: a reader supplies its own. */ declare function styleMetadata(builder: StyleBuilder, options: object, base?: StyleSpecification['metadata']): StyleSpecification['metadata']; /** * The options a style records in its metadata, or `undefined` if it carries none — which is the case for * any style not written with {@link styleMetadata}, including every foreign style. Reconstructing options * from one of those is what `@versatiles/style/migrate` is for. * * Never throws: anything malformed reads as `undefined`. */ declare function readStyleOptions(style: StyleSpecification): StyleOptionsRecord | undefined; /** * Download a TileJSON document and make its relative `tiles[]` entries absolute. * * `osm()` and `satellite()` do no I/O, so this is how you obtain a TileJSON when the * style needs one at build time — to inline it via `urls`, or to inspect it with * `osm.languages()`. To resolve an already-built style instead, use `inlineSources()`. */ declare function fetchTileJSON(url: string, options?: { fetch?: FetchLike; }): Promise; /** * Resolve every `url`-referencing source in a style into an inlined, self-contained one. * * This is the asynchronous half of the API: `osm()` and `satellite()` build a style with * no I/O, and this fetches whatever those styles left as a reference. It is orthogonal to * style building — it works on any style, including `guessStyle()` output and hand-written * documents. * * Use it when the style must stand on its own: published `style.json` artifacts, offline * or air-gapped deployments, and anywhere the first tile request should not wait for a * TileJSON round-trip. It is also the only place that can set the two properties MapLibre * cannot infer from a TileJSON — raster `tileSize` and raster-dem `encoding`. * * Every fetched document is checked before it is inlined: it must be a valid TileJSON, and it must * describe the kind of tiles the source claims — a `type: "vector"` source needs a TileJSON with * `vector_layers`, a raster or raster-dem source one without. A `url` pointing at the wrong tileset * fails here, naming the offending source, rather than turning into an empty map later. * * Returns a new style; the input is not mutated. Sources without a `url` are left alone, and so is a * `url` with a scheme other than `http(s)`: `pmtiles://…` is not a TileJSON reference but an archive * MapLibre reads through a registered protocol, so there is nothing for `fetch` to download. */ declare function inlineSources(style: StyleSpecification, options?: { fetch?: FetchLike; }): Promise; /** * Options for {@link guessStyle}. * * - `urls` — the same shape as for `osm()` and `satellite()`: `base` resolves relative URLs (`tiles`, * glyphs, sprites) and defaults to the page origin, or tiles.versatiles.org outside a browser; * `glyphsPattern` and `sprite` set where the guessed style loads fonts and icons from. * - `fetch` — used when `source` is a URL, as for `inlineSources()` and `fetchTileJSON()`. * - `schemas` — additional schemas to recognise, each the builder from its own subpath. * * ── Why `schemas` is an option and a registry was not ───────────────────────── * * Auto-dispatch is the one thing one-function-per-subpath does not * get for free. `guessStyle` lives in the root entry, so importing every schema here would reintroduce * exactly the bundle cost that design avoids — a CDN user who never touches OpenMapTiles would still * download it. Injection keeps the cost with the caller who asked for it: * * ```ts * import { guessStyle } from '@versatiles/style'; * import { omt } from '@versatiles/style/omt'; * await guessStyle(tileJSON, { schemas: [omt] }); * ``` * * This is acceptable here precisely where a global registry was not: `guessStyle` is already * async, already does runtime detection, already takes a function-valued option (`fetch`), and its * options never pass through `minimizeOptions`/`toCode`, so nothing here has to round-trip. * * The list is **additive**: which built-in schema a tileset is comes from `guessSchema`, which does not * depend on `schemas` at all — injecting `omt` only decides whether a tileset already detected as * OpenMapTiles gets its style or the inspector style. A schema of the caller's own (any other * `tileset.id`) is tried after that, by its `sourceLayers`, in the order given. */ type GuessStyleOptions = { urls?: Pick; fetch?: FetchLike; schemas?: readonly SchemaBuilder[]; }; /** * Inspect a tileset and return the most appropriate MapLibre style. * * `source` is either the URL of a TileJSON document, which is downloaded (relative `tiles` are * resolved against the document), or a TileJSON object the caller already holds — a tile server has * one in memory from its container's metadata — which is used without any network access (relative * `tiles` are resolved against `options.urls.base`). The caller's object is not modified. * * Styles by tileset: * - Shortbread vector tiles → full `osm()` style * - OpenMapTiles or Protomaps vector tiles, with that schema injected (`options.schemas`) → its style * - Vector tiles matching a caller's own injected schema → that schema's style * - Unknown vector tiles → inspector style (one color-coded fill+line+label per source-layer) * - Raster tiles with satellite name hint → `satellite()` style * - Other raster tiles → minimal single-layer raster style * * Never throws — an invalid argument, a failed download or a malformed document all yield a * blank (but valid) StyleSpecification. */ declare function guessStyle(source: string | TileJSONSpecification, options?: GuessStyleOptions): Promise; /** * Just enough of each schema to recognise its tiles: the source-layer ids, and, for the ids two schemas * share, a few fields only one of them carries. * * ── Why a copy, and not the vendored records ────────────────────────────────── * * `guessSchema` lives in the root entry and has to know all three schemas. Importing `OMT_SCHEMA` or * `PROTOMAPS_SCHEMA` would put `src/omt/` and `src/protomaps/` into the root bundle, which the bundle * isolation test forbids, and the records are mostly field lists detection has no use for. This table is * a few hundred bytes. `schema-signatures.test.ts` checks it against the three records, so it cannot * drift from them unnoticed. * * ── Why fields, for shared ids ──────────────────────────────────────────────── * * Several ids are used by more than one schema with different data behind them: `boundaries`, * `buildings` and `pois` (Shortbread and Protomaps), `landcover`, `landuse` and `water` (OpenMapTiles, * Protomaps and Mapbox), and `aeroway`, `building` and `waterway` (OpenMapTiles and Mapbox). By name * alone they are evidence for all of them. The fields listed here are structural ones — `class` against * `kind`, `admin_level` against `kind_detail` — rather than language fields, which vary with how a * tileset was built. A field need not rule out every other schema: OpenMapTiles and Mapbox both put a * `class` on `landuse`, which tells either from Protomaps and neither from the other. * * ── Mapbox ──────────────────────────────────────────────────────────────────── * * Mapbox Streets v8, with the two tilesets its styles load beside it (Terrain v2: `landcover`, * `hillshade`, `contour`; Bathymetry v2: `depth`) — a Mapbox style reads all three through one * composite source. There is no vendored record behind this one: Mapbox publishes its schema as a * documentation page, not as data, so the list is kept by hand. It is here to *recognise* such tiles, * which is all reading a Mapbox style needs; there is no builder for them (issue #137). */ /** The schemas `guessSchema` recognises. */ type SchemaName = 'shortbread' | 'openmaptiles' | 'protomaps' | 'mapbox'; /** How well a tileset's source-layers fit one schema. */ type SchemaScore = { schema: SchemaName; /** Tileset source-layers counted as this schema's. */ matched: string[]; /** This schema's source-layers the tileset does not carry — an extract, or a different schema. */ missing: string[]; /** Tileset source-layers not counted as this schema's. */ extra: string[]; /** `matched` as a share of all the tileset's source-layers, 0–1. */ score: number; }; /** * The result of {@link guessSchema}. * * - `vector` — `schema` is the recognised schema, or `undefined` when none qualifies or two tie. * `candidates` scores every schema, best first, so a caller can show why. * - `raster` — a TileJSON without `vector_layers`. Raster tiles have no schema. * - `unknown` — not a TileJSON at all. */ type SchemaGuess = { type: 'vector'; schema: SchemaName | undefined; candidates: SchemaScore[]; } | { type: 'raster'; } | { type: 'unknown'; }; /** * Recognise the vector tile schema of a tileset from its TileJSON. * * Reads only `vector_layers`: the source-layer ids, and the `fields` of the few ids two schemas share. * `name`, `attribution` and the like are not used — they describe who built a tileset, not what is in * it. Synchronous and free of I/O; for a URL, download the document with `fetchTileJSON()` first. * * Knows Shortbread, OpenMapTiles, Protomaps and Mapbox Streets without importing any of their styles, * so it costs the root bundle a small table rather than two schemas. Mapbox tiles are recognised only: * the package has no builder for them. For the others, build the style from the matching subpath: * * ```ts * import { guessSchema, osm } from '@versatiles/style'; * import { omt } from '@versatiles/style/omt'; * const guess = guessSchema(tileJSON); * if (guess.type === 'vector' && guess.schema === 'openmaptiles') style = omt({ urls: { omt: tileJSON } }); * ``` * * A schema is recognised when it qualifies (at least half the tileset's source-layers are its, or at * least eight are) and scores strictly higher than every other schema. Never throws. */ declare function guessSchema(tileJSON: TileJSONSpecification): SchemaGuess; /** Options for {@link inspectorStyle}. The same `urls` shape as `osm()`, minus what this never reads. */ type InspectorStyleOptions = { urls?: Pick; }; /** * A colour-coded style that draws every source-layer of a vector tileset, whatever it contains. * * Each source-layer gets three layers — a translucent fill, a line, and a `name` label — in one hue * derived from its name, over a neutral background. Nothing is filtered and nothing is hidden, so * geometry shows up whichever type it turns out to be. This is what you want when the tiles are * unfamiliar, or when you are checking what a tileset actually carries rather than how it should look. * * It is the style {@link guessStyle} falls back to for vector tiles whose schema it cannot build, and * exporting it means you can ask for it deliberately — including for a tileset that *is* recognised, * where `guessStyle` would give you real cartography instead: * * ```ts * import { inspectorStyle } from '@versatiles/style'; * const style = inspectorStyle(tileJSON); // raw source-layers, even for Shortbread tiles * ``` * * Synchronous and performs no I/O, like `osm()` and `guessSchema()`: it takes a TileJSON **object**. * Pass a URL through {@link fetchTileJSON} first. * * Unlike `guessStyle`, this throws rather than returning a blank style — it is a builder, and you * asked for this style specifically, so a malformed tileset is an error rather than something to * paper over. * * @param tileJSON a vector TileJSON, i.e. one carrying `vector_layers` * @param options `urls.base` resolves relative tile URLs; `urls.glyphsPattern` says where the labels * load fonts from. No `sprite`: nothing here places an icon. * @throws if `tileJSON` is not a valid TileJSON, carries no `vector_layers`, or `options` has an * unknown key. */ declare function inspectorStyle(tileJSON: TileJSONSpecification, options?: InspectorStyleOptions): StyleSpecification; /** * Where a snippet is meant to run. * * `'npm'` is an ES module in a project with a bundler. `'browser'` is a plain HTML page that loads the * CDN bundle from a `