HBUMNMapServer ger Capter 6

From OSGeo
Jump to: navigation, search

MapScript

(1)\index{MapScript}

Bisher haben Sie den MapServer als Applikation kennengelernt, die in einem Webserver wie zum Beispiel Apache arbeitet, und direkt über Parameter des URL aufgerufen und in ihrem Verhalten beeinflußt werden kann.

Man kann sich aber natürlich auch vorstellen, die Funktionen von MapServer -- das Lesen von Mapfiles, das Rendern der Karten und so weiter -- auch aus eigenen Programmen nutzen zu wollen, natürlich auch solchen, die nicht in einem Webserver laufen sollen. Dazu müßte man diese Funktionen separat zur Verfügung stellen, beispielsweise in einer eigenen Bibliothek, auf die dann aus anderen Programmiersprachen zugegriffen werden kann. Diese Bibliothek existiert bereits und trägt den Namen MapScript.

Zu den Sprachen wie Java, Python, Perl und einigen anderen, die mit MapScript benutzt werden können, gesellt sich auch PHP. PHP MapScript soll im Folgenden als Anknüpfungspunkt für die Erklärung von MapScript dienen -- die genaue Terminologie für einzelne Programmiersprachen entnehmen Sie bitte der jeweilgen Dokumentation der Sprache bzw. den Beispielen, die dem MapServer-Quellcode beiliegen.

=PHP MapScript=(2)\index{PHP MapScript}\index{MapScript!PHP}

Ich erspare dem Leser die an dieser Stelle üblichen Hymnen auf PHP\footnote{Beziehungsweise die üblichen Flames. Die Sprache ist definitiv nichts für Puristen oder Entwickler mit eng gesteckten Vorgaben, was den Resourcenverbrauch angeht.} und seine Flexibilität als Sprache für Webanwendungen. Es ist in der Tat sehr weit verbreitet. Das ist (neben der besonderen Pflege, die gerade diese Sprachanbindung erfährt) auch der Grund, warum wir an dieser Stelle detailliert auf PHP MapScript eingehen, und nicht beispielsweise auf Perl.

An dieser Stelle gehen wir nicht darauf ein, wie Sie PHP installieren, oder wie Sie Ihren Webserver korrekt für den Einsatz von PHP konfigurieren. Wie man das tut, erfahren Sie in der Dokumentation von PHP bzw. Ihres Webservers. Die Installation von PHP MapScript wird im Anhang dieses Handbuches abgehandelt.

Beachten Sie bitte, dass PHP MapScript im Moment zwingend voraussetzt, dass Sie PHP nicht als Modul laden, sondern als CGI-Programm einsetzen. Es kann ansonsten zu nicht vorhersehbaren Komplikationen kommen.

Als erstes deutschsprachiges Einsteigerbuch zu PHP eignet sich beispielsweise~theis:2000:php; allerdings gibt es eine solche Unzahl an Büchern über diese Sprache, dass eine persönliche Auswahl im Buchladen erfolgen sollte. Die Website zur PHP http:website:php stellt ausführliche Dokumentation bereits, die interessanterweise auch vollständig ins deutsche übersetzt ist.

Des weiteren ist es eine gute Idee, sich mit der FAQ von =de.comp.lang.php= ~faq:dclp auseinanderzusetzen, der deutschsprachigen Newsgroup zu PHP, die in einem eigenen Kapitel auch auf Sicherheitsbelange eingeht.

\begin{figure} \begin{center} \mmfig{0.75}{mapscript-hyk}{Ein Beispiel für eine PHP MapScript Anwendung sind die Hydrogeologischen Karten Brandenburg} \end{center} \end{figure}

Laden des Moduls

Um die MapScript-Funktionen in einer PHP-Seite verwenden zu können, muss das Modul dafür geladen werden. Das geschieht ganz einfach durch dl :


dl ("php_mapscript.so");


Dies sollte die erste Zeile sein, die Sie in jede Seite einfügen, die MapScript-Funktionen benutzt. Sollten Sie sich unter Windows befinden, ändert sich diese Zeile leicht in:


dl ("php_mapscript.dll");


Falls Sie eine portable Anwendung schreiben sollen, ist es demnach angebracht, gleich zu Beginn des Skripts auf das verwendete Betriebssystem zu prüfen und dann die korrekte Datei zu laden.

Setzen von Objekt-Eigenschaften

Bevor Sie auch nur eine einzige Klasse aus MapScript kennenlernen, möchte ich zeigen, wie man die Eigenschaften von Objekten setzt, da diese Methode für alle Objekte (die sie unterstützen) gleich ist.

Ein Beispiel:


<math>layer -> set ("name", "gruenflaechen");


Mit diesem Aufruf wird der Name eines Layer-Objektes auf =gruenflaechen= gesetzt.

Der 'korrekte' objektorientierte Ansatz wäre natürlich gewesen, dafür eine eigene Routine =setName()= zu implementieren. Da einige Klassen in MapScript jedoch über eine ziemlich große Anzahl an Eigenschaften verfügen, hat man sich auf diese Methode beschränkt.

Das Setzen von Eigenschaften auf diese Weise funktioniert nur für Eigenschaften, die nicht =read-only= sind. Nicht jedes Objekt implementiert zudem die =set()= -Methode. In der Referenz im Anhang dieses Handbuchs sehen Sie, welche Klasse eine solche Methode zur Verfügung stellt.

