GeoMap

GeoMap draws a geographic source as vector paths. It belongs to @gum-jsx/maps; the CLI and editor include its bindings by default. Start with Making maps for a source-to-annotation walkthrough.

PropertyDefaultMeaning
source—A source returned by geojson, topojson, world_countries, or us_states
source-resource—Name of a GeoSource or PreparedSource resource on the layout pass; supply exactly one of this and source
width / heightNatural view fits within 720px × 400pxAllocated map size; omit an axis to derive it from the projected fit target and padding
aspectInferred from the projected fit targetOverride the preferred outer width divided by height
projection"naturalEarth1"Projection preset; see the table below
fit-to"sphere"; "data" for Albers USA"sphere", "data", or an array of feature IDs such as ['276', '040']
bounds—[west, south, east, north] in degrees; fits and clips to this box, overriding fit-to
padding0Inset around the fitted geography
centerFit target centered automatically[longitude, latitude] in degrees to pan to the viewport midpoint after fitting
rotateProjection preset[lambda, phi, gamma?] spherical rotation in degrees; omitted gamma is zero
clip-angleProjection presetSpherical clipping angle in degrees, greater than zero and at most 180; null uses antimeridian cutting
precisionProjection presetNonnegative adaptive sampling tolerance in pixels
fill"#dce5e8"Default feature fill
backgroundnoneFill the projected sphere behind the features; the exterior stays transparent
styles—(id) => style callback or dictionary mapping feature IDs to style objects
border-mode"all""all", "interior", or "none"; interior borders require TopoJSON
border-color"#ffffff"Paint for borders and line features
border-widthpx(0.7)Nonnegative border width
point-radiuspx(3)Nonnegative radius for geographic point features

Sources and feature IDs

One map owns one source. Load or prepare external data before rendering; GeoMap does not fetch URLs or read files. The bundled accessors run offline:

HelperSource
world_countries(options?)World-atlas 2.0.2 countries at 1:110m; three-digit country IDs such as "840" and "076", plus stable local IDs for three features
us_states(options?)US-atlas 3.0.1 states and territories at 1:10m; two-digit FIPS IDs such as "06"
geojson(data, options?)GeoJSON geometry, feature, or feature collection in longitude/latitude coordinates
topojson(data, objectName, options?)A named Topology object, retaining shared arcs for borders

Each bundled accessor returns a fresh copy with its version, source URL, and boundary viewpoint in provenance. Preserve leading zeros in the atlas IDs. The world atlas's local IDs are local:northern-cyprus, local:somaliland, and local:kosovo.

All four helpers accept an ids option to filter what the map draws: world_countries({ ids: ['276', '040'] }) selects Germany and Austria, and us_states({ ids: ['06', '32'] }) selects California and Nevada. Your own sources use geojson(data, { ids: [...] }) or topojson(data, objectName, { ids: [...] }). Filtering happens before borders are built, preserving shared TopoJSON arcs. Inputs are not mutated. The source retains its original feature order, repeated requested IDs select a feature once, and unknown IDs are errors.

IDs are strings matched exactly, including leading zeros, and honor id_property when specified. Filtering requires explicit matching IDs; autogenerated positional IDs cannot be selected. Omit ids for all features; ids: [] selects none. An empty source can use sphere or bounds fitting, but cannot fit to data. Styles apply only to retained features; dictionaries still reject absent IDs.

Both source constructors accept id_property and provenance options. Feature IDs normally come from feature.id; for a property-based ID, use geojson(data, { id_property: 'ADM0_A3' }). IDs become strings and must be unique. Feature styles require explicit IDs on every feature. Dictionaries reject unknown keys. Styles match IDs, not names or feature order.

geojson expects RFC 7946 polygon winding by default. It stitches antimeridian and polar cuts and reverses rings for the spherical projection pipeline without mutating the input. Use { winding: 'd3' } only for data already using D3's winding convention. See GeoJSON edge cases for runnable sources with holes and antimeridian cuts.

