Copyright (c) 2002, DM Solutions Group Inc. * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ /* * This widget's purpose is to maintain a history * of extents that the user has visited and allow movement forward and * backward through the hisory if possible. * * Extents are maintained with the projection in case the user changes projection * Moving to an extent in the history is a matter of calling a javascript function * * ExtentHistory_Navigate( szDirection='' ) * * Calling this function will cause the page to be submitted and the extents for * the appropriate direction loaded. The projection may also change. * * The widget has one unique attribute, DIRECTION, which is the direction that the * history will navigate when the widget is clicked. * * Session variables: * gaExtentHistory: array of previous extents * gnExtentHistoryPosition: current position in the extent history */ class ExtentHistory extends CWCWidget { /** * mbNavigate boolean track whether this widget has changed the extents to avoid * pushing them onto the stack. */ var $mbNavigate = false; /** * moButton object the widget's interface is represented by a button */ var $moButton = null; /** * mszDirection string the direction to navigate when activated */ var $mszDirection = ''; /** * initialize the ExtentHistory widget * * Note priority is set to last so that all other widgets have * a chance to change the projection or extents first */ function ExtentHistory() { parent::CWCWidget(); $this->moButton = new CWCButton( $this ); $this->mnPriority = PRIORITY_LAST; // set the description for this widget $this->szWidgetDescription = <<maAttributes['DIRECTION'] = new StringAttribute( 'DIRECTION', true, array( 'forward', 'backward' ) ); // set the maturity level $this->mnMaturityLevel = MATURITY_BETA; } /** * initialize the default setup of the widget */ function InitDefaults() { parent::InitDefaults(); if (isset($this->maParams['DIRECTION'])) { $this->mszDirection = strtolower( $this->maParams['DIRECTION'] ); } $this->moButton->InitDefaults(); $this->moButton->setOnClick( 'ExtentHistory_Navigate', $this->mszDirection ); } /** * handle the widget's navigation actions if requested by the user * @return boolean always true. */ function ParseURL() { if ($this->isVarSet('EXTENTHISTORY_NAVIGATE')) { $szDir = $this->getVar('EXTENTHISTORY_NAVIGATE'); //make sure it was this widget that caused the navigation ... if ($szDir == $this->mszDirection) { if ($szDir == 'backward') { $this->previousExtent(); } elseif($szDir == 'forward') { $this->nextExtent(); } } } return true; } /** * returns an array of HTML inputs type=hidden that are used by this widget * * EXTENTHISTORY_NAVIGATE: set to 'next' or 'previous' to navigate * through the extent history * * @return array an array of strings */ function GetHTMLHiddenVariables() { $aReturn = $this->moButton->GetHTMLHiddenVariables(); $szVar = 'EXTENTHISTORY_NAVIGATE'; $aReturn[$szVar] = ''; return $aReturn; } /** * return Button javascript stuff * @return array an array of javascript stuff for the button */ function GetJavascriptInitFunctions() { //TODO: set button as inactive if we can't go in //the appropriate direction if ($this->mszDirection == 'backward' && $_SESSION['gnExtentHistoryPosition'] == 0) $this->moButton->mbEnabled = false; elseif ($this->mszDirection == 'forward' && count($_SESSION['gaExtentHistory']) == $_SESSION['gnExtentHistoryPosition']+1) $this->moButton->mbEnabled = false; return $this->moButton->GetJavascriptInitFunctions(); } /** * return Button javascript stuff * @return array an array of javascript stuff for the button */ function GetJavascriptVariables() { return $this->moButton->GetJavascriptVariables(); } /** * return Button javascript stuff * @return array an array of javascript stuff for the button */ function GetJavascriptOnLoadFunctions() { return $this->moButton->GetJavascriptOnLoadFunctions(); } /** * return Button javascript stuff * @return array an array of javascript stuff for the button */ function GetJavascriptIncludeFunctions() { return $this->moButton->GetJavascriptIncludeFunctions(); } /** * returns the javascript function used to access the extent history * It first checks to see if it is necessary to record the current extents. * * Functions returned: * * key: ExtentHistory_Navigate * function: ExtentHistory_Navigate( szDirection ) * purpose: called with direction "previous" or "next" to naviagage * through the extent history. * * @return array an array of javascript functions */ function GetJavascriptFunctions() { if (!$this->mbNavigate) $this->pushExtents(); $aReturn = $this->moButton->GetJavascriptFunctions(); $nCurrentPosition = $_SESSION['gnExtentHistoryPosition']; $nCount = count( $_SESSION['gaExtentHistory'] ); $szFunction = 'ExtentHistory_Navigate'; $aReturn[$szFunction] = <<mszHTMLForm}.EXTENTHISTORY_NAVIGATE.value = szDirection; {$this->mszHTMLForm}.submit(); } } EOT; return $aReturn; } /** * draw the widget, returns the button. * @return string the button */ function DrawPublish() { return $this->moButton->DrawPublish(); } /** * push the current extents onto the session extent stack. This function * takes care of initializing the stack if necessary. It also tracks * the current position in the stack and deletes future extents if the * user navigates to new extents after having gone backwards in the history * Finally, it checks to see if the current extents match the last extents * and will only push a new entry on if the extents (and projection) * don't match. */ function pushExtents() { //this ensures that pushExtents only gets called once regardless //of how many ExtentHistory widgets there are. if (isset($GLOBALS['ExtentHistoryPushExtentsCalled'])) { return; } $GLOBALS['ExtentHistoryPushExtentsCalled'] = true; $oExt = $this->moMapObject->oMap->extent; $szProj = $this->moMapObject->oMap->getProjection(); $aEntry = array( $oExt->minx, $oExt->miny, $oExt->maxx, $oExt->maxy, $szProj ); //check for session stack of extents and initialize if not set if (!isset($_SESSION['gaExtentHistory'])) { $_SESSION['gaExtentHistory'] = array(); } //check for session current position and initialize if not set if (!isset($_SESSION['gnExtentHistoryPosition'])) { $_SESSION['gnExtentHistoryPosition'] = count( $_SESSION['gaExtentHistory'] ) - 1; } //check to see if current position is valid and reset if it is not if (!isset($_SESSION['gaExtentHistory'][$_SESSION['gnExtentHistoryPosition']])) { $_SESSION['gnExtentHistoryPosition'] = count( $_SESSION['gaExtentHistory'] ) - 1; } //check current extents to see if they DONT match the last extents and push the new //extents on if they DONT match if ($_SESSION['gnExtentHistoryPosition'] == -1) { $_SESSION['gaExtentHistory'][] = $aEntry; $_SESSION['gnExtentHistoryPosition'] = 0; } else { if (!$this->compareEntries($aEntry, $_SESSION['gaExtentHistory'][$_SESSION['gnExtentHistoryPosition']])) { //check to see if there are extents in the 'future' and remove them if (count($_SESSION['gaExtentHistory']) > $_SESSION['gnExtentHistoryPosition'] + 1) { $_SESSION['gaExtentHistory'] = array_slice($_SESSION['gaExtentHistory'], 0, $_SESSION['gnExtentHistoryPosition'] + 1 ); } $_SESSION['gaExtentHistory'][] = $aEntry; $_SESSION['gnExtentHistoryPosition'] = count( $_SESSION['gaExtentHistory'] ) - 1; } } } /** * navigate to the previous extent if there is one. */ function previousExtent( ) { if ($_SESSION['gnExtentHistoryPosition'] > 0) { $_SESSION['gnExtentHistoryPosition'] -= 1; $this->applyExtent(); } } /** * navigate to the next available extent if there is one. */ function nextExtent() { if ($_SESSION['gnExtentHistoryPosition'] < count( $_SESSION['gaExtentHistory'] ) - 1) { $_SESSION['gnExtentHistoryPosition'] += 1; $this->applyExtent(); } } /** * apply the current extents to the map object. This will change * the projection if necessary. It will not compare the extents * to see if they are the same */ function applyExtent() { if (isset($_SESSION['gaExtentHistory'][$_SESSION['gnExtentHistoryPosition']])) { $aEntry = $_SESSION['gaExtentHistory'][$_SESSION['gnExtentHistoryPosition']]; if ($this->moMapObject->oMap->getProjection() != $aEntry[4]) { //change projections (and reset units) $this->moMapObject->oMap->setProjection( $aEntry[4], true ); } $this->moMapObject->oMap->setextent( $aEntry[0], $aEntry[1], $aEntry[2], $aEntry[3] ); $this->mbNavigate = true; } else { $nPos = $_SESSION['gnExtentHistoryPosition']; $nCount = count( $_SESSION['gaExtentHistory'] ); $_SESSION['gErrorManager']->setError( ERR_WARNING, 'ExtentHistory: attempt to set extents to an invalid position ($nPos), only $nCount available'); } } /** * compare two extent entries to see if they are approximately the same * * Extent entries are in the form (minx, miny, maxx, maxy, proj) * * Extents are considered equal if projections match and: * * - width is the same within 0.01% * - height is the same within 0.01% * - x coordinates match within error of width * - y coordinates match within error of height * * @param e1 array the first extent * @param e2 array the second extent * @param tolerance float optional, the percentage difference to use, * default is 0.001 (0.1%) * @return boolean true if the extents are the same, false if not */ function compareEntries( $e1, $e2, $tolerance=0.005 ) { //compare projections first if ($e1[4] != $e2[4]) return false; //test width $w1 = abs($e1[2] - $e1[0]); $w2 = abs($e2[2] - $e2[0]); $pw = abs($w1 - $w2)/$w2; if ($pw > $tolerance) return false; //test height $h1 = abs($e1[3] - $e1[1]); $h2 = abs($e2[3] - $e2[1]); $ph = abs($h1 - $h2)/$h2; if ($ph > $tolerance) return false; //text minx $d = abs($e1[0] - $e2[0]); if ($d > $w1 * $pw) return false; //text miny $d = abs($e1[1] - $e2[1]); if ($d > $h1 * $ph) return false; //text maxx $d = abs($e1[2] - $e2[2]); if ($d > $w1 * $pw) return false; //text maxy $d = abs($e1[3] - $e2[3]); if ($d > $h1 * $ph) return false; //evertyhing matches (within the tolerance return true; } } ?>