GNavigia und sein Hintergrund
COM-Server für Hintergrundbilder bereitstellen

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.
|