Feature styles

Use a callback to derive a style from each geographic ID: styles={id => ({ fill: highlights[id] })}. Or supply an ID-to-style dictionary: styles={{ '840': { fill: blue }, '076': { fill: green, opacity: 0.6 } }}. Missing dictionary entries, undefined callback results, and omitted style fields inherit the map's style. Callback IDs are strings, including leading zeros.

Style objects accept ordinary Gum style props such as fill, opacity, stroke, stroke_width, stroke_dasharray, stroke_linecap, and stroke_linejoin, plus point_radius for geographic points. Use underscore keys inside JavaScript objects. Colors can use theme paints; lengths accept px/em/fractions as on the map. A feature's font_size controls its em lengths.

Callbacks run once per source feature at element construction, and their results are copied into immutable props. They are not rerun when the map is resized. Callbacks require an inline source; maps using source-resource accept the dictionary form, which is checked against the resource at layout time.

Feature strokes draw after the shared borders, so a selected feature can have its own highlighted outline. Shared borders keep the map's border-color, border-width, and opacity. Use border-mode="none" for feature strokes alone; adjacent feature strokes can overlap. See the world countries callback and US states dictionary.

Projection and fitting

PresetTypical use
naturalEarth1General world illustrations; the default
equalEarthEqual-area world maps
equirectangularRectangular longitude/latitude views
mercatorMercator views
orthographicGlobe views, with the far side clipped by default
albersUsaLower 48 states with Alaska and Hawaii insets

The projection fits its target into the allocated rectangle minus padding. Use fit-to="data" for a regional source, or fit-to={['276', '040']} to fit selected features from a larger source. Selected IDs set the extent; they do not filter the drawn features. Fitting is independent of styles. Unknown selected IDs and fit targets without geometry are errors.

For a coordinate box, use bounds={[-10, 35, 30, 60]}. When supplied, bounds takes precedence over fit-to for both fitting and natural sizing; the unused fit target is not looked up. Bounds are [west, south, east, north] in degrees. Longitudes must be in [-180, 180], and latitudes must satisfy -90 <= south < north <= 90. Longitude span must be positive; [-180, south, 180, north] spans all longitudes. A west value greater than east crosses the antimeridian, such as [170, -20, -170, 20]. Rotation is unchanged: use rotate={[-180, 0, 0]} for a continuous Pacific-facing view.

Bounds fit a sampled geographic rectangle and clip all map content to its projected outline, including curved edges. Water, features, borders, and children share this clip. The projection keeps its proportions, so a narrow longitude range in a wide allocation leaves transparent space on either side instead of showing extra geography. Source features themselves remain intact. Bounds fit the visible portion under the chosen projection; a fully hidden or degenerate target is an error.

Source selection and bounds fitting combine independently:

Only Germany and Austria draw, inside the requested frame. Omit bounds and use fit-to="data" to fit tightly around the filtered geography. See Filtering and bounds.

center={[30, 20]} places 30° east, 20° north at the viewport midpoint. Fitting sets the scale first, then the map pans to this location. This can move parts of the fitted geometry outside the viewport; omit center to keep the fit target centered automatically. An explicit center={[0, 0]} also overrides the automatic center of a regional fit.

center pans the projected map; rotate turns the globe before projection. For an orthographic globe facing 30° east, 20° north, use rotate={[-30, -20, 0]}. Panning alone does not reveal points hidden on the far side. If both props are set, center refers to the original longitude/latitude, before rotation.

albersUsa fits data by default and cannot fit the whole sphere. It has fixed center, rotation, and spherical clipping, so it rejects center, rotate, and clip-angle. The projection excludes territories outside the lower 48 states, Alaska, and Hawaii. Compare the projection gallery, US states, and selected-region fit.