Darüber hinaus haben einige Klassen für einige Eigenschaften doch noch eigene Methoden implementiert. Studieren Sie die Referenz im Anhang, um herauszufinden, für welche Klassen und Eigenschaften das gilt.

Formulareingaben in PHP verarbeiten

In älteren PHP-Versionen war es kein Problem, URL-Parameter einfach als Variable in ein PHP-Skript zu übernehmen. Wenn ein Aufruf also etwa so aussah:


.../skript.php?name=Calli


Dann konnte man ohne Probleme das folgende in sein Skript schreiben:


echo </math>name;


Das produzierte dann die Ausgabe =Calli= .

In neueren Fassungen von PHP\footnote{Und aufgrund der bekannt gewordenen Sicherheitslücken möchte man eine alte Version nicht benutzen. Verwenden Sie nichts unter 4.3.3!} geht das so nicht mehr. Weil es durch diverse Tricksereien möglich war, globale PHP-Variablen von außen zu überschreiben, und weil man die Programmierer zwingen wollte, sich mit den Werten in ihren Variablen genauer zu befassen, muß man sich jeden Wert nun explizit aus einem Array holen, das den Namen =\<math>_GET[]= trägt.

Für unser obiges Beispiel benötigen Sie dann also folgendes:


<math>dername = </math>_GET ['name'];
echo </math>dername;


Manchmal werden durch eine Eingabemöglichkeit in einem HTML-Formular mehrere Werte übergeben. Ein Beispiel dafür ist ein angeklicktes Bild, wie es etwa unsere Karte ist -- ein Klick produziert natürlich eine X- und eine Y-Koordinate. PHP verhält sich an dieser Stelle etwas unerwartet. Betrachten wir das folgende Bild in einem HTML-Formular:


<input type="image" name="img" src="[img]">


Nach einem Klick finden Sie im URL zwei Werte, namentlich =img.x= und =img.y= . Diese beiden Variablen werden Sie aber im Array =\<math>_GET[]= vergeblich suchen, denn für PHP werden die Punkte durch Unterstriche ersetzt. Sie müssen sich also =img_x= und =img_y= aus dem Array holen.

Es ist übrigens immer angezeigt, den Wert jeder Variablen, die von außen belegt wird, eingehend auf seine Sinnhaftigkeit zu prüfen.

==Die mapObj-Klasse==\index{Mapscript!mapObj}\index{mapObj}

Als nächstes möchten wir betrachten, wie MapScript ein Mapfile behandelt. Betrachten Sie das erste Beispiel:


</math>map = ms_newMapObj ("/path/to/mapfile.map");
<math>image = </math>map -> draw ();
<math>imageurl = </math>image -> saveWebImage (MS_PNG, 1, 1, 0);


In der ersten Zeile wird ein =mapObj= erzeugt, das auf ein bereits existentes Mapfile zugreift. Danach wird daraus ein =ImageObj= erzeugt, es wird also die Karte gezeichnet. Dabei werden die gesetzten Werte für die Extents aus dem Mapfile übernommen.

Beachten Sie bitte, dass dadurch noch keine fertige Bilddatei erzeugt wird. Die Karte existiert zu diesem Zeitpunkt nur als Puffer im Speicher.

In einer Datei tatsächlich gespeichert wird das Bild in der letzten Zeile, in unserem Beispiel als Bild im PNG-Format. Die Rückgabe der Funktion ist der URL für den Webserver, unter der er das Bild ausliefern kann.

Man kann daher weiter unten in der Seite folgendes HTML verwenden:


<html>
<head><title>Beispiel</title></head>
<body>

<img src="<?php echo <math>imageurl; ?>">

</body>
</html>


Dadurch wird der betreffende URL in den =<img>= -Tag eingefügt und an den Client ausgeliefert. Das Resultat ist natürlich ein zentriertes Bild der Karte im Webbrowser.

Auf die gleiche Weise werden übrigens auch Legenden, Maßstäbe und Referenzkarten erzeugt:


<math>img_legend = </math>map -> drawLegend ();
<math>img_scalebar = </math>map -> drawScaleBar ();
<math>img_refmap = </math>map -> drawReferenceMap ();


Auch diese Bildobjekte werden wieder über deren Methode =saveWebImage= abgespeichert.

\subsubsection*{Zoomen}

Das Zoomen in eine Karte kann auf drei Weisen geschehen: Zoomen in Richtung eines Punktes, Zoomen auf ein Rechteck und Zoomen auf einen Punkt in einem bestimmten Maßstab.

Betrachten wir den ersten Fall. Wir gehen davon aus, dass wir ein Bild in einem HTML-Formular eingebettet hatten, und dieses Bild wie gerade beschrieben per PHP Mapscript erzeugt hatten. Wurde das Bild angeklickt, werden die Pixelkoordinaten des Klicks über das Formular mit übergeben. Gehen wir von einem festen Zoomfaktor von 2 aus (diesen Wert legen wir für dieses Beispiel jetzt fest), können wir mit diesen Pixeln folgendes tun:


</math>map = ms_newMapObj ("/path/to/file.map");
<math>map -> zoomPoint (2, </math>imgxy, <math>breite, </math>hoehe, <math>extent);
<math>img = </math>map -> draw ();

...

