5.3 TestsTop5.1 Vorbereitung5.2 ImplementierungInhaltsverzeichnisEnglish

5.2 Implementierung

Die nachfolgende Beschreibung der Implementierung teilt sich in aufeinander aufbauende Umsetzungsschritte, die in einzelnen Abschnitten behandelt werden. Die Beschreibung stützt sich auf die Verwendung einer gekachelten WMS-Ebene.

Die entwickelte animated zooming und panning Erweiterung wurde vor Abgabe dieser Arbeit auf die OpenLayers-Version 2.4 RC5 (vom 25.5.2007) aktualisiert und ist über zwei Medien verfügbar:

Alle im folgenden genannten Quellcodeverweise beziehen sich auf die o. g. Revisionsnummer 3197. Die im Root-Verzeichnis liegende Beispieldemo demo.html wurde nach Abschnitt 4.3 angepasst und ist direkt von der CD bzw. über die aktuelle Sandbox-Version (http://dev.openlayers.org/sandbox/emanuel/animatedZooming/demo.html) ausführbar.

Das Klassendiagramm im Anhang 6.3 markiert alle, für die Umsetzung der animated zooming und panning Erweiterung, veränderten Klassen farblich. Zusätzlich sind alle editierten Methoden aufgelistet. Das Diagramm basiert ebenfalls auf der OpenLayers-Version 2.4 RC5 (Stand: 25.5.2007).

5.2.1 Zoomslider-Events

Das Benutzen des Zoomsliders läuft in der PanZoomBar.js in drei (Event-)Schritten ab: MouseDown - MouseMove - MouseUp.

Beim MouseDown-Event wird die aktuelle Zoomstufe vor Beginn des Zoomprozesses in der Map-Objekt-Eigenschaft zoomlevel_startScale gespeichert. Dieser Wert gilt als Berechnungsgrundlage für die Skalierung.

Tritt das MouseMove-Event ein (durch Bewegen des Sliders), werden mit jeder Bewegungsänderung folgende drei Werte neu berechnet:

  1. Sliderposition:
    Die aktuelle Mausposition bildet ein Pixel-Wertepaar, dessen y-Wert die Sliderposition darstellt. Werte, die außerhalb der Zoombar liegen, werden abgefangen (vgl. zoomBarDrag(); PanZoomBar.js).
  2. Zoomstufe:
    Die Differenz zwischen Sliderstartposition und aktueller Sliderposition geteilt durch den definierten Sliderraster ergibt die Zoomstufenabweichung. Addiert mit dem Zoomlevelstartwert folgt die neue Zoomstufe zoomlevel_scale (vgl. calculateNewZoomlevel(); Map.js): var deltaY_zoomlevel = this.zoomStart.y - sliderPosition.y;
    this.zoomlevel_scale = this.zoomlevel_startScale + deltaY_zoomlevel/zoomStopHeight;
  3. Kachelgröße:
    Die Kachelgröße verdoppelt bzw. halbiert sich beim Skalieren mit jeder Zoomstufe (vgl. Abschnitt 4.2 und calculateNewTileSize(); Map.js). Für den ZoomIn-Prozess gilt exemplarisch für die Kachelbreite: this.tileSize_scale.w = Math.pow(2, (this.zoomlevel_scale - this.zoomlevel_startScale)) * this.tileSize_startScale.w; Analog gilt für den ZoomOut-Vorgang: this.tileSize_scale.w = 1 / (Math.pow(2, (this.zoomlevel_startScale - this.zoomlevel_scale))) * this.tileSize_startScale.w;

Nach Berechnung dieser drei Werte wird jede Kachel auf die neu bestimmte Kachelgröße skaliert (genaue Beschreibung folgt im nächsten Abschnitt).

Nach dem Loslassen der Maustaste (MouseUp-Event) rastert der Slider in die nächstliegende ganze Zoomstufenzahl ein. Alle sichtbaren Kacheln werden neu gezeichnet.

5.2.2 Skalierung

Die Skalierung wird nur auf die aktive Basisebene angewandt; andere aktive Ebenen werden während des Skalierungsvorgangs ausgeblendet (vgl. prepareZoomAnimation(); Map.js). Diese Maßnahme ist notwendig geworden, da bereits bei zwei aktiven Ebenen eine spürbare Performanceverschlechterung zu verzeichnen war. Genaue Messungen sind nicht erfolgt. Es wurde entschieden, zu Gunsten eines schnelleren (und damit gebrauchstauglicheren) Zoom-Skalier-Prozesses auf die Anforderung, mehrere Overlays zu skalieren, zu verzichten.

Zu Beginn des Zoomprozesses muss die Kachel bestimmt werden, die den Mittelpunkt des sichtbaren Bereichs enthält. Liegt das Zentrum auf der Grenze mehrerer Kacheln, wird die erste gefundene Kachel als centerTile definiert (vgl. getCenterTile(); Grid.js).

Beim Bewegen des Zoomsliders werden die unter Abschnitt 5.2.1 bestimmten Werte genutzt, um zunächst die Zentrumskachel zu skalieren und zu positionieren (vgl. scaleTileTo(); Grid.js). Die neue Kachelgröße wird als width- und height-Styleparameter des HTML-imgDiv-Elements der Kachel gesetzt. Der Positionierungsalgorithmus basiert auf dem Strahlensatz. Durch elementargeometrische Streckenverhältnisse zwischen dem Kartenzentrum sowie den Eckpunkten der originalen und der skalierten Kachel lassen sich die neuen top- und left-Pixelpositionen der Kachelgrafik bestimmen.

Auf Grundlage der skalierten Zentrumskachel werden die restlichen (sichtbaren) Kacheln skaliert und neu positioniert.

Zeitgleich mit der Skalierung aktualisiert sich die Übersichtskarte (vgl. updateOverview(); OverviewMap.js). Der rote Markierungsrahmen wird mit dem Zoomvorgang vergrößert bzw. verkleinert und stellt stets den exakten Ausschnitt der Hauptkarte dar. Die Zoomstufe der Übersichtskarte passt sich beim Bewegen des Sliders an die aktuelle Hauptkarte an. Dazu werden neu Kacheln für die Referenzkarte geladen. Der Zoomprozess wird dadurch nicht beeinträchtigt.

5.2.3 ZoomOut-Kachel

Beim Initialisieren der aktuellen Basisebene wird eine (um den Faktor x größere) Basiskachel geladen, um einen weißen Rahmen um die kleiner werdende Karte beim ZoomOut-Prozess zu verhinden (vgl. Abschnitt 4.2). Es wird eine Kachel bestimmt, deren Grenzen die Gesamtausdehnung der Karte (maxExtent) beinhalten; d. h. die Karte muss auf einer einzigen Kachel abbildbar sein. Beginnend mit der Auflösung von Zoomstufe 0 wird die Auflösung solange verdoppelt (und die Karte dadurch verkleinert), bis die Kachelgrenzen diese erfüllen (vgl. setZoomOutTile_share; Layer.js).

Als optimaler Kachelgrößenfaktor x hat sich nach einigen Tests der Wert 4 herausgestellt. Die Standardkachelgröße bei OpenLayers beträgt 256 Pixel (px). Damit würde die ZoomOut-Kachel mit 1024 px geladen werden. Hintergrund: 2048 px ist in der Regel die von vielen WMS-Servern (u. a. vom MapServer) vordefinierte maximale Kartengröße, die nicht überschritten werden sollte. Je größer die Kachelgröße, desto höher ist die Qualität beim Skalieren. Unter dem Gesichtspunkt der Ladezeit sollte eine möglichst geringe Kachelgröße (und damit ein geringer Faktor x) gewählt werden. Faktor 4 ist bei einer definierten Standardkachelgröße von 512 px noch in dem erlaubten Größenintervall (2048 px) und hat im Normalfall mit 1024 px eine vertretbare Ladezeit. Die Qualität ist mit 1024 px bei geringen Zoomstufenänderungen akzeptabel. Bei großen Zoomänderungen treten jedoch deutliche »Verpixelungen« auf, die auch bei einem höheren Faktor nicht ganz vermeidbar sind.

Um die ZoomOut-Kachel in der definierten Kachelgröße vom WMS-Server anzufordern, wurde die getURL-Methode in WMS.js um einen Parameter tileSize erweitert.

5.2.4 Automatische Zoomanimation

Alle unter Abschnitt 3.3.2 beschriebenen Anforderungen sind zu diesem Zeitpunkt berücksichtigt und (bis auf die Overlay-Skalierung) vollständig umgesetzt. Das animated zooming Feature über die Zoomnavigationsleiste wird nachfolgend mit den genannten Wunschkriterien (vgl. Abschnitt 3.3.3) erweitert.

Nach Absprache mit den PSC-Mitgliedern von OpenLayers wird der aktuell im Review-Status stehende animated panning Patch als Grundlage für die zeitabhängige Berechnung der Zoomanimationschritte genutzt. Die easeInOutZoom-Methode in der Util.js ist das Ergebnis aus der Anpassung der analog aufgebauten animated panning Funktion (vgl. Listing 5.2.4). Dabei ist delta die Differenz zwischen den Zoomstufen vor und nach der Animation, totalsteps die vordefinierte Anzahl der Animationsschritte, step der aktuelle »Schrittzähler« und power der vordefinierte Kraftfaktor für die Beeinflussung der Beschleunigungskurve.

   
Listing: Kraft-Bewegungskurven-Algorithmus für Zoomanimationen; Util.js
OpenLayers.Util.easeInOutZoom = function(delta, totalSteps, step, power) {
    var stepVal = Math.pow(((1 / totalSteps) * step), power) * delta;
    return stepVal; 
}

Nach ausführlichem Testen mit unterschiedlichen Werten für die o. g. vordefinierten Variablen wurden totalsteps = 4 und power = 0.7 als ideale Werte für den Zoomprozess empfunden. Es wurde auf eine schnell ablaufende Zoomanimation geachtet, deren Dauer zwei Sekunden nicht überschreiten soll. Daher wurde eine geringe Schrittanzahl gewählt. Für den Kraftfaktor wurde ein Wert gesucht, der die Zoombewegung zu Beginn in kurzer Zeit beschleunigt und zum Ende allmählich langsamer macht. Die Wartezeit zwischen Benutzerinteraktion und erster sichtbarer Zoombewegung ist dadurch sehr gering.

Die unter Abschnitt 4.5.1 beschriebene window.setInterval-Methode kommt als »Taktgeber« für die schrittweise ablaufende Animation zum Einsatz und wird in der zoomSlide-Methode in der Map.js aufgerufen. Durch Klicken auf die Zoombar(-Buttons), Doppelklicken auf die Karte, Bewegen des Mausrades, Aufziehen einer Zoombox oder Drücken der +/- Tasten wird die automatische Zoomanimation von der zoomTo-Methode aus gestartet. Ein optionaler Funktionsparameter kann die Animation deaktivieren und einen »sprunghaften« Zoomvorgang ausführen.

Während des Ablaufens einer Zoomanimation ist ein wiederholtes Auslösen einer Animation durch o. g. Interaktionen nicht möglich. Beispielsweise bei mehreren Mausradbewegungen wird nur um eine Zoomstufe gezoomt. Eine Funktionserweiterung ist hier denkbar.

Bei den Zoommöglichkeiten per Doppelklick, Mausrad und Zoombox wird gleichzeitig zur Zoomanimation die Karte animiert zum neuen Mittelpunkt verschoben. Durch die Kombination von animated zooming und panning laufen parallel zwei unabhängige window.setInterval-Methoden ab - eine für das Zooming und eine für das Panning.

5.2.5 Kachelaufbau durch smooth tile update

Standardmäßig wird bei OpenLayers bei jedem Zoomvorgang die alte Karte komplett gelöscht (ein weißer Hintergrund erscheint) und anschließend, durch allmähliches Nachladen der Kacheln in der neuen Zoomstufe, wieder aufgebaut. Die beim animated zooming prognostizierte Verbesserung der Nutzer-Orientierung wäre durch diesen Effekt empfindlich gestört. Es wird daher eine Lösung umgesetzt, welche die skalierte Ebene im Hintergrund solange sichtbar hält, bis (in der Ebene darüber) alle neuen Kacheln im sichbaren Bereich vollständig geladen sind. Dieser Effekt wird in dieser Arbeit mit smooth tile update bezeichnet und lässt sich nach der Definition von Smart Map Browsing ebenfalls zu dessen Eigenschaften zählen.

Die konkrete Realisierung (vgl. cloneBaseLayerDiv_share(); Layer.js):
Zunächst wird die aktive Basisebene mit allen ihren Kacheln geklont (baseLayerDivClone) und zu dem Ebenencontainer hinzugefügt. Der z-Index von baseLayerDivClone wird um eins erniedrigt, damit die Ebene direkt unter der Originalebene liegt. Anschließend muss die ZoomOut-Kachel der Originalebene unsichtbar geschaltet werden, um die darunter liegende geklonte Ebene beim Kachelneuaufbau nicht zu überdecken.

Ein loadend-Event in der Layer.js wird dann ausgelöst, wenn alle Kacheln der Originalebene im sichtbaren Bereich geladen wurden (vgl. spiralTileLoad(); Grid.js). Danach wird die geklonte Ebene wieder gelöscht und die Möglichkeit zur erneuten Zoomanimation freigegeben (vgl. setLoadendVisibility(); Layer.js).

5.2.6 Unterstützte Ebenen

Das animated zooming Feature unterstützt mit Abschluss der Implementierung die Ebenen Image, WMS Untiled sowie alle von Grid abgeleiteten Ebenenklassen (WMS, MapServer, KaMap, TMS und WorldWind; vgl. auch Klassendiagramm im Anhang 6.3). TMS und WorldWind bieten jedoch keine ZoomOut-Kachel-Unterstützung. Hierfür ist eine ebenenspezifische Anpassung der getURL-Methode notwendig (analog zu WMS.js).

Aktuell noch nicht animated zooming fähig sind MapServer Untiled sowie die Ebenen der Drittanbieter (Google, VirtualEarth, Yahoo und MultiMap). Canvas dient nur als Overlay und wird damit auch nicht unterstützt.

Die unter Abschnitt 4.3 beschriebene Erweiterbarkeit neuer Ebenentypen wird durch die Layer-Basisklasse gewährleistet. Wie konzipiert, unterstützen neue Ebenentypen standardmäßig zunächst keine Zoomanimation. Durch das Implementieren der ebenenspezifischen Methoden (getTileSize(), getCenterTile() und ggf. cloneBaseLayerDiv()), wird das Default-Verhalten von Layer.js überschrieben.

5.2.7 Animated Panning

Das animated panning Feature wird für die Kombination von animierten Zoom- und Panvorgängen genutzt (vgl. Abschnitt 5.2.4). Abgesehen von dem Beheben eines Rundungsfehlers in der getLonLatFromViewPortPx-Methode (Layer.js) und dem Ergänzen von Unittests wurde an dem vorliegenden animated panning Patch nichts verändert.

Die Panning-Tastatursteuerung über die Pfeiltasten wurde nach den Smart Map Browsing Eigenschaften (vgl. Abschnitt 3.2.2; Punkt Zooming/Panning per Tastatur) erweitert. Danach ist es möglich, die Karte mit den Tasten Pos1, Ende, Bild, Bild um jeweils 75% der aktuellen Kartenbreite bzw. -höhe zu verschieben.

5.2.8 Quellcode-Struktur

Die Map-Klasse übernimmt als zentrales Objekt in OpenLayers die Steuerung der Zoomanimation. Der animated zooming Quellcode gliedert sich im Kern in die Funktionen prepareZoomAnimation(), runZoomAnimation() und finishZoomAnimation(). Alle Zoomänderungen laufen über die zoomTo-Methode, von wo aus die drei o. g. Animationsfunktionen aufgerufen werden.

Zum Abschluss der Realisierung wurde der gesamte geschriebene bzw. geänderte Quellcode in logisch gruppierte Patches unterteilt. Tabelle 5.2.8 zeigt die acht erstellten Patchdateien mit der jeweiligen Anzahl der Gesamtzeilen sowie der hinzugefügten und gelöschten Quellcodezeilen. Listing 5.2.8 veranschaulicht exemplarisch an einem Auszug des Patchfiles final_animatedZooming_PanZoomBar.patch das Prinzip der hinzugefügten und gelöschten Zeilen. Die zugehörigen Unitteständerungen (Zeile 1 - 53) werden aus Platzgründen hier nicht dargestellt.

Zeilen
Nr. Patchdatei gesamthinzugefügt gelöscht
1final_animatedZooming_Core.patch18831293105
2final_animatedZooming_LayerImage.patch1541154
3final_animatedZooming_LayerWMSUntiled.patch3391293105
4final_animatedZooming_Mouse.patch32522034
5final_animatedZooming_OverviewMap.patch32913654
6final_animatedZooming_PanZoomBar.patch140778
7animatedPanning#2.patch38420745
8keyboardDefaults.patch18313910
Erstellte Patchdateien für animated zooming und panning
Gesamt37372480269

Die acht Patchdateien befinden sich auf der beiliegenden CD und sind zusätzlich unter den folgenden drei Tickets bei OpenLayers online verfügbar:

   
Listing: final_animatedZooming_PanZoomBar.patch
© 1. Juni 2007, Emanuel Schütze, some rights reserved.
Diese Arbeit ist unter der Creative Commons Lizenz Namensnennung-Weitergabe unter gleichen Bedingungen 2.0 Deutschland lizensiert.

5.3 TestsTop5.1 Vorbereitung5.2 ImplementierungInhaltsverzeichnisEnglish