Skip to content

[Feature]: Publication layout in one command: a finished map sheet or figure sized to the page (layout.publication) #682

Closed

Description

@elpidioassinaturas

Before you start

  • I searched the existing issues and the roadmap and did not find a duplicate request.

What problem does this solve?

RustGIS has the building blocks of a finished map sheet:

Turning those into a sheet that meets cartographic conventions is still manual work, and it has to be redone for every page size:

  • The page size doesn't carry the composition. layout.page changes the sheet but leaves every element where it was. An A4 composition doesn't become an A3 one, nor a 90 mm journal figure, whose texts must stay at 6–7 pt at the final size while the graphics shrink.
  • The locator is several steps. A second map in globe, its scale set so the globe has the right diameter, a frame, the area highlighted.
  • The footer is placed by hand. Data sources, the cartographic reference (projection, datum, graticule), notes and authorship are text elements positioned one by one, with no wrapping. Long lines run past the frame unless shortened by hand.
  • Elements land on the theme. Nothing keeps the legend, the scale bar or the locator off the area being mapped, or warns when they overlap.
  • Numbers follow one convention. Colorbar and scale labels use a decimal point, while maps in Portuguese, Spanish, French or German expect a comma.

The example attached is a teaching map (NDVI trend in Piauí, Brazil, 2001–2025, from #579's Trend Analysis). It took about 1,100 lines of Python (an 830-line layout generator and a 270-line wrapper for this map) driving rustgis-cli. The script:

  • computes the scale for the frame;
  • places every element by lon/lat;
  • draws the footer columns and their boxes;
  • sizes the globe;
  • warns about overlaps.

The same configuration produces A4, A3, A2, a journal figure of one, one and a half or two columns, and a thesis figure. This is the part I'd like RustGIS to do itself.

Proposed solution

A command, layout.publication, that builds a complete layout from the active map and a few texts. It is also offered in Publish › Layout as New Publication Layout…, a dialog with the same fields.

layout.publication {
  "profile": "A4-portrait",          // A4/A3/A2 portrait|landscape, figure-1col|-1.5col|-2col, thesis-full|-half
  "extent": [-46.8, -11.6, -38.5, -2.2],   // lon/lat that must fit; default: the map view
  "title": "PIAUÍ",
  "subtitle": "Greening and browning of vegetation: NDVI trend, 2001–2025",
  "sources": ["NDVI: MOD13Q1 v6.1 (NASA LP DAAC), via the Brazil Data Cube (INPE)", "Boundaries: IBGE 2022"],
  "reference": ["Projection: {projection}", "Datum: {datum} · Graticule {grid}"],
  "note": "Only cells with a significant trend (p < 0.05) are coloured.",
  "author": "Made with RustGIS · {date}",
  "locator": "globe",                // "globe" | "inset" | "none"
  "decimal": ","                     // number labels in the layout: "." (default) or ","
}

What it builds:

  • Sheet profiles (A4/A3/A2, portrait or landscape): a frame and margin, then title and subtitle, the map, and a footer in two boxed columns (sources | cartographic reference) with the note and authorship below. Text sizes scale with the sheet by the short side ÷ 210 mm.
  • Figure profiles (journal 90/140/190 mm, thesis 160 × 225 / 160 × 110 mm): no frame, title or footer, which belong to the figure caption. The map runs edge to edge, with a one-line projection and datum note under it. Texts stay at 7 pt (never below 6) at the final size, and graphics follow the width.
  • The map frame: the extent fitted at a round scale (two significant figures), the graticule labelled on its edges, the step chosen from the extent (10°, 5°, 1°, 0.5°, minutes).
  • Legend: the visible layers' symbology.
    • Only classes drawn in the frame, as the legend element already does.
    • A raster on a ramp becomes a colorbar inside the legend box.
    • Titles come from the layers' names.
  • Scale bar with a round length (1, 2, 2.5 or 5 × 10ⁿ), with the numeric scale under it. It reads "approximate" below 1:1,000,000, and the note says the scale holds at the centre.
  • North arrow at the top right.
  • Locator:
    • globe: a second map in globe, centred on the area, with the area highlighted and the globe's diameter set for the profile;
    • inset: a rectangle of the surrounding region.
  • Placement: legend, scale bar and locator each go to the frame corner that covers the least of the mapped data. The visible layers' features and raster cells are rasterised at low resolution to find it. Anything placed over data, or overlapping another element, is reported in the command's result ("warnings": [...]), and every element can be moved afterwards as usual.
  • Text that fits: footer lines and the legend are measured with the layout's font and wrapped to their box, so nothing runs past the frame.

Behaviour:

  • One layout: the result is an ordinary layout, every element editable and saved with the project. Running it again with another profile makes a second layout from the same map, without touching the first.
  • Tokens: {projection}, {datum} and {grid} in the texts are filled from the map, and {date} as dynamic text is today.
  • Docs: a section in layouts.md, with the profiles and an example sheet and figure.
  • Tests:
    • every profile builds, with no element outside the page or the frame;
    • the scale and grid steps for a few extents;
    • a long footer line wraps;
    • decimal: "," gives 0,05 on a colorbar;
    • the corner choice avoids a raster that fills one corner.

Choices in this proposal

  • One command over a fixed set of profiles, not a template file format. The profiles are the sizes people publish at, and adding one later is a table row. A user-defined template format can come after, if wanted.
  • Corners, not free placement, in this first version. Choosing among the four corners by data coverage is predictable and testable. A free search for empty space, such as the ocean, can follow.
  • No new element kinds. The command composes existing ones (frame, grid, legend, colorbar, scale bar, north arrow, text, rectangle, a second map for the locator). Everything it makes is editable with the tools users already know.
  • decimal as an option on number labels (colorbar ticks, scale bar), defaulting to ., rather than tied to the interface language. A map's language is not always the app's.
  • The footer texts are the user's. RustGIS fills tokens but doesn't guess sources or credits.

Estimated scope

Medium: a broader feature that is feasible in a reasonable timeframe. I would split it into three PRs:

  1. profiles, sizing and the footer (with wrapping);
  2. the locator;
  3. corner placement, the warnings and decimal.

Alternatives considered

  • layout.new with the map template, then layout.page: this is today's path. The template doesn't adapt to the page, and the page doesn't move the elements.
  • An external script (the one behind the attached map, Python driving rustgis-cli): it works, but it lives outside RustGIS, so the app and the assistant can't use it. Its positions are computed against RustGIS's internals (the scale definition, the globe's size), which a change in RustGIS silently breaks.
  • A plugin wrapping the script: it puts it in the menus, but the coupling to internals remains, and it can't measure text with the layout's own font.

Related

#326 (colorbar), #327 (grid labels and surrounds), #517/#518 (legend layers), #213 (the earlier round of layout work), #205/#210/#211 (earlier layout issues the script had to work around), #579 (the analysis behind the example map).

Activity

  1. elpidioassinaturas commented on Oct 24, 2026

    @elpidioassinaturas
    ContributorAuthor

    The three PRs this issue planned have all landed, plus two more, so I'm closing it. Thanks for the quick reviews.

    Two follow-ups were offered in #742, if they're wanted: splitting publication's styles into functions of their own, and a radius control in the properties pane. I'm happy to do either on request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions