For the prototype map for my Let’s Map Bainbridge project, I wanted to have at least one unusual font on the map. Perhaps a font that looks like the sort of fonts traditionally used for features on the coastline, that would look good on the screen. I found Eben Sorkin’s Merriweather font family, which has an Open Font License, so I can use it for free. I chose just Merriweather Light Italic to get us going.
Making the font available to MapLibre
This is at the top level of lmb-prototype.json:
"glyphs":"/font/{fontstack}/{range}.pbf",
The glyphs value is a template for the URL MapLibre will use to fetch font data, where {fontstack} is (at its simplest) the name of the font (Merriweather Light Italic), and {range} is, for example, 512-767.pbf.
So the URL might be: /font/Merriweather Light Italic/512-767.pbf.
What those *.pbf files are
These files aren’t the TTF font file I downloaded from Google Fonts, but data representing what each character (or “glyph”) looks like. The data is in an extremely compact, binary format: a Protobuf Binary Format (PBF) file.
To generate the glyph files from the TTF file, I used the MapLibre project’s Font Maker service. There’s a command-line tool available, too, if you really must.
The number is a code point, which is a number refering to a particular entry in a table. In this case, what’s in the table is glyphs for each letter in the alphabet (and so much more): ‘B’, ‘l’, ‘a’ and so on. A code point, written in hexadecimal, can be prefixed with U+ to make it clear that it’s a code point.
A few code points for you, generated by this bit of Python code:
import unicodedatadef show_unicode_chars(text):for char in text: code_point =f"U+{ord(char):04X}" name = unicodedata.name(char)print(f"- {code_point} ({name}): {char}")show_unicode_chars("Blakely: 67 🎉")
- U+0042 (LATIN CAPITAL LETTER B): B
- U+006C (LATIN SMALL LETTER L): l
- U+0061 (LATIN SMALL LETTER A): a
- U+006B (LATIN SMALL LETTER K): k
- U+0065 (LATIN SMALL LETTER E): e
- U+006C (LATIN SMALL LETTER L): l
- U+0079 (LATIN SMALL LETTER Y): y
- U+003A (COLON): :
- U+0020 (SPACE):
- U+0036 (DIGIT SIX): 6
- U+0037 (DIGIT SEVEN): 7
- U+0020 (SPACE):
- U+1F389 (PARTY POPPER): 🎉
Each file on the server, such as 512-767.pbf, contains the symbols for 256 code points. The file 0-255.pbf file has all we need for basic Latin characters. But 🎉, which is U+1F389, and 127881 in decimal. So that would need a different file. Which one? Let’s do the math:
def filename_for_codepoint(point): start = point //256*256 end = start +255returnf"{start}-{end}.pbf"filename_for_codepoint(127881)
'127744-127999.pbf'
Using the font in a layer
So now we can use the font in a layer:
"text-font":["Merriweather Light Italic"],
Creating a visual hierarchy for the labels
Cartographers want a nice visual hierarchy for labels, but using different font families altogether, or just varying the size or color of a particular font, or using bold or italics. With just this one variant of the font (Light Italic), I’m relying on font size to do all the work.
There are four levels in the hierarchy for labels around the coastline:
Blakely Harbor, Eagle Harbor
Bill Point, Hawley Cove
Hornbeak Spit, Midden Point
The Mill Pond at Blakely Harbor
The font size is fixed for each of these levels (19pt, 15pt, 13pt, 10pt). Eventually the font size for each level will need to change depending on the zoom level (and each level should only be visible at a particular range of zoom levels).
That 30pt default is there just in case I forget to allocate a level to each of these labels: it’ll make the mistake really obvious, so I can fail fast. These labels are my own dataset, not coming from OpenStreetMap.