Glyphen, Distancefields und der ganze Rest
Geschrieben von Wolf am .
Ich hab eine Karte gebaut mit einer 3D-Darstellung des Siebengebirges, wegen meiner vereinfachten Berechnung der Terrain-Tiles noch mit Lagefehlern und Stufen.
Die Namen der Gipfel kleben auf der Oberfläche und sind spätestens bei einer Drehung unlesbar. Also braucht es Texte, die oberhalb der Gipfel schweben und die Drehung nicht mitmachen.
Ich hab im Karten-Framework gesucht, aber zuerst nichts gefunden, bis ich auf die Symbole
gestoßen bin, mit der man in der Karte ein POI markieren kann, das Hotel oder die Bushaltestelle:
Symbole kann man mit einem Label versehen, und dann auch ohne Icon anzeigen.
Die schwebenden Texte sind jetzt also Symbole mit Text, aber ohne Symbol.
Die Dokumentation zu den Symbolen zeigt Parameter wie Textfarbe, Textgröße, Textschatten usw., all das, was man im HTML per CSS ändern kann, darunter auch eine Liste von Fontnamen. Mit dem Hinweis, dass wenn man Textlabels benutzt, man die Quelle für die Glyphen in einem anderen Parameter an einer anderen Stelle definieren muss. Nachvollziehbar.
Ich schau' an der Stelle nach und finde:
https://api.maptiler.com/fonts/{fontstack}/{range}.pbf?key=get_your_own_OmIi9ZULNHzrESv6T2vL
Die GL benutzt also nicht wie der Browser selbst die eingebauten oder die Systemfonts, sondern will die von einem Server laden. Beim Server soll ich mich registrieren und bekomme dann einen Key, der für eine begrenzte Zahl von Zugriffen auf die Fonts gültig ist.
Wenn ich nun etwas überhaupt nicht mag, dann sind das die Einbindung externer Dienste im Allgemeinen, von denen ich mich sinnfrei abhängig mache, und API-Keys im Besonderen, die jederzeit gesperrt werden können, was meine ganze Arbeit unbrauchbar macht.
Ich meine, warum zum Teufel soll ich dämliche Fonts mit all dem Bohei von einem Fremdserver laden, wenn ich noch hunderte GB Speicherplatz auf meinem Server verfügbar habe?
Ein Blick in die Browser-Konsole zeigt: ja, der Browser ruft wirklich diese URL auf, und zwar mit eingestanztem
https://api.maptiler.com/fonts/Roboto%20Regular,Noto%20Sans%20Regular/0-255.pbf?key=get_your_own_OmIi9ZULNHzrESv6T2vL
Besser nicht darüber nachdenken. Also flugs ein Unterverzeichnis
angelegt, einen Font aus font/usr/share/fonts hineinkopiert,
den Parameter glyph ausgetauscht gegen:
fonts/{fontstack}/{range}.pbf
und per .htaccess alle Abrufe auf meinen Font umgebogen.
Ergebnis: Fehlermeldung. Das GL mag keine TTF-Fonts (alle mögen die!), sondern will SDF-Fonts in
der PBF-Codierung. Das
hätte mich warnen sollen.
.pbf
Doch was zum Teufel ist SDF?
Duckduckgo findet: Louisville Airport SDF
, Silver Diamine Fluoride
,
Syrian Democratic Forces
, einen Hersteller von Landmaschinen und das Dateiformat
SQL Server Compact Edition
. WTF?
Etwas Herumspielen mit den Suchbegriffen führt mich im Bereich Mathematik zu Signed distance function
.
Wie bei vielen Mathematik-Artikeln ist auch dieser so geschrieben, dass man den Artikel nur dann versteht, wenn man den Inhalt bereits kannte, also den Artikel nicht braucht. Aber offensichtlich der Treffer.
Kurzgefasst: wenn ich eine zwei- oder mehrdimensionale Figur in einem Raster definiere, kann ich je Pixel oder Voxel nicht nur speichern, ob drinnen oder draußen, sondern auch die Entfernung zur nächsten Grenze zwischen drinnen und draußen. Anschaulich: jedes Pixel erzeugt einen Kreis, der vollständig entsprechend dem Pixel drinnen oder draußen ist. Die überlappenden Kreise definieren die Figur von innen und von außen. Das wird sinnvoll, wenn ich die Figur vergrößern will: während die durch Pixel definierte Figur schnell pixelig würde, bleibt die durch die Kreise definierte Figur halbwegs glatt.
Soweit die Theorie.
Jetzt zur Anwendung, Spezifikation der Dateiformate und verfügbare Algorithmen. Gleich vorab: es gibt keine Spezifikationen, oder wenn, denn nur innerhalb irgendwelcher Firmen. Die Dateiformate werden durch die Programme definiert, die dieses Format erzeugen. Das scheint sich in letzter Zeit zum Standard zu entwickeln.
Da fange ich lieber klein an. Ich war darauf gestoßen, dass es SDF-Icons gibt. Ich lade ein Beispiel runter und stelle fest: Es sieht aus wie ein normales Icon, bis darauf, dass die eigentliche Form im Alpha-Kanal definiert ist.
Eine weitere Suche findet ein (grausliches) JavaScript. Das weiteres JavaScript einbindet. Nun hasse ich es, wenn ein Programm beim Compilieren weitere Programme nachzieht; ich will wissen, was auf meinem Rechner installiert ist, und noch wichtiger: Ich will ein Projekt gezippt weitergeben können, ohne dass beim Anwender Zeug nachgeladen wird.
Besonders schlimm finde ich es, wenn Zeug nachgeladen wird, das ich auch in zehn Zeilen in mein Programm hätte einfügen können.
Und nachgeladen wird: ein Modul zum Parsen von Zahlen aus einem Text. Dabei ist das seit Urzeiten in JS eingebaut:
const text = '....';
const wert = parseInt(text); // o. parseFloat()
if( isNaN(wert) ){ throw 'Fehler!'; }
( isNaN() = is not a number )
Ich fräse mich durch den Code und notiere auf Papier den Ablauf. Daraus ergibt sich die Spezifikation des SDF-Icons: ein PNG, dessen RGB- oder Gray-Kanäle irrelevant sind (wobei weiß empfehlenswert ist), und in dessen Alpha-Kanal gespeichert wird: 127: auf der Grenze bis 255 tief in der Figur und 127 … 0 auf der Grenze bis weit außerhalb der Figur.
Nur: starten werde ich das Programm bei mir nicht, wegen all des Nachinstallierens.
Zum Glück stoße ich auf einen Link zu einem Java-Projekt (hab mich daran angelehnt.
). Das
ist eine einzelne Datei, die lade ich herunter, compiliere und starte:
läuft.
(Ich werde den Algorithmus aber dennoch selber implementieren, weil ich niemandem, besonders keinen Normalmenschen vor einer Windows-Maschine, das Nachinstallieren der Java-RT zumuten will.)
Da ich jetzt weiß, wie SDF-Icons funktionieren, wie die encodiert werden und dass ich die selber erzeugen kann, zurück zu den Fonts.
Da finde ich erst recht keine Spezifikation, diesmal auch kein Java-Programm,
sondern nur ein GitHub-Projekt, das freie Fonts gesammelt hat (die hab ich selber im
liegen), mit einem JavaScript-Programm, das diese nach /usr/lib/fontssdf.pbf
konvertieren soll.
Ein verdächtig kleines Programm. Und wie befürchtet: Es konvertiert keinen Font, sondern importiert ein Modul, das Fonts konvertiert, das weitere Module importiert, die weitere … :-/
An dieser Stelle habe ich genug und breche ab. Denn ich will eigentlich nur und immer noch die Gipfel des Siebengebirges beschriften.
Also Fallback auf etwas, was ich sehr ungern tu: gescripteter Missbrauch eines öffentlich
zugänglichen Dienstes: Mit ein paar Zeilen Shellskript und dem Test-API-Key lade ich die 256
Teil-Fonts von api.maptiler.org runter und lege die auf meinen Server.
Done.