GNavigia und sein Hin­ter­grund

COM-Ser­ver für Hin­ter­grund­bil­der be­reit­stel­len

His­to­risch, nur noch sinn­ge­mäß an­wend­bar!

GNavigia wird mit ei­nem Back­ground Pro­vi­der aus­ge­lie­fert, der in der La­ge ist, Bil­der mit ei­ner ex­trem ho­hen Auf­lö­sung be­reit­zu­stel­len. Wie das geht, be­schrei­ben wir auf ei­ner ei­ge­nen Sei­te, die sich ganz all­ge­mein mit der Nut­zung von WMS-Ser­vices be­fasst. Ins­be­son­de­re die Lan­des­ver­mes­sungs­äm­ter ver­fü­gen über Da­ten mit ei­nem 40 Zen­ti­me­ter Ras­ter in Far­be. Die­se Bil­der wur­den mit Steu­er­gel­dern er­mit­telt, sol­len aber den Bür­gern nicht (oder nur sehr ein­ge­schränkt) kos­ten­frei zur Ver­fü­gung ste­hen; ein Skan­dal, der be­reits von der EU-Kom­mis­si­on auf­ge­grif­fen wur­de und (hof­fent­lich bald) zu frei­en Geo­da­ten füh­ren wird!

Neu ist auch ei­ne An­bin­dung an das OSM-Pro­jekt (Open Street Map), ei­ne ers­te Be­schrei­bung ist er­stellt. Der Ge­dan­ke, den Geo­ser­ver ein­zu­set­zen, ist we­gen Lauf­zeit­pro­ble­men und auf­grund fast per­fek­ter Dar­stel­lung durch den Map­nik-Ren­de­rer auf­ge­ge­ben wor­den. Ein Bei­spiel aus der Re­gi­on Bonn ist als Er­geb­nis der Be­mü­hun­gen üb­rig ge­blie­ben, ei­ne PNG-Datei (True-Co­lor-GIF).

