phpDocumentor SMap
[ class tree: SMap ] [ index: SMap ] [ all elements ]

Source for file Form.php

Documentation is available at Form.php

  1. <?php
  2. /**
  3.  * The User Interface
  4.  * 
  5.  * The form is the user interface, it handles the settings that the map uses to
  6.  * draw itself. User interface code is always a pain, so I've tried to add
  7.  * useful bits as protected functions.
  8.  * 
  9.  * SMap requires at least PHP version 5, but there are important bug fixes in
  10.  * more recent versions of PHP. Try to stay current.
  11.  *
  12.  * <b>License</b>:
  13.  * 
  14.  * Copyright (c) 2006-2007, Seth Price <{@link mailto:seth@pricepages.org}
  15.  * seth@pricepages.org}> All rights reserved.
  16.  *
  17.  * Redistribution and use in source and binary forms, with or without
  18.  * modification, are permitted provided that the following conditions
  19.  * are met:
  20.  *
  21.  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  22.  * - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  23.  * - The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
  24.  *
  25.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  26.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  27.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  28.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  29.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  30.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  31.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  32.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  33.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  34.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  35.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36.  *
  37.  * @copyright    Copyright (c) 2006-2007, Seth Price
  38.  * @author        Seth Price <seth@pricepages.org>
  39.  * @license        http://opensource.org/licenses/bsd-license.php New BSD License
  40.  * @access        public
  41.  * @package        SMap
  42.  * @version        0.1
  43.  */
  44.  
  45. /**
  46.  * This is partially user interface, but mostly a form controller
  47.  * 
  48.  * It controls all interaction between the user. Nothing here should throw
  49.  * exceptions unless passed an incorrect object. We can't trust the user, so
  50.  * incomplete vars are just set to default.
  51.  * 
  52.  * @package        SMap
  53.  */
  54. abstract class SMap_Form {
  55.  
  56.     /**
  57.      * GET variable prefix
  58.      * 
  59.      * Set so the mapping vars don't collide with anything else. Altering this
  60.      * will theoretically allow people to place more than one map on a page
  61.      * without them interfering with eachother.
  62.      * 
  63.      * Note that there are static variables in the layout engine that may
  64.      * collide, so exercise caution.
  65.      * 
  66.      * @var        string 
  67.      */
  68.     public $formPrefix = 'm';
  69.     
  70.     /**
  71.      * Options for the screen size selection
  72.      * 
  73.      * Here are some suggestions for common opt groups, option labels, and
  74.      * option values. Values are automatically parsed via regex in
  75.      * {@link getScrPxDim()}.
  76.      * 
  77.      * @var        array 
  78.      */
  79.     protected $scrSizeSel = array(
  80.         'Standard' => array(
  81.             '800x600 (small)' => '800x600',
  82.             '1024x768 (medium)' => '1024x768',
  83.             '1280x1024 (large)' => '1280x1024',
  84.             '1600x1200 (x-large)' => '1600x1200' ),
  85.             
  86.         'Widescreen' => array(
  87.             '1024x640 (small)' => '1024x640',
  88.             '1280x800 (medium)' => '1280x800',
  89.             '1600x1000 (large)' => '1600x1000',
  90.             '1920x1200 (x-large)' => '1920x1200' ),
  91.             
  92.         'Print' => array(
  93.             'US Letter' => '1075x1395p',
  94.             'US Legal' => '1075x1775p',
  95.             'A5' => '740x1050p',
  96.             'A4' => '1050x1485p',
  97.             'A3' => '1485x2100p'    ));
  98.     
  99.     /**
  100.      * Store the language package
  101.      * 
  102.      * Set by the constructor
  103.      * 
  104.      * @var        object 
  105.      */
  106.     protected $lang;
  107.     
  108.     /**
  109.      * Store the geography
  110.      * 
  111.      * Set by the constructor
  112.      * 
  113.      * @var        object 
  114.      */
  115.     protected $geo;
  116.     
  117.     /**
  118.      * Use a SForm to handle all user transactions
  119.      * 
  120.      * http://www.phpclasses.org/browse/package/3465.html
  121.      * 
  122.      * @var        object 
  123.      */
  124.     protected $sform;
  125.     
  126.     /**
  127.      * Permanent variables
  128.      * 
  129.      * All values set here are permanently set. For example, if you want a view
  130.      * to always be shown, set its ID in here. Same thing for layer(s).
  131.      * 
  132.      * @var        array 
  133.      */
  134.     protected $perm = array('views'=>array(),'layers'=>array());
  135.     
  136.     /**
  137.      * Currently set variables
  138.      * 
  139.      * Once the form variables are deciphered, they are set here. After
  140.      * {@link __construct() object construction}, the final value of the form's
  141.      * values should be stored here. When in doubt, use these values.
  142.      * 
  143.      * @var        array 
  144.      */
  145.     protected $curr = array();
  146.     
  147.     /**
  148.      * The incoming request variables
  149.      * 
  150.      * Use whatever method you wish to place the appropriate request in this
  151.      * array.
  152.      * 
  153.      * @var        array 
  154.      */
  155.     protected $request = array();
  156.     
  157.     /**
  158.      * Any user preferences should be stored here.
  159.      * 
  160.      * Variables in this array will take priority over the
  161.      * {@link $defs defaults}, but will be slave to the {@link $request}
  162.      * variables.
  163.      * 
  164.      * @var        array 
  165.      */
  166.     protected $prefs = array();
  167.     
  168.     /**
  169.      * Defaults
  170.      * 
  171.      * When a visitor first visits the page, the settings should be these.
  172.      * 
  173.      * @var        array 
  174.      */
  175.     protected $defs = array();
  176.     
  177.     /**
  178.      * Is the environment a tile image?
  179.      * 
  180.      * Set when we will be drawing the image for a single tile. The process is
  181.      * normally that a script pretends to be
  182.      * 
  183.      * @var        boolean 
  184.      */
  185.     protected $isTile;
  186.     
  187.     /**
  188.      * Init the form object
  189.      * 
  190.      * The construction of the SMap_Form should handle all of the input, and
  191.      * from here on out the values in {@link $curr} should be constant.
  192.      * 
  193.      * @param    object    Language used
  194.      * @param    object    Geographic def
  195.      * @param    boolean    Are we drawing a single tile?
  196.      * @uses        $isTile
  197.      * @uses        $lang
  198.      * @uses        $geo
  199.      * @uses        $sform
  200.      * @uses        initDefs()
  201.      * @uses        initPrefs()
  202.      * @uses        initRequest()
  203.      * @uses        formSubmitted()
  204.      * @uses        $curr
  205.      * @uses        getViewDim()
  206.      * @uses        SMap_Util::normalizeBounds()
  207.      * @uses        SMap_Util::wrapBounds()
  208.      */
  209.     function __construct(SMap_Lang $langSMap_Geo $geo$isTile){
  210.         //Save the obects
  211.         $this->lang = $lang;
  212.         $this->geo = $geo;
  213.         
  214.         $this->isTile = $isTile;
  215.         
  216.         //Init the SForm
  217.         $this->sform = $this->initSForm();
  218.         
  219.         //Set our input arrays
  220.         $this->initDefs();
  221.         $this->initPrefs();
  222.         $this->initRequest();
  223.         
  224.         //Merge input arrays
  225.         $this->curr = array_merge($this->defs$this->prefs$this->request);
  226.         $this->curr['layers'array_unique(array_merge($this->curr['layers']$this->perm['layers']));
  227.         $this->curr['views'array_unique(array_merge($this->curr['views']$this->perm['views']));
  228.  
  229.         //Handle the user form if needed
  230.         if($this->sform->wasSubmitted()){
  231.             $this->formSubmitted();
  232.         }
  233.         
  234.         if(    !isset($this->curr['lang']||
  235.             !isset($this->curr['bounds']||
  236.             !isset($this->curr['viewId']||
  237.             !isset($this->curr['zoom'])){
  238.             
  239.             throw new SMap_Ex('Missing a required form setting! Check the defaults.');
  240.         }
  241.         
  242.         //Set the language that we will be using
  243.         $map $this->getMapVars();
  244.         $lang->setLanguage($map['lang']);
  245.         
  246.         //Adjust bounds to make sure we are good
  247.         $scale $geo->zoomFctr($map['zoom']);
  248.         $B =$this->curr['bounds'];
  249.         
  250.         list($tilesX$tilesY$this->getViewDim($B$scale);
  251.         
  252.         $this->curr['tileGrid'][SMap_Tile::TILESX$tilesX;
  253.         $this->curr['tileGrid'][SMap_Tile::TILESY$tilesY;
  254.         
  255.         $muPerTileX SMap::getOption('tileWidthPx')  $scale[0];
  256.         $muPerTileY SMap::getOption('tileHeightPx'$scale[1];
  257.  
  258.         SMap_Util::normalizeBounds(    $B,
  259.                                     $muPerTileX$muPerTileY,
  260.                                     $tilesX$tilesY);
  261.         
  262.         SMap_Util::wrapBounds($B$geo->getWrap());
  263.     }
  264.     
  265.     /**
  266.      * Create an SForm object
  267.      * 
  268.      * SForm is another of my packages. I've kept it loosly coupled with SMap.
  269.      * If you wish to use some other method than SForm, you'll have to override
  270.      * this method, and possible any other method which uses the SForm. It is,
  271.      * however, possible.
  272.      * 
  273.      * @return    object    SForm with appropriate elements
  274.      * @uses        $formPrefix
  275.      */
  276.     protected function initSForm(){
  277.         if(!class_exists('SForm'))
  278.             require 'SForm.php';
  279.         
  280.         $f new SForm($this->formPrefix'get');
  281.         
  282.         $f->addElement(new SForm_Elm_Hidden('lrs'))// Layers
  283.         $f->addElement(new SForm_Elm_Hidden('l'))// Language
  284.         $f->addElement(new SForm_Elm_Hidden('v'))// Views
  285.         $f->addElement(new SForm_Elm_Hidden('vId'))// View ID
  286.         $f->addElement(new SForm_Elm_Hidden('z'))// Zoom
  287.         $f->addElement(new SForm_Elm_Hidden('bds'))// Bounds
  288.         $f->addElement(new SForm_Elm_Hidden('grd'))// Tile Grid
  289.         
  290.         return $f;
  291.     }
  292.     
  293.     /**
  294.      * Init the default array
  295.      * 
  296.      * @uses        $defs
  297.      */
  298.     protected function initDefs(){
  299.         //Assume that defaults are hardcoded
  300.         //Do nothing; override if needed
  301.     }
  302.     
  303.     /**
  304.      * Init the prefernce array
  305.      * 
  306.      * Handle things here like reading a users preferences from a database,
  307.      * session, or cookie.
  308.      * 
  309.      * @uses        $prefs
  310.      */
  311.     protected function initPrefs(){
  312.         //Do nothing; override if needed
  313.     }
  314.     
  315.     /**
  316.      * Init the request array
  317.      * 
  318.      * Uses the SForm to construct an appropriate request array from the user's
  319.      * input. If you want to use something other than the SForm, you can always
  320.      * take care of that urge here. Just construct the {@link $request} array
  321.      * correctly and you're good to go.
  322.      * 
  323.      * @uses        $request
  324.      * @uses        $sform
  325.      */
  326.     protected function initRequest(){
  327.         //Decode Layers
  328.         $val $this->sform->getElement('lrs')->getValue();
  329.         if($val !== null){
  330.             $newLrs array();
  331.             foreach(explode(','$valas $layer){
  332.                 $newLrs[= (int) $layer;
  333.             }
  334.             $this->request['layers'$newLrs;
  335.         }
  336.         
  337.         //Decode Views
  338.         $val $this->sform->getElement('v')->getValue();
  339.         if($val !== null){
  340.             $newViews array();
  341.             foreach(explode(','$valas $view){
  342.                 $newViews[= (int) $view;
  343.             }
  344.             $this->request['views'$newViews;
  345.         }
  346.         
  347.         //Decode zoom
  348.         $val $this->sform->getElement('z')->getValue();
  349.         if($val !== null){
  350.             $this->request['zoom'= (int) $val;
  351.         }
  352.         
  353.         //Decode view id
  354.         $val $this->sform->getElement('vId')->getValue();
  355.         if($val !== null){
  356.             $this->request['viewId'= (int) $val;
  357.         }
  358.         
  359.         //Decode language
  360.         $val $this->sform->getElement('l')->getValue();
  361.         if($val !== null){
  362.             $this->request['lang'substr($val03);
  363.         }
  364.         
  365.         //Decode bounds
  366.         $val $this->sform->getElement('bds')->getValue();
  367.         if($val !== null){
  368.             $bds explode(','$val);
  369.             if(count($bds== 4){
  370.                 $this->request['bounds'array(
  371.                     SMap::MAXY => floatval($bds[0]),
  372.                     SMap::MAXX => floatval($bds[1]),
  373.                     SMap::MINY => floatval($bds[2]),
  374.                     SMap::MINX => floatval($bds[3]));
  375.             else {
  376.                 unset($this->request['bounds']);
  377.             }
  378.         }
  379.         
  380.         //Decode tile grid
  381.         $val $this->sform->getElement('grd')->getValue();
  382.         if($val !== null){
  383.             $this->request['tileGrid'$this->decodeTileGrid($val);
  384.         }
  385.     }
  386.     
  387.     /**
  388.      * Handles a submitted user form
  389.      * 
  390.      * Sets up the correct structure of the SForm with the correct defaults,
  391.      * elements, values, etc.
  392.      * 
  393.      * @uses        $curr
  394.      * @uses        $sform
  395.      * @uses        addScrSizeElm()
  396.      * @uses        getScrPxDim()
  397.      * @see        addCheckedElms()
  398.      */
  399.     public function formSubmitted(){
  400.         
  401.         $sf $this->sform;
  402.  
  403.         if(!isset($this->curr['bounds']|| !isset($this->curr['zoom'])){
  404.             trigger_error('Bounds or zoom not set.'E_USER_NOTICE);
  405.             return;
  406.         }
  407.         
  408.         $B =$this->curr['bounds'];
  409.         $z $this->curr['zoom'];
  410.         
  411.         //Initialize hidden values
  412.         $sf->getElement('bds')->setDefault(
  413.                             SMap_Util::reduceSigFigs($B[SMap::MAXY]).','.
  414.                             SMap_Util::reduceSigFigs($B[SMap::MAXX]).','.
  415.                             SMap_Util::reduceSigFigs($B[SMap::MINY]).','.
  416.                             SMap_Util::reduceSigFigs($B[SMap::MINX]));
  417.         
  418.         $sf->getElement('z')->setDefault($z);
  419.         $sf->getElement('lrs')->setDefault(implode(','$this->curr['layers']));
  420.         $sf->getElement('l')->setDefault($this->curr['lang']);
  421.         $sf->getElement('v')->setDefault(implode(','$this->curr['views']));
  422.         
  423.         //These aren't useful in the user submitted form
  424.         $sf->removeElement('vId');
  425.         $sf->removeElement('grd');
  426.         
  427.         //Add the screen size element
  428.         $ssizeElm $this->addScrSizeElm($sf$B$z);
  429.         
  430.         //Handle the new screen size
  431.         $ssize $ssizeElm->getValue();
  432.         $scrPxDim $this->getScrPxDim();
  433.         
  434.         //Was it a valid request?
  435.         if(empty($scrPxDim[$ssize])){
  436.             trigger_error('The screen size was not valid: '.$ssizeE_USER_NOTICE);
  437.             return;
  438.         }
  439.         
  440.         $widthPx  SMap::getOption('tileWidthPx');
  441.         $heightPx SMap::getOption('tileHeightPx');
  442.         
  443.         $scale $this->geo->zoomFctr($z);
  444.         
  445.         /*
  446.          * Keep the map centered at the current location, but change the number
  447.          * of tiles by the amount specified in $scrPxDim.
  448.          */
  449.         $B SMap_Util::centerBounds(
  450.                             ($B[SMap::MINX$B[SMap::MAXX])/2,
  451.                             ($B[SMap::MAXY$B[SMap::MINY])/2,
  452.                             $widthPx  $scale[0],
  453.                             $heightPx $scale[1],
  454.                             floor($scrPxDim[$ssize][0]/$widthPx),
  455.                             floor($scrPxDim[$ssize][1]/$heightPx));
  456.         
  457.         //Update the tile dim
  458.         $tiles $this->getViewDim($B$scale);
  459.         
  460.         $this->curr['tileGrid'][SMap_Tile::VIEWX$tiles[0];
  461.         $this->curr['tileGrid'][SMap_Tile::VIEWY$tiles[1];
  462.     }
  463.     
  464.     /**
  465.      * Returns an array of the current map's info.
  466.      * 
  467.      * The returned array is the set of variables that describe the current map
  468.      * view. These include an array of all currently enabled views ['views'],
  469.      * layers ['layers'], the zoom level ['zoom'], and the language ['lang'].
  470.      * The language variable is a three letter ISO 639-2 language code.
  471.      * 
  472.      * @return    array 
  473.      */
  474.     public function getMapVars(){
  475.         return array(    'views' => $this->curr['views'],
  476.                         'layers' => $this->curr['layers'],
  477.                         'zoom' => $this->curr['zoom'],
  478.                         'lang' => $this->curr['lang']);
  479.     }
  480.     
  481.     /**
  482.      * Returns an array of the current bounding information
  483.      * 
  484.      * The bounding array. All bounding indicies used in SMap are defined in
  485.      * SMap. Specifically: {@link SMap::MAXX}{@link SMap::MAXY},
  486.      * {@link SMap::MINX}, and {@link SMap::MINY}.
  487.      * 
  488.      * @return    array    Current bounds
  489.      * @uses        SMap::MAXX
  490.      * @uses        SMap::MAXY
  491.      * @uses        SMap::MINX
  492.      * @uses        SMap::MINY
  493.      */
  494.     public function getBoundVars(){
  495.         return $this->curr['bounds'];
  496.     }
  497.     
  498.     /**
  499.      * Returns an array of the current tile's info.
  500.      * 
  501.      * This returns the positioning information array that should represent
  502.      * either. The positioning information includes positioning info
  503.      * ['tileGrid'] and view ID ['viewId']. Also included is ['isTile'], which
  504.      * defines if the map is being drawn as a single tile (as opposed to a set
  505.      * of tiles).
  506.      * 
  507.      * The tile grid is an array of integers with keys at SMap_Tile::POSX,
  508.      * VIEWX, POSY, VIEWY. The array describes the position of the tile that
  509.      * we are viewing in relation to the other tiles. It contains enough
  510.      * information to determine which edge a tile is on.
  511.      * 
  512.      * The position 1,1 is the tile in the upper left. If the view is 7 tiles
  513.      * wide and 4 tiles high then the numbers in VIEWX and VIEWY will be 7 and
  514.      * 4 respectively. The tile in the upper right corner will be at position
  515.      * 7,1.
  516.      * 
  517.      * The til