Bus Stop density heatmap
[busui.git] / labs / busstopdensity.tile.php
blob:a/labs/busstopdensity.tile.php -> blob:b/labs/busstopdensity.tile.php
  <?php
  include ('../include/common.inc.php');
  $debugOkay = Array();
   
  /*
  *DISCLAIMER
  * http://blog.gmapify.fr/create-beautiful-tiled-heat-maps-with-php-and-gd
  *THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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: Olivier G. <olbibigo_AT_gmail_DOT_com>
  * @version: 1.0
  * @history:
  * 1.0 creation
  */
  set_time_limit(120);//2mn
  ini_set('memory_limit', '256M');
  error_reporting(E_ALL ^ E_DEPRECATED);
  require_once ('lib/GoogleMapUtility.php');
  require_once ('lib/HeatMap.php');
   
  //Root folder to store generated tiles
  define('TILE_DIR', 'tiles/');
  //Covered geographic areas
  define('MIN_LAT', -35.48);
  define('MAX_LAT', -35.15);
  define('MIN_LNG', 148.98);
  define('MAX_LNG', 149.25);
  define('TILE_SIZE_FACTOR', 0.5);
  define('SPOT_RADIUS', 30);
  define('SPOT_DIMMING_LEVEL', 50);
   
  //Input parameters
  if(isset($_GET['x']))
  $X = (int)$_GET['x'];
  else
  exit("x missing");
  if(isset($_GET['y']))
  $Y = (int)$_GET['y'];
  else
  exit("y missing");
  if(isset($_GET['zoom']))
  $zoom = (int)$_GET['zoom'];
  else
  exit("zoom missing");
   
  $dir = TILE_DIR.$zoom;
  $tilename = $dir.'/'.$X.'_'.$Y.'.png';
  //HTTP headers (data type and caching rule)
  header("Cache-Control: must-revalidate");
  header("Expires: " . gmdate("D, d M Y H:i:s", time() + 86400) . " GMT");
  if(!file_exists($tilename)){
  $rect = GoogleMapUtility::getTileRect($X, $Y, $zoom);
  //A tile can contain part of a spot with center in an adjacent tile (overlaps).
  //Knowing the spot radius (in pixels) and zoom level, a smart way to process tiles would be to compute the box (in decimal degrees) containing only spots that can be drawn on current tile. We choose a simpler solution by increeasing geo bounds by 2*TILE_SIZE_FACTOR whatever the zoom level and spot radius.
  $extend_X = $rect->width * TILE_SIZE_FACTOR;//in decimal degrees
  $extend_Y = $rect->height * TILE_SIZE_FACTOR;//in decimal degrees
  $swlat = $rect->y - $extend_Y;
  $swlng = $rect->x - $extend_X;
  $nelat = $swlat + $rect->height + 2 * $extend_Y;
  $nelng = $swlng + $rect->width + 2 * $extend_X;
   
  if( ($nelat <= MIN_LAT) || ($swlat >= MAX_LAT) || ($nelng <= MIN_LNG) || ($swlng >= MAX_LNG)){
  //No geodata so return generic empty tile
  echo file_get_contents(TILE_DIR.'empty.png');
  exit();
  }
   
  //Get McDonald's spots
  $spots = fGetPOI('Select * from stops where
  (stop_lon > '.$swlng.' AND stop_lon < '.$nelng.')
  AND (stop_lat < '.$nelat.' AND stop_lat > '.$swlat.')', $im, $X, $Y, $zoom, SPOT_RADIUS);
   
   
  if(empty($spots)){
  //No geodata so return generic empty tile
  header('Content-type: image/png');
  echo file_get_contents(TILE_DIR.'empty.png');
  }else{
  if(!file_exists($dir)){
  mkdir($dir, 0705);
  }
  //All the magics is in HeatMap class :)
  $im = HeatMap::createImage($spots, GoogleMapUtility::TILE_SIZE, GoogleMapUtility::TILE_SIZE, heatMap::$WITH_ALPHA, SPOT_RADIUS, SPOT_DIMMING_LEVEL, HeatMap::$GRADIENT_FIRE);
  //Store tile for reuse and output it
  header('content-type:image/png;');
  imagepng($im, $tilename);
  echo file_get_contents($tilename);
  imagedestroy($im);
  unset($im);
  }
  }else{
  //Output stored tile
  header('content-type:image/png;');
  echo file_get_contents($tilename);
  }
  /////////////
  //Functions//
  /////////////
  function fGetPOI($query, &$im, $X, $Y, $zoom, $offset){
  global $conn;
  $nbPOIInsideTile = 0;
   
  $result = pg_query($conn, $query);
  $spots = Array();
  if (!$result) {
  databaseError(pg_result_error($result));
  return Array();
  }
  foreach( pg_fetch_all($result) as $row){
  $point = GoogleMapUtility::getOffsetPixelCoords($row['stop_lat'], $row['stop_lon'], $zoom, $X, $Y);
  //Count result only in the tile
  if( ($point->x > -$offset) && ($point->x < (GoogleMapUtility::TILE_SIZE+$offset)) && ($point->y > -$offset) && ($point->y < (GoogleMapUtility::TILE_SIZE+$offset))){
  $spots[] = new HeatMapPoint($point->x, $point->y);
  }
   
  }//while
  return $spots;
  }//fAddPOI
  ?>