Raster basemaps ​
A map sheet can include imagery, such as a topographic map or satellite image, beneath its grid and graphics. The package never fetches or renders map tiles itself. Your app renders the imagery, warps it into the sheet's projection, and passes the finished PNG or JPEG bytes in the request.
1. Plan the target ​
Call resolveMapSheetRasterPlan with the same page, center, and scale you will use for the sheet. It tells you exactly what area and projection to render:
import {
mapSheetRasterPointToGeographic,
resolveMapSheetRasterPlan,
} from "@orbat-mapper/tactical-map-sheet";
const plan = resolveMapSheetRasterPlan({
page: { widthMm: 297, heightMm: 210, marginMm: 10 },
center: [-2.5, 49.2],
scaleDenominator: 25_000,
});
plan.crs; // { kind: "utm", zone: 30, hemisphere: "north" }
plan.epsg; // 32630
plan.frame; // map frame on the page, in millimeters
plan.projectedBounds; // frame extent in projected meters
// Geographic position of any point in the frame. (0, 0) is top-left, (1, 1) bottom-right.
const topLeft = mapSheetRasterPointToGeographic(plan, 0, 0);2. Render and warp the imagery ​
Render your basemap so it covers plan.projectedBounds in the plan's UTM or UPS projection. Web map tiles are in Web Mercator, so you need to warp them; use mapSheetRasterPointToGeographic to find where each output pixel comes from. Choose the pixel size from the DPI you want: 150 DPI or more avoids a warning.
A reference host for MapLibre
The repository's preview app includes a self-contained MapLibre host that renders, warps, and tiles a basemap for a sheet. Its folder, apps/preview/src/lib/maplibre-map-sheet-host/, depends only on maplibre-gl and this package, so you can copy it into your app.
For MapLibre, render a flat Mercator view (no pitch, bearing, globe, or terrain), wait for the map's idle event, and read the canvas. Keep the style at 96 CSS pixels per inch so labels print at their intended size, and use the drawing buffer's pixel ratio to reach the target DPI.
3. Add it to the request ​
request.rasterBasemap = {
layers: [
{
id: "terrain",
opacity: 0.85,
attribution: [{ text: "© National mapping authority", required: true }],
tiles: [
{
id: "terrain-full",
mediaType: "image/png",
data: pngBytes, // Uint8Array
pixelWidth: 3272,
pixelHeight: 2244,
frame: plan.frame, // where this tile goes on the page, in mm
},
],
},
],
};
request.marginalia = {}; // required attribution is printed in the marginaliaA basemap has one or more layers, painted bottom to top. Each layer has an ID, one opacity, attribution, and one or more tiles. A tile places one image at a rectangle on the page. The tiles of each layer must cover the map frame exactly, without gaps or overlaps; they do not need to form a regular grid. Split a large image into several tiles to stay within browser memory limits.
Validation ​
The request is checked before anything is written, and any problem rejects the whole request:
- IDs and attribution text must not be blank; layer and tile IDs must be unique.
opacitymust be between 0 and 1.pixelWidthandpixelHeightmust be positive integers that match the image.- Tiles must cover the frame exactly, within 0.000001 mm.
- Images must be PNG (not animated) or JPEG. JPEGs must not be rotated by EXIF orientation.
- Required attribution must fit in the marginalia, so
marginaliacannot befalse.
Image data is never decoded or recompressed: the bytes you pass are embedded as they are.
Output ​
Both SVG and PDF embed the tiles with their transparency, opacity, and clipping. The grid, graphics, symbols, text, and marginalia stay vector on top. Required attribution is de-duplicated and printed in a band in the marginalia.
metadata.rasterBasemap in the result lists each layer and tile with its placement and effective DPI, without the image bytes. Two warnings flag quality problems:
low-raster-dpi: a tile is below 150 DPI on either axis.raster-dpi-anisotropy: a tile's horizontal and vertical DPI differ by more than 1%.
A raster basemap cannot be combined with a registered overlay, which must be transparent.
What your app is responsible for ​
Choosing and licensing the basemap, downloading it, rendering the style, reprojecting and warping it, choosing the DPI, encoding the images, and splitting them into tiles all belong to your app. The package accepts no map object, style, URL, canvas, or tile coordinate.