Die Übung soll einen Einblick in den Aufbau und die Funktionsweise des WMS-Clients geben. Dazu soll eine GetFeatureInfo-Funktion implementiert werden. Hierfür wird eine neue Oberfläche zum Anzeigen der Featureinfos eingebunden und eine neue Anfrage an den Server erstellt. Die Dauer der Übung ist auf 90 Minuten angesetzt. Zur Bearbeitung des Quelltextes soll Eclipse zum Einsatz kommen, da es auch für die Erstellung des WMS-Clients genutzt wurde und auf den Rechnern des Informatik- und GIS-Labors vorinstalliert ist.

Übungsmaterial

Datei Beschreibung
WMS_Client_Uebung.zip
md5: 221066eac78b035cbb199cbedfddcf93
Projektordner

Anleitung

  1. Als erstes muss der Projektordner mit Datei – Importieren... Vorhandenen Projekte in den Arbeitsbereich in den Arbeitsbereich übernommen werden. Im folgenden Dialog ist das Stammverzeichnis auf .../WMS_Cliente_Uebung und der Haken bei Projekte in Arbeitsbereich kopieren zu setzen. Somit sollte nun im Paket-Explorer ein Projekt mit dem Namen „WMS_Client_Uebung“ verfügbar sein.

  2. Ordner/Datei Inhalt
    src Programmpakete und Quelltexte (*.java)
    resourcen Ressourcen für den Buildprozess
    (Standardpaket) leer
    build compilierte Klassendateien (*.class)
    MinML.jar XML-Parser-Bibliothek
    build.xml Steuerdatei für den Buildprozess
  3. Der erste Schritt der Programmerweiterung soll die Oberflächengestaltung betreffen. Es soll eine neue Seite angelegt werden, auf der später die entsprechenden Informationen zum ausgewählten Feature's angezeigt werden. Dafür wird eine neue Klasse im Paket de.htwDresden.wmsClient.featureInfo mit dem Namen „FeatureInfoPanel“ angelegt. Als Superklasse wird java.awt.Panel ausgewählt.

  4. Im nächsten Schritt werden alle lokalen Variablen und der Standardkon-struktor wie folgt deklariert.

    protected WmsClientFrame owner; protected TextArea text; protected String infoText; private FeatureInfoPanel myself; public FeatureInfoPanel(WmsClientFrame owner) { super(); this.owner = owner; this.makeLayout(); this.myself = this; }
  5. Die Generierung des Layouts soll mit Hilfe der Methode makeLayout() erfolgen, die schon im Konstruktor aufgerufen wurde. Dazu wird ein neuer Layoutmanager erzeugt und dem Panel zugewiesen.

    private void makeLayout() { this.setLayout(new BorderLayout());

    Danach wird der Variable text ein Wert, in diesem Fall ein neues Objekt vom Typ TextArea zugewiesen und dieses auf nicht editierbar gesetzt. Die Hintergrundfarbe soll die Farbe (Rot [186], Grün[191], Blau[159]) haben und als Text der infoText verwendet werden.

    this.text = new TextArea(); this.text.setEditable(false); this.text.setBackground(new Color(186, 191, 159)); this.text.setText(this.infoText);

    Zum Schluss wird text auf das Panel eingefügt und die schließende Metho-denklammer gesetzt.

    this.add(this.text, BorderLayout.CENTER); }
  6. Um das nun neu erzeugte FeatureInfoPanel in die bestehende Oberfläche zu integrieren sind einige Erweiterungen in der Klasse WmsClientFrame des Pakets de.htwDresden.wmsClient.gui notwendig. Durch einen Doppelklick auf den Dateinamen im Paket-Explorer wird die Datei im Editorfenster geöffnet.

    private FeatureInfoPanel info;

    Zuerst soll eine neue Klassenvariable für das FeatureInfoPanel deklariert werden. Da diese Variable auf private gesetzt wurde, was einen Zugriff nur von dieser Klasse, in der sie deklariert wurde, aus möglich macht, wird eine getFeaturePanel()-Methode hinzugefügt. Diese soll, soweit der Varia-ble noch kein Wert zugewiesen wurde, ein neues Objekt vom Typ FeatureInfoPanel erzeugen und dann den Wert der Variable zurückgeben. Die Me-thode kann in den Bereich für alle get-Methoden der Übersichtlichkeit halber oder ans Ende vor der letzten schließenden „}“ Klassenklammer hinzugefügt werden.

    public FeatureInfoPanel getFeatureInfo() { if (this.info == null) this.info = new FeatureInfoPanel(this); return this.info; }

    Als Layout wurde in dieser Klasse das CardLayout gewählt, das es ermög-licht, mehrere Darstellungsflächen (Panels) übereinander zu legen und über einen Index eine davon anzuzeigen. In vielen Programmen stellt dies die Grundlage für die Darstellung von Registerkarten dar. Um nun diesem Layout das neue FeatureInfoPanel hinzuzufügen, muss folgende Zeile eingefügt werden.

    cardPanel.add(this.getFeatureInfo(), "info");
  7. Jetzt ist es soweit, einen ersten Testlauf zu tätigen und die neue Oberfläche zu betrachten. Dazu muss eine neue Startkonfiguration angelegt werden, weil im Hochschulnetzwerk mit einem Proxy-Server gearbeitet wird und dieser dem Programm beim Start übermittelt werden muss. Dies geschieht über sogenannte Anwendungsparameter.

    Unter Ausführen – Ausführen... öffnet sich ein neuer Dialog. Mittels Doppelklick auf Java-Anwendung wird eine neuen Konfiguration erstellt. Folgende Werte sind dabei auszufüllen:

    • Name: WMS_Client_Übung
    • Projekt: WMS_Client_Uebung
    • Hauptklasse: de.htwDresden.wmsClient.WMSClient

    Unter der Registerkarte (x)=Argumente müssen folgende Programmargumente hinzugefügt werden:

    -host www-cache.htw-dresden.de -port 3128

    Damit sind der Host und der Port für den Proxy-Server festgelegt. Mit Anwenden werden die Informationen gespeichert und mit Ausführen wird die Anwendung gestartet. Wenn bis hier her alles richtig war, sollte nun in der Konsole folgenden Zeile stehen:

    ProxyConfiguration: www-cache.htw-dresden.de:3128

    Ein Klick auf den Button zeigt jetzt ein leeres Textfeld mit horizontaler und vertikaler Scrollbar und der festgelegten Hintergrundfarbe an.

  8. Um die Informationen des ausgewählten Features auszulesen und anzu-zeigen müssen noch einige Erweiterungen in der Klasse FeatureInfoPanel gemacht werden.

    Das Aufstellen und Absenden des GetFeatureInfo-Request soll eine innere Klasse FeatureInfoLoader übernehmen. Damit diese Aufgabe als paralleler Task läuft, ist die Klasse eine Erweiterung von java.lang.Thread. Desweiteren enthält sie Klassenvariablen für die Liste der ausgewählten Layer, die Requestparameter und die Bildkoordianten x,y , für welche die Featureinformationen angefordert werden.

    protected class FeatureInfoLoader extends Thread { private Enumeration _layers; private RequestParameter _reqParam; private int x, y;

    Im Konstruktor dieser Klasse soll allen Klassenvariablen ein Wert zugewiesen werden. Die Werte stammen aus den Parameter des Konstruktors.

    public FeatureInfoLoader(Enumeration layers, RequestParameter reqParam, int x, int y) { this._layers = layers; this._reqParam = reqParam; this.x = x; this.y = y; }

    Um den Thread zu starten muss eine Methode mit dem Namen run() deklariert werden. Diese beinhaltet alle auszuführenden Aufgaben, die der Task erledigen soll. In diesem Fall ist ein GetFeatureInfo-Request nach OGC-Standards aus den gelieferten Parameter zu erstellen und dieser nachfolgend an den WMS-Server abzusenden. Die Antwort muss dann interpretieren und die Informationen in das Textfeld geschrieben werden.

    Da der GetFeatureInfo-Request auf den GetMap-Request aufbaut, kann dieser als Grundlage verwendet werden. Dazu ist zuerst die run()-Methode aus der Klasse LayerLoader, einer inneren Klasse von LayerPanel im Paket de.htwDresden.wmsClient.map, zu kopieren.

    public void run() { StringBuffer url = null; try { . : } catch (InterruptedException ie) { . : } _reqParam.submitDone(); }

    Als nächstes wird eine zusätzliche lokale Variable vom Typ StringBuffer in der run()-Methode eingefügt. Diese wird benötigt um später den Text des GetFeatureInfo-Request auszulesen.

    StringBuffer text = new StringBuffer();

    Nun wird der try{}-Block angepasst. Die Ausgabe in der Statuszeile wird vom Inhalt her den Gegebenheiten entsprechend abgeändert.

    myself.owner.getStatusBar().setText("Lade FeatureInfo...");

    Die nächsten fünfzehn Zeilen (url.append("&version=1.1.1");) bleiben unverändert, da dies der GetMap-Request ist. Was nun noch fehlt sind die angezeigten bzw. abzufragenden Layer. Um etwas Ordnung und Übersicht zu schaffen, können die restlichen Zeilen des try{}-Blocks gelöscht werden, da sie ohnehin nicht mehr benötigt werden.

    Struktogramm zum auslesen der Layerliste

    Als Nächstes wird eine vorüberge-hende Variable layerList vom Typ StringBuffer deklariert, in der die Layer der Reihe nach aufgelistet werden.

    Die weitere Vorgehensweise beim GetFeatureInfo-Request unterschei-det sich dahingehend, das nicht mehr alle Elemente von _layer ausgelesen werden sondern nur noch die, welche auch ''queryable'' sind, was bedeutet, dass für diese Layer Featureinformationen vorhanden sind. Diese Auswahl muss zwingend getroffen werden, da sonst eine Feh-lermeldung vom Server zurück kommt.

    StringBuffer layerList = new StringBuffer(); if (_info.getField("queryable").equals("1")) layerList.append(_info.getField("name"));

    Nun folgt eine Schleife um die restlichen Elemente von _layer auf dieselbe Weise abzufragen.

    while(_layers.hasMoreElements()) { _info = (LayerInformation)_layers.nextElement(); if (_info.getField("queryable").equals("1")) layerList.insert(0, ",").insert(0, _info.getField("name")); }

    Im Gegensatz zum ersten if{}-Block wird diesmal der Layername nicht hinten an gehangen (append()), sondern wird vorne eingefügt (insert()). Dies liegt daran, das die Layerliste nur von oben herab abgearbeitet werden kann, weil es sich um eine Enumeration handelt. Da aber der oberste Layer der Layerliste der zuletzt dargestellte bzw. abgefragte Layer sein soll, muss somit die Reihenfolge umgekehrt werden. Die so erstellte Zei-chenfolge der abzufragenden Layer ist jetzt noch an die Zeichenkette _url anzufügen.

    url.append("&layers=").append(layerList); url.append("&query_layers=").append(layerList)

    Zum Schluss des GetFeatureInfo-Request müssen noch das Ausgabefor-mat, sowie die x- und die y-Koordinate angefügt werden. Das Ausgabefor-mat kann nur text/plain sein, da es die reinen Informationen enthält, die im nächsten Schritt ausgelesen werden sollen.

    url.append("&info_format=text/plain"); url.append("&x=" + String.valueOf(x)); url.append("&y=" + String.valueOf(y));

    Damit wäre die URL des Request vollständig und kann jetzt abgeschickt werden. Dazu muss zuerst ein neues Objekt vom Typ URL erzeugt werden, mit dessen Hilfe ein Objekt URLConnection erstellt werden kann.

    URL getcap = new URL(url.toString()); URLConnection getcapConn = getcap.openConnection();

    Die soeben erstellte Verbindung erlaubt es jetzt den Inhalt der Antwortda-tei Zeile für Zeile auszulesen und der am Anfang deklarierten Variable text anzufügen.

    BufferedReader reader = new BufferedReader( new InputStreamReader(getcapConn.getInputStream())); String line; while ((line = reader.readLine()) != null) { text.append( " " ).append( line ).append( "\r\n" ); }

    Als letztes soll noch eine Meldung in der Statuszeile ausgegeben und der ausgelesene Text in das Textfeld geschrieben werden.

    myself.owner.getStatusBar().setText("Laden beendet"); myself.text.setText(text.toString());

    Die catch{}-Blöcke können soweit übernommen werden, genauso wie die letzte Zeile _reqParam.submitDone();, da diese zum Abschluss der Über-mittlung dient.

  9. Der nächste Schritt befasst sich mit der programminternen Kommunikation. Diese soll über einen Listener realisiert werden. Da das FeatureInfoPanel von java.awt.Panel abstammt, wird zuerst die Schnittstelle FeatureListener, welche sich schon im Paket de.htwDresden.wmsClient.featureInfo befindet, implementiert. Dazu wird die Klassendeklaration wie folgt erweitert:

    public class FeatureInfoPanel extends Panel implements FeatureListener {

    Somit fungiert jede Instanz von FeatureInfoPanel als Listener. Darüber hinaus ermöglicht diese Form der Kommunikation die Erstellung verschie-denster Programmstrukturen. Es können auch weitere Klassen erstellt werden, die sich mit dem GetFeatureInfo-Request befassen und ihn inter-pretieren.

    Durch die Implementierung des FeatureListeners muss nun noch eine set-FeatureInfo()-Methode hinzugefügt werden. Diese bekommt beim Aufruf zwei Parameter übergeben. Dabei handelt es sich um die x- und die y-Ko-ordinate des im Bild ausgewählten Features, dessen Informationen abge-fragt werden sollen. Die Klasse MapPanel des Paketes de.htwDresden.wm-sClient.map beinhaltet die Liste der ausgewählten Layer sowie die Re-questparameter für den GetMap-Request. Dieses Panel ist über die get-Map()-Methode des WmsClientFrames, welcher in Punkt 4 als owner über-geben wurde, abrufbar. Demzufolge kann nun in der setFeatureInfo()-Me-thode der FeatureLoader instanziert und gestartet werden.

    public void setFeatureInfo(int x, int y) { FeatureInfoLoader loader = new FeatureInfoLoader( owner.getMap().getLayerList(), owner.getMap(), x, y); loader.run(); }

    Damit wäre die Klasse FeatureInfoPanel fertig gestellt.

  10. Als letzte Modifikation muss nun noch das FeatureInfoPanel, welches im Programm verwendet wird als „Zuhörer“ (Listener) registriert werden. Die beste Stelle dafür ist die Zeichenfläche, auf der das Kartenbild gezeichnet wird und auf der die x- und y-Koordinate bestimmt werden. Als Zeichenflä-che wird ein Objekt vom Typ ZoomableImageCanvas im Paket de.htwDres-den.wmsClient.map verwendet.

    Als erster Schritt wird wieder eine neue Klassenvariable vom Typ FeatureListener erzeugt.

    FeatureListener featureListener;

    Mit Hilfe von Eclipse kann auch auf einfachem Weg eine Set-Methode für diese Variable integriert werden. Der Dialog ist unter Quelle - Getter und Setter generieren... zu finden. In der Liste wird durch einen Klick auf den Pfeil neben featureListener dessen Untermenü geöffnet und die setFeatureListener()-Methode kann markiert werden. Mit OK den Dialog bestätigen und beenden.

    Die letzte Änderung betrifft den Zeitpunkt der Ausführung. Dazu wird die mouseReleased()-Methode um den Aufruf von setFeatureInfo() erweitert. Hierfür wird ein neuer case (action = ACTIONQUERY) in den switch{}-Block eingefügt.

    case ACTIONQUERY: this.featureListener.setFeatureInfo(e.getX(), e.getY()); break;
  11. Ein jetziger Testlauf sollte nun eigentlich bei der Aktivierung des Infotools die Featureinformationen auf der vorher eingebunden Seite anzeigen. Dennoch bleibt diese leer. Das liegt daran, dass bis jetzt gesagt wur-de, das die Klasse FeatureInfoPanel ein Zuhörer ist und die Klasse ZoomeableImageCanvas eine Variable und eine set()-Methode für einen Listener besitzt, aber noch keine Registrierung einer Instanz des Featureinfopanels bei einer Instanz der Zeichenfläche statt gefunden hat. Diese Aufgabe soll der Klasse MapPanel im Paket de.htwDresden.wmsClient.map anvertraut werden. Das hat den Hintergrund, dass diese Klasse die Zeichenfläche beinhaltet und Zugriff auf das Featureinfopanel, durch den übergeordneten WmsClientFrame, hat.

    Dazu wird im Konstruktor der Klasse MapPanel die setFeatureListener()-Methode der Zeichenfläche aufgerufen, das FeatureInfoPanel als Zuhörer übergeben.

    this.canvas.setFeatureListener(this.owner.getFeatureInfo());
  12. Durch eine Klick auf den schwarzen Pfeil neben dem Ausführenicon öffnet sich ein Dialog, in dem sich die vorhin angelegte Ausführen-Konfiguration mit dem Name WMS_Client_Übung befindet. Die-se startet das Programm erneut, mit dem es jetzt möglich ist, eine Get-FeatureInfo-Request abzusenden.

  13. Zum Abschluß soll noch eine ausführbare jar-Datei erstellt werden. Weil dazu interne Kopiervorgänge der Ressourcen und das Entpacken der verwendeten MinML-Bibliothek notwendig sind, soll diese Aufgabe mit Hilfe von Ant, einem Eclipse-Plugin automatisiert werden.

    Hierfür ist zuerst die Sicht für Ant über Fenster - Sicht anzeigen - Andere... - - zu öffnen. Mit der neuen Sicht kann nun die build.xml des Projektes hinzugefügt wer-den. Ein Doppelklick auf den Punkt startet den Build-Vorgang. Das Programm liegt nun im bin-Verzeichnis des Projektordners. Um diese An-wendung nun mit den Parametern des Proxy-Servers zu starten, gibt es zwei Möglichkeiten.

    Die erste Variante ist über Start - Ausführen. Dort ist folgender Komman-dozeilenbefehl abzusetzen:

    java -jar Dateipfad/wmsClient.jar -host www-cache.htw-dresden.de -port 3128

    Die zweite Möglichkeit ist über eine Verknüpfung zur .jar-Datei. Unter deren Eigenschaften können die Parameter im Feld für die zu verknüpfende Datei angehängt werden.

  14. Zum Testen der GetFeatureInfo-Funktion bietet sich der Server @wms1.ccgis.de an. Durch Doppelklick auf den Servernamen werden des-sen Layer abgerufen. Hier können jetzt die anzuzeigenden Layer mittels Doppelklick auf den Layernamen zur Layerliste hinzugefügt werden. Die Layer „postcode areas“ und „places as point“ sind queryable, bieten dem-zufolge Informationen an, die abgefragt werden können.

    In der Kartenansicht wird über den Button der GetMap-Request er-stellt und abgesendet und die erhaltenen Bilddaten dargestellt. Jetzt können Information eines Features über die Aktivierung des Infotools und einen Klick in die Karte angefordert werden. Die entsprechenden Daten werden dann auf der neu erstellten Ansicht angezeigt.