Skip to content

feat(rendering): add static map overlay endpoint#2794

Draft
CommanderStorm wants to merge 55 commits into
maplibre:mainfrom
CommanderStorm:static-rendering-squashed
Draft

feat(rendering): add static map overlay endpoint#2794
CommanderStorm wants to merge 55 commits into
maplibre:mainfrom
CommanderStorm:static-rendering-squashed

Conversation

@CommanderStorm

@CommanderStorm CommanderStorm commented May 9, 2026

Copy link
Copy Markdown
Member

Adds two new HTTP endpoints under our rendering feature:

  • POST /style/{id}/static/{camera}/{wxh.fmt} -> render a static map image at a center point, bounding box, or auto-fit to overlays.
  • MLT generates the map
  • The static_overlay module then draws with tiny-skia over the rendered map.

Comment thread martin/src/srv/static_overlay/mod.rs Outdated
@github-actions

github-actions Bot commented May 9, 2026

Copy link
Copy Markdown

Performance Comparison mainstatic-rendering-squashed

Total Elapsed Time: 69.69s → 68.91s (-1.1%)
CPU Baseline: 92.10µs → 91.95µs (-0.2%)
Benchmark ID: timing

timing - Function execution time metrics.

+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| Function                   | Calls                        | Avg                             | P95                             | Total                           | % Total                      |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| martin::main               | 1 → 1 (+0.0%)                | 69.69s → 68.89s (-1.1%)         | 69.73s → 68.92s (-1.2%)         | 69.69s → 68.91s (-1.1%)         | 100.00% → 100.00% (+0.0%)    |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| martin::start              | 1 → 1 (+0.0%)                | 69.69s → 68.89s (-1.1%)         | 69.73s → 68.92s (-1.2%)         | 69.68s → 68.91s (-1.1%)         | 99.99% → 100.00% (+0.0%)     |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| content::get_tile          | 2200600 → 2200600 (+0.0%)    | 26.53µs → 25.14µs (-5.2%)       | 41.02µs → 41.53µs (+1.2%)       | 58.38s → 55.32s (-5.2%)         | 83.76% → 80.28% (-4.2%)      |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| content::get_http_response | 2200600 → 2200600 (+0.0%)    | 19.34µs → 18.30µs (-5.4%)       | 33.98µs → 34.40µs (+1.2%)       | 42.56s → 40.26s (-5.4%)         | 61.07% → 58.43% (-4.3%)      |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| content::get_tile_content  | 2200600 → 2200600 (+0.0%)    | 17.00µs → 15.62µs (-8.1%)       | 31.73µs → 31.95µs (+0.7%)       | 37.42s → 34.37s (-8.2%)         | 53.69% → 49.87% (-7.1%)      |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| content::new               | 2200600 → 2200600 (+0.0%)    | 2.59µs → 2.16µs (-16.6%)        | 2.03µs → 1.77µs (-12.8%)        | 5.71s → 4.76s (-16.6%)          | 8.19% → 6.90% (-15.8%)       |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| content::recompress        | 2200600 → 2200600 (+0.0%)    | 1.76µs → 1.76µs (+0.0%)         | 100.00ns → 111.00ns (+11.0%)    | 3.87s → 3.88s (+0.3%)           | 5.55% → 5.63% (+1.4%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| content::encode            | 100100 → 100100 (+0.0%)      | 37.16µs → 37.12µs (-0.1%)       | 51.84µs → 53.41µs (+3.0%)       | 3.72s → 3.72s (+0.0%)           | 5.34% → 5.39% (+0.9%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| source::get_sources        | 2200600 → 2200600 (+0.0%)    | 1.13µs → 1.18µs (+4.4%)         | 1.62µs → 1.46µs (-9.9%)         | 2.49s → 2.59s (+4.0%)           | 3.57% → 3.75% (+5.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| server::new_server         | 1 → 1 (+0.0%)                | 265.60µs → 303.23µs (+14.2%)    | 265.73µs → 303.36µs (+14.2%)    | 265.55µs → 303.13µs (+14.2%)    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| optimizer::encode          | 1 → 1 (+0.0%)                | 64.66µs → 53.94µs (-16.6%)      | 64.67µs → 53.95µs (-16.6%)      | 64.67µs → 53.95µs (-16.6%)      | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| optimizer::encode_into     | 4 → 4 (+0.0%)                | 8.93µs → 7.20µs (-19.4%)        | 20.91µs → 13.99µs (-33.1%) 🚀   | 35.74µs → 28.81µs (-19.4%)      | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| encode::write_to           | 4 → 4 (+0.0%)                | 8.33µs → 6.59µs (-20.9%) 🚀     | 19.38µs → 12.46µs (-35.7%) 🚀   | 33.33µs → 26.37µs (-20.9%) 🚀   | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| writer::with               | 72 → 72 (+0.0%)              | 131.00ns → 130.00ns (-0.8%)     | 310.00ns → 311.00ns (+0.3%)     | 9.48µs → 9.38µs (-1.1%)         | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| 🗑️ optimizer::analyze      | 1 → 0 (-100.0%) 🚀           | 9.61µs → 0.00ns (-100.0%) 🚀    | 9.62µs → 0.00ns (-100.0%) 🚀    | 9.61µs → 0.00ns (-100.0%) 🚀    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+
| 🆕 tile::from_tile         | 0 → 4 (+100.0%) ⚠️           | 0.00ns → 1.75µs (+100.0%) ⚠️    | 0.00ns → 2.75µs (+100.0%) ⚠️    | 0.00ns → 6.98µs (+100.0%) ⚠️    | 0.00% → 0.00% (+0.0%)        |
+----------------------------+------------------------------+---------------------------------+---------------------------------+---------------------------------+------------------------------+

alloc-bytes - Exclusive allocation bytes by each function (excluding nested calls).

+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| Function                       | Calls                        | Avg                            | P95                            | Total                          | % Total                    |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::get_tile_content      | 2200600 → 2200600 (+0.0%)    | 91.6 KB → 91.6 KB (+0.0%)      | 180.2 KB → 180.2 KB (+0.0%)    | 192.2 GB → 192.2 GB (+0.0%)    | 84.00% → 84.00% (+0.0%)    |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::encode                | 100100 → 100100 (+0.0%)      | 344.5 KB → 344.5 KB (+0.0%)    | 347.2 KB → 347.2 KB (+0.0%)    | 32.9 GB → 32.9 GB (+0.0%)      | 14.37% → 14.37% (+0.0%)    |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| source::get_sources            | 2200600 → 2200600 (+0.0%)    | 1.3 KB → 1.3 KB (+0.0%)        | 2.4 KB → 2.4 KB (+0.0%)        | 2.7 GB → 2.7 GB (+0.0%)        | 1.20% → 1.20% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::get_tile              | 2200600 → 2200600 (+0.0%)    | 297 B → 297 B (+0.0%)          | 296 B → 296 B (+0.0%)          | 624.7 MB → 624.7 MB (+0.0%)    | 0.27% → 0.27% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| content::get_http_response     | 2200600 → 2200600 (+0.0%)    | 182 B → 182 B (+0.0%)          | 200 B → 200 B (+0.0%)          | 383.6 MB → 383.6 MB (+0.0%)    | 0.16% → 0.16% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| martin::start                  | 1 → 1 (+0.0%)                | 3.0 MB → 3.0 MB (+0.0%)        | 3.0 MB → 3.0 MB (+0.0%)        | 3.0 MB → 3.0 MB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| martin::main                   | 1 → 1 (+0.0%)                | 139.9 KB → 139.9 KB (+0.0%)    | 139.9 KB → 139.9 KB (+0.0%)    | 139.9 KB → 139.9 KB (+0.0%)    | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| optimizer::encode              | 1 → 1 (+0.0%)                | 67.7 KB → 67.7 KB (+0.0%)      | 67.7 KB → 67.7 KB (+0.0%)      | 67.7 KB → 67.7 KB (+0.0%)      | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| server::new_server             | 1 → 1 (+0.0%)                | 31.1 KB → 31.1 KB (+0.0%)      | 31.1 KB → 31.1 KB (+0.0%)      | 31.1 KB → 31.1 KB (+0.0%)      | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| tile::from_tile                | 4 → 4 (+0.0%)                | 376 B → 376 B (+0.0%)          | 376 B → 376 B (+0.0%)          | 1.5 KB → 1.5 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| encode::write_to               | 4 → 4 (+0.0%)                | 370 B → 370 B (+0.0%)          | 624 B → 624 B (+0.0%)          | 1.4 KB → 1.4 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| writer::with                   | 72 → 72 (+0.0%)              | 16 B → 16 B (+0.0%)            | 380 B → 380 B (+0.0%)          | 1.2 KB → 1.2 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| encode::dict_may_be_beneficial | 4 → 4 (+0.0%)                | 256 B → 256 B (+0.0%)          | 256 B → 256 B (+0.0%)          | 1.0 KB → 1.0 KB (+0.0%)        | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| sort::sort                     | 4 → 4 (+0.0%)                | 120 B → 120 B (+0.0%)          | 160 B → 160 B (+0.0%)          | 480 B → 480 B (+0.0%)          | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+
| writer::write_header           | 4 → 4 (+0.0%)                | 69 B → 69 B (+0.0%)            | 104 B → 104 B (+0.0%)          | 276 B → 276 B (+0.0%)          | 0.00% → 0.00% (+0.0%)      |
+--------------------------------+------------------------------+--------------------------------+--------------------------------+--------------------------------+----------------------------+

Threads

Total Alloc: 3.0 MB → 3.0 MB (+0.0%)
Total Dealloc: 300.9 MB → 301.0 MB (+0.0%)
Mem Diff: -297.9 MB → -298.1 MB (-0.1%)

+-------------+-----------------------------+------------------------------+------------------------------+------------------------------+--------------------------------+
| Thread      | CPU % Avg                   | CPU % Max                    | Alloc                        | Dealloc                      | Mem Diff                       |
+-------------+-----------------------------+------------------------------+------------------------------+------------------------------+--------------------------------+
| martin      | 0.00% → 0.10% (+100.0%) ⚠️  | 12.00% → 15.90% (+32.5%) ⚠️  | 2.8 MB → 2.8 MB (+0.0%)      | 1.9 MB → 1.9 MB (+0.0%)      | 933.1 KB → 947.0 KB (+1.5%)    |
+-------------+-----------------------------+------------------------------+------------------------------+------------------------------+--------------------------------+
| hp-mcp      | 0.00% → 0.00% (+0.0%)       | 0.00% → 0.00% (+0.0%)        | 54.7 KB → 54.7 KB (+0.0%)    | 3.4 KB → 3.4 KB (+0.0%)      | 51.4 KB → 51.4 KB (+0.0%)      |
+-------------+-----------------------------+------------------------------+------------------------------+------------------------------+--------------------------------+
| 🆕 hp-debug | 0.00% → 5.70% (+100.0%) ⚠️  | 0.00% → 19.90% (+100.0%) ⚠️  | 0 B → 10.3 KB (+100.0%) ⚠️   | 0 B → 299.1 MB (+100.0%) ⚠️  | 0 B → -299.1 MB (+100.0%) ⚠️   |
+-------------+-----------------------------+------------------------------+------------------------------+------------------------------+--------------------------------+

Generated with hotpath-rs

@CommanderStorm CommanderStorm marked this pull request as ready for review May 9, 2026 23:21
Copilot AI review requested due to automatic review settings May 9, 2026 23:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds static map rendering capabilities to Martin’s Linux-only rendering feature by introducing /style/{id}/static/... endpoints that render an image via maplibre-native and then draw optional path/marker overlays using tiny-skia. This also updates configuration + schema generation to include the new rendering surface area.

Changes:

  • Add GET/POST /style/{style_id}/static/{static_params}/{size_fmt} endpoints with query/body overlay parameters and image encoding.
  • Introduce overlay parsing/projection/bounds + drawing modules and add E2E/snapshot-style tests.
  • Add a static render pool in martin-core and update OpenAPI/config schemas + docs to include rendering config/routes.

Reviewed changes

Copilot reviewed 20 out of 46 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
schemas/openapi.json Documents new style rendering + static map endpoints and overlay schema.
schemas/config.json Adds styles.rendering configuration schema (bool-or-object).
martin/tests/styles_server_test.rs Adds E2E pixel-matching tests for the static endpoint and overlays.
martin/src/srv/styles_static.rs Implements static map endpoints, request parsing/validation, overlay merge, and encoding.
martin/src/srv/styles_rendering.rs Adjusts schema param handling for rendered tile endpoint.
martin/src/srv/static_overlay/project.rs Adds Web Mercator projection helpers for overlay placement.
martin/src/srv/static_overlay/parse.rs Adds parsers for path, marker, and paint/line style params.
martin/src/srv/static_overlay/mod.rs Defines overlay types, defaults, and exports helpers.
martin/src/srv/static_overlay/draw.rs Implements tiny-skia overlay drawing and snapshot tests.
martin/src/srv/static_overlay/bounds.rs Computes overlay bounds and derives camera framing for auto mode.
martin/src/srv/server.rs Registers static endpoints before the rendered-tile route to avoid routing conflicts.
martin/src/srv/mod.rs Wires in styles_static and static_overlay under rendering (Linux-only).
martin/src/schemas.rs Ensures rendering OpenAPI derive includes the new static endpoints.
martin/Cargo.toml Extends rendering feature deps to include overlay/encoding dependencies and test deps.
martin-core/src/resources/styles/static_render_pool.rs Adds a global single-worker static render pool with renderer/style caching.
martin-core/src/resources/styles/mod.rs Exposes render_static and StaticRenderParams behind rendering (Linux-only).
martin-core/src/resources/styles/error.rs Extends StyleError to include static render pool errors.
justfile Updates gen-schemas to include rendering routes on Linux and sets MLN_PRECOMPILE=1.
docs/content/files/generated_config.md Documents styles.rendering config in generated docs.
Cargo.toml Adds workspace deps for overlay/rendering support crates.
Cargo.lock Updates lockfile for new dependencies and version resolutions.

Comment thread martin/src/srv/static_overlay/draw.rs Outdated
Comment thread martin/src/srv/styles_static.rs Outdated
Comment thread martin/src/srv/styles_static.rs Outdated
Comment thread martin-core/src/resources/styles/static_render_pool.rs Outdated
Comment thread martin-core/src/resources/styles/static_render_pool.rs Outdated
@CommanderStorm CommanderStorm force-pushed the static-rendering-squashed branch from 283adf9 to 1bf0a66 Compare May 10, 2026 11:15
@CommanderStorm CommanderStorm changed the title feat(rendering): add static map and overlay endpoints feat(rendering): add static map overlay endpoint May 10, 2026
@CommanderStorm CommanderStorm requested a review from nyurik May 10, 2026 12:18
@github-actions github-actions Bot removed the bless label May 11, 2026

@nyurik nyurik left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is huge! And unlike experimental MLT, this is for a production system... can we somehow break it into smaller chunks?

Bigger issues:

  • lots of dup code - ai was going all out on that
  • is there a full doc on all functionality? I'm not yet convinced google polyline is the right approach
  • should we support geojson with styling approach as used in https://geojson.io/next -- this would be far cleaner imo, rather than reinventing this language

Also, is this all still part of experimental, or is this built by default?

Comment thread martin/src/srv/static_overlay/parse.rs Outdated
Comment thread martin/src/srv/static_overlay/parse.rs Outdated
Comment thread martin/src/srv/static_overlay/parse.rs Outdated
Comment thread martin/src/srv/static_overlay/project.rs Outdated
Comment thread martin/src/srv/styles_static.rs Outdated
Comment thread martin/src/srv/styles_static.rs Outdated
Comment thread martin/src/srv/static_overlay/parse.rs Outdated
@CommanderStorm

CommanderStorm commented May 14, 2026

Copy link
Copy Markdown
Member Author
  • I'm not yet convinced google polyline is the right approach
  • should we support geojson with styling approach as used in https://geojson.io/next -- this would be far cleaner imo, rather than reinventing this language

It is not reinventing a language.
This is what tileserver-gl does -> https://tileserver.readthedocs.io/en/latest/endpoints.html#static-images

The entire API is modeled off what seems to work already.

I have not heard of the simplestyle apprach, that is one alternative of doing it, but an editor comes later for this.
If this is what helps me get this merged we can first do geojson and then the polyline support.
Polylines are attractive given that valhalla & co output that natively...

@CommanderStorm CommanderStorm marked this pull request as draft May 14, 2026 16:52
@github-actions github-actions Bot removed the bless label May 14, 2026
@github-actions github-actions Bot removed the bless label May 14, 2026
@CommanderStorm CommanderStorm force-pushed the static-rendering-squashed branch 2 times, most recently from 3243bea to ce1894a Compare May 15, 2026 11:33
@github-actions github-actions Bot removed the bless label May 15, 2026
@CommanderStorm CommanderStorm force-pushed the static-rendering-squashed branch 7 times, most recently from d45545f to 6ad6337 Compare May 24, 2026 00:19
CommanderStorm added a commit that referenced this pull request May 26, 2026
…ng (#2830)

breaks out one of the items of
#2794

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants