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

Assign shield icons based on ref length #1086

Merged
merged 3 commits into from
Jun 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions shieldlib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ You should create one definition entry for each network. The entry key must matc
"top": 3,
"bottom": 3
},
"spriteBlank": "name_of_image_1",
"spriteBlank": ["name_of_image_2", "name_of_image_3", "name_of_image_4"],
"noref": {
"spriteBlank": "name_of_image_2"
"spriteBlank": "name_of_image_noref"
}
"shapeBlank": {
"drawFunc": "pentagon",
Expand Down Expand Up @@ -168,7 +168,8 @@ You should create one definition entry for each network. The entry key must matc
- **`textColor`**: determines what color to draw the `ref` on the shield.
- **`textHaloColor`**: color to draw a knockout halo around the `ref` text.
- **`padding`**: padding around the `ref`, which allows you to squeeze the text into a smaller space within the shield.
- **`spriteBlank`**: specify the name of an image in the sprite sheet to use as the shield background. This can either be a single string or an array of strings if there are multiple options for different width. If it's an array of strings, they must be ordered from narrowest to widest, and the engine will choose the narrowest shield graphic that fits the text at a reasonable size.
- **`spriteBlank`**: specify the name of an image in the sprite sheet to use as the shield background. This can either be a single string or an array of strings if there are multiple options for different width.
- If `spriteBlank` is an array of strings, they must be ordered from narrowest to widest, and the filenames must be suffixed with a consecutive range of integers, representing the optimal number of characters to display in each icon.
- **`noref`**: specify alternate attributes to apply in the event that no `ref` is supplied. This allows you to use one graphic for numbered routes and a separate unitary graphic for non-numbered routes within the same network. Supports **`spriteBlank`**, **`colorLighten`**, and **`colorDarken`**.
- **`shapeBlank`**: specify that a shield should be drawn as a common shape (rectangle, ellipse, pentagon, etc), with colors and dimensions as specified. See the [drawn shield shapes](#defining-drawn-shield-shapes) section for available drawing options.
- **`banners`**: specify that one or more short text strings (up to 4 characters) should be drawn above the shield. This is specified as an array, and text will be drawn in order from top to bottom. Below is an example of bannered shields with up to three banners:
Expand Down Expand Up @@ -234,7 +235,7 @@ This effect can be achieved by overriding the text and sprite color in the route

```json
"US:GA": {
"spriteBlank": ["shield_us_ga_narrow", "shield_us_ga_wide"],
"spriteBlank": ["shield_us_ga_2", "shield_us_ga_3"],
"textColor": "black",
"overrideByRef": {
"520": {
Expand Down
30 changes: 17 additions & 13 deletions shieldlib/src/shield.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import {
import { TextPlacement } from "./shield_text";
import { StyleImage } from "maplibre-gl";

const narrowCharacters = /[1IJijl .-]/g;

function compoundShieldSize(
r: ShieldRenderingContext,
dimension: Dimension,
Expand Down Expand Up @@ -51,20 +53,22 @@ function getRasterShieldBlank(
let bounds: Dimension;

if (Array.isArray(shieldDef.spriteBlank)) {
for (var i = 0; i < shieldDef.spriteBlank.length; i++) {
shieldArtwork = r.spriteRepo.getSprite(shieldDef.spriteBlank[i]);
// Certain narrow characters count as two-thirds of a character
let narrowCharacterCount = (routeDef.ref.match(narrowCharacters) ?? [])
.length;
let refLength = Math.ceil(routeDef.ref.length - narrowCharacterCount / 3);

// Choose icon based on optimal character length at end of filename
claysmalley marked this conversation as resolved.
Show resolved Hide resolved
let finalIndex = shieldDef.spriteBlank.length - 1;
let optimalCharacters = shieldDef.spriteBlank.map((blank) =>
parseInt(blank.split("_").reverse()[0])
);
let spriteIndex =
refLength > optimalCharacters[finalIndex]
? finalIndex
: Math.max(0, optimalCharacters.indexOf(refLength));

bounds = compoundShieldSize(r, shieldArtwork.data, bannerCount);
textPlacement = ShieldText.layoutShieldTextFromDef(
r,
routeDef.ref,
shieldDef,
bounds
);
if (textPlacement.fontPx > r.px(Gfx.fontSizeThreshold)) {
break;
}
}
shieldArtwork = r.spriteRepo.getSprite(shieldDef.spriteBlank[spriteIndex]);
} else {
shieldArtwork = r.spriteRepo.getSprite(shieldDef.spriteBlank);
}
Expand Down
Loading