Das Mapfile wird in seinem ursprünglichen Zustand geladen. Dann werden der Zoomfunktion die nötigen Parameter übergeben: zuerst der Zoomfaktor, den wir hier mit 2 festlegen, aber natürlich auch im Formular einstellen lassen können. Der folgende Wert ist ein pointObj, das die Bildkoordinaten des angeklickten Punktes darstellt. Falls Sie mit diesen Werten irgendwelche Berechnungen anstellen wollen, beachten Sie, dass der Ursprung der Grafik auf dem Bildschirm in der linken oberen Ecke ist.

Breite und Höhe beziehen sich auf das Bild und sind Pixel-Werte. Der Extent (als rectObj) ist schließlich die Bounding Box der Karte in ihrem jeweiligen Koordinatensystem.

Als letzten Wert könnte man übrigens noch ein weiteres rectObj angeben, dass den maximalen Extent angibt, der beim Zoomen nicht verlassen werden darf. Diesen Aufwand muß man aber nicht unbedingt treiben, da das Schlüsselwort =MAXSCALE= in der Web-Sektion das gleiche leistet.

Im Grunde machen alle Zoomfunktionen nichts anderes, als neue Extents für einen Kartenausschnitt festzulegen. Das bedeutet aber auch, dass Ihre Applikation durcheinander kommen kann, wenn sie beispielsweise gleichzeitig die Möglichkeit zum Zoomen und zum Ändern der Größe der Karte anbietet. Stellen Sie in Ihrem Code sicher, dass Sie in einem solchen Fall zuerst die Änderung des Ausschnitts vornehmen (z.B. Zoomen) und erst danach die Werte für die Kartengeometrie setzen (zum Beispiel Höhe und Breite der Karte).

Das Zoomen auf ein Rechteck gestaltet sich ähnlich. Über normale Anwendungen im Webbrowser lassen sich zwei angeklickte Punkte nicht übertragen. Diese Funktion kommt also dann zum Tragen, wenn man mit JavaScript oder wie auch immer ein Rechteck aufziehen und entsprechende Werte übergeben kann.

Der aufmerksame Leser kommt natürlich auch auf die Idee, diese Funktion zu benutzen, wenn er auf ein bestimmtes Feature zoomen möchte. Man bestimme dann die Extents des betreffendes Shapes (das geht auch mit MapScript, siehe weiter unten) und zoomt eben auf diese.

Dummerweise geht das nur, wenn man die Pixel -Koordinaten des Rechtecks kennt, denn die Funktion =zoomRectangle= wird folgendermaßen genutzt:


</math>map = ms_newMapObj ("/path/to/mapfile.map");
<math>map -> zoomRectangle (</math>pixext, <math>breite, </math>hoehe, <math>extent);


Der erste Parameter ist wieder ein rectObj; er gibt die Pixelkoordinaten des Rechtecks an, auf die gezoomt werden soll. Die Breite und die Höhe sind ebenfalls Pixelangaben im Bild. Die Extents schließlich sind wieder ein RectObj, das diesmal die Bounding Box der Karte im Bild im entsprechenden Koordinatensystem angibt.

Wenn Sie einfach auf bestimmte Extents zoomen wollen, ohne vorher in Besitz von Pixelkoordinaten und so weiter zu sein, dann können Sie einfach das Mapfile laden und diese neuen Extents setzen:


</math>map = ms_newMapObj ("/path/to/mapfile.map");
<math>map -> setExtent (</math>minx, <math>miny, </math>maxx, <math>maxy);
</math>map -> draw ();


Warum diese Methode vier einzelne Koordinaten erwartet und kein RectObj, können wir leider nicht erklären.

Die letzte Zoommethode schließlich ist =zoomScale= :


<math>map = ms_newMapObj ("/path/to/mapfile.map");
<math>map -> zoomScale (100000, </math>imgxy, <math>breite, </math>hoehe, </math>extent);


Der erste Wert ist ein SCALE, wie er bereits auf Seite~\pageref{text:mapfile:scale} besprochen worden ist. Beachten Sie bitte, dass so ein Maßstab nicht viel mit dem Maßstab auf einer gedruckten Karte zu tun hat.

Die restlichen Parameter sind wieder die Pixelkoordinaten des Mausklicks, Höhe und Breite des Bildes in Pixeln sowie die Extents des Kartenausschnitts. Auch bei dieser Funktion läßt sich als letzter Parameter wieder als rectObj ein Ausschnitt angeben, aus dem nicht herausgezoomt werden darf.

\subsubsection*{Weitere Eigenschaften und Methoden}

Die restlichen Eigenschaften des mapObj-Objektes dienen dazu, Informationen beziehungsweise Referenzen auf andere Objekte aus dem Mapfile zu ziehen; aber auch, Queries auszuführen. Mehr zu Queries in PHP erfahren Sie weiter unten in dem Abschnitt über Layer. Sie finden die Queries dort (und nicht hier im mapObj, wo sie ausgeführt werden), da die Resultate auf Layer-Ebene eingeholt werden.

Alle weiteren Methoden und Eigenschaften, die mapObj bietet, sind im Anhang ab Seite~\pageref{ref:phpms:mapobj} aufgelistet.



==Fehlerbehandlung==(3)

Jetzt da Sie ein Gefühl dafür haben, wie man Objekte in PHP MapScript anspricht, sollen Sie etwas über die Wege der Fehlerbehandling erfahren, die mit MapServer 4.0 dazugekommen ist.