Wer sich mit dem Com­po­nent Ob­ject Mo­del, kurz «COM», aus­kennt, kann ei­nen zur Lauf­zeit nach­lad­ba­ren Ser­ver zur Be­reit­stel­lung von Hin­ter­grund­bil­dern für GNavigia schrei­ben. (Ei­ne Bei­spie­lan­bin­dung, von der .NET-Pro­gram­me ab­lei­ten kön­nen, liegt GNavigia un­ter dem Na­men GpsBack­groun­dPro­vi­der bei. (Al­ler­dings un­ter­stützt die­se zur­zeit nicht die asyn­chro­ne Schnitt­stel­le!)

Das Ob­jekt muss da­zu le­dig­lich ei­nen pa­ra­me­ter­lo­sen Kon­struk­tor so­wie die IGpsBack­groun­dPro­vi­derA­sync Schnitt­stel­le im­ple­men­tie­ren und die Re­gis­trie­rung als COM Ob­jekt um den Un­ter­schlüs­sel

{353899B1-448D-4d3b-876D-5F6BBF59BBB6}

er­wei­tern. Wer von der o.a. Klas­se ab­lei­tet, er­hält die Er­zeu­gung des Un­ter­schlüs­sels «frei Haus».

Die­ser Schlüs­sel, bei dem es sich um ei­ne GUID han­delt, wird aus­ge­wer­tet, wenn im Me­nü «An­sicht/Ser­ver der Hin­ter­grund­bil­der aus­wäh­len» ge­wählt wird. Es er­scheint dann ei­ne Lis­te al­ler Ob­jek­te zur Aus­wahl, die die­sen Un­ter­schlüs­sel an­ge­ben. Die Auf­lis­tung dau­ert ei­nen klei­nen Mo­ment. Der dar­ge­stell­te Na­me ist der des COM Ob­jekts, auch als «Prog-ID» be­zeich­net; in­tern wird die GUID ge­spei­chert.

Zur Re­gis­trie­rung des COM-Objekts muss der Ent­wick­ler über Ad­mi­nis­tra­tor­rech­te ver­fü­gen, da die Ein­trä­ge un­ter HKEY_CLASSES_ROOT er­fol­gen. Wer nicht als Ad­mi­nis­tra­tor ent­wi­ckeln will, der kann, zu­min­dest un­ter .NET, zur ers­ten Re­gis­trie­rung die Ent­wick­lungs­um­ge­bung als Ad­min­stra­tor star­ten und da­nach die Op­ti­on «Für COM re­gis­trie­ren» aus­schal­ten. Die Re­gis­trie­rung ist nur ein­mal nö­tig, um das COM Ob­jekt und das In­ter­face be­kannt zu ma­chen, da­nach er­folgt die Ent­wick­lung als ganz nor­ma­le Klas­sen­bi­blio­thek.

An­for­de­rung der Hin­ter­grund­bil­der

Die Funk­tio­na­li­tät, die die Schnitt­stel­le IGpsBack­groun­dPro­vi­derA­sync sicht­bar zur Ver­fü­gung stellt, be­steht aus ei­ner ein­zi­gen Metho­de, die stets ein ge­nau pas­sen­des Bild zur Ver­fü­gung stellt. Da­zu wer­den die Aus­ma­ße der Dar­stel­lungs­flä­che und des Ge­biets in UTM Koor­di­na­ten an den Ser­ver über­ge­ben, so­wie Da­ten, die der Ser­ver zu­sam­men mit dem Bild an GNavigia zu­rück­sen­den muss. Op­ti­mie­run­gen, wie das an­spruchs­vol­le Ca­ching von Bil­dern oder das Aus­schnei­den von Ge­bie­ten aus über­großen Bild­da­ten muss der Ser­ver be­han­deln. Nur beim Ver­schie­ben des Mit­tel­punkts so­wie beim Ver­grö­ßern und Ver­klei­nern von Bil­dern be­rech­net GNavigia vor­über­ge­hend ein Teil­bild.

Ini­tia­li­sie­rung des Menüs

Das Me­nü wird ei­ner­seits aus der An­fra­ge an die Schnitt­stel­len­me­tho­den in­itia­li­siert, an­de­rer­seits aber auch aus In­for­ma­tio­nen aus der Re­gis­trie­rungs­da­ten­bank. Wenn ei­ne gül­ti­ge Hin­ter­grund­ver­bin­dung aus­ge­wählt wird, er­scheint sie beim Wie­der­auf­star­ten der Appli­ka­ti­on, in­itia­li­siert aus der Re­gis­trie­rung.

Ei­gen­schaf­ten der Hin­ter­grund­bil­der

Hin­ter­grund­bil­der ha­ben die Ei­gen­schaft, dass sie für das ebe­ne Koor­di­na­ten­sys­tem, hier im­mer ein UTM Koor­di­na­ten­sys­tem, an­ge­for­dert wer­den. Er­for­dert der Dienst, der letzt­lich die Bil­der zur Ver­fü­gung stellt, die An­ga­ben der Eck­punk­te im Welt­ko­or­di­na­ten­sys­tem in geo­gra­fi­schen Koor­di­na­ten, so muss der Back­ground­pro­vi­der die­se be­rech­nen. Die­sem und dem The­ma der trans­pa­ren­ten Dar­stel­lung der Bil­der ist ei­ne Fol­ge­sei­te ge­wid­met.

Die IGpsBack­groun­dPro­vi­derA­sync Schnitt­stel­le

Die frü­her ein­mal do­ku­men­tier­te IGpsBack­groun­dPro­vi­der Schnitt­stel­le ist auf­ge­ge­ben wor­den. Statt des­sen wur­de ein asyn­chro­nes Ver­fah­ren im­ple­men­tiert, das es er­laubt, das Hin­ter­grund­bild zu emp­fan­gen statt ab­zu­ho­len. Da­für muss der zu­rück­ru­fen­de COM-Ser­ver ei­nen dy­na­mi­schen Auf­ruf an GNavigia im­ple­men­tie­ren. Der Na­me der Metho­de, die auf­zu­ru­fen ist, wird von GNavigia an den Ser­ver über­ge­ben. Der Ser­ver hat fol­gen­de Schnitt­stel­le zu im­ple­men­tie­ren:

[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 for­ma­le Pa­ra­me­ter «hwnd» ist vom Typ int (Int32) und re­prä­sen­tiert je­nes Win­dowhand­le, das die Ur­ge­stei­ne der Fens­ter­pro­gram­mie­rung seit den aus­ge­hen­den 80er Jah­ren des vo­ri­gen Jahr­hun­derts ken­nen soll­ten. Es hat nur ei­nen ein­zi­gen Wert, näm­lich ei­nen even­tu­ell not­wen­di­gen Dia­log an ei­nem Fens­ter fest­ma­chen zu kön­nen und so die Ket­te der durch mo­da­le Dia­lo­ge ab­ge­schal­te­ten Fens­ter nicht zu un­ter­bre­chen. Der .NET-Pro­gram­mie­rer kann das Hand­le di­rekt nut­zen, in­dem er ein Ob­jekt der fens­ter­na­hen Klas­se «Tem­po­ra­ryOw­ner» an­legt, die selbst wie­der­um kei­ne an­de­re Auf­ga­be hat, als die Schnitt­stel­le «IWin32Win­dow» be­reit­zu­stel­len, die ih­rer­seits kei­ne an­de­re Auf­ga­be hat, als das Win­dowhand­le ver­füg­bar zu ma­chen. COM Pro­gram­mie­rer ha­ben es an die­ser (und nur an die­ser) Stel­le ein­fa­cher: Sie cas­ten den Wert auf HWND und kön­nen ihn als pa­rent win­dow hand­le be­nut­zen.

Bil­der­mitt­lung star­ten

Die Bil­der­mitt­lung wird asyn­chron ge­st­ar­tet. Da­durch kann man im bes­ten Fall mit GNavigia wei­ter­ar­bei­ten, als sei kei­ne An­for­de­rung er­folgt. Nur ein kur­zes Zu­cken des Maus­zei­gers deu­tet an, dass ein Bild an­ge­for­dert wur­de.

HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(mapUrl);
if (httpWebRequest == null) 
  ...; // handle error.
httpWebRequest.BeginGetResponse(new MyAsyncCallback(AsyncResult), httpWebRequest);

Die in­ter­ne Call­back­me­tho­de des Hin­ter­grund­bild­be­reit­stel­lers, die das En­de der Bil­der­über­tra­gung er­lebt, führt den Rück­ruf durch. Sie ist zur­zeit wie folgt im­ple­men­tiert:

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-Doku­ment

Das XML-Doku­ment in der Schnitt­stel­len­be­schrei­bung ent­hält zur­zeit fol­gen­de Kno­ten:

  • Re­sult, 0: Er­folg, an­de­re Wer­te si­gna­li­sie­ren ei­nen Feh­ler. Die­se Feh­ler­num­mer wird in an­ge­zeigt.
  • Co­py­right (mehr­zei­lig, ge­trennt im Bei­spiel­pro­gramm durch senk­rech­ten Strich), hier er­folgt ei­ne au­to­ma­ti­sche Er­set­zung von (C), (R) und (at) durch die ent­spre­chen­den Son­der­zei­chen.
<?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>

Ex­cep­ti­ons

Wer­fen Sie nie­mals ei­ne Aus­nah­me (Ex­cep­ti­on), die die Schnitt­stel­le ver­lässt. Sor­gen Sie da­für, dass die Aus­nah­men in­ner­halb der Schnitt­stel­le blei­ben und be­nut­zen Sie das vor­ge­se­he­ne XML-Doku­ment als In­di­ka­tor für Feh­ler beim Auf­ruf der Call­back­me­tho­de. Set­zen Sie im Feh­ler­fall den Pa­ra­me­ter Ima­ge auf null. Wer .NET be­nutzt kann das Feh­ler­do­ku­ment als Se­ria­li­sie­rung er­stel­len oder Schnitt­stel­len aus der Um­ge­bung der

Utilities.dll 

auf­ru­fen. Da­mit ist das Ver­pa­cken der In­for­ma­tion be­son­ders ein­fach.

Trans­pa­renz

Ich ha­be mich da­zu ent­schie­den, die Trans­pa­renz der Hin­ter­grund­bil­der im Pro­gramm GNavigia selbst zu ver­wal­ten. Das ent­schei­den­de Ar­gu­ment ist wohl, dass der ak­tu­ell zu­sam­men­ge­stell­te In­halt und die Lay­out­wahl den Grad der er­for­der­li­chen Trans­pa­renz be­stimmt. Die Trans­pa­renz wird zu­sam­men mit den An­ga­ben zum Back­ground­pro­vi­der in der GTD-Datei ge­spei­chert. Die Trans­pa­renz kann Wer­te zwi­schen 0 und 90% an­neh­men. Bei 100% wür­de es zu der Si­tua­ti­on kom­men, dass der Hin­ter­grund Aus­füh­rungs­zeit er­for­dert, oh­ne dass dem ei­ne er­kenn­ba­re Ge­gen­leis­tung ent­sprä­che. Wer 100% Trans­pa­renz be­nö­tigt, kann die An­zei­ge des Hin­ter­grund­bil­des aus­schal­ten. Ab­wei­chend vom Stan­dard kann im Dia­log durch Ankli­cken der je­wei­li­gen Zahl ein (mehr oder min­der) runder Trans­pa­renz­wert ein­ge­stellt wer­den.

Koor­di­na­ten und Trans­for­ma­tio­nen

Koor­di­na­ten im Sin­ne von GNavigia sind ebe­ne UTM Koor­di­na­ten und el­lip­soi­di­sche Län­ge und Brei­te. Da ein El­lip­so­id kei­ne Tor­se ist, kann auch kei­ne ver­zer­rungs­freie Ab­bil­dung in die Ebe­ne er­fol­gen. Da­mit hat der Kar­to­graf die Wahl zwi­schen ei­ner «län­gen­treu­en» und ei­ner «win­kel­treu­en» Ab­bil­dung. Wäh­rend die län­gen­treue Ab­bil­dung die Ver­gleich­bar­keit der Flä­chen er­hält, bie­tet die win­kel­treue dem Geo­dä­ten und dem Na­vi­ga­tor ge­rin­ge­re Kor­rek­tu­ren bei Win­kel­mes­sun­gen. Da sich Win­kel frü­her bei glei­chem Auf­wand we­sent­lich ge­nau­er mes­sen lie­ßen als Stre­cken, ha­ben sich die «im Dif­fe­ren­ti­el­len win­kel­treu­en» Gauß­schen Koor­di­na­ten durch­ge­setzt, und zwar in Deutsch­land auf dem von Bes­sel be­rech­ne­ten El­lip­so­id un­ter dem Na­men «Gauß-Krü­ger», im Os­ten auf dem El­lip­so­id von Kras­sow­ski und im Wes­ten auf dem von Hay­ford.

Die be­reits in ers­ter Nä­he­rung von Mer­ca­tor ge­fun­de­nen und zur Kar­tie­rung be­nutz­ten For­meln wer­den da­her auch als Mer­ca­tor-Koor­di­na­ten be­zeich­net, in der um ei­nen Maß­stabs­fak­tor von 0,9996 ver­kürz­ten Form schließ­lich als trans­ver­sa­le Mer­ka­tor Pro­jek­ti­on. In der Fas­sung mit welt­weit gül­ti­gen Ken­nun­gen wer­den dar­aus «uni­ver­sa­le, trans­ver­sa­le Mer­ka­tor­ko­or­di­an­ten» (UTM). Der Maß­stabs­fak­tor ist not­wen­dig, um die Ver­zer­run­gen am Rand der 6° brei­ten Strei­fen in er­träg­li­chem Rah­men zu hal­ten. Der dar­aus re­sul­tie­ren­de Feh­ler von 4cm/100m am Mit­tel­me­ri­di­an ist aber so groß, dass er für geo­dä­ti­sche Zwe­cke kor­ri­giert wer­den muss. Da UTM Koor­di­na­ten so­wohl im Nord- als auch im Ost­wert von Län­ge und Brei­te zu­gleich ab­hän­gen, muss ein Koor­di­na­ten­paar stets ge­mein­sam um­ge­rech­net wer­den.

Pro­gram­mie­rer, die .NET ein­set­zen, kön­nen die Biblio­the­ken

Mathematics.dll  
Utilities.dll 

be­nut­zen, um Koor­di­na­ten zwi­schen den Sys­te­men um­zu­rech­nen. Für al­le an­de­ren ist ei­ne Schnitt­stel­le in Pla­nung, die die Trans­for­ma­tio­nen rea­li­siert. Als Bei­spiel die­ne hier der auf die Be­rech­nun­gen be­schränk­te Teil des Kon­struk­tors der Dia­log­klas­se GpsDl­gTrans­for­ma­tio­nen. Der Dia­log ist aus dem Me­nü her­aus zu er­rei­chen un­ter Ex­tras/Trans­for­ma­ti­ons­ge­nau­ig­keit, x und y sind geo­dä­tisch ver­tauscht. Die Ge­nau­ig­keit der Trans­for­ma­ti­on liegt bei et­wa 2 Zen­ti­mer am Rand des Koor­di­na­ten­sys­tems, al­so 3° vom Mit­tel- oder Be­zugs­me­ri­di­an ent­fernt:

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));
}

