Session V-MAP

Integration MapKit

Rolf Riethmüller
Wizards & Builders GmbH


Einleitung

MapKit! bietet dem Entwickler die Möglichkeit, geographische Analysefunktionen und Landkarten in eine Anwendung zu integrieren. MapKit! ist eine 32-bit-ActivX-Komponente, die auf der Grundlage des Microsoft Standards für Object Linking and Embedding (OLE 2.0) entwickelt wurde und ist damit auch für den Einsatz in Visual FoxPro Applikationen geeignet. Die Einsatzgebiete von MapKit! reichen von der Regionalstatistik, Markt- und Standortanalyse, den Wahl- und Umweltstatistiken über sozio-demographische Untersuchungen bis hin zu Studien zum Ausbreitungsverhalten  von Krankheiten.

Architektur von MapKit!

Eine Landkarte in MapKit! kann aus unterschiedlichen Layern bestehen. Layer kann man sich wie Folien auf einem Overhead-Projektor vorstellen. Es gibt Folien mit speziellem Inhalt, man kann beliebig viele Folien übereinanderlegen, die Reihenfolge der übereinandergelegten Folien ist beliebig, die Folien können bearbeitet werden. Nach dem gleichen Prinzip (Layertechnik) arbeitet auch MapKit!

Es werden drei Typen von Layern unterschieden: Punkte-, Linien- und Flächenlayer.

Punktelayer werden zur Darstellung von Standorten, markanten Punkten oder auch topologischen Informationen  verwendet. Linienlayer können zur Visualisierung von Straßen, Flüssen oder auch Fahrtrouten eingesetzt werden. Fläachenlayer bilden meisten hoheitliche Gebiete wie PLZ-Gebiete, Gemeinden, Kreise, Länder und Kontinente ab. Zu den Layern sind häufig auch demoskopische Daten verfügbar. So können Auswertungen  wie Umsatz pro Einwohner und ähnliche Analysen erstellt werden. Mit MapKit! kann so auch die Vertriebsplanung unterstützt werden.

Ein Layer besteht aus zwei Komponenten: der Folie und eine Tabelle. Die Tabelle enthält die Beschreibung der Folienelemente (Punkte, Linien oder Flächen). Diese Tabellen können um eigene Spalten erweitert werden und ermöglichen so die Zuordnung von (eigene) Daten zu den Folienelementen. Die Zusammenstellung – verwendete Layer, Konfiguration der Layer und die Daten - der erstellten Karte kann als Projekt gespeichert und für zukünftige Zwecke wiederverwendet werden.

Die Vollversion von MapKit! enthält alle von der Fa. Macon verfügbaren Karten. Zusätzlich können entsprechende Datenbestände erworben werden, um umfassende Analysen und Planungen durchführen zu können.

Integration MapKit! und Visual FoxPro

MapKit! ist ein normales ActiveX Control und kann in eine Visual FoxPro Form oder einen Container eingebettet werden.

Ein Problem von Visual FoxPro ist jedoch die Übergabe von Referenzen des Datentyps SHORT INTEGER an Methoden eines ActiveX Controls. MapKit! enthält etliche Funktionen, die eine solche Übergabe erfordern und so könnte das Tool normalerweise nicht verwendet werden. Die Fa. Macon hat MapKit! für die Integration mit VFP um neue Methoden erweitert. Diese erweiterten Funktionen beginnen mit dem Präfix VFP_ und sind die Anpassung der Funktionen mit Übergabe von POINTER auf SHORT INTEGER Werte. Dies bedeutet: wenn die  Dokumentation eine Übergabe von Pointern auf Short Integern vorsieht, ist der Name dieser Methode um das Präfix VFP_ zu erweitern.

Wie bei vielen ActiveX Controls ist bei MapKit! auch zu beachten, daß die Zählung der Elemente bei 0 beginnt. Die erste Spalte einer Layertabelle ist die Spalte 0.

Erstellen einer Karte

Die Erstellung einer Karte beginnt im Normalfall mit der Definition der zu verwenden Folien (Layer). Ein Layer basiert immer auf einer der mitgelieferten Vorlagen. In unserem Beispiel verwenden wir die Flächenlayer für die Bundesländer, den Regierungsbezirken und der Gemeinden des Landes Schleswig-Holstein. Der Layer mit den Gemeinden enthält auch schon Daten wie Einwohner, Anzahl der männlichen und weiblichen Einwohner pro Gemeinde.

Das Erstellen eines Layers ist im folgenden Beispiel beschrieben. Die Layer erhalten

local lcMapDir, lcLayerDir, loMap

 

** Verzeichnis für MapKit

cd \Projekte.60\MapKit

 

** Pfade festlegen

lcMapDir= fullpath( curdir())+ "Map\"

lcLayerDir= fullpath( curdir())+ "Layer\"

loMap= thisform.oleMap

 

** Haben wir vielleicht schon die Karten geladen

if loMap.GetLayerCount() > 0

   return

endif

 

** Layer erstellen und laden

loMap.CreateLayer( lcMapDir+ "DE_BL97.MKB", lcLayerDir+ "BLand.mkl")

loMap.CreateLayer( lcMapDir+ "DE_RB97.MKB", lcLayerDir+ "RBezirke.mkl")

loMap.LoadLayer( lcLayerDir+ "SH_GM97.MKL")

loMap.SetActiveLayer( 2)

 

** Für die Anzeige der Elemente den Text ausgeben (2. Spalte)

loMap.SetLabelColumn( 2, 1)

loMap.LabelOn( 2, -2)

  

** Das Verhalten der Maus setzen (Click-Modus)

loMap.SetClickMode( 1)

 

** Nach Fertigstellung einer Aktion unbedingt ein Refresh durchführen!!

loMap.Refresh()

Arbeiten mit den Layertabellen

Nachdem wir nun unsere erste Karte erstellt haben, schauen wir uns mal die Layertabelle für die Gemeinden an. Um die Struktur der Layertabelle kennenzulernen, erstellen wir zuerst eine VFP Tabelle, in der die Spaltendefinitionen gespeichert werden.

local loMap, lnLayer, lnCols, lnDataType, lnLen, lcTitel, i

loMap= thisform.OleMap

lnDataType= 0

lnLen= 0

lcTitel= chr(255)

 

** zuerst mal den Layer aktivieren

lnLayer= loMap.GetActiveLayer()

 

** den Cursor für den aktiven Layer erstellen

lcCursor= "_LayerDef"+ padl( lnLayer, 2, "0")

create cursor (lcCursor) ( iSpalte i, cName c(32), iDataType i, iLen i)

 

** zuerst mal die Tabelle auslesen

lnCols= loMap.GetColumnCount( lnLayer)

for i= 0 to lnCols- 1

   ** Methode, die Pointer auf Short Integer benötigt:

   loMap.VFP_GetColumnDefinition( lnLayer, i, @lnDataType, ;

                              @lnLen, @lcTitel)

   insert into (lcCursor) (iSpalte, cName, iDataType, iLen) ;

      values ( i, lcTitel, lnDataType, lnLen)

endfor

Als nächstes Lesen wir die Daten der Layertabelle aus und speichern diese ebenfalls in einer Visual FoxPro Tabelle ab. Im folgenden Beispiel lesen wir die ID, den Namen, die Einwohner und die weiblichen Einwohner aus der Layertabelle aus. Vorher fügen wir der Layertabelle jedoch eine Spalte hinzu, um unsere Auswertung - Anteil der weiblichen Einwohner - gleich in der Layertabelle zu speichern. Um zu demonstrieren wie die Objekte auf den Folien dargestellt (referenziert) werden, lesen wir auch den geodätischen Referenzpunkt der Fläche aus. Beachten Sie, daß viele der verwendeten Methoden als ersten Parameter die Nummer des zu verwendenden Layer erwarten.

local loMap, lnLayer, lnRows, i, lnRefX, lnRefY, liEinwohner, liAnteil, lcCursor

loMap= thisform.OleMap

 

** Den Datentyp für die geodätischen Koordinaten auf Fließkomma setzen

lnRefX= 0.0

lnRefY= 0.0

 

** zuerst mal den aktiven Layer festhalten

lnLayer= loMap.GetActiveLayer()

 

** Der Layertabelle ggf. eine Spalte hinzufügen,

** falls noch nicht vorhanden

if loMap.GetColumnCount( lnLayer) < 13

   loMap.DefineNewColumn( lnLayer, TLONG, 0, "Anteil")

   loMap.CreateNewColumns( lnLayer)

