Source for file SMap.php
Documentation is available at SMap.php
* The layered map view is not for the faint of heart. If you are new to this,
* expect to spend quite a bit of time going back and fourth trying to
* understand what is going on. Of course I've done my best to make things
* simple, but this is simply complicated by nature.
* I'll attempt an overview:
* I've tried to implement common functionality, but you'll <i>need</i> to
* extend classes. At a minimum, you need to extend {@link SMap_Data},
* {@link SMap_Form}, {@link SMap_Lang}, and {@link SMap_Geo} to define the
* characteristics of your map. You will most likely extend varients of
* {@link SMap_Object} and {@link SMap_Layer} to draw out your map.
* The calling structure is to create instances of your custom classes, and pass
* them to the {@link SMap::__construct() constructor} of {@link SMap}. Once you
* have an instance of SMap, you call {@link SMap::addView()} to add your view
* to the map. Each view can be thought of as a drawing window. By default, they
* are linked to move and zoom together. So, you can display the same area with
* different layers showing.
* The interesting work takes place within {@link SMap_Layer the layer}. Layers
* are added to each view via {@link SMap_View::addLayer()}. When the time
* comes, {@link SMap_Layer::getImgObjs()} or {@link SMap_Layer::getMapObjs()}
* will be called. The layer will construct, organize, and return a set of
* objects appropriate for the view and bounds. The objects will draw your info.
* {@link SMap_Object} handles the brunt work of translating from whatever your
* layer wants to the {@link SMap_Canvas canvas}. Because these are primitives,
* you may or may not need to extend them.
* Returning to our instance of {@link SMap}, we have now set up our map
* structure. SMap takes over the hard part from here. Layers have been turned
* on or off and other changes may have been returned via {@link SMap_Form}.
* Those are collected and applied. Call {@link SMap::getViewXHTML()} to get the
* XHTML to insert into the page. Or call {@link SMap::dispTileImg()} to
* generate the raw image to return to the browser. And you're done!
* - {@link http://wms.jpl.nasa.gov/}
* - {@link http://bluemarble.nasa.gov/}
* - {@link http://www2.jpl.nasa.gov/srtm/}
* SMap requires at least PHP version 5, but there are important bug fixes in
* more recent versions of PHP. Try to stay current.
* Copyright (c) 2006-2007, Seth Price <{@link mailto:seth@pricepages.org}
* seth@pricepages.org}> All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* - 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.
* - The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* @copyright Copyright (c) 2006-2007, Seth Price
* @author Seth Price <seth@pricepages.org>
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @see SMap::__construct()
* SMap is split into parts for file size reasons
require 'SMap/Object.php';
require 'SMap/Layer.php';
* Inetrface for the main mapping functions
* The SMap is the object which binds all the mapping objects together. Kind of
* like {@link http://en.wikipedia.org/wiki/One_Ring The One Ring}.
* @link http://en.wikipedia.org/wiki/One_Ring
* These are used to define a rectangle on the map. The rectangle is
* commonly some sort of viewport, or some sort of clickable label.
* The form that will be used to display the map view(s) and process input
* The data that this will be pulled from
* Geography centric calculations
* Anything that isn't in the native X,Y system should be passed through
* The views that this controller controls
* The main view is at index 0.
* The layers that are currently visible
* What zoom level are we at?
* Generate it now so we don't have to later.
* How many tiles are we viewing?
* What is the scale in each direction?
* This is SMap Units per one pixel.
* @see SMap_Geo::getWrap()
* Options that can be set
* The size of the tile is the granularity of the movement of the view.
* Smaller tiles mean more requests, but the view is more precise and it is
* more likely that a tile will stay cached on the broweser.
* Directory to dump the cached data into. The web process must have write
* Maximum cache time of a tile. Applies mostly to images.
* It is strongly suggested that data caching be enabled. Labels may not
* work correctly if it isn't.
* It is strongly suggested that caching be enabled. The only reason that it
* isn't enabled by default is so people don't get confused when
* experimenting with the package and things don't show up right away.
* Dubugging prints out a mass of debugging info, instead of doing anything
* This library relies on comparing floating point numbers often. This is
* normally a fairly stupid thing to do, but there is no good way around it
* here. To get around the problem of 1.0 turning into 0.99999... we are
* going to be rounding floating point numbers often. How many significant
* figures should we round to when using {@link SMap_Util::reduceSigFigs()}?
* The hack that I use to enable PNG transparency in IE requires an image
* with binary transparency set to clear.
* When producing JPEG compressed images, we can control the quality of the
* compression using 'jpegQuality'. When caching images, we put some extra
* effort into making them small. Specifically, we use the jpegtran command
* line utility to compress the files. If we want to create a progressive
* jpeg in this manner, we can use 'jpegProgressive'.
private static $options = array(
'backgroundColor' => array(255, 255, 255, 127),
'cacheDir' => '/tmp/map_cache',
'cacheEnabledData' => true,
'cacheEnabledImg' => false,
'maxFileSize' => 4000000, // ~4MB
'transPNG' => '/imgs/spacer.png',
'jpegProgressive' => true,
'jpegtranBin' => '/usr/local/bin/jpegtran',
'optipngBin' => '/usr/local/bin/optipng',
private static $regViews = array(
'raster' => array(false, 'SMap_View_Raster'),
'inset' => array(false, 'SMap_View_Inset'),
'links' => array(false, 'SMap_View_Links'));
* Constructs with the form and data objects that will be used
* @param object SMap_Form subclass
* @param object SMap_Data subclass
* @param object SMap_Geo subclass
* @param object SMap_Lang subclass
* @uses SMap_Form::getMapVars()
* @uses SMap_Util::genMapKey()
* @uses SMap_Geo::getWrap()
* @uses SMap_Geo::zoomFctr()
* @uses SMap_Form::getBoundVars()
* @uses SMap_Form::getTileVars()
function __construct(SMap_Form $form, SMap_Data $data, SMap_Geo $geo, SMap_Lang $lang){
//Log any options that should be changed
//While we are here, make sure that the cache dir works
$cacheDir = self::getOption('cacheDir');
//Save the current map information
$map = $form->getMapVars();
$this->zoom = $map['zoom'];
//Save the geographical data
$this->wrap = $geo->getWrap();
//Get the current viewport size & save
$this->scale = $geo->zoomFctr($map['zoom']);
$this->bounds = $form->getBoundVars();
$tg = $form->getTileVars();
throw new SMap_Ex('Was not able to create cacheDir "'. $dir. '"', SMap_Ex::FILE_WRITE);
throw new SMap_Ex('cacheDir "'. $dir. '" not writable', SMap_Ex::FILE_WRITE);
* Access useful vars read-only
* @param string Name of member
* @return mixed Value of member
public function __get($nm){
throw new SMap_Ex('Attempted to read non-existant member '. $nm);
* Gets XHTML for a map view window
* The main map view is always stored in index 0
* @param integer Index of view
* @uses SMap_View::getXHTML()
* @uses SMap_View::getTiles()
* @uses SMap_Form::getMapVars()
if(empty($this->views[$viewId])){
throw new SMap_Ex( 'SMap_View ID '. $viewId. ' not found',
$map = $this->form->getMapVars();
$tiles = $this->views[$viewId]->getTiles($this->form);
return $this->views[$viewId]->getXHTML($tiles);
* The main view is always stored at index 0. Throws an exception if there
* is already a view at that index.
* @param mixed SMap_View subclass or view to create
* @param mixed Passed to the view constructor, normally a View ID
* @param mixed Additional arguments passed to view constructor
* @return object The added view
public function addView( $view, $arg1 = null, $arg2 = null,
$arg3 = null, $arg4 = null, $arg5 = null,
$arg6 = null, $arg7 = null, $arg8 = null){
$view = $this->createView($view, $this, $arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8);
throw new SMap_Ex('Passed $view not an instance of SMap_View.');
//Throw error if already exists
if(!empty($this->views[$vid])){
throw new SMap_Ex( 'SMap_View of ID '. $vid. ' already exists.',
return $this->views[$vid] = $view;;
* Serves as a view factory. The first argument is the name of a view that
* is registered with, and the rest of the arguments are passed to the view.
* @param string View name
* @param mixed Additional arguments...
* @return object The created view
* @uses SMap_View::__construct()
public static function createView( $view, $arg1, $arg2 = null, $arg3 = null,
$arg4 = null, $arg5 = null, $arg6 = null,
$arg7 = null, $arg8 = null, $arg9 = null){
if(empty(self::$regViews[$view])){
throw new Exception('The given view type "'. $view. '" is not built in. ' .
'If you have created a custom view, please simply ' .
'pass the object to addView().');
$meta = self::$regViews[$view];
if($meta[0] && !class_exists($meta[1])){
$v = new $meta[1]($arg1, $arg2, $arg3, $arg4, $arg5, $arg6, $arg7, $arg8, $arg9);
throw new SMap_Ex('View not a subclass of SMap_View.');
* Display a map tile image with appropriate headers.
* This displays the image itself, and should be called from a script
* expecting a dump of the image. Caching is handled internally. The ini
* value of 'display_errors' is unset so an image is always displayed. For
* errors, please look in your log file.
* @uses SMap_View_Raster::dispTileImg()
$tileVars = $this->form->getTileVars();
if(empty($this->views[$tileVars['viewId']])){
throw new SMap_Ex('View ID of '. $tileVars['viewId']. ' requested but doesn\'t exist.');
$view = $this->views[$tileVars['viewId']];
//Make sure we are dealing with what we think we are dealing with
$view->dispTileImg(new SMap_Tile($this,$view,$this->bounds,$tileVars['tileGrid']));
* Handle a possible imagemap click
* Handle the possibility that the user clicked on a server-side image map.
* @return mixed Clicked URL or false
* @uses SMap_View::getTiles()
* @uses SMap_Form::getClickCoords()
* @uses SMap_Tile::getImageMap()
$tg = $this->form->getTileVars();
//See if we have positioning info and an accurate view ID
if( !isset ($tg['tileGrid'][SMap_Tile::POSX]) ||
if(!isset ($this->views[$viewId])){
throw new SMap_Ex('Attempted to handle map on non-existant view id: '. $viewId);
$tiles = $this->views[$viewId]->getTiles($this->form);
if(!isset ($tiles[$ty][$tx])){
* This may ocour when the parameters passed as the tile grid
* position are actually outside of the current grid. This can
* happen when changing the dimensions of the grid after using the
|