Map dimensions use ordinary Gum sizing. The projected fit target supplies the map's natural proportions, including rotation and spherical clipping. Specify only height for a narrow region and the map derives its width; specify only width to derive its height. This also works for sphere, data, and selected-ID fits. Map padding adds space around that projected extent. Box, Frame, and stacks can hug the result without a manually supplied aspect:

With neither dimension nor an offer, the view fits within 720px × 400px. An explicit aspect overrides these natural proportions for the outer map box. Two exact dimensions, including parent stretch allocations, take precedence; the map then fits inside that rectangle with any extra space transparent. See Natural map sizes.

padding, border-width, and point-radius accept px/em lengths or fractions of the shorter allocated axis. Padding must be nonnegative and leave a positive drawing area. Projection precision controls curve sampling, not source-data simplification. Geometry is always clipped to the map rectangle.

Fills, borders, and annotations

Fills are drawn before borders. TopoJSON draws the selected shared-arc mesh once; GeoJSON draws each feature's edges, so adjacent edges can repeat. border-mode="interior" selects only shared TopoJSON boundaries. border-mode="none", a zero border-width, or border-color={none} hides borders. Use the border props to style the shared borders and line features. Ordinary stroke props add feature outlines after those borders; styles can override them by ID. Points render as circular paths using point-radius.

Set background={interp(white, blue, 0.15)} to color the water. The background is the projected sphere, drawn before feature fills and borders. It follows the same projection, rotation, clipping, and fitted extent as the geography, leaving the area outside the projection transparent. An orthographic globe gets a circular background; bounds fitting crops it to the projected geographic box. Albers USA uses the composite projection's clip regions. Background colors can also use theme paints such as "theme:accent"; the default is none.

This is a backdrop for the supplied geometry: gaps and polygon holes show the background, but lakes need to be represented as holes or omitted areas in the source. A surrounding Box can supply a separate rectangular background outside the projection.

Nest marks and annotations directly inside GeoMap. Use {lon, lat} records in degrees for mark coordinates and direct-child pos values. Tuples [longitude, latitude] and records {x: longitude, y: latitude} are aliases. Both components are required, and records cannot mix lon/lat with x/y. Children draw above the geography and share its fitted size, padding, center, rotation, and clipping. For example:

Text and marker shapes keep their ordinary layout sizes and orientations. Points hidden by the projection are omitted, and sampled paths break at hidden points. Supply an Arrow's intermediate longitude/latitude samples when the route should curve; ordinary marks do not automatically resample or split at the antimeridian. space="local" opts marks out, and Cartesian lengths such as pos={[px(12), px(24)]} position annotations in local pixels. Geographic components must be numeric. GeoJSON, TopoJSON, center, and the geographic helpers retain their array formats. Children inherit the map's ordinary styles, so set stroke explicitly on route arrows. See Map routes and Projections.

Line, HLine, VLine, Polyline, and Polygon use the map projection by default, just like Points. Use space="local" for local decorations.

For annotations outside the map subtree, the helper project_geo_point(source, view, width, height, [longitude, latitude]) accepts a raw or prepared source and returns local pixel coordinates, or null if hidden by the projection's spherical clipping. The map's bounds and viewport clips apply to its subtree; external annotations need their own clipping. Use the same source, view, and allocated dimensions as the map. Helper padding is in pixels; element padding accepts Gum lengths. See projected city markers for this lower-level sibling-overlay pattern.

For lower-level work, create_geo_projection(prepared, view, width, height) returns the fitted D3 projection. Prepare its source with prepare_geo_source. Directly calling that projection does not test spherical visibility; use project_geo_point to check that visibility.

Host integration and shared sources

Embedded evaluators can import * as maps from @gum-jsx/maps and pass scope: maps to Evaluator. Host element props use underscores, such as fit_to and border_color; evaluated JSX also accepts dashed names. See Rendering for the host rendering workflow.

For a large source shared by several maps, install it once on a LayoutPass instead of copying it into every element's immutable props: