Source for file Layer.php
Documentation is available at Layer.php
* Layers display the content 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.
* @author Seth Price <seth@pricepages.org>
* @copyright Seth Price, 2006-2007
* A layer that can be added to the map
* Layers are the basis for users drawing on the map. Create a layer and add it
* to a view using {@link SMap_View::addLayer()}. If the layer is enabled in the
* view, it will be drawn.
* Layers are drawn using {@link SMap_Layer::getMapObjs()}, {@link SMap_Layer::}
* getMapObjs()}, and {@link SMap_View::sendObj()}. The objects passed via these
* calls are sent to {@link SMap_Tile}, which then composites and renders them.
* Each layer that can be active has an integer ID. This ID is used to
* determine if the layer is enabled or not, and it is used to keep track of
* which layers are enabled in a given map.
* The map that this layer is embedded in
* Each layer should have a unique ID
* @param integer This layer's ID
* Is this layer zoomed to a useful resolution?
* Note that a layer should be drawable at any resolution, but sometimes it
* just doesn't make sense to zoom in anymore. This may be because an image
* is at it's maximum resolution, or all the labels are displayed easily, or
* only a few regions are displayed.
* Recommended format for this layer
* What is the best image format for this layer? The IMAGETYPE_PNG should be
* used for line art and text. IMAGETYPE_JPEG should be used for
* photographs, such as satelite imagery.
* {@link http://en.wikipedia.org/wiki/Image_file_formats A PNG image is a}
* "lossless" image}. All information is kept. This is the default, but for
* the best file size, use JPEG on photographic images.
* To be safe, this value is not determined within the SMap package, so if
* you wish your layer to express its preference, you will have to override
* @return integer An image format
* Might have cached rects in the DB
* Might this layer have cached rects in the DB? Layers which have cached
* rects include layers which use labels.
* How cachable is this layer?
* Return the "cache status" of this layer. The intent of this is to
* determine whether we should use the cache images generated by this layer.
* False is returned if the layer should not be cached. True is returned if
* the layer can be cached, but it really doesn't matter either way. For
* real control when caching, return an integer. The returned number should
* be the number of seconds the cache is valid for.
* Note that {@link SMap_Tile::tileGrid} is ignored when caching.
* @return mixed True, false, or time in seconds.
* Composite this layer onto the tile
* @param object The view we are compositing in
* @param object The tile we are compositing to
* @return array The objects to composite
abstract public function getMapObjs(SMap_View $view, SMap_Tile $tile);
* Composite this layer onto the tile (in a raster way)
* @param object The view we are compositing in
* @param object The tile we are compositing to
* @return array The objects to composite
abstract public function getImgObjs(SMap_View $view, SMap_Tile $tile);
* Adjust the view coords if we are in an image
* Uses the current bounds and the tile grid to calculate the view bounds of
* @param array The current tile grid
* @return array The overall view bounds
$diffX = $B[SMap::MAXX] - $B[SMap::MINX];
$diffY = $B[SMap::MAXY] - $B[SMap::MINY];
$B[SMap::MAXX] = $B[SMap::MAXX] + $diffX *
$B[SMap::MINY] = $B[SMap::MINY] - $diffY *
* Get the bound at a pixel offset from a bound.
* @param object View we are drawing in
* @param integer Index of the side we are working with
* @param integer Pixels offset
protected function pxOffBound(SMap_View $view, $boundSide, $pixels){
return $view->bounds[SMap::MAXY] - $view->scale[1] * $pixels;
return $view->bounds[SMap::MAXX] - $view->scale[0] * $pixels;
return $view->bounds[SMap::MINY] + $view->scale[1] * $pixels;
return $view->bounds[SMap::MINX] + $view->scale[0] * $pixels;
throw new SMap_Ex('Unknown bound side passed: '. $boundSide);
* Returns the ID of this layer
* No two layers should have the same ID
* @return integer This layer's ID
* Return the map that contains this layer
* @return object The map object
* Represents a group of layers
* Composite each child layer
* @param object Pass this view to each layer
* @param object Pass this tile to each layer
* @return array The objects
public function getMapObjs(SMap_View $view, SMap_Tile $tile){
$objs = array_merge($objs, $l->getMapObjs($view, $tile));
* Composite each child layer
* @param object Pass this view to each layer
* @param object Pass this tile to each layer
* @return array The objects
public function getImgObjs(SMap_View $view, SMap_Tile $tile){
$objs = array_merge($objs, $l->getImgObjs($view, $tile));
* An OGR readable vector file
* Render a set of layers out of a OGR compatable file using the OGR library
* with the php_ogr binding.
* For more information on the ogr library, see http://www.gdal.org/ogr/ . For
* information on php_ogr, see http://dl.maptools.org/dl/php_ogr/ .
* To render a shapefile in any reasonable timeframe via OGR, you must index it.
* I've been indexing it via this command:
* ogr2ogr -update -sql 'CREATE SPATIAL INDEX ON admin_polyg' admin_polyg
* replace 'admin_polyg' with the layer name you're working with.
* Have the drivers been registered?
private static $regDrivers = false;
* OGR Data source file name
* Each layer should have a unique ID
* @param integer This layer's ID
//Register the drivers if needed
self::$regDrivers = true;
* Add a set of files as a datasource
* The array is integer indicies and string file names. The files are
* organized such that each index is the maximum zoom level which the
* datasource should be applied at.
* If you save different resolutions of your vector files you can prevent
* the drawing of too much or too little detail as needed.
* @param array List of files and zoom levels
$zoom = $this->map->zoom;
//Decrease the sampling zoom until we find one that fits
while(!isset ($files[$zoom]) && $zoom > 0){
$this->dsFn = $files[$zoom];
throw new SMap_Ex('DS already open!');
throw new SMap_Ex('Unable to open OGR source "'. $this->dsFn. '"');
* Appends a layer by name
* @param string Layer name
//Get the requested layer
$layer = OGR_DS_GetLayerByName($this->ds, $layerName);
throw new SMap_Ex('Unable to get layer "'. $layerName. '"');
$this->layers[$layerName] = array($layer);
* Actually just queues the SQL statement until the actual objects are
* requested in order to implement spatial filtering.
* Make the spatial filter
* @return resource OGR geometry
$sFilter = OGR_G_CreateGeometry(wkbLinearRing);
OGR_G_AddPoint($sFilter, $B[SMap::MINX] + $adjX,
$B[SMap::MINY] + $adjY, 0.0 );
OGR_G_AddPoint($sFilter, $B[SMap::MINX] + $adjX,
$B[SMap::MAXY] + $adjY, 0.0 );
OGR_G_AddPoint($sFilter, $B[SMap::MAXX] + $adjX,
$B[SMap::MAXY] + $adjY, 0.0 );
OGR_G_AddPoint($sFilter, $B[SMap::MAXX] + $adjX,
$B[SMap::MINY] + $adjY, 0.0 );
OGR_G_AddPoint($sFilter, $B[SMap::MINX] + $adjX,
$B[SMap::MINY] + $adjY, 0.0 );
* Get the objects to be rendered
* @param array Tile bounds
* @param integer Bounds moved in X dir
* @param integer Bonuds moved in Y dir
* @param array Dump objects here
protected function createObjects(SMap_View $view, $B, $adjX, $adjY, &$objs){
//First handle the spatial filtering
//Then run any SQL queries
$sqlLayers[] = array(OGR_DS_ExecuteSQL($this->ds, $sql, $sFilter, null));
//Collect objects from each layer
if($this->layers || $sqlLayers){
foreach($this->layers as $val){
OGR_L_SetSpatialFilter($val[0], $sFilter);
$this->fetchLayer($view, $val, $adjX, $adjY, $objs);
//Print out the results of the SQL statement
foreach($sqlLayers as $val){
$this->fetchLayer($view, $val, $adjX, $adjY, $objs);
$num = OGR_DS_GetLayerCount($this->ds);
for($i = 0; $i < $num; ++ $i){
$layer = OGR_DS_GetLayer($this->ds, $i);
OGR_L_SetSpatialFilter($layer, $sFilter);
$this->fetchLayer($view, array($layer), $adjX, $adjY, $objs);
//Release SQL result sets
foreach($sqlLayers as $rs){
OGR_DS_ReleaseResultSet($this->ds, $rs);
* Create and append a set of objects from the given layer
* @param object The SMap view
* @param array The layer and assoc info
* @param array Append created objects here
protected function fetchLayer(SMap_View $view, $layer, $adjX, $adjY, &$objs){
while(($feat = OGR_L_GetNextFeature($layer[0])) != null){
if(($geom = OGR_F_GetGeometryRef($feat)) != null ){
switch(OGR_G_GetGeometryType($geom)){
$ret = $this->fetchPoint($view,$geom,$adjX,$adjY,$objs);
|