Welt­weit ein­deu­ti­ge Po­si­ti­ons­an­ga­ben

Ei­ne Po­si­ti­ons­an­ga­be im UTM For­mat hat die Form «32 P 666666 UTM 1234567». Da­rin be­deu­ten:

  • 32: UTM-Zone 32 mit dem Be­zugs­me­ri­di­an 9 Grad Ost. Die Zo­nen zäh­len von 1 (180°-174° West) in öst­li­cher Rich­tung bis 60 (174°-180° Grad E).
  • P: Teil­be­reich zwi­schen 8° Nord und 16° Nord. Den Teil­be­rei­chen von je 8° Grad sind Buch­sta­ben zu­ge­ord­net, be­gin­nend mit C (80° bis 72° Süd) bis X (72°-80° Nord) un­ter Aus­las­sung der Buch­sta­ben I und O. C bis M lie­gen auf der Süd­halb­ku­gel, N bis X nörd­lich des Äqua­tors.
  • 666666: Ost- oder Rechts-Wert des Or­tes. Er liegt in die­sem Bei­spiel rund 166666 Me­ter öst­lich des Be­zugs­me­ri­dians, der den Ost-Wert 500000 Me­ter auf­weist, um ne­ga­ti­ve Koor­di­na­ten zu ver­mei­den. Für Or­te öst­lich oder west­lich da­von wird der Ab­stand zum Off­set (mul­ti­pli­ziert mit dem Ska­len­fak­tor 0,9996) hin­zu ad­diert oder ab­ge­zo­gen (West: mi­nus, Ost: plus).
  • 1234567: Nord- oder Hoch­wert des Or­tes in Me­ter. Für ei­nen Ort auf dem Be­zugs­me­ri­di­an (und nörd­lich des Äqua­tors) ist das die Län­ge des Me­ri­dian­bo­gens ge­mes­sen vom Äqua­tor, mul­ti­pli­ziert mit dem Ska­len­fak­tor von 0,9996.