Source for file Tile.php
Documentation is available at Tile.php
* The subdivision of a view
* 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
* Represents a piece of a view
* Each tile is a section of the {@link SMap_View view}. A tile is as large or
* small as needed. Larger tiles mean fewer requests to the webserver. Smaller
* tiles mean a greater opportunity for caching.
* Array indicies for tile position
* POSX,POSY are the position of the current tile in the grid. VIEWX,VIEWY
* are the dimensions of the view. TILESX,TILESY are the dimensions of the
* current map. VIEWX,VIEWY normaly equals TILESX,TILESY, except when a tile
* is being rendered. TILESX,TILESY are the only indicies that always are
* The map that contains this tile
* View that this tile is in
* List of layers that have been applied to this tile
* The bounds of this tile
* This tile's position relative to other tiles
* The tile grid marks the position of this tile in the rest of the view.
* This should be used sparingly in order to allow caching. The grid also
* won't be available if we have some sort of dynamic {@link }
* http://maps.google.com Google Maps} thing going on.
* Is the tile grid needed to draw this tile?
* If not, then we shouldn't pass those vars to the form. Not placing the
* grid for every tile will allow more effective caching.
* The raw image as retrieved from cache
* Image map for this image
* Cache time of this tile's data in seconds
* The option 'cacheTime' by default. There is no 'setter' function for
* cache time because {@link flatten()} calls
* {@link SMap_Layer::cacheStatus()} in order to set this value.
* Last modified time for this tile
* Might we be dealing with cached rects?
* Have we flattened this tile yet?
* Have we constrained this tile yet?
* Have we rendered this tile yet?
* Seperate each layer into groups of objects. Once {@link constrain()} is
* called, the objects are placed in {@link $objs}.
* Objects that need to be rendered
* Objects are {@link constrain() constrained} and dumped in here (in
* order) via {@link addObject()}.
protected $objs = array();
* Set the properties of this tile
* @param object The map that contains this tile
* @param object The view that contains this tile
* @param array The real bounds of the tile
* @param array Where this tile is in relation to the others
function __construct(SMap $map, SMap_View $view, $bounds, $grid){
if( $bounds[SMap::MAXX] <= $bounds[SMap::MINX] ||
$bounds[SMap::MAXY] <= $bounds[SMap::MINY] ){
throw new SMap_Ex( 'Poorly formatted bounds',
* 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);
* Display the current image
* Also produces the appropriate headers.
//Check if debugging is enabled
echo 'Image is not being displayed because debugging is enabled.'. "<br />\n";
//Send last modified & cache headers
header('Cache-Control: public, max-age='. $cacheTime);
//Default to last modified now
header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastMod). ' GMT');
header('Content-Type: image/jpeg');
header('Content-Type: image/png');
* Add a layer to this tile.
* Adding the {@link SMap_Layer layer} is the first step in getting it
* rendered. Each layer can only be added once. After all layers are added,
* {@link flatten()} must be called.
* The creation of layers should use few resources. Use {@link }
* SMap_Layer::applyTile()} to do any major setup. If there is a cache hit,
* @param object SMap_Layer
public function addLayer(SMap_Layer $layer){
throw new SMap_Ex('Tile already flattened, you can\'t add layer id '. $id);
//Die if layer already added
elseif(isset ($this->layers[$id])){
throw new SMap_Ex( 'SMap_Layer of ID '. $id. ' already exists',
* Sets the background color
* Only useful if called before the image is internally allocated. Exception
* thrown if called after image allocation.
public function setBackground($color){
throw new SMap_Ex('Background set after canvas allocated!');
} elseif($this->imgCache){
$this->initColor = $color;
* Ensures that we have a rendered image
* If the image doesn't already exist, create it. If layers have been
* added but not rendered, render them.
* @uses SMap_Object_Area_Label::drawLabels()
* @uses SMap_Object::draw()
//Image is already created
throw new SMap_Ex('Tile must be constrained before an image can be created.');
//The cache is the canvas if that's what we're doing
//Render returned objects
foreach($this->objs as $obj){
//Finish any labels that need drawing
* Get GET request vars to create an image URL
* Returns the variables that need to be passed to this tile in order to
* @return array GET variables
//If there is no image map, we need to calculate that first
'viewId' => $this->view->id,
'lang' => substr($this->map->lang->getLanguage(), 0, 3),
* Return the parameters for an image map (links)
* If we are returning a server side image map, one of the returned objects
* will be of shape 'ismap'. The form should be able to handle this case.
* If a user clicks on a server side image map, {@SMap::handleImageMap()}
* should be called, which will return the appropriate URL the user should
* If we are returning a client side image map, returned set of arrays
* represent the "area" of the image map. They have indexes at ['alt'],
* ['coords'], ['shape'], and ['href'].
* If there is no link here, we will return an empty array.
* @return array All image map parameters.
* @uses SMap_Object::map()
throw new SMap_Ex('Tile must be constrained before it is rendered.');
//Render returned objects
foreach($this->objs as $obj){
* Flattens the layers by reducing them to objects
* This is the second step in the process of rendering. The first is {@link }
* addLayer() adding the layers}. Here we take care of reducing each layer
* to objects, or retrieving the cached image.
* Flattening is done after all layers are added so background labels can
* avoid foreground objects (such as the panning graphic). This is done
* before the final render is done so that labels have a chance to organize
* and {@link SMap_View::sendObj() send themselves} to corrisponding tiles.
* Image format selection is also decided here. We hope that photo realistic
* images (such as satellite) can be compressed as JPEG, just because it's
* more efficient compression. If a single layer requests PNG compression,
* though, we'll have to use PNG. This will ensure that we aren't forcing
* lossy compression where there shouldn't be any.
* If all layers can agree on a different format (not PNG or JPEG), that
* format will be used. Ensure that the chosen format is enabled in
* {@link SMap_Object_CachedImage::cache()}, {@link }
* SMap_Object_CachedImage::getCachedFName()}, and {@link display()}.
* If applicable, we are also handling caching here. {@link }
* SMap_Layer::cacheStatus()} is called, and we figure out which layers
* should be cached, and which we can't cache at all. If the tile is
* cacheable and we have a cached tile, we retrieve it and use it. Otherwise
* we need to break it down to layers. Is each layer cacheable? Use a
* {@link SMap_Object_CachedImage cached layer's image} in place of
* @param boolean Are we rendering an image or image map?
* @uses SMap_Layer::getImageFmt()
* @uses SMap_Layer::getMapObjs()
//Handle error conditions
throw new SMap_Ex('Attempting to flatten a flat tile.');
} elseif(empty($this->layers)){
trigger_error('Tile mapped with no layers. Maybe you should add ' .
foreach($this->layers as $layer){
* Determine the filetype for this tile (ex: PNG or JPEG). Note that
* it would be dumb to layer a JPEG over a PNG because a JPEG has no
$fmt = $layer->getImageFmt();
//We are only caching images currently.
foreach($this->layers as $lid => $layer){
//Determine if we should look for cached rects
* The image specific part of flattening
* @uses SMap_Layer::cacheStatus()
* @uses SMap_Canvas_Cache
* @uses SMap_Object_CachedImage
* Check each layer for its preference. Layers with a specific timestamp
* return such, cacheable layers with no preference return true. This
* way, we can cache the entire (flattened, rasterized) tile easily, but
* we can also cache individual layers otherwise.
//Get the cache status of each layer
foreach($this->layers as $lid => $layer){
if($time = $layer->cacheStatus(true)){
$cacheLayer[$lid] = $time;
$cacheLayer[$lid] = false;
* If all layers can be cached, then check for a cached tile. If the
* cached tile exists, then we have everything done. Otherwise continue
|