import { MapLibreMap, type FitBoundsOptions, type LngLatBoundsLike, type MapOptions } from 'maplibre-gl'; import type { MapProjection } from './previewers/map'; import type Theme from './themes/theme'; import type MapLibreTheme from './themes/maplibre'; export type MapExtras = Omit, 'container' | 'style' | 'trackResize'>; /** * A map with our basemap on it, pointed at the whole world until something says otherwise. */ export declare const createMap: (container: HTMLElement, theme: MapLibreTheme, extras?: MapExtras) => MapLibreMap; /** * Keep the map at the size of its container, leaving alone a container that has no size at all. * * This is MapLibre's own trackResize minus the one case that goes wrong for us, which is why * createMap turns theirs off in favour of it. Their _containerDimensions() reads `clientWidth || 400`, * so a container with no box - a map inside a `display: none` subtree, an inactive , a * pane an embedding page has hidden - is read as a 400x300 one, and the canvas is resized to match. * * MapLibre puts that right the moment the container is back, but by then something else may have * copied it. deck.gl's overlay draws into this same canvas and syncs its own drawing buffer from * whatever size it finds there: hidden, it finds the 400x300 one, and its first frame after the map * is shown again stamps that back over the size MapLibre has just corrected. The canvas then holds a * buffer of one size stretched across a box of another, which is what drew every COG preview skewed * after a trip through the overview. Nothing resizes the container a second time, so nothing puts it * right either. */ export declare const trackContainerSize: (map: MapLibreMap, container: HTMLElement) => void; /** * Resolve once the element has a box to be measured - which, for one that already has, is now. * * The other half of trackContainerSize. MapLibre reads a container with no box as a 400x300 one when * it is built as well, and that happens in the constructor, before there is any observer of ours to * skip it. Resizing afterwards isn't enough to undo: deck.gl's overlay copies its drawing buffer from * the canvas as it attaches, so a map built into a hidden container hands it a size that was never * real, and it stamps that back the first time it draws. Building nothing until there is something to * build into is what closes that, and a hidden map has nothing to show in the meantime anyway. */ export declare const whenSized: (element: HTMLElement) => Promise; /** * Swap the basemap for the one the theme now asks for; resolve once the new style document is up. * * Whatever was drawn on the old one has to be drawn again by the caller: setStyle empties the style * document and takes every source and layer on it away. The listener goes on before the swap rather * than after, so a style that loads from cache can't be up before anyone is listening for it. */ /** * Which projection a map is in, or nothing at all before it has a style document to carry one. * * MapLibre has two names for a sphere: 'globe' draws as one until it is zoomed in far enough that a * sphere and a flat map are the same picture, and 'vertical-perspective' stays one throughout. Either * is a globe as far as a camera is concerned - see frameLocation - so either comes back as one. * * Asked of the map rather than remembered, because the map is the one that knows: a projection can * change without anything here having asked for it. A style document names its own, and applying one * announces the change as the same event a reader pressing the globe button does. */ export declare const readProjection: (map: MapLibreMap) => MapProjection | undefined; export declare const setBasemap: (map: MapLibreMap, theme: MapLibreTheme) => Promise; /** * Where to open a map, for a caller that already knows where its map is going. * * MapLibre reads `bounds` in place of the center and zoom createMap opens on, and points the camera * there inside the constructor, before there is a frame for anyone to see. Nothing else here can be * that early: a preview is fitted once it has drawn and a location once its style document is up, and * until whichever it waits on lands, a map that could have opened on its record is showing the whole * world instead. The camera that follows still runs - what a record declares isn't always the whole * of what a resource turns out to cover - but it moves from one view of the record to another rather * than in from the world. * * The gap is measured against the container, because there is no canvas to measure yet: it is the same * box either way, and MapLibre resizes to it before it fits anything. Nothing of ours holds the camera * still, either - MapLibre hands this one its own `duration: 0`. * * Nothing at all for a caller with nowhere to point, which leaves createMap's own view where it is. */ export declare const openingCamera: (container: HTMLElement, theme: Theme, target: LngLatBoundsLike | undefined, extras?: FitBoundsOptions) => MapExtras; /** * Point the map at the given bounds; resolve once it has finished moving. * * The theme's gap, on all four edges, so what's drawn reads as having edges instead of running off * the canvas. Only the theme's: what a sidebar covers is the map's own padding, and MapLibre already * takes that off the space it fits bounds into. `extras` is anything else the caller wants of this * one camera - a maxZoom, say - as against the limits it set on the map itself, which apply to every * camera including the ones a reader drives. */ export declare const fitBounds: (map: MapLibreMap, theme: Theme, bounds: LngLatBoundsLike, extras?: FitBoundsOptions) => Promise; /** * How far in a map would have to be to frame these bounds, or nothing if no camera would - see * cameraForBounds above for when that happens. * * Worked out without moving the camera, and without the gap fitBounds leaves around what it frames, * so it lands a shade further in than where the camera really settles - by well under a zoom. What * it is for is deciding whether some floor a caller is considering would cost a reader the view of * a whole record, and that is a question about which side of a whole zoom the fit falls on. See * MapPreviewer.minZoom. */ export declare const zoomToFit: (map: MapLibreMap, bounds: LngLatBoundsLike) => number | undefined; /** * What a map that says where records are is allowed to do. * * Both of them take exactly this - a locator's one record and an overview's several - so a reader can * pan and zoom and nothing else. These maps are read at a glance, and one that has been turned or * tilted can't be compared with the next one, so neither is offered. And below createMap's own floor * of 2, because one record can cover the world, and an extent that wide framed with a gap around it * inside a container this small wants a camera further out than a map of data ever does. * * Cooperative gestures isn't in here even though both callers want it on by default: each of them * offers it as a prop, so each has to be able to pass its own answer through rather than have this * override it. */ export declare const LOCATION_MAP: MapExtras; export declare const disableRotation: (map: MapLibreMap) => void; /** * Zoom buttons and the projection toggle, ordered from the top down. * * MapLibre's own globe control rather than ours: GlobeControl here exists to take itself off the map * for a preview that can only be drawn flat, and a location can be drawn on anything. * * The globe button waits for a style document; the zoom buttons don't need one. Its click reaches * Style.setProjection, which opens by checking that a style has loaded and throws when one hasn't - * and on a map this small the window between building it and CARTO's style landing is easy to click * in. Waiting also means the button reads the projection we set rather than the one a styleless map * reports, so it opens showing the state it is actually in. */ export declare const addLocationControls: (map: MapLibreMap) => void; export declare const LOCATION_MAX_ZOOM = 12; /** * Point the camera at an area, holding it to what the projection the map is in can face. * * A globe camera has no answer for a box wider than the half of the world facing it: the solve takes * the flat answer and shrinks the globe until the box fits in front of it, and past 180 degrees no * size does, so MapLibre warns and hands back nothing and the camera stays where it was. Half of a * wide area is worth more than none of it, and the projection button is right there for a reader who * wants the whole of it. A flat map has no such limit and gets what it was given. * * The theme's overview gap either way, rather than the one a preview gets: these are whole maps read * at once rather than a pane filled with one record, and a shape drawn against the edge reads as * running off it - on a globe, that edge is where the sphere turns away. */ export declare const frameLocation: (map: MapLibreMap, theme: MapLibreTheme, target: LngLatBoundsLike, globe: boolean, extras?: FitBoundsOptions) => Promise; /** * Where to open a map that says where records are: the camera frameLocation would settle on, worked * out before there is a map to ask for one. * * The same overview gap, and the same holding to what a globe can face - because that is the * projection these maps open in, and a wide record framed as though it were flat would be re-framed * the moment the style document lands, which is the jump this exists to avoid. */ export declare const openingLocation: (container: HTMLElement, theme: MapLibreTheme, target: LngLatBoundsLike, globe: boolean, extras?: FitBoundsOptions) => MapExtras;