Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add property for viewport-aligned images in formatted expressions #188

Closed
ZeLonewolf opened this issue Jun 29, 2021 · 13 comments
Closed

Add property for viewport-aligned images in formatted expressions #188

ZeLonewolf opened this issue Jun 29, 2021 · 13 comments

Comments

@ZeLonewolf
Copy link
Contributor

ZeLonewolf commented Jun 29, 2021

Motivation

Rendering of highway shields is a high priority in the US mapping community. This issue is discussed at length in gravitystorm/openstreetmap-carto#508, where the importance of pictoral shields in North American maps is discussed. One of the challenges in rendering shields is the rendering of concurrent routes, in which a road shares multiple multiple routes. There are a few general strategies for this, as discussed in ZeLonewolf/openstreetmap-americana#6.

Of particular interest is the ability to render concurrent routes such that the highway shields are arranged along the run of a route, centered on the rendered road. In order to achieve this effect, a vector tile server must generate tiles which have all of the desired route concurrencies rendered. This issue is described in openmaptiles/openmaptiles#1128, where code is in development to expose route concurrencies.

Once route concurrencies are exposed, the routes and refs can be packed into custom image ids and concatenated into a formatted expression containing the images. A JavaScript handler is then hooked up in order to extract the ref and network from the custom image ids in order to render route numbers on shield blanks. This approach is also described in ZeLonewolf/openstreetmap-americana#6.

The result of this combination of exposed route concurrencies in OpenMapTiles, image ID packing, and JavaScript shield blank drawing, looks like this (for a particularly long concurrency in Indiana, USA):

image

Note that these shields are rotated along the baseline of the road (as you would expect for a text label). For a road that runs roughly east/west, this is a reasonable rendering. However, for north/south roads, the shields are drawn sideways, which is not desirable:

image

Ideally, there should be an option to suppress line-aligned rotation of images in formatted expressions, so that they can be viewport-aligned for highway shield use cases.

Design Alternatives

In the specific case of highway shields, one alternative is to render a grid of shields instead of arranging the shields along the route of the highway. However, this takes up more space and is less aesthetically pleasing. This is the approach used by Kevin Kenny's mapnik-based osm-shields project:
image

The approach taken by openstreetmap-carto is to combine the refs into a text label box, however, this approach is unsatisfying to American users, which expect to see shields rendered on highways:
image

Yet another option is to combine multiple refs onto a single shield backdrop, as is done in this paper map. However, this approach does not scale to cases where multiple route networks are combined in a concurrency:
image

Design

Ideally, a style author should be able to set a property that sets viewport-alignment rotation to images in formatted expressions, while maintaining their position along the LineString.

@ZeLonewolf
Copy link
Contributor Author