Bisher sind Fehler einfach aufgetreten und haben meist den weiteren Ablauf des Skripts abgebrochen. Das kann eventuell nicht erwünscht sein; so wäre es zum Beispiel vorstellbar, dass eine Datenbank, die räumliche Daten für einen Layer Ihrer Applikation vorhält, nicht erreichbar ist. Anstatt nun einfach mit einem Fehler abzubrechen, wäre es natürlich angenehmer, wenn man beim Auftreten eines Fehlers dessen Ursache prüfen könnte, nd dann über das weitere Vorgehen entscheiden könnte.

Dieses Vorgehen wird in MapScript 4.0 mit dem neuen Objekt =errorObj= umgesetzt. Dabei werden Fehler bei ihrem Auftreten in eine interne Liste eingefügt. Aus dieser Liste können sie sodann zu einem Zeitpunkt nach Wahl des Programmierers entnommen und verarbeitet werden.

Schauen wir uns das Beispiel von der offiziellen MapServer-Dokumentationsseite an:


ms_resetErrorList ();
<math>img = </math>map -> draw ();
<math>error = ms_getErrorObj ();
while (<math>error && </math>error -> code != MS_NOERR) {
  printf ("Error in 
           <math>error -> routine, </math>error -> message);
           <math>error = </math>error -> next ();
} 


Zuerst sollten Sie zu Beginn jedes Skriptes die Fehlerliste zurücksetzen; das passiert hier in der ersten Zeile.

Danach holt man sich das globale Fehlerobjekt, über welches dann iteriert wird. Für jeden erzeugten Fehler wird dessen Eigenschaft =code= abgefragt. Dieser Code ist eine aus einer Liste von Fehlerkonstanten. Falls es sich tatsächlich um einen Fehler handelt -- auch keine Fehler ist ein Fehler, allerdings einer vom Typ =MS_NOERR= wird eine entsprechende Fehlermeldung ausgegeben. Danach wird zum nächsten unbearbeiteten Fehler in der Liste gesprungen.

Beachten Sie bitte, dass Sie nach der Abfrage des nächsten Fehlers keine Möglichkeit mehr haben, zu bereits abgearbeiteten Fehlern zurückzukehren.

Sie finden alle Details zu =errorObj= in der Referenz ab Seite~\pageref{ref:phpms:errorobj}. Die möglichen Fehlercodes sind außerdem Bestandteil von Tabelle~\ref{tab:ref:mapscript:const}, die auf Seite~\pageref{tab:ref:mapscript:const} beginnt.

==Die layerObj-Klasse==\index{Mapscript!layerObj}\index{layerObj}

Dieses Objekt läßt Sie auf alle Bestandteile einer Layer-Sektion im Mapfile zugreifen.

Dafür müssen Sie natürlich erst einmal wissen, welche Layer sich in einem gegebenen Mapfile befinden. Natürlich könnte man einfach alle Namen in seinem Script hartkodieren. Allerdings kommt man dann schnell in Bedrängnis, wenn sich am Mapfile etwas ändert. Außerdem läßt sich auf diese Weise keine Software schreiben, die sich mit beliebigen Mapfiles auseinandersetzt.

Zuerst einmal können alle Layer in einem Mapfile durch einen Index angesprochen werden, der ihrer Position im Mapfile entspricht. Der erste Layer erhält dabei den Index =0= , der zweite den Index =1= und so weiter\footnote{Informatiker beginnen immer bei Null mit dem Zählen, nicht bei Eins. Das liegt daran, dass sie sich bei Listen und so weiter nicht dafür interessieren, welche Position ein Element absolut besitzt, sondern welchen Abstand vom Beginn der Liste. Und der ist für das erste Element Null, und so weiter.}. Mit einer Schleife, die über die Anzahl der Layer läuft, kann man also beispielsweise folgendes machen:


<math>num = </math>map -> numlayers;
for (<math>i = 0; </math>i < numlayers; </math>i++) {
  <math>layer = </math>map -> getlayer (<math>i);
  ... jetzt passieren Dinge mit </math>layer ...
}


Man kann die Layer aber auch über Ihren Namen ansprechen. Dabei holt man sich zuerst alle Layer-Namen via =getAllLayerNames()= aus dem mapObj (man erhält ein Array aller Namen zurück), und spricht dann die Layer einzeln mit =getLayerByName()= an.

Die häufigste Aktion mit einem Layer ist, Queries auszuführen, beziehungsweise danach Query-Ergebnisse aus ihm herauszuholen.

Queries auf Layer

Queries liefern seit Version~{3.5} des MapServers keine Objekte vom Type resultObj mehr direkt zurück. Vielmehr werden die Ergebnisse von Queries nun in den Layer-Objekte festgehalten.

Damit Anfragen auf einen Layer gemacht werden können, muß dieser im Mapfile einen =TEMPLATE= -Eintrag besitzen. Dieser Eintrag kann durchaus leer sein. Alternativ zur Angabe auf Layer-Ebene kann =TEMPLATE= auch nur in einigen (oder allen) =CLASS= es des Mapfiles stehen. Die Queries werden dann nur auf Mitglieder dieser Klasse angewendet.

Am häufigsten werden Queries auf einem mapObj ausgeführt; alle Query-Funktionen lassen sich jedoch auch auf einzelne Layer anwenden. Werden Queries auf mapObj-Objekte angewendet, werden einfach alle in Frage kommenden Layer der Reihe nach mit der entsprechenden Funktion bedacht.

