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

Source for file Layer.php

Documentation is available at Layer.php

  1. <?php
  2. /**
  3.  * Layers display the content of a view
  4.  * 
  5.  * SMap requires at least PHP version 5, but there are important bug fixes in
  6.  * more recent versions of PHP. Try to stay current.
  7.  *
  8.  * <b>License</b>:
  9.  * 
  10.  * Copyright (c) 2006-2007, Seth Price <{@link mailto:seth@pricepages.org}
  11.  * seth@pricepages.org}> All rights reserved.
  12.  *
  13.  * Redistribution and use in source and binary forms, with or without
  14.  * modification, are permitted provided that the following conditions
  15.  * are met:
  16.  *
  17.  * - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  18.  * - 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.
  19.  * - The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  22.  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  23.  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  24.  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  25.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  26.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  29.  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32.  *
  33.  * @access public
  34.  * @author Seth Price <seth@pricepages.org>
  35.  * @copyright Seth Price, 2006-2007
  36.  * @package        SMap
  37.  */
  38.  
  39. /**
  40.  * A layer that can be added to the map
  41.  * 
  42.  * Layers are the basis for users drawing on the map. Create a layer and add it
  43.  * to a view using {@link SMap_View::addLayer()}. If the layer is enabled in the
  44.  * view, it will be drawn.
  45.  * 
  46.  * Layers are drawn using {@link SMap_Layer::getMapObjs()}{@link SMap_Layer::}
  47.  * getMapObjs()}, and {@link SMap_View::sendObj()}. The objects passed via these
  48.  * calls are sent to {@link SMap_Tile}, which then composites and renders them.
  49.  * 
  50.  * @package        SMap
  51.  * @subpackage    Layers
  52.  */
  53. abstract class SMap_Layer {
  54.     /**
  55.      * Each layer that can be active has an integer ID. This ID is used to
  56.      * determine if the layer is enabled or not, and it is used to keep track of
  57.      * which layers are enabled in a given map.
  58.      * 
  59.      * @var        integer 
  60.      */
  61.     protected $id;
  62.     
  63.     /**
  64.      * The map that this layer is embedded in
  65.      * 
  66.      * @var        object 
  67.      * @see        SMap
  68.      */
  69.     protected $map;
  70.     
  71.     /**
  72.      * Construct the layer
  73.      * 
  74.      * Each layer should have a unique ID
  75.      * 
  76.      * @param    integer    This layer's ID
  77.      */
  78.     function __construct(SMap $map$id){
  79.         $this->id = (int) $id;
  80.         $this->map = $map;
  81.     }
  82.     
  83.     /**
  84.      * Is this layer zoomed to a useful resolution?
  85.      * 
  86.      * Note that a layer should be drawable at any resolution, but sometimes it
  87.      * just doesn't make sense to zoom in anymore. This may be because an image
  88.      * is at it's maximum resolution, or all the labels are displayed easily, or
  89.      * only a few regions are displayed.
  90.      * 
  91.      * @return boolean 
  92.      */
  93.     function isValidZoom($zoomNum){
  94.         return true;
  95.     }
  96.     
  97.     /**
  98.      * Recommended format for this layer
  99.      * 
  100.      * What is the best image format for this layer? The IMAGETYPE_PNG should be
  101.      * used for line art and text. IMAGETYPE_JPEG should be used for
  102.      * photographs, such as satelite imagery.
  103.      * 
  104.      * {@link http://en.wikipedia.org/wiki/Image_file_formats A PNG image is a}
  105.      * "lossless" image}. All information is kept. This is the default, but for
  106.      * the best file size, use JPEG on photographic images.
  107.      * 
  108.      * To be safe, this value is not determined within the SMap package, so if
  109.      * you wish your layer to express its preference, you will have to override
  110.      * this method.
  111.      * 
  112.      * @return    integer    An image format
  113.      */
  114.     public function getImageFmt(){
  115.         return IMAGETYPE_PNG;
  116.     }
  117.     
  118.     /**
  119.      * Might have cached rects in the DB
  120.      * 
  121.      * Might this layer have cached rects in the DB? Layers which have cached
  122.      * rects include layers which use labels.
  123.      * 
  124.      * @return    boolean 
  125.      */
  126.     public function hasCachedRects(){
  127.         return true;
  128.     }
  129.     
  130.     /**
  131.      * How cachable is this layer?
  132.      * 
  133.      * Return the "cache status" of this layer. The intent of this is to
  134.      * determine whether we should use the cache images generated by this layer.
  135.      * 
  136.      * False is returned if the layer should not be cached. True is returned if
  137.      * the layer can be cached, but it really doesn't matter either way. For
  138.      * real control when caching, return an integer. The returned number should
  139.      * be the number of seconds the cache is valid for.
  140.      * 
  141.      * Note that {@link SMap_Tile::tileGrid} is ignored when caching.
  142.      * 
  143.      * @return    mixed    True, false, or time in seconds.
  144.      */
  145.     public function cacheStatus(){
  146.         return false;
  147.     }
  148.     
  149.     /**
  150.      * Composite this layer onto the tile
  151.      * 
  152.      * @param    object    The view we are compositing in
  153.      * @param    object    The tile we are compositing to
  154.      * @return    array    The objects to composite
  155.      */
  156.     abstract public function getMapObjs(SMap_View $viewSMap_Tile $tile);
  157.     
  158.     /**
  159.      * Composite this layer onto the tile (in a raster way)
  160.      * 
  161.      * @param    object    The view we are compositing in
  162.      * @param    object    The tile we are compositing to
  163.      * @return    array    The objects to composite
  164.      */
  165.     abstract public function getImgObjs(SMap_View $viewSMap_Tile $tile);
  166.     
  167.     /**
  168.      * Adjust the view coords if we are in an image
  169.      * 
  170.      * Uses the current bounds and the tile grid to calculate the view bounds of
  171.      * the original image.
  172.      * 
  173.      * @param    array    The current tile grid
  174.      * @return    array    The overall view bounds
  175.      */
  176.     protected function getAdjViewBounds($tG){
  177.         if(    !isset($tG[SMap_Tile::POSX]||
  178.             !isset($tG[SMap_Tile::POSY]||
  179.             !isset($tG[SMap_Tile::VIEWX]||
  180.             !isset($tG[SMap_Tile::VIEWY])  ){
  181.             
  182.             return null;
  183.         }
  184.         
  185.         $B $this->map->bounds;
  186.  
  187.         $diffX $B[SMap::MAXX$B[SMap::MINX];
  188.         $diffY $B[SMap::MAXY$B[SMap::MINY];
  189.         
  190.         $B[SMap::MAXY$B[SMap::MAXY$diffY $tG[SMap_Tile::POSY];
  191.         
  192.         $B[SMap::MAXX$B[SMap::MAXX$diffX *
  193.             ($tG[SMap_Tile::VIEWX$tG[SMap_Tile::POSX1);
  194.             
  195.         $B[SMap::MINY$B[SMap::MINY$diffY *
  196.             ($tG[SMap_Tile::VIEWY$tG[SMap_Tile::POSY1);
  197.         
  198.         $B[SMap::MINX$B[SMap::MINX$diffX $tG[SMap_Tile::POSX];
  199.         
  200.         return $B;
  201.     }
  202.     
  203.     /**
  204.      * Get the bound at a pixel offset from a bound.
  205.      * 
  206.      * @param    object    View we are drawing in
  207.      * @param    integer    Index of the side we are working with
  208.      * @param    integer    Pixels offset
  209.      */
  210.     protected function pxOffBound(SMap_View $view$boundSide$pixels){
  211.         switch($boundSide){
  212.             case SMap::MAXY:
  213.                 return $view->bounds[SMap::MAXY$view->scale[1$pixels;
  214.             case SMap::MAXX:
  215.                 return $view->bounds[SMap::MAXX$view->scale[0$pixels;
  216.             case SMap::MINY:
  217.                 return $view->bounds[SMap::MINY$view->scale[1$pixels;
  218.             case SMap::MINX:
  219.                 return $view->bounds[SMap::MINX$view->scale[0$pixels;
  220.             default:
  221.                 throw new SMap_Ex('Unknown bound side passed: '.$boundSide);
  222.         }
  223.     }
  224.     
  225.     /**
  226.      * Returns the ID of this layer
  227.      * 
  228.      * No two layers should have the same ID
  229.      * 
  230.      * @return    integer    This layer's ID
  231.      */
  232.     public function getID(){
  233.         return $this->id;
  234.     }
  235.     
  236.     /**
  237.      * Return the map that contains this layer
  238.      * 
  239.      * @return    object    The map object
  240.      */
  241.     public function getMap(){
  242.         return $this->map;
  243.     }
  244. }
  245.  
  246. /**
  247.  * Represents a group of layers
  248.  * 
  249.  * @package        SMap
  250.  * @subpackage    Layers
  251.  */
  252. class SMap_Layer_Group extends SMap_Layer {
  253.     
  254.     /**
  255.      * Store a set of layers
  256.      * 
  257.      * @var        array 
  258.      */
  259.     protected $layers = array();
  260.     
  261.     /**
  262.      * Composite each child layer
  263.      * 
  264.      * @param    object    Pass this view to each layer
  265.      * @param    object    Pass this tile to each layer
  266.      * @return    array    The objects
  267.      */
  268.     public function getMapObjs(SMap_View $viewSMap_Tile $tile){
  269.         $objs array();
  270.         
  271.         foreach($this->layers as $l){
  272.             $objs array_merge($objs$l->getMapObjs($view$tile));
  273.         }
  274.         
  275.         return $objs;
  276.     }
  277.     
  278.     /**
  279.      * Composite each child layer
  280.      * 
  281.      * @param    object    Pass this view to each layer
  282.      * @param    object    Pass this tile to each layer
  283.      * @return    array    The objects
  284.      */
  285.     public function getImgObjs(SMap_View $viewSMap_Tile $tile){
  286.         $objs array();
  287.         
  288.         foreach($this->layers as $l){
  289.             $objs array_merge($objs$l->getImgObjs($view$tile));
  290.         }
  291.         
  292.         return $objs;
  293.     }
  294. }
  295.  
  296. /**
  297.  * An OGR readable vector file
  298.  * 
  299.  * Render a set of layers out of a OGR compatable file using the OGR library
  300.  * with the php_ogr binding.
  301.  * 
  302.  * For more information on the ogr library, see http://www.gdal.org/ogr/ . For
  303.  * information on php_ogr, see http://dl.maptools.org/dl/php_ogr/ .
  304.  * 
  305.  * To render a shapefile in any reasonable timeframe via OGR, you must index it.
  306.  * I've been indexing it via this command:
  307.  * 
  308.  * ogr2ogr -update -sql 'CREATE SPATIAL INDEX ON admin_polyg' admin_polyg
  309.  * admin_polyg
  310.  * 
  311.  * replace 'admin_polyg' with the layer name you're working with.
  312.  * 
  313.  * @package        SMap
  314.  * @subpackage    Layers
  315.  */
  316. class SMap_Layer_OGR extends SMap_Layer {
  317.     
  318.     /**
  319.      * Have the drivers been registered?
  320.      * 
  321.      * @param    boolean 
  322.      */
  323.     private static $regDrivers false;
  324.     
  325.     /**
  326.      * OGR Data source
  327.      * 
  328.      * @param    resource 
  329.      */
  330.     protected    $ds = null;
  331.     
  332.     /**
  333.      * OGR Data source file name
  334.      * 
  335.      * @param    string 
  336.      */
  337.     protected    $dsFn = null;
  338.     
  339.     /**
  340.      * OGR Driver
  341.      * 
  342.      * @param    resource 
  343.      */
  344.     protected    $driver = null;
  345.     
  346.     /**
  347.      * OGR Layers
  348.      * 
  349.      * @param    array 
  350.      */
  351.     protected $layers = array();
  352.     
  353.     /**
  354.      * SQL queries
  355.      * 
  356.      * @param    array 
  357.      */
  358.     protected $queries = array();
  359.     
  360.     /**
  361.      * Construct the layer
  362.      * 
  363.      * Each layer should have a unique ID
  364.      * 
  365.      * @param    object 
  366.      * @param    integer    This layer's ID
  367.      */
  368.     function __construct(SMap $map$id){
  369.         parent::__construct($map$id);
  370.         
  371.         //Register the drivers if needed
  372.         if(!self::$regDrivers){
  373.             OGRRegisterAll();
  374.             self::$regDrivers true;
  375.         }
  376.     }
  377.     
  378.     /**
  379.      * Add a set of files as a datasource
  380.      * 
  381.      * The array is integer indicies and string file names. The files are
  382.      * organized such that each index is the maximum zoom level which the
  383.      * datasource should be applied at.
  384.      * 
  385.      * If you save different resolutions of your vector files you can prevent
  386.      * the drawing of too much or too little detail as needed.
  387.      * 
  388.      * @param    array    List of files and zoom levels
  389.      */
  390.     function addFiles($files){
  391.         $zoom $this->map->zoom;
  392.         
  393.         //Decrease the sampling zoom until we find one that fits
  394.         while(!isset($files[$zoom]&& $zoom 0){
  395.             --$zoom;
  396.         }
  397.         
  398.         $this->dsFn = $files[$zoom];
  399.     }
  400.     
  401.     /**
  402.      * Open the datasource
  403.      */
  404.     protected function dsOpen(){
  405.         if(!empty($this->ds)){
  406.             throw new SMap_Ex('DS already open!');
  407.         }
  408.         
  409.         //Open the data source
  410.         $this->ds = OGROpen($this->dsFn0$this->driver);
  411.         
  412.         if(empty($this->ds)){
  413.             throw new SMap_Ex('Unable to open OGR source "'.$this->dsFn.'"');
  414.         }
  415.     }
  416.     
  417.     /**
  418.      * Appends a layer by name
  419.      * 
  420.      * @param    string    Layer name
  421.      */
  422.     function appendLayerByName($layerName){
  423.         if(empty($this->ds)){
  424.             $this->dsOpen();
  425.         }
  426.         
  427.         //Get the requested layer
  428.         $layer OGR_DS_GetLayerByName($this->ds$layerName);
  429.         
  430.         if(!$layer){
  431.             throw new SMap_Ex('Unable to get layer "'.$layerName.'"');
  432.         }
  433.         
  434.         $this->layers[$layerNamearray($layer);
  435.     }
  436.     
  437.     /**
  438.      * Appends a layer by SQL
  439.      * 
  440.      * Actually just queues the SQL statement until the actual objects are
  441.      * requested in order to implement spatial filtering.
  442.      * 
  443.      * @param    string    SQL
  444.      */
  445.     function appendLayerBySQL($sql){
  446.         $this->queries[$sql;
  447.     }
  448.     
  449.     /**
  450.      * Make the spatial filter
  451.      * 
  452.      * @return    resource    OGR geometry
  453.      */
  454.     protected function createSFilter($B$adjX$adjY){
  455.         $sFilter OGR_G_CreateGeometry(wkbLinearRing);
  456.         OGR_G_AddPoint($sFilter,    $B[SMap::MINX$adjX,
  457.                                 $B[SMap::MINY$adjY0.0 );
  458.         OGR_G_AddPoint($sFilter,    $B[SMap::MINX$adjX,
  459.                                 $B[SMap::MAXY$adjY0.0 );
  460.         OGR_G_AddPoint($sFilter,    $B[SMap::MAXX$adjX,
  461.                                 $B[SMap::MAXY$adjY0.0 );
  462.         OGR_G_AddPoint($sFilter,    $B[SMap::MAXX$adjX,
  463.                                 $B[SMap::MINY$adjY0.0 );
  464.         OGR_G_AddPoint($sFilter,    $B[SMap::MINX$adjX,
  465.                                 $B[SMap::MINY$adjY0.0 );
  466.         
  467.         
  468.         return $sFilter;
  469.     }
  470.     
  471.     /**
  472.      * Get the objects to be rendered
  473.      * 
  474.      * @param    object    The view
  475.      * @param    array    Tile bounds
  476.      * @param    integer    Bounds moved in X dir
  477.      * @param    integer    Bonuds moved in Y dir
  478.      * @param    array    Dump objects here
  479.      */
  480.     protected function createObjects(SMap_View $view$B$adjX$adjY&$objs){
  481.         $sqlLayers array();
  482.         
  483.         //First handle the spatial filtering
  484.         $sFilter $this->createSFilter($B$adjX$adjY);
  485.         
  486.         //Then run any SQL queries
  487.         foreach($this->queries as $sql){
  488.             $sqlLayers[array(OGR_DS_ExecuteSQL($this->ds$sql$sFilternull));
  489.         }
  490.         
  491.         //Collect objects from each layer
  492.         if($this->layers || $sqlLayers){
  493.             //Print out a full layer
  494.             foreach($this->layers as $val){
  495.                 OGR_L_SetSpatialFilter($val[0]$sFilter);
  496.                 $this->fetchLayer($view$val$adjX$adjY$objs);
  497.             }
  498.             
  499.             //Print out the results of the SQL statement
  500.             foreach($sqlLayers as $val){
  501.                 $this->fetchLayer($view$val$adjX$adjY$objs);
  502.             }
  503.         else {
  504.             //Print all layers
  505.             $num OGR_DS_GetLayerCount($this->ds);
  506.             for($i 0$i $num++$i){
  507.                 $layer OGR_DS_GetLayer($this->ds$i);
  508.                 OGR_L_SetSpatialFilter($layer$sFilter);
  509.                 $this->fetchLayer($viewarray($layer)$adjX$adjY$objs);
  510.             }
  511.         }
  512.         
  513.         //Release SQL result sets
  514.         foreach($sqlLayers as $rs){
  515.             OGR_DS_ReleaseResultSet($this->ds$rs);
  516.         }
  517.     }
  518.     
  519.     /**
  520.      * Create and append a set of objects from the given layer
  521.      * 
  522.      * @param    object    The SMap view
  523.      * @param    array    The layer and assoc info
  524.      * @param    array    Append created objects here
  525.      */
  526.     protected function fetchLayer(SMap_View $view$layer$adjX$adjY&$objs){
  527.         
  528.         while(($feat OGR_L_GetNextFeature($layer[0])) != null){
  529.             if(($geom OGR_F_GetGeometryRef($feat)) != null ){
  530.                 switch(OGR_G_GetGeometryType($geom)){
  531.                 case wkbPoint:
  532.                 case wkbPoint25D:
  533.                     $ret $this->fetchPoint($view,$geom,$adjX,$adjY,$objs);
  534.                     break;
  535.                 case wkbLineString:
  536.                 case wkbLineString25D:
  537.                     $ret $this->fetchLineString($view,$geom,$adjX,$adjY,$objs);
  538.                     break;
  539.                 case wkbPolygon:
  540.                 case wkbPolygon25D:
  541.                     $ret $this->fetchPolygon($view,$geom,$adjX,$adjY,$objs);
  542.                     break;
  543.        &