CartoType: Einsatz mit Windows Forms
Mapping mit CartoType, Teil 2
Am Beginn des Weges zu unserer eigenen Big-Tech-freien Kartenanwendung steht der Download des CartoType Windows SDK. Extrahieren Sie die ZIP-Datei danach in einen bequem zugänglichen Ort im Dateisystem, um das Projekt CartoTypeDemo\CartoTypeDemo.sln zu öffnen.
In manchen Fällen erweisen sich die Sicherheit-Konfigurationsrichtlinien von Windows an dieser Stelle als hinderlich. Falls der in Visual Studio enthaltene Dialog nicht ausreicht, bietet es sich an, die integrierte Entwicklungsumgebung zu schließen und ein Windows-Terminal zu öffnen. Führen Sie dort in der PowerShell folgenden Befehl im Projekt-Stammverzeichnis aus:
PS C:\...\NMGCartoType\Online - Classic .net\CartoTypeDemo> Unblock-File *
Daraufhin startet die aus dem Leitartikel zu CartoType [1] bekannte Demo-Anwendung. Sie kann danach mehr oder weniger beliebige CTM1-Dateien laden, auf den Bildschirm bringen und als Grundlage für die Navigation heranziehen.
Willkommen in Windows Forms
Ältere Versionen von Visual Studio kommen mit Windows-Forms-Anwendungen im Allgemeinen besser zurecht. Dennoch werden wir in den folgenden Schritten mit Version 2022 der IDE arbeiten. Als erster Schritt bietet sich wie immer das Öffnen der Datei MainForm.cs an – da das Beispiel allerdings eine klassische MDI-Applikation realisiert, ist dort eigentlich nur die für das Laden von Dateien notwendige Methode interessant. Ihr Korpus präsentiert sich folgendermaßen:
private void openMenuItem_Click(object sender, EventArgs e){
...
if (open_file_dialog.ShowDialog() == DialogResult.OK){
var map_form = new MapForm();
map_form.MdiParent = this;
...
if (map_form.Construct(open_file_dialog.FileName, ""))
map_form.Show();
Die Aufrufe nach dem Schema map_form.Show() haben dabei die Aufgabe, eine neue Instanz von MapForm ins Leben zu rufen. In ihr beziehungsweise in der für die Realisierung zuständigen Dateiengruppe findet sich die eigentliche Intelligenz der Applikation.
Die Codierungsrichtlinien im Hause CartoType sind insofern eigenwillig, als die Member-Deklarationen in der Regel am Ende der Datei stehen. Wir scrollen deshalb in der Datei zunächst nach unten, wo die folgende Member-Deklaration von Relevanz ist:
private CartoType.Framework m_framework; private CartoType.MapRenderer m_map_renderer; private CartoType.Point m_map_drag_anchor = new();
Am wichtigsten ist dabei die Klasse CartoType.Framework. Sie gilt als Dreh und- Angelpunkt für alle in CartoType vorgesehenen beziehungsweise auszuführenden Operationen und ist auch für das Vorhalten des Zustands und für die Steuerung verantwortlich.
Von Interesse sind außerdem die Klassen vom Typ CartoType.MapRenderer, die einen Darstellungspunkt abbilden, der auf die Framework-Klasse bezogen ist.
Das Laden der Kartendaten erfolgt dann in der Methode Construct, die im Fall der vorliegenden Beispiel-Applikation wie nachfolgend gezeigt aufgebaut ist. Angemerkt sei, dass so gut wie alle in diesem Artikel abgedruckten Snippets im Interesse der Kompaktheit stark gekürzt sind – der vollständige Quellcode findet sich, wie weiter oben beschrieben, im frei herunterladbaren SDK.
var param = new CartoType.FrameworkParam(); param.MapFileName = aFileName; param.Key = aKey; param.StyleSheetFileName = style; param.FontFileName = font; param.ViewWidth = ClientSize.Width; param.ViewHeight = ClientSize.Height; m_framework = new CartoType.Framework(param)!;
Von besonderem Interesse ist die Nutzung eines Objekts vom Typ CartoType.FrameworkParam. Es dient als Einlaufstelle für alle Parameter, die danach zur Belebung der Framework-Klasseninstanzen herangezogen werden.
Die Erzeugung des Renderers erfolgt weiter unten gemäß nachfolgendem Schema:
private void MapForm_Load(object sender, EventArgs e) {
...
m_map_renderer = new CartoType.MapRenderer(m_framework, Handle);
Die Verwendung des Eventhandlers MapForm_Load ist erforderlich, weil die Initialisierung der Klasse auf das Formular zugreifen muss. In Handle findet sich dabei ein Verweis auf den GDI-Frame, in dem die Rendering-Instanz die durchzuführenden Zeichen-Operationen erledigen wird.
Interessant ist in diesem Zusammenhang, dass die Verarbeitung von Mausereignissen in der Framework-Klasse erfolgt. Mausbewegungen werden beispielsweise gemäß folgendem Schema in Richtung des Systems weitergereicht:
private void MapForm_MouseMove(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
m_framework.Pan(m_map_drag_anchor.X, m_map_drag_anchor.Y, CartoType.CoordType.Map,
e.Location.X, e.Location.Y, CartoType.CoordType.Display);
m_map_drag_anchor = new CartoType.Point(e.Location.X, e.Location.Y);
m_framework.ConvertPoint(m_map_drag_anchor, CartoType.CoordType.Display, CartoType.CoordType.Map);
}
}
Der vom Entwickler anzulegende Code beschränkt sich dabei darauf, Bewegungs-Distanzen und ähnliche Werte zu berechnen. Der Gutteil der Arbeit, beispielsweise im Bereich der Aktualisierung des Displays, wird vom Framework transparent und automatisch erledigt.
Ähnliches findet sich in der Methode MapForm_MouseWheel, die den Zoom-Faktor der angezeigten Karte in Reaktion auf das Drehen des Mausrades erhöht oder reduziert:
private void MapForm_MouseWheel(object sender, MouseEventArgs e) {
var pos = e.Location;
int zoom_count = e.Delta / 120;
double zoom = Math.Sqrt(2);
if (zoom_count == 0)
zoom_count = e.Delta >= 0 ? 1 : -1;
zoom = Math.Pow(zoom, zoom_count);
if (ClientRectangle.Contains(pos))
m_framework.ZoomAt(zoom, pos.X, pos.Y, CartoType.CoordType.Screen);
...
}
Zu guter Letzt sei noch darauf hingewiesen, dass das Neu-Zeichnen der vom Renderer vorgehaltenen Ansicht naturgemäß nur als Reaktion auf ein Betriebssystemereignis erfolgen kann. Die nach dem folgenden Schema erfolgende Weiterreichung des Events fällt dabei in die alleinige Verantwortung des Entwicklers:
private void MapForm_Paint(object sender, PaintEventArgs e)
{
m_map_renderer.Draw();
}Value-Added-Features des Frameworks
Die als Dreh- und Angelpunkt dienende Klasse stellt Nutzern des Frameworks verschiedene Hilfsfunktionen zur Verfügung. Interessant ist beispielsweise das Exportieren in eine PNG-Datei.
Der Gutteil des zur Verfügung gestellten Codes ist dabei für die Auswertung der SaveFileDialog-Klasse verantwortlich, die von der Beispielapplikation zur Ermittlung des Dateinamens herangezogen wird:
private void saveImageAsPNGFileMenuItem_Click(object sender, EventArgs e) {
var save_file_dialog = new SaveFileDialog();
...
if (save_file_dialog.ShowDialog() == DialogResult.OK) {
var error = m_framework.WriteMapImage(save_file_dialog.FileName, CartoType.FileType.Png, true);
...
}
}
Das eigentliche Herausschreiben der PNG-Datei erfolgt dann lediglich durch Aufrufen der Methode WriteMapImage.
Fazit
Das Windows-Forms- beziehungsweise .NET-API von CartoType ist ein durchaus durchdachtes Programmiersystem. Wer ein wenig Zeit in das Verstehen der Architektur investiert, kann danach schnell und mit geringem Aufwand ansehnliche und Big-Tech-freie Kartenapplikationen zaubern.
[1] Tam Hanna, Mapping, Marke Eigenbau, dotnetpro 4/2026, Seite 122 ff.