Queries können auf drei verschiedene Weisen ausgeführt werden: an bestimmten Punkten, mit Hilfe eines Rechtecks und, ganz neu, anhand beliebiger Shapes. Doch wir beginnen mit den Punkt-Queries.

Betrachten Sie folgendes Beispiel:


<math>punkt = ms_newPointObj ();
</math>punkt -> setXY (300000, 500000);
<math>layer -> queryByPoint (</math>punkt, MS_SINGLE, -1);


Hier wird zuerst ein Punkt-Objekt aus den Koordinaten gebaut, die abgefragt werden sollen. Für gewöhnlich kommen diese Koordinaten durch einen Mausklick auf ein Bild in einem Formular zustande. Beachten Sie bitte, dass dieser Punkt im =Koordinatensystem der Karte= vorliegen muß; die Pixelwerte eines Mausklicks auf eine Karte müssen Sie also zuerst in passende Werte umrechnen.

Danach wird auf dem gewünschten Layer eine Query durchgeführt. Dazu wird der Punkt übergeben, die Art der Anfrage (hier soll nur ein einzelnes Ergebnis erzeugt werden; mit =MS_MULTIPLE= wären mehrere Ergebnisse möglich), und schließlich ein Puffer. Dieser Puffer ist ein Radius um den Abfragepunkt herum, in den Einheiten der Karte. Ein negativer Wert führt dazu, dass der im Mapfile gesetzte Wert verwendet wird.

Bevor man sich auf eventuelle Resultate stürzt, sollte man prüfen, ob es überhaupt Resultate gab -- die Anfrage kann schließlich auch ins Leere gegangen sein. Dazu prüft man den Rückgabewert der Funktion. Ist dieser =MS_SUCCESS= , so ist alles in Ordnung und man kann fortfahren. Trifft man auf =MS_FAILURE= , so gab es keine Ergebnisse.

Etwas problematisch ist, dass alle Query-Funktionen auch Fehlernachrichten produzieren, wann immer es keine Resultate gab. Dieses Verhalten läßt sich für einzelne Funktionen jedoch mit einem vorangestellten =@= abstellen. Ein korrekterer Aufruf der obigen Funktion wäre also:


if ((<math>layer -> @queryByPoint (</math>punkt, MS_SINGLE, -1))
                                          == MS_SUCCESS) {
  ... es gab Ergebnisse ...
} else {
  ... Fehlerbehandlung ...
}


Jetzt können wir uns endlich mit dem Ergebnis des Mausklicks auseinandersetzen. Oder besser: mit den Ergebnissen, denn es können ja mehre sein. Mit =getnumresult()= bringen wir die Anzahl der Resultate eines Layers in Erfahrung; haben wir nur ein Resultat, ist das nicht nötig. Dieses eine Resultat hat immer den Index =0= :


<math>resultat = </math>layer -> getResult (0);
<math>si = </math>resultat -> shapeindex;
<math>ti = </math>resultat -> tileindex;
<math>ti = </math>resultat -> classindex;


Das Resultat-Objekt besitzt also drei Eigenschaften: einen Shapeindex, der die Nummer des Shapes im Shapefile angibt, das dem Layer zugrunde liegt; einen Tileindex, der für gekachelte Shapefiles angibt, in welcher Kachel das Ergebnis aufgetreten ist; und schließlich einen Classindex, der die Class im Layer bezeichnet, für den das Ergebnis aufgetreten ist.

Mit diesen Angaben können Sie dann verfahren, wie Sie es für richtig halten. Sie können zum Beispiel die Daten aus dem =.dbf= -File herausfischen, die dem Shape zugrunde liegen\footnote{Beachten Sie an dieser Stelle die eigenwillige Zählweise in =.dbf= -Dateien, die auch in PHP übernommen wurde: die Zeilen in einer solche Datei sind nicht bei 0 beginnend durchnummeriert, sondern bei 1}. Sie könnten beispielsweise auch auf das gesuchte Shape heranzoomen, indem Sie anhand des gefundenen Index die Bounding Box des Shapes aus dem Shapefile auslesen (siehe auch shapefileObj weiter unten) und diese Bounding Box als neue Extents für die Karte setzen.

Von den einfachen Punkt-Queries kann man nun voranschreiten zu den Anfragen, die über ein Rechteck definiert sind:


<math>layer -> queryByRect (</math>rechteck);


Das Rechteck ist dabei ein rectObj, wie es weiter unten im Text besprochen wird. Die restliche Vorgehensweise entspricht exakt demjenigen bei Punktabfragen.

Hinweise: Rechtecke lassen sich in "`normalem"' HTML nicht erzeugen. Mit JavaScript oder dem ROSA-Applet sieht das schon wieder anders aus. Beachten Sie aber, dass insbesondere öffentliche Institutionen unter Umständen äußerst restriktive Bestimmungen haben, was den Einsatz von JavaScript, dynamischem HTML, Java und so weiter angeht. Auch sicherheitsbewußte Privatanwender haben JavaScript erst gar nicht aktiviert. Überdenken Sie den Einsatz dieser Technologien.

Das gilt auch für die folgende Weise, Queries zu stellen, nämlich anhand eines Shapes, also eines beliebigen Polygons:


<math>layer -> queryByShape (</math>shape);


Diese Art der Query entfaltet Ihren Nutzen weniger anhand von Punkten, die in einem Web-Interface ausgewählt werden können, als vielmehr beim Vergleich von Shapes mit Shapes aus einem anderen Shapefile: wenn Sie beispielsweise ein Shapefile mit Bundesländern haben und eines, indem ohne eine solche Referenzierung Angaben von Verkaufszahlen gespeichert sind, dann lassen sich durch den 'Verschnitt' beider Daten aussagekräftige Ergebnisse erzeugen.

Wie man mit MapScript einzelne Shapes aus einem Shapefile extrahiert erfahren Sie weiter unten bei der Besprechung von shapefileObj. Ein schneller Weg involviert allerdings die Methode =getShape()= , die bereits auf der Ebene des Layers vorhanden ist.

\subsubsection*{Weitere Eigenschaften und Methoden}

Die weiteren Funktionen in layerObj dienen im wesentlichen dazu, Zugriff auf die einzelnen Werte eines Layers zu erhalten, beziehungsweise auf die weiteren Objekte darin bezug nehmen zu können, insbesondere auf die Classes im Layer.

Alle weiteren Methoden und Eigenschaften, die layerObj bietet, sind im Anhang ab Seite~\ref{ref:phpms:layerobj} aufgelistet.

==Die classObj-Klasse==\index{Mapscript!classObj}\index{classObj}

Dieses Objekt entspricht einer Class innerhalb eines Layers. Mit einem classObj können Sie im wesentlichen Farben und Symbole für eine Class setzen -- insbesondere aber die Expressions für eine Class.

Ein Beispiel, wie man das verwenden kann: Nehmen wir an, Sie haben einen Datenbestand, der Ihnen die Deutschland in lauter kleine Gebiete einteilt. In der dazu gehörigen .dbf -Datei haben Sie in einer Spalte für jedes Gebiet den prozentualen Anteil an Waldfläche gespeichert. Diese Spalte trägt den Namen =WALD= .

In Ihrer Applikation haben Sie nun ein Eingabefeld, das es dem Benutzer ermöglicht, eine Anzahl von Grün-Abstufungen zu bestimmen, in die der Layer eingeteilt werden soll. Gibt der Benutzer beispielsweise =4= ein, werden dem Layer vier Klassen hinzugefügt, die von grün bis weiß vier Farbstufen annehmen.

Sie errechnen in der PHP-Seite dann die Unterteilung in vier Grünstufen und setzen vier verschiedene Classes.

==Die imageObj-Klasse==\index{Mapscript!imageObj}\index{imageObj}

Diese Objekte werden niemals 'von Hand' erzeugt, sondern immer nur durch den Aufruf von Methoden anderer MapScript-Objekte. Bei diesen Image-Objekten handelt es sich nicht um Bild-Dateien, sondern lediglich um die Bilddaten, die in einem Puffer im Speicher liegen und noch darauf warten, gespeichert zu werden.

Alle =draw= -Methoden von mapObj erzeugen ein imageObj. Am häufigsten wird danach die Methode =saveWebImage()= aufgerufen:


<math>image = </math>map -> draw ();
<math>url = </math>image -> saveWebImage (MS_JPEG, 0, 0, 90);


Der erste Parameter der Methode ist das gewünschte Bildformat, das meistens =MS_PNG= sein wird, ab und zu =MS_JPEG= und nur, wenn es sich nicht vermeiden läßt, =MS_GIF= .

Die beiden folgenden Parameter legen eine Transparenz der Hintergrundfarbe bzw. ein Interlacing für das Bild fest. Der letzte (optionale) Parameter legt für Bildformate, die eine Kompression zur Verfügung stellen, den Grad dieser Kompression fest. Werte zwischen 0 und 100 sind hier möglich.

Eine vollständige Aufzählung aller Methoden dieses Objektes finden Sie im Anhang ab Seite~\pageref{ref:phpms:imageobj}.

==Die labelObj-Klasse==\index{Mapscript!labelObj}\index{labelObj}

Labels befinden sich innerhalb von classObj-Objekten. Diese Klasse kennt erstaunlicherweise keine einzige Methode außer =set()= , dafür eine große Menge von Eigenschaften. In der Referenz finden Sie ab Seite~\pageref{ref:phpms:labelobj} eine ausführliche Aufzählung.

Das bedeutet übrigens auch, dass es keinen Konstruktor für diese Klasse gibt und demnach keine Möglichkeit, ein labelObj 'von Hand' zu erstellen.

==Die webObj-Klasse==\index{Mapscript!webObj}\index{webObj}

Ebenso wie beim labelObj gibt es auch für diese Klasse keinen Konstruktor und keine weiteren Methoden neben der =set()= -Methode. In der Referenz im Anhang finden Sie ab Seite~\pageref{ref:phpms:webobj} eine Liste aller Eigenschaften dieser Klasse.

webObj-Objekte finden Sie immer als Bestandteil eines mapObj.

==Die referenceMapObj-Klasse==\index{Mapscript!referenceMapObj}\index{referenceMapObj}

Auch Objekte dieser Klasse kommen nur innerhalb eines mapObj vor, es gibt keinen Konstruktor und keine Methode außerhalb von =set()= .

In der Referenz ab Seite~\pageref{ref:phpms:referencemapobj} finden Sie eine Liste aller Eigenschaften dieser Klasse.

==Die colorObj-Klasse==\index{Mapscript!colorObj}\index{colorObj}

Jede Karte verfügt über eine bestimmte Farbpalette. Diese Palette wird beim Generieren der Karte erstellt.

Neue Farben lassen sich der Palette hinzufügen, indem man Objekte der Klasse colorObj erzeugt:


<math>weiss = ms_newColorObj ();
</math>weiss -> setRGB (255, 255, 255);


Diese Farbe kann dann an den Stellen, wo es möglich und nötig ist, verwendet werden.

==Die pointObj-Klasse==\index{Mapscript!pointObj}\index{pointObj}

Punkte werden an diversen Stellen in MapScript für verschiedene Dinge benötigt. Wann immer eine Funktion die zwei Koordinaten eines Punktes übergeben bekommt, wird ein pointObj verlangt. Sehen Sie sich beispielsweise die Verwendung von =queryByPoint= im Abschnitt über das Objekt =layerObj= an.

Dieses Objekt besitzt aber noch einige andere Methoden in MapScript. Insbesondere kann man damit die Abstände von Punkten zu anderen Punkten, zu Linien und zu Polygonen bestimmen; außerdem lassen sich einzelne Punkte mithilfe von Objekten der Klasse projectionObj projizieren. Des weiteren kann man einzelne Punkte aus Linien-Objekten extrahieren (siehe lineObj).

pointObj gehört zu den Klassen, deren Speicher nach Gebrauch explizit freigegeben werden muß (siehe Seite~\pageref{text:mapscript:memory}).

Die Referenz zu allen Methoden von pointObj finden Sie auf Seite~\pageref{ref:phpms:pointobj}.

==Die lineObj-Klasse==\index{Mapscript!lineObj}\index{lineObj}

Linien lassen sich entweder von Hand konstruieren (indem man der Reihe nach einzelne Punkte hinzufügt), oder aber aus Shapes der Klasse shapeObj extrahieren. Außerdem lassen sich Linien natürlich ebenfalls projizieren.

lineObj gehört zu den Klassen, deren Speicher nach Gebrauch explizit freigegeben werden muß (siehe Seite~\pageref{text:mapscript:memory}).

Die Referenz zu allen Methoden von lineObj finden Sie auf Seite~\pageref{ref:phpms:lineobj}.

==Die projectionObj-Klasse==\index{Mapscript!projectionObj}\index{projectionObj}

Dieses Objekt beschreibt einen Projektions-Abschnitt in einem Mapfile. Aber es kann auch jenseits von Mapfiles verwendet werden, um einzelne Punktkoordinaten umzuprojizieren.

\subsubsection*{Beispiel}

Dieses Beispiel projiziert einen einzelnen Punkt von einer Längen-/Breitenangabe in einen Punkt im UTM 32-System:


<math>projin = ms_newProjectionObj ("proj=latlong");
</math>projout = ms_newProjectionObj ("proj=utm,zone=32,ellps=WGS84,
                                 datum=WGS84,no_defs");
<math>punkt = ms_newPointObj ();
</math>punkt -> setXY (32.0, 51.0);
<math>punkt = </math>punkt -> project ();
printf ("


Die genauen Parameter, die Sie für die von Ihnen gewünschte Projektion verwenden müssen, finden Sie in der Dokumentation der Projektionsbibliothek proj.4.

==Die shapefileObj-Klasse==\index{Mapscript!shapefileObj}\index{shapefileObj}

Diese Klasse dient dazu, Shapefiles direkt manipulieren zu können. Sie können ein Shapefile als Objekt öffnen, einzelne Shapes referenzieren, die Eigenschaften des Shapefiles auslesen und sogar neue Shapes an das Shapefile anhängen und ganz neue Shapfiles erschaffen.

Beachten Sie bitte, dass Sie kein mapObj erzeugen müssen, um Shapefiles auf diese Weise zu bearbeiten. Das shapefileObj ist unabhängig von den MapServer-verwandten Klassen in MapScript, kann aber natürlich mit diesen zusammen benutzt werden.

Die Referenz für alle Eigenschaften und Methoden von shapefileObj finden Sie ab Seite~\ref{ref:phpms:shapefileobj}.

\subsubsection*{Beispiel}

Das folgende Beispiel gibt eine simple Statistik über alle Shapefiles aus, die es im aktuellen Verzeichnis finden kann.


foreach (glob ("*.shp") as <math>shpfname) {
  <math>shpf = ms_newShapefileObj (</math>shpfname, -1);
  <math>noshapes = </math>shpf -> numshapes;
  <math>shpftype = </math>shpf -> type;
  <math>bounds = </math>shpf -> bounds;
  printf ("Anzahl der Shapes: 
          "Typ des Shapefiles: 
          "Extents: 
          <math>noshapes, </math>shpftype,
          <math>bounds -> minx, </math>bounds -> miny,
          <math>bounds -> maxx, </math>bounds -> maxy);
  </math>shpf -> free ();
}


Struktur von PHP MapScript-Seiten

Wenn Sie mit PHP MapScript das Verhalten einer 'normalen' MapServer-Applikation nachbilden möchten, kann Ihnen das folgende Schema einige Anhaltspunkte geben, wie beim Aufbau einer solchen Applikation vorgegangen werden kann.

Weitere Optionen für Ihre Applikation müssen an sinnvollen Stellen in das Gerüst eingefügt werden.

Diese Anleitung ist natürlich kein in Stein gemeißeltes Gesetz, sondern kann von Fall zu Fall abgeändert werden. Es bietet aber eine gute Orientierungshilfe.


  1. Prüfen der übergebenen Parameter auf sinnvolle Werte
  2. Laden des Mapfiles
  3. Fallunterscheidung nach gewünschtem Modus:


##Simples Browsen (Pan) der Karte: ##

  • Ermitteln der Koordinaten des Mausklicks ##
  • Zoomen auf den Punkt mit einer Zoomtiefe von 1

##Zoomen auf/um den betreffenden Punkt ##

  • Ermitteln der Koordinaten des Mausklicks ##
  • Zoomen auf den Punkt mit dem übergebenen Zoomfaktor

##Zoomen auf das angeklickte Shape ##

  • Ermitteln der Shapenummer mittels queryByPoint ##
  • Öffnen des entsprechenden Shapefiles und Ermitteln der Extents des angeklickten Shapes ##
  • Zoomen auf das Shape mittels zoomByRect

##Query auf den geklickten Punkt ##

  • Ermitteln der Kartenkoordinaten des angeklickten Punktes (oder mehrerer Punkte, falls es ein queryByRect ist) ##
  • Ausführen der Query ##
  • Auslesen des Ergebnis' (oder der Ergebnisse) aus einem oder mehreren Layer-Objekten ##
  • Präsentation der Ergebnisse
  1. Rendern der Karte und Abspeichern in einer Datei
  2. Eintragen des URL des fertigen Bildes in den entsprechenden HTML-Tag


Speichern von Map-Objekten in PHP-Sessions

Leider können Objekte aus PHP-MapScript (insbesondere ein mapObj) nicht in Sessions gespeichert werden. Der Grund dafür ist, dass die Objekte in PHP-MapScript keine 'reinen' PHP-Objekte sind, sondern lediglich als Wrapper um die C-Daten\-struk\-turen des MapServer-Quellcodes angelegt sind. Das führt dazu, dass beim Speichern in einer Sitzungsvariablen lediglich der Wrapper gespeichert wird, nicht aber das darunter liegende Objekt.

Man muß also entweder (um beim Beispiel eines mapObj zu beiben) die vorgenommenen Änderungen auf eine genehme Art und Weise speichern und mitschleifen, um diese dann jedesmal auf ein neu erstelltes mapObj anzuwenden; oder man speichert die fertige Karte als Datei im System ab und speichert den Dateinamen der Karte in der Sitzungsvariablen.

==Speicherverwaltung mit MapScript==(4)

Die Speicherverwaltung aller MapScript-Objekte, die im Zusammenhang mit dem Mapfile stehen, geschieht automatisch. Sie müssen also nicht für jedes Objekt Speicher anfordern und wieder freigeben.

Etwas anders verhält es sich bei Objekten zu Shapefiles und allen geometrischen Objekten wie Punkten, Linien und so weiter. Das Anfordern von Speicher entfällt zwar. Allerdings sollten alle Objekte dieser am Ende (also nachdem man Gebrauch von ihnen gemacht hat und sie nicht mehr benötigt) explizit zerstört und alle verwendeten Resourcen freigegeben werden:


<math>punkt = ms_newPointObj (); ... </math>punkt -> free ();

Zwar wird jeder Speicher, der innerhalb eines PHP-Skriptes benutzt wird, nach dem Abarbeiten der Seite automatisch wieder freigegeben. Allerdings kann bei beim exzessiven Gebrauch von Objekten innerhalb einer Seite vielleicht in Speicherschwierigkeiten kommen.

Außerdem können Sie in Ihrem Programm zwar keine Probleme mit dem Speicher haben. Denken Sie aber immer daran, dass Kollegen, Freunde, Mitarbeiter und so weiter später vielleicht auf die Idee kommen können, das Programm abzuändern und zu erweitern. Wenn Sie von vornherein vernünftig mit dem Speicher umgegangen sind, so sind Sie auf der sicheren Seite. Es zeugt darüber hinaus von einem sauberen Programmierstil.

Weitere Sprachen

MapScript kann mit verschiedenen Sprachen genutzt werden. Neben PHP sind die populärsten Perl, Java und Python.

Die MapServer-Entwickler bedienen sich bei diesen Sprachen des Interface-Generators SWIG~http:website:swig. Die Verwendung von SWIG und die Erstellung von MapScript-Modulen für andere Programmiersprachen wird im Anhang besprochen.

Perl

Zu Perl gibt es unglaublich viel Literatur. Nichts falsch machen kann man wohl mit~wall:1996:perl5, dem berühmten Camel Book. Es ist auch auf deutsch erschienen.

Java

Java ist eine Sprache, aus der ein unglaublicher Hype hervorgegangen ist. Die Literatur, die sich mit Java auseinandersetzt, ist dermaßen umfangreich, dass Sie sich von dem Buchhändler Ihres Vertrauens beraten lassen sollten.

Python

Python hat sich einen Namen als einfache, strukturierte und schnell zu lernende Programmiersprache gemacht. Wer des Englischen mächtig ist, ist mit der Lektüre von~lutz:1999:python recht gut beraten; das Buch ist auch auf deutsch erschienen.



\chapter{Java-Applets}\index{Applets}(1)


=Mapplet=\index{Mapplet}(2)


=ROSA=(3)\index{ROSA}