endif

 

** den Cursor für den aktiven Layer erstellen

lcCursor= "_LayerData"+ padl( lnLayer, 2, "0")

create cursor (lcCursor) (cID c(9), cName c(40), iEinwohner i ;

            , iAnteil i, nRefPointX n(12,8), nRefPointY n(12,8))

 

** Vorsicht, die Zählung beginnt bei 0 und geht bis Count-1

lnRows= loMap.GetObjectCount( lnLayer)

for i= 0 to lnRows- 1

   ** Die Zählung beginnt immer bei 0

 

** Die Basiswerte (ID, Text) auslesen

lcID= loMap.GetValue( lnLayer, i, 0)

lcText= loMap.GetValue( lnLayer, i, 1)

 

** Einwohner und Frauenanteil auslesen und berechnen

liEinwohner= loMap.GetValue( lnLayer, i, 2)

liAnteil= loMap.GetValue( lnLayer, i, 4)

liAnteil= iif( liEinwohner # 0, int( liAnteil/liEinwohner* 100), 0)

 

** die geodätischen Referenzkoordinaten auslesen

loMap.GetReferencePoint( lnLayer, i, @lnRefX, @lnRefY)

 

** und gleich den weibl. Bevölkerungsanteil im Layer speichern

loMap.SetValue( lnLayer, i, 12, liAnteil)

 

insert into (lcCursor) ( cID, cName, iEinwohner, iAnteil, ;

                        nRefPointX, nRefPointY ) ;

         values (lcID, lcText, liEinwohner, liAnteil, lnRefX, lnRefY)

endfor

Daten visualisieren

Die eigentliche Stärke von MapKit! ist die Visualisierung von Daten, die geographischen Koordinaten oder hoheitlichen Gebieten zugeordnet sind. Die graphische Darstellung von Daten in einer Karte sind – wie das nachfolgende Beispiel zeigt – ohne großen Aufwand möglich. Die Darstellung der Daten bezieht sich immer auf einen Layer. Da meistens mehrere Layer geladen sind, können wir unterschiedliche Auswertungen gleichzeitig in der Karte darstellen. Beispielsweise ist so die farbliche Darstellung von Umsätzen pro PLZ-Gebiet kombiniert mit einer Portfolio-Analyse möglich.

Eine häufige Anwendung ist die farbliche Unterscheidung von Gebieten aufgrund ihren zugeordneten Werten  wie beispielsweise der Umsatz in den PLZ-Gebieten. Wir färben im folgenden Beispiel die Fläche einer Gemeinde anhand ihrer Einwohnerzahl ein.

local loMap, lnLayer, lnRows, i, lnRefX, lnRefY, lcCursor

 

** Objektreferenz holen

loMap= thisform.OleMap

 

** zuerst mal den aktiven Layer festhalten

lnLayer= loMap.GetActiveLayer()

 

** Einfache Einfärbung der Karte nach Einwohner

** Zuerst mal die Anzahl der Klassen (Datenbereiche) festlegen

** und mitteilen, welche Spalte benutzt werden soll

** void SetSimpleShading (short nLayer; short nColumn; short nRanges)

loMap.SetSimpleShading( lnLayer, 2, 4)

 

** hier werden nun die einzelenen Wertebereiche definiert

** void SetRange( nLayer, nRange, LowValue, bIncludeLow, HighValue, bIncludeHigh);

loMap.SetRange( lnLayer, 0, 0, MK_FALSE, 999, MK_TRUE)

loMap.SetRange( lnLayer, 1, 1000, MK_TRUE, 4999, MK_TRUE)

loMap.SetRange( lnLayer, 2, 5000, MK_TRUE, 9999, MK_TRUE)

loMap.SetRange( lnLayer, 3, 10000, MK_TRUE, 10000000, MK_TRUE)

 

** und nun darf MapKit! die Anzahl der Elemente pro Bereich berechnen

loMap.CountRangeObjects( lnLayer)

 

** Und nun werden die Farben für die Bereiche festgelegt

** void SetRangeBrush (nLayer, nRange, iStyle, iHatch, uiForeColor, uiBackColor, nbOpaque)

loMap.SetRangeBrush( lnLayer, 0, BS_SOLID, 0, ;

                     RGB(255, 255, 255), 0, MK_TRUE)

loMap.SetRangeBrush( lnLayer, 1, BS_SOLID, 0, ;

                     RGB(255, 255, 0), 0, MK_TRUE)

loMap.SetRangeBrush( lnLayer, 2, BS_SOLID, 0, ;

                     RGB(255, 0, 0), 0, MK_TRUE)

loMap.SetRangeBrush( lnLayer, 3, BS_SOLID, 0, ;

                     RGB(0, 255, 0), 0, MK_TRUE)

 

** Aktualisierung des Bildschirms nicht vergessen

loMap.refresh()

 

** Sinnvoll ist auch die Festlegung des Legendentitels, hier

** wurde jedoch eine Formeigenschaft benutzt

** thisform.cLegendSubTitle= "Bevölkerung"

Erstellen einer Legende

Die Darstellung einer Legende ist immer auf den entsprechenden Layer bezogen. Dadurch ist es möglich, mehrere Legenden anzuzeigen. In diesem Fall müssen wir jedoch dafür sorgen, daß diese auch sichtbar sind. Die Positionierung einer Legende erfolgt in logischen Koordinaten. Zur Umrechnung von Pixel in logische Koordinaten, stell MapKit! entsprechende Funktionen bereit. Das folgende Beispiel erstellt die Legende für den Gemeindenlayer und Positioniert die Legende an den rechten unteren Rand der Beispielkarte.

local loMap, lnLayer, lnTop, lnLeft, lnRight, lnBottom

loMap= thisform.OleMap

 

** gleich mal den richtigen Datentyp bereitstellen

store 0 to lnTop, lnLeft, lnRight, lnBottom

 

** zuerst mal den aktiven Layer festhalten

lnLayer= loMap.GetActiveLayer()

 

** Pixelkoordinaten der Legende

lnLeft= 325

lnRight= 470

lnTop= 350

lnBottom= 470

 

** Pixelkoordinaten in logische Koordinaten umrechnen

** void DPtoLP (long *lpX, long *lpY)

loMap.DPtoLP( @lnLeft, @lnTop)

loMap.DPtoLP( @lnRight, @lnBottom)

 

** und nun die Position der Legende setzen

loMap.SetLegendRect( lnLayer, lnLeft, lnTop, lnRight, lnBottom)

 

** Das Layout der Legende festlegen

** SetLegendParam(short nLayer, short bDrawBorder, short ;

**                bRoundEdge, short bTransparent)

loMap.SetLegendParam( lnLayer, MK_TRUE, MK_FALSE, MK_FALSE)

 

** Zuerst den Legendentitel und anschließend den Untertitel setzen

** void SetLegendText(short nLayer, short WhichElement,

**                      LPCTSTR strNewText)

loMap.SetLegendText( lnLayer, 0, "Schleswig-Holstein")

loMap.SetLegendText( lnLayer, 1, thisform.cLegendSubTitle)

 

** und nun die Legende anzeigen

loMap.SetLegendVisible( lnLayer, MK_TRUE)

 

** Die obligatorische Bildschirmaktualisierung nicht vergessen

loMap.refresh()

MapKit! und die Maus

Bisher haben wir alles programmatisch gesteuert. MapKit! bietet jedoch auch die Möglichkeit, den Anwender Aktionen durchführen zu lassen. Wir können der Maus verschiedene Verhaltensweisen mitgeben und auf die Aktionen des Benutzers reagieren. Die Beispielmaske enthält die Umschaltung der Maus in die sechs möglichen Mausmodi. So könnten wir dem Benutzer die Positionierung der Legende ermöglichen oder er könnte den aktiven Kartenausschnitt verschieben. Der gewünschte Modus wird mit der Methode SetMouseClick( Modus) gesetzt und kann auch mit der Methode GetMouseClick() abgefragt werden.

Zusammenfassung

MapKit! ist interessantes Werkzeug zur Visualisierung von Daten, die an geographische Punkte oder hoheitliche Gebiete gebunden sind. Die vorgestellten Beispiele sind nur ein kleiner Einblick in die Möglichkeiten dieses Tools. Bei der Arbeit mit Mapkit! und Visual FoxPro ist jedoch darauf zu achten, daß bei Pointern auf Short Integer Werte die angepaßten Methoden benutzt werden.