The footer's attribution is a licence statement, so it has to be true rather than merely present. posterDefaults ships "(c) Mapbox (c) OpenStreetMap" and any caller that did not override it printed exactly that -- including country-posters, whose maps run on the fleet's MapLibre default, where Mapbox serves nothing at all and OpenFreeMap, OpenMapTiles and OpenStreetMap are the three parties whose licences actually apply. research-map had a hand-written pair for the two providers, which was right for research-map and helped nobody else. The obvious approach is to read the style's sources. Checked against the live style rather than assumed, and it does not work: tiles.openfreemap.org/styles/ liberty declares two sources and NEITHER carries an attribution field. The credit lives in the TileJSON the vector source points at, which the engine fetches and merges into the SOURCE OBJECT once it loads. The style is the request and the source is what came back, and only the second one knows. map-attribution.js reads the live source first and falls back to any style-declared value. The TileJSON value is HTML with anchors and a © entity, and a poster footer is text, so it is flattened rather than printed raw. Repeated credits collapse. When nothing can be read -- no map, a style that throws before it has loaded -- it returns empty and the caller keeps its own string, because a blank licence line is the one outcome that is never acceptable. The check uses the real TileJSON string as its fixture. A tidy invented style would have passed while the real map printed the wrong credit, and its first negative test is exactly that: reading the style alone returns nothing for OpenFreeMap. Cut directly on main rather than on the branch this began life in, so it does not wait behind a poster-layout change it has nothing to do with. A wrong tile credit is a licence problem; the layout question it was sitting behind is not.
112 lines
3.4 KiB
JavaScript
112 lines
3.4 KiB
JavaScript
/**
|
||
* posterDefaults.js — default margins, bleed, slot sizes, and typography
|
||
* used by the poster pipeline.
|
||
*
|
||
* These are reasonable starting values for a 24×36" research poster at
|
||
* 300 DPI. Override per-spec via the PosterSpec passed to useMapPoster().
|
||
*
|
||
* All measurements are in INCHES unless noted. The renderer converts to
|
||
* pixels using the spec's DPI.
|
||
*/
|
||
|
||
export const posterDefaults = {
|
||
dpi: 300,
|
||
|
||
// Outside margins (printed area inset from page edge, after bleed).
|
||
margins: {
|
||
top: 0.75,
|
||
right: 0.75,
|
||
bottom: 0.75,
|
||
left: 0.75,
|
||
},
|
||
|
||
// Bleed area (pulled outside the trimmed page); 0.125" = 3 mm is industry standard.
|
||
bleed: 0.125,
|
||
|
||
// Vertical/horizontal gutter between sections.
|
||
gutter: { in: 0.20 },
|
||
|
||
// Title strip across the top of content area.
|
||
title: {
|
||
heightIn: 1.25,
|
||
fontFamily: 'Inter, system-ui, -apple-system, "Segoe UI", sans-serif',
|
||
fontWeightTitle: 700,
|
||
fontWeightSubtitle: 500,
|
||
fontSizeTitlePt: 36,
|
||
fontSizeSubtitlePt: 16,
|
||
color: '#0a0a0a',
|
||
},
|
||
|
||
// Legend column on the right.
|
||
legend: {
|
||
widthIn: 3.0,
|
||
fontFamily: 'Inter, system-ui, sans-serif',
|
||
fontSizePt: 11,
|
||
swatchSize: 14, // px (in legend SVG)
|
||
backgroundColor: 'rgba(255,255,255,0.94)',
|
||
borderColor: '#cccccc',
|
||
},
|
||
|
||
// Chrome band (scale + north arrow) under the map.
|
||
chromeBand: { heightIn: 0.6 },
|
||
|
||
// Scale bar.
|
||
scaleBar: {
|
||
widthIn: 2.5,
|
||
units: 'both', // 'km' | 'mi' | 'both'
|
||
color: '#111',
|
||
backgroundColor: 'rgba(255,255,255,0.9)',
|
||
},
|
||
|
||
// North arrow.
|
||
north: {
|
||
sizeIn: 0.6,
|
||
color: '#111',
|
||
},
|
||
|
||
// Footer strip — ALWAYS present (Mapbox attribution requirement).
|
||
footer: {
|
||
heightIn: 0.45,
|
||
fontFamily: 'Inter, system-ui, sans-serif',
|
||
fontSizePt: 10,
|
||
color: '#222',
|
||
// Caller may set left/right text; attribution is enforced by FooterSlot.
|
||
// ★ A FALLBACK OF LAST RESORT, NOT THE ANSWER. useMapPoster reads the credit from the map's
|
||
// LOADED sources (library/maps/map-attribution.js) and overrides this, because under the
|
||
// fleet's MapLibre default Mapbox serves nothing and this line would credit the wrong party.
|
||
// It stays non-empty only so a map that cannot be read still prints something: a licence
|
||
// footer may never be blank.
|
||
attribution: '© Mapbox © OpenStreetMap',
|
||
backgroundColor: 'rgba(255,255,255,0.92)',
|
||
},
|
||
};
|
||
|
||
/**
|
||
* Build a default PosterSpec. Caller usually overrides title/legend/etc.
|
||
*
|
||
* @param {{ widthIn: number, heightIn: number, dpi?: number, orientation?: 'portrait'|'landscape' }} size
|
||
* @returns {import('../composables/poster/useMapPoster.js').PosterSpec}
|
||
*/
|
||
export function defaultPosterSpec(size) {
|
||
return {
|
||
widthIn: size.widthIn,
|
||
heightIn: size.heightIn,
|
||
dpi: size.dpi || posterDefaults.dpi,
|
||
orientation: size.orientation || (size.heightIn >= size.widthIn ? 'portrait' : 'landscape'),
|
||
margins: { ...posterDefaults.margins },
|
||
bleed: posterDefaults.bleed,
|
||
slots: {
|
||
title: { text: 'Untitled', subtitle: '', heightIn: posterDefaults.title.heightIn },
|
||
legend: { rows: [], type: 'generic', widthIn: posterDefaults.legend.widthIn },
|
||
scaleBar: true,
|
||
north: true,
|
||
footer: {
|
||
left: '',
|
||
right: '',
|
||
attribution: posterDefaults.footer.attribution,
|
||
heightIn: posterDefaults.footer.heightIn,
|
||
},
|
||
},
|
||
renderPath: 'auto',
|
||
};
|
||
}
|