GNavigia und sein Hintergrund
COM-Server für Hintergrundbilder bereitstellen
Historisch, nur noch sinngemäß anwendbar!
GNavigia wird mit einem Background Provider ausgeliefert, der in der Lage ist, Bilder mit einer extrem hohen Auflösung bereitzustellen. Wie das geht, beschreiben wir auf einer eigenen Seite, die sich ganz allgemein mit der Nutzung von WMS-Services befasst. Insbesondere die Landesvermessungsämter verfügen über Daten mit einem 40 Zentimeter Raster in Farbe. Diese Bilder wurden mit Steuergeldern ermittelt, sollen aber den Bürgern nicht (oder nur sehr eingeschränkt) kostenfrei zur Verfügung stehen; ein Skandal, der bereits von der EU-Kommission aufgegriffen wurde und (hoffentlich bald) zu freien Geodaten führen wird!
Neu ist auch eine Anbindung an das OSM-Projekt (Open Street Map), eine erste Beschreibung ist erstellt. Der Gedanke, den Geoserver einzusetzen, ist wegen Laufzeitproblemen und aufgrund fast perfekter Darstellung durch den Mapnik-Renderer aufgegeben worden. Ein Beispiel aus der Region Bonn ist als Ergebnis der Bemühungen übrig geblieben, eine PNG-Datei (True-Color-GIF).
Wer sich mit dem Component Object Model, kurz «COM», auskennt, kann einen zur Laufzeit nachladbaren Server zur Bereitstellung von Hintergrundbildern für GNavigia schreiben. (Eine Beispielanbindung, von der .NET-Programme ableiten können, liegt GNavigia unter dem Namen GpsBackgroundProvider bei. (Allerdings unterstützt diese zurzeit nicht die asynchrone Schnittstelle!)
Das Objekt muss dazu lediglich einen parameterlosen Konstruktor sowie die IGpsBackgroundProviderAsync Schnittstelle implementieren und die Registrierung als COM Objekt um den Unterschlüssel
{353899B1-448D-4d3b-876D-5F6BBF59BBB6}
erweitern. Wer von der o.a. Klasse ableitet, erhält die Erzeugung des Unterschlüssels «frei Haus».
Dieser Schlüssel, bei dem es sich um eine GUID handelt, wird ausgewertet, wenn im Menü «Ansicht/Server der Hintergrundbilder auswählen» gewählt wird. Es erscheint dann eine Liste aller Objekte zur Auswahl, die diesen Unterschlüssel angeben. Die Auflistung dauert einen kleinen Moment. Der dargestellte Name ist der des COM Objekts, auch als «Prog-ID» bezeichnet; intern wird die GUID gespeichert.
Zur Registrierung des COM-Objekts muss der Entwickler über Administratorrechte verfügen, da die Einträge unter HKEY_CLASSES_ROOT erfolgen. Wer nicht als Administrator entwickeln will, der kann, zumindest unter .NET, zur ersten Registrierung die Entwicklungsumgebung als Adminstrator starten und danach die Option «Für COM registrieren» ausschalten. Die Registrierung ist nur einmal nötig, um das COM Objekt und das Interface bekannt zu machen, danach erfolgt die Entwicklung als ganz normale Klassenbibliothek.
Anforderung der Hintergrundbilder
Die Funktionalität, die die Schnittstelle IGpsBackgroundProviderAsync sichtbar zur Verfügung stellt, besteht aus einer einzigen Methode, die stets ein genau passendes Bild zur Verfügung stellt. Dazu werden die Ausmaße der Darstellungsfläche und des Gebiets in UTM Koordinaten an den Server übergeben, sowie Daten, die der Server zusammen mit dem Bild an GNavigia zurücksenden muss. Optimierungen, wie das anspruchsvolle Caching von Bildern oder das Ausschneiden von Gebieten aus übergroßen Bilddaten muss der Server behandeln. Nur beim Verschieben des Mittelpunkts sowie beim Vergrößern und Verkleinern von Bildern berechnet GNavigia vorübergehend ein Teilbild.
Initialisierung des Menüs
Das Menü wird einerseits aus der Anfrage an die Schnittstellenmethoden initialisiert, andererseits aber auch aus Informationen aus der Registrierungsdatenbank. Wenn eine gültige Hintergrundverbindung ausgewählt wird, erscheint sie beim Wiederaufstarten der Applikation, initialisiert aus der Registrierung.
Eigenschaften der Hintergrundbilder
Hintergrundbilder haben die Eigenschaft, dass sie für das ebene Koordinatensystem, hier immer ein UTM Koordinatensystem, angefordert werden. Erfordert der Dienst, der letztlich die Bilder zur Verfügung stellt, die Angaben der Eckpunkte im Weltkoordinatensystem in geografischen Koordinaten, so muss der Backgroundprovider diese berechnen. Diesem und dem Thema der transparenten Darstellung der Bilder ist eine Folgeseite gewidmet.
Die IGpsBackgroundProviderAsync Schnittstelle
Die früher einmal dokumentierte IGpsBackgroundProvider Schnittstelle ist aufgegeben worden. Statt dessen wurde ein asynchrones Verfahren implementiert, das es erlaubt, das Hintergrundbild zu empfangen statt abzuholen. Dafür muss der zurückrufende COM-Server einen dynamischen Aufruf an GNavigia implementieren. Der Name der Methode, die aufzurufen ist, wird von GNavigia an den Server übergeben. Der Server hat folgende Schnittstelle zu implementieren:
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] [Guid("4435F26B-7F4D-4732-AB01-0D2F98B24CE5")] interface IGpsBackgroundProviderAsync { // <summary> // // </summary> // <param name="xmlDocument"></param> // <param name="userdata"></param> // <param name="hwnd"></param> // <param name="canvasWidth"></param> // <param name="canvasHeight"></param> // <param name="utmNorth"></param> // <param name="utmWest"></param> // <param name="utmSouth"></param> // <param name="utmEast"></param> void GpsGetBitmapExactAsync(string xmlDocument, object userdata, int hwnd, int canvasWidth, int canvasHeight, double utmNorth, double utmWest, double utmSouth, double utmEast); // <summary> // If GNavigia uses a callback named TheImageCallback, than the server has to // invoke a call to "TheImageCallback" with the parameters userdata from the // GpsGetBitmapExactAsync call, the .NET Bitmap Image and a string that offers // an XML document. This string may be NULL if no data is to be transfered. If // the user specifies a copyright string, it is transfered in this string. // The callback: // void TheImageCallback(object userdata, Image image, string xmlDocument) // </summary> // <param name="theCaller"></param> // <param name="GpsImageCallbackName"></param> void GpsSetImageCallback(object theCaller, string GpsImageCallbackName); }
Der formale Parameter «hwnd» ist vom Typ int (Int32) und repräsentiert jenes Windowhandle, das die Urgesteine der Fensterprogrammierung seit den ausgehenden 80er Jahren des vorigen Jahrhunderts kennen sollten. Es hat nur einen einzigen Wert, nämlich einen eventuell notwendigen Dialog an einem Fenster festmachen zu können und so die Kette der durch modale Dialoge abgeschalteten Fenster nicht zu unterbrechen. Der .NET-Programmierer kann das Handle direkt nutzen, indem er ein Objekt der fensternahen Klasse «TemporaryOwner» anlegt, die selbst wiederum keine andere Aufgabe hat, als die Schnittstelle «IWin32Window» bereitzustellen, die ihrerseits keine andere Aufgabe hat, als das Windowhandle verfügbar zu machen. COM Programmierer haben es an dieser (und nur an dieser) Stelle einfacher: Sie casten den Wert auf HWND und können ihn als parent window handle benutzen.
Bildermittlung starten
Die Bildermittlung wird asynchron gestartet. Dadurch kann man im besten Fall mit GNavigia weiterarbeiten, als sei keine Anforderung erfolgt. Nur ein kurzes Zucken des Mauszeigers deutet an, dass ein Bild angefordert wurde.
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(mapUrl); if (httpWebRequest == null) ...; // handle error. httpWebRequest.BeginGetResponse(new MyAsyncCallback(AsyncResult), httpWebRequest);
Die interne Callbackmethode des Hintergrundbildbereitstellers, die das Ende der Bilderübertragung erlebt, führt den Rückruf durch. Sie ist zurzeit wie folgt implementiert:
private void MyAsyncResult(IAsyncResult asyncResultI) { if (asyncResultI.IsCompleted) { HttpWebRequest request = (HttpWebRequest)asyncResultI.AsyncState; WebResponse webResponse = request.EndGetResponse(asyncResultI); if (webResponse != null) try { Stream stream = webResponse.GetResponseStream(); if (stream != null) { // ACHTUNG: Nicht auf den Stream zugreifen! // Diese Zeile crashed mit der Meldung: // "Dieser Stream unterstützt keine Suchvorgänge." // long streamLength = stream.Length; Image image = new Bitmap(stream); if (image != null) m_theCaller.GetType().InvokeMember(m_callersCallbackName, BindingFlags.InvokeMethod, null, m_theCaller, new object[] { image, m_theCallersData, m_xmlDocument }); stream.Close(); // Resourcenschonend, unser Beitrag zum Kyoto-Protokoll! } } catch (Exception ex) { ...; } } }
Das XML-Dokument
Das XML-Dokument in der Schnittstellenbeschreibung enthält zurzeit folgende Knoten:
- Result, 0: Erfolg, andere Werte signalisieren einen Fehler. Diese Fehlernummer wird in angezeigt.
- Copyright (mehrzeilig, getrennt im Beispielprogramm durch senkrechten Strich), hier erfolgt eine automatische Ersetzung von (C), (R) und (at) durch die entsprechenden Sonderzeichen.
<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?> <GNavigiaBackgroundProvider> <Result>0</Result> <Error> <line>Alles Bestens!</line> <line>Result ist 0, das erfreut das Herz!</line> </Error> <Copyright> <line>Bilddaten: (C) LVermGeo Rheinland-Pfalz</line> <line>Freie topographische Rasterdatendienste</line> <line>poststelle(at)lvermgeo.rlp.de</line> </Copyright> </GNavigiaBackgroundProvider>
Exceptions
Werfen Sie niemals eine Ausnahme (Exception), die die Schnittstelle verlässt. Sorgen Sie dafür, dass die Ausnahmen innerhalb der Schnittstelle bleiben und benutzen Sie das vorgesehene XML-Dokument als Indikator für Fehler beim Aufruf der Callbackmethode. Setzen Sie im Fehlerfall den Parameter Image auf null. Wer .NET benutzt kann das Fehlerdokument als Serialisierung erstellen oder Schnittstellen aus der Umgebung der
Utilities.dll
aufrufen. Damit ist das Verpacken der Information besonders einfach.
Transparenz
Ich habe mich dazu entschieden, die Transparenz der Hintergrundbilder im Programm GNavigia selbst zu verwalten. Das entscheidende Argument ist wohl, dass der aktuell zusammengestellte Inhalt und die Layoutwahl den Grad der erforderlichen Transparenz bestimmt. Die Transparenz wird zusammen mit den Angaben zum Backgroundprovider in der GTD-Datei gespeichert. Die Transparenz kann Werte zwischen 0 und 90% annehmen. Bei 100% würde es zu der Situation kommen, dass der Hintergrund Ausführungszeit erfordert, ohne dass dem eine erkennbare Gegenleistung entspräche. Wer 100% Transparenz benötigt, kann die Anzeige des Hintergrundbildes ausschalten. Abweichend vom Standard kann im Dialog durch Anklicken der jeweiligen Zahl ein (mehr oder minder) runder Transparenzwert eingestellt werden.
Koordinaten und Transformationen
Koordinaten im Sinne von GNavigia sind ebene UTM Koordinaten und ellipsoidische Länge und Breite. Da ein Ellipsoid keine Torse ist, kann auch keine verzerrungsfreie Abbildung in die Ebene erfolgen. Damit hat der Kartograf die Wahl zwischen einer «längentreuen» und einer «winkeltreuen» Abbildung. Während die längentreue Abbildung die Vergleichbarkeit der Flächen erhält, bietet die winkeltreue dem Geodäten und dem Navigator geringere Korrekturen bei Winkelmessungen. Da sich Winkel früher bei gleichem Aufwand wesentlich genauer messen ließen als Strecken, haben sich die «im Differentiellen winkeltreuen» Gaußschen Koordinaten durchgesetzt, und zwar in Deutschland auf dem von Bessel berechneten Ellipsoid unter dem Namen «Gauß-Krüger», im Osten auf dem Ellipsoid von Krassowski und im Westen auf dem von Hayford.
Die bereits in erster Näherung von Mercator gefundenen und zur Kartierung benutzten Formeln werden daher auch als Mercator-Koordinaten bezeichnet, in der um einen Maßstabsfaktor von 0,9996 verkürzten Form schließlich als transversale Merkator Projektion. In der Fassung mit weltweit gültigen Kennungen werden daraus «universale, transversale Merkatorkoordianten» (UTM). Der Maßstabsfaktor ist notwendig, um die Verzerrungen am Rand der 6° breiten Streifen in erträglichem Rahmen zu halten. Der daraus resultierende Fehler von 4cm/100m am Mittelmeridian ist aber so groß, dass er für geodätische Zwecke korrigiert werden muss. Da UTM Koordinaten sowohl im Nord- als auch im Ostwert von Länge und Breite zugleich abhängen, muss ein Koordinatenpaar stets gemeinsam umgerechnet werden.
Programmierer, die .NET einsetzen, können die Bibliotheken
Mathematics.dll Utilities.dll
benutzen, um Koordinaten zwischen den Systemen umzurechnen. Für alle anderen ist eine Schnittstelle in Planung, die die Transformationen realisiert. Als Beispiel diene hier der auf die Berechnungen beschränkte Teil des Konstruktors der Dialogklasse GpsDlgTransformationen. Der Dialog ist aus dem Menü heraus zu erreichen unter Extras/Transformationsgenauigkeit, x und y sind geodätisch vertauscht. Die Genauigkeit der Transformation liegt bei etwa 2 Zentimer am Rand des Koordinatensystems, also 3° vom Mittel- oder Bezugsmeridian entfernt:
public GpsDlgTransformationen() { string l = "6.144386528"; string b = "50.799758619"; UTM utm = new UTM("WGS 84"); utm.SetNotifyCaller = new UTM.NotifyCaller(Notification); // Rechne Länge/Breite in UTM um. Eingabe in Dezimalaltgrad. listBox.Items.Add("== UTM Transformation =="); listBox.Items.Add("l = " + l + " b = " + b); utm.Transform(l, b); string fmt = "{0} - Y: {1,10} X: {2,10}"; double y = 298778.194; // Sollwert Hoch/Nord double x = 5631445.022; // Sollwert Rechts/Ost listBox.Items.Add(String.Format(fmt, "Soll", Str.ToString(y, 3), Str.ToString(x, 3))); listBox.Items.Add(String.Format(fmt, " Ist", Str.ToString(utm.Y, 3), Str.ToString(utm.X, 3))); listBox.Items.Add(""); // Rechne UTM in Länge/Breite um. listBox.Items.Add("== UTM Umkehrtransformation =="); listBox.Items.Add("Y = " + Str.ToString(y, 3) + " X = " + Str.ToString(x, 3)); double l1, b1; utm.Transform(32, y, x, out l1, out b1); listBox.Items.Add(String.Format("L = {0,11} B = {1,11}", Str.ToString(l1, 9), Str.ToString(b1, 9))); // Meridanbogenlänge ausgeben. Mittelmeridiane liegen bei 3°, 9°, 15° etc. listBox.Items.Add(""); listBox.Items.Add("== Nebenrechnung: Meridianbogenlänge =="); l = "9.0"; listBox.Items.Add("l = " + l + " b = " + b); utm.Transform(l, b); listBox.Items.Add("Bogenlänge: " + Str.ToString(utm.X, 3)); }
Weltweit eindeutige Positionsangaben
Eine Positionsangabe im UTM Format hat die Form «32 P 666666 UTM 1234567». Darin bedeuten:
- 32: UTM-Zone 32 mit dem Bezugsmeridian 9 Grad Ost. Die Zonen zählen von 1 (180°-174° West) in östlicher Richtung bis 60 (174°-180° Grad E).
- P: Teilbereich zwischen 8° Nord und 16° Nord. Den Teilbereichen von je 8° Grad sind Buchstaben zugeordnet, beginnend mit C (80° bis 72° Süd) bis X (72°-80° Nord) unter Auslassung der Buchstaben I und O. C bis M liegen auf der Südhalbkugel, N bis X nördlich des Äquators.
- 666666: Ost- oder Rechts-Wert des Ortes. Er liegt in diesem Beispiel rund 166666 Meter östlich des Bezugsmeridians, der den Ost-Wert 500000 Meter aufweist, um negative Koordinaten zu vermeiden. Für Orte östlich oder westlich davon wird der Abstand zum Offset (multipliziert mit dem Skalenfaktor 0,9996) hinzu addiert oder abgezogen (West: minus, Ost: plus).
- 1234567: Nord- oder Hochwert des Ortes in Meter. Für einen Ort auf dem Bezugsmeridian (und nördlich des Äquators) ist das die Länge des Meridianbogens gemessen vom Äquator, multipliziert mit dem Skalenfaktor von 0,9996.