mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
fix(maps): let users and admins enter a CARTO API key (#2060)
CARTO stamps an "API KEY REQUIRED" watermark onto every basemap tile fetched without a key since 26.08.2026, and both CartoDB presets plus the built-in fallback are CARTO. The key is free and needs no account, so add it as a setting rather than dropping the provider. carto_api_key sits next to mapbox_access_token: per user in Settings/Map, instance-wide under Admin/User Defaults, encrypted at rest, injected from CARTO_API_KEY on managed instances. Unlike Mapbox it never switches the map provider, since a raster key says nothing about the renderer. The key is appended as ?key= at render time instead of being stored in the template, so a saved URL stays portable and survives a rotation. That is also what reaches the four maps that pinned CARTO and ignored the user's tile setting: atlas, collections, the public trip share and the public journey. The two share pages read it from their token-gated payload, the same way baseCurrency already travels. Also lifts the release notice above page chrome. Its overlay sat at z-index 50, under the Vacay mode toolbar at 61, so the dialog was neither on top nor modal. Adds a six-step layer scale to index.css and moves the three notice shells onto it. Closes #2054 Closes #2052
This commit is contained in:
@@ -9,6 +9,7 @@ import { Swatches } from './StudioSwatches'
|
||||
import { BOOK_FONTS, BOOK_FONT_ORDER, hasWeight, nearestWeight } from './bookFonts'
|
||||
import { sourceIdOf, useMapSources } from './mapSources'
|
||||
import { printDpi, tileView } from './mapTiles'
|
||||
import { useCartoApiKey } from '../../hooks/useTileUrl'
|
||||
import { routeFor } from './travelRefresh'
|
||||
import { Choice, Line, Picks, Section, Switch } from './StudioControls'
|
||||
import { fetchRoads } from './roadRoute'
|
||||
@@ -217,6 +218,7 @@ function ListProps({ el, set, t }: Props<BookListElement>) {
|
||||
}
|
||||
|
||||
function MapProps({ el, set, t, stats }: Props<BookMapElement> & { stats: JourneyStats | null }) {
|
||||
const cartoKey = useCartoApiKey()
|
||||
const [roadState, setRoadState] = useState<'idle' | 'busy'>('idle')
|
||||
const [roadDone, setRoadDone] = useState(0)
|
||||
const hasRoads = (el.roads ?? []).some(r => r && r.length > 1)
|
||||
@@ -248,7 +250,7 @@ function MapProps({ el, set, t, stats }: Props<BookMapElement> & { stats: Journe
|
||||
* say what it will print at. Same call the renderer makes, same answer.
|
||||
*/
|
||||
const view = el.source === 'tiles' && el.tileUrl
|
||||
? tileView(el.points, { w: el.frame.w, h: el.frame.h }, el.tileUrl, el.zoom)
|
||||
? tileView(el.points, { w: el.frame.w, h: el.frame.h }, el.tileUrl, el.zoom, undefined, 'print', cartoKey)
|
||||
: null
|
||||
const dpi = view ? printDpi(view, el.tileUrl) : null
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { fontStack } from './bookFonts'
|
||||
import { brightness } from './folioColour'
|
||||
import { COUNTRY_SHAPES, countryParts, countryWorldPath, projectMercator, unprojectMercator } from './countryShapes'
|
||||
import { projectOntoTiles, tileView, usableStaticUrl } from './mapTiles'
|
||||
import { useCartoApiKey } from '../../hooks/useTileUrl'
|
||||
import { FLAG_H, FLAG_W, flagBands, flagDisc, flagSpec } from './flags'
|
||||
import { MOOD_CONFIG, WEATHER_CONFIG } from '../../pages/journeyDetail/JourneyDetailPage.constants'
|
||||
import { useTranslation } from '../../i18n'
|
||||
@@ -200,6 +201,7 @@ function MapView({ el, frameStyle, big = false }: {
|
||||
/** False in a thumbnail, where a page of imagery would be a page of waste. */
|
||||
big?: boolean
|
||||
}) {
|
||||
const cartoKey = useCartoApiKey()
|
||||
const palette = MAP_PALETTES[el.style]
|
||||
// Per element: two cut maps on one spread sharing a stencil would both be cut
|
||||
// to whichever of them rendered last.
|
||||
@@ -591,7 +593,7 @@ function MapView({ el, frameStyle, big = false }: {
|
||||
maxLat: topLeft.lat,
|
||||
maxLng: bottomRight.lng,
|
||||
minLat: bottomRight.lat,
|
||||
}, big ? 'print' : 'preview')
|
||||
}, big ? 'print' : 'preview', cartoKey)
|
||||
: null
|
||||
|
||||
/*
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
* placed so the renderer cannot lose it.
|
||||
*/
|
||||
|
||||
import { withTileApiKey } from '../../utils/tileUrl'
|
||||
|
||||
/** A slippy-map tile, in the standard XYZ scheme. */
|
||||
export interface Tile {
|
||||
x: number
|
||||
@@ -168,6 +170,14 @@ export function tileView(
|
||||
* tiles, deliberately soft, because nobody prints the pages rail.
|
||||
*/
|
||||
quality: 'print' | 'preview' = 'print',
|
||||
/**
|
||||
* The instance's CARTO key, for a book still on a CARTO template.
|
||||
*
|
||||
* Applied here rather than stored: keyless CARTO tiles carry a watermark
|
||||
* since 26.08.2026 (#2054), and a key frozen into the document would be
|
||||
* printed into every export and would outlive the key itself.
|
||||
*/
|
||||
cartoKey?: string,
|
||||
): TileView | null {
|
||||
if (!template || (!points.length && !extent)) return null
|
||||
|
||||
@@ -290,6 +300,7 @@ export function tileView(
|
||||
const originY = frame.h / 2 - (cy * scale - y0) * size
|
||||
|
||||
const tiles: Tile[] = []
|
||||
const source = withTileApiKey(template, cartoKey)
|
||||
const wrap = (n: number) => ((n % scale) + scale) % scale
|
||||
for (let y = y0; y <= y1; y++) {
|
||||
// Past the poles there is no tile — the row simply does not exist.
|
||||
@@ -299,7 +310,7 @@ export function tileView(
|
||||
x: x - x0,
|
||||
y: y - y0,
|
||||
z: zoom,
|
||||
url: template
|
||||
url: source
|
||||
.replace('{z}', String(zoom))
|
||||
// Longitude wraps, so a route across the antimeridian keeps its tiles.
|
||||
.replace('{x}', String(wrap(x)))
|
||||
|
||||
Reference in New Issue
Block a user