Here is an example from Apple Maps of a grouped highway concurrency rendering with upright shields (the effect we're trying to achieve):
image

@wipfli
Copy link
Member

wipfli commented Jun 30, 2021

Thanks for the great example pictures. It is clear what the goal is. So route concurrencies are like different names for the same route?

@wipfli
Copy link
Member

wipfli commented Jun 30, 2021

I guess what would be helpful for the implementation in maplibre gl js is a minimal example of code together with some data. Maybe you could provide a style file plus an .mbtiles file which shows the shields rotated along the line, i.e. which has the wrong behavior. Then we can elaborate from there how the shield rotation can be fixed.

@ZeLonewolf
Copy link
Contributor Author

A route concurrency is when multiple routes share a single road. When long distance routes cross in similar directions, it is common for them to briefly share the same roadway. Also, concurrencies are common in the case of geographic choke points, such as river crossings, where multiple routes might cross a single bridge.

Below are some examples of route concurrencies from US paper maps:

A test case for this functionality is provided in two files:

  1. tiles.mbtiles.tar.gz: a tiles.mbtiles containing vector tile highway data for Indiana, USA
  2. highway-shield-test-style.tar.gz: an executable example style demonstrating shield rendering

The style uses javascript callbacks in order to dynamically generate the shield numbers

The style can be run using "make run" (requires python3) and will run on port 1776. A suggested test area on the map is: http://localhost:1776/#10/39.8086/-86.109

The relevant code for generating the shield ids in formatted text is in layer/road.js, specifically this block:

    "text-field": [
      "format",
      [
        "case",
        ["!=", ["get", "ref_1"], null],
        [
          "image",
          ["concat", "shield_", ["get", "network_1"], "=", ["get", "ref_1"]],
        ],
        ["literal", ""],
      ],
      " ",
      [
        "case",
        ["!=", ["get", "ref_2"], null],
        [
          "image",
          ["concat", "shield_", ["get", "network_2"], "=", ["get", "ref_2"]],
        ],
        ["literal", ""],
      ],
      " ",
      [
        "case",
        ["!=", ["get", "ref_3"], null],
        [
          "image",
          ["concat", "shield_", ["get", "network_3"], "=", ["get", "ref_3"]],
        ],
        ["literal", ""],
      ],
      " ",
      [
        "case",
        ["!=", ["get", "ref_4"], null],
        [
          "image",
          ["concat", "shield_", ["get", "network_4"], "=", ["get", "ref_4"]],
        ],
        ["literal", ""],
      ],
      " ",
      [
        "case",
        ["!=", ["get", "ref_5"], null],
        [
          "image",
          ["concat", "shield_", ["get", "network_5"], "=", ["get", "ref_5"]],
        ],
        ["literal", ""],
      ],
      " ",
      [
        "case",
        ["!=", ["get", "ref_6"], null],
        [
          "image",
          ["concat", "shield_", ["get", "network_5"], "=", ["get", "ref_6"]],
        ],
        ["literal", ""],
      ],
    ],

Note that the schema used in the .mbtiles is not stock OpenMapTiles; it's a development version of openmaptiles/openmaptiles#1140, which creates a new transportation_ref layer dedicated to route rendering.

@wipfli
Copy link
Member

wipfli commented Jul 8, 2021

Thanks Brian for the files. I get this in the console:

➜  highway-shield-concurrency-style make run
python3 -m http.server 1776
Serving HTTP on 0.0.0.0 port 1776 (http://0.0.0.0:1776/) ...
***.***.***.*** - - [08/Jul/2021 20:38:44] "GET /layer/tunnel.js HTTP/1.1" 200 -
***.***.***.*** - - [08/Jul/2021 20:38:44] "GET /layer/oneway.js HTTP/1.1" 200 -
***.***.***.*** - - [08/Jul/2021 20:38:44] code 404, message File not found
***.***.***.*** - - [08/Jul/2021 20:38:44] "GET /layer/highway_exit.js HTTP/1.1" 404 -
***.***.***.*** - - [08/Jul/2021 20:38:44] code 404, message File not found
***.***.***.*** - - [08/Jul/2021 20:38:44] "GET /layer/highway_exit.js HTTP/1.1" 404 -

and the browser does not show anything. Can you maybe package this code in a GitHub repo such that we can work together on it?

@ZeLonewolf
Copy link
Contributor Author

I realize I've left out a key piece in my writeup - the style is pointing to a local openmaptiles server, so it's expecting the tarred up .mbtiles to be hosted on a local tileserver running on port 8080. In any case, the 404s indicate that I goofed up in the attachment, so let me work on the suggested GH repo.

@wipfli
Copy link
Member

wipfli commented Jul 8, 2021

Great, thanks!

@ZeLonewolf
Copy link
Contributor Author

I've built up a github sample project that has the running sample:

https://github.com/ZeLonewolf/maplibre-shield-rotation-sample

TomPohys pushed a commit to openmaptiles/openmaptiles that referenced this issue Jul 26, 2021
Fixes #1128

This PR adds 6 new columns to the `transportation_name` column, named `route_1` through `route_6`.  These columns contain route information for a section of roadway.  The value is stored in the form **network=ref**.  For example, Interstate 95 in the United States would be `US:I=95`.  Thus, each `route_N` value contains enough information to render a highway shield.  Since a section of road can be a part of more than one route, the `route_2`, `route_3`, etc, will contain the 2nd, 3rd, etc., concurrent routes.

The technical approach was to extend the change in #1135, which added ordered concurrency indexes to the `osm_route_member` table by joining up to the the first six entries to the `osm_transportation_name_network` table.  In addition, that PR provided a ranking system for concurrent highways, ordering first by `network_type` (for example, `us-interstate`, `us-state`, etc), then alphabetically by network name, and then by ref in ascending order.  This ordering of concurrent route memberships is now exposed in this PR in the sequential `route_N` values, meaning that rendered concurrent highway shields will be reasonably sorted.

The renderings below were generated using this branch of OpenMapTiles, as well as a separate branch of openstreetmap-americana:
https://github.com/ZeLonewolf/openstreetmap-americana/tree/openmaptilers-new-features-test

The rendering approach is to use the [formatted expressions](https://maplibre.org/maplibre-gl-js-docs/style-spec/expressions/) feature in mapLibre to insert images into a string of text.  Blank shields are added to the sprite sheet for all possible route networks.  Next, a [styleimagemissing](https://maplibre.org/maplibre-gl-js-docs/api/map/) callback is registered.  As each shield ID is requested, the callback retrieves the sprite shield blank associated with the route's network, draw the `ref` text on the shield, and insert the complete shield back into the map.

Of note, this approach currently results in shields which are rotated about the road rather than being viewport aligned.  This issue is currently documented as maplibre/maplibre-gl-js#188.  A separate repository (https://github.com/ZeLonewolf/maplibre-shield-rotation-sample) has been created as a test case to fix this rotation issue.

Adding route concurrency information to OpenMapTiles would be a major step forward in achieving comprehensive highway shield renderings in a vector map!

**Renderings**:

![routes_1](https://user-images.githubusercontent.com/3254090/126054350-fa7475a7-1b60-4989-bbc2-107678e6c73b.png)
![routes_2](https://user-images.githubusercontent.com/3254090/126054351-fe73bc70-d75f-4ab5-8365-0ee3c3d3eab0.png)
![routes_3](https://user-images.githubusercontent.com/3254090/126054353-a1e74c8f-df21-423c-a300-b7f1a7c9231c.png)
![routes_4](https://user-images.githubusercontent.com/3254090/126054355-6b5dcc83-c611-42b3-bb67-d4f26d789744.png)
@github-actions
Copy link
Contributor

This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.

@github-actions github-actions bot added the Stale label Oct 14, 2021
@ZeLonewolf
Copy link
Contributor Author

This issue remains a blocker for American-style map rendering, as discussed in ZeLonewolf/openstreetmap-americana#6.

@wipfli
Copy link
Member

wipfli commented Dec 16, 2021

#716

@JannikGM
Copy link
Contributor

JannikGM commented Apr 1, 2022

Should have been closed by #716, or is there anything left to do?

@HarelM
Copy link
Member

HarelM commented Apr 1, 2022

Yup. Resolved by #716

@HarelM HarelM closed this as completed Apr 1, 2022
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

No branches or pull requests

4 participants