Files
oc-server3/htdocs/okapi/services/caches/map/tile.php
2014-10-08 10:06:07 +02:00

229 lines
7.8 KiB
PHP

<?php
namespace okapi\services\caches\map\tile;
use Exception;
use okapi\Okapi;
use okapi\Settings;
use okapi\Cache;
use okapi\FileCache;
use okapi\Db;
use okapi\OkapiRequest;
use okapi\OkapiHttpResponse;
use okapi\ParamMissing;
use okapi\InvalidParam;
use okapi\BadRequest;
use okapi\DoesNotExist;
use okapi\OkapiInternalRequest;
use okapi\OkapiInternalConsumer;
use okapi\OkapiServiceRunner;
use okapi\OkapiLock;
use okapi\services\caches\map\TileTree;
use okapi\services\caches\map\DefaultTileRenderer;
use okapi\services\caches\search\SearchAssistant;
require_once('tiletree.inc.php');
require_once('tilerenderer.inc.php');
require_once($GLOBALS['rootpath']."okapi/services/caches/search/searching.inc.php");
class WebService
{
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging the tile renderer.
*/
private static $USE_ETAGS_CACHE = true;
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging the tile renderer.
*/
private static $USE_IMAGE_CACHE = true;
/**
* Should be always true. You may temporarily set it to false, when you're
* testing/debugging. Grep the code to check when this flag is used.
*/
private static $USE_OTHER_CACHE = true;
public static function options()
{
return array(
'min_auth_level' => 3
);
}
private static function require_uint($request, $name, $min_value = 0)
{
$val = $request->get_parameter($name);
if ($val === null)
throw new ParamMissing($name);
$ret = intval($val);
if ($ret < 0 || ("$ret" !== $val))
throw new InvalidParam($name, "Expecting non-negative integer.");
return $ret;
}
public static function call(OkapiRequest $request)
{
$checkpointA_started = microtime(true);
# Make sure the request is internal.
if (!in_array($request->consumer->key, array('internal', 'facade')))
throw new BadRequest("Your Consumer Key has not been allowed to access this method.");
# zoom, x, y - required tile-specific parameters.
$zoom = self::require_uint($request, 'z');
if ($zoom > 21)
throw new InvalidParam('z', "Maximum value for this parameter is 21.");
$x = self::require_uint($request, 'x');
$y = self::require_uint($request, 'y');
if ($x >= 1<<$zoom)
throw new InvalidParam('x', "Should be in 0..".((1<<$zoom) - 1).".");
if ($y >= 1<<$zoom)
throw new InvalidParam('y', "Should be in 0..".((1<<$zoom) - 1).".");
# Now, we will create a search set (or use one previously created).
# Instead of creating a new OkapiInternalRequest object, we will pass
# the current request directly. We can do that, because we inherit all
# of the "save" method's parameters.
$search_set = OkapiServiceRunner::call('services/caches/search/save', $request);
$set_id = $search_set['set_id'];
# Get caches which are present in the result set AND within the tile
# (+ those around the borders).
$rs = TileTree::query_fast($zoom, $x, $y, $set_id);
$rows = array();
if ($rs !== null)
{
while ($row = mysql_fetch_row($rs))
$rows[] = $row;
unset($row);
}
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointA", null,
microtime(true) - $checkpointA_started);
$checkpointB_started = microtime(true);
# Add dynamic, user-related flags.
if (count($rows) > 0)
{
# Load user-related cache ids.
$cache_key = "tileuser/".$request->token->user_id;
$user = self::$USE_OTHER_CACHE ? Cache::get($cache_key) : null;
if ($user === null)
{
$user = array();
# Ignored caches.
$rs = Db::query("
select cache_id
from cache_ignore
where user_id = '".mysql_real_escape_string($request->token->user_id)."'
");
$user['ignored'] = array();
while (list($cache_id) = mysql_fetch_row($rs))
$user['ignored'][$cache_id] = true;
# Found caches.
$rs = Db::query("
select distinct cache_id
from cache_logs
where
user_id = '".mysql_real_escape_string($request->token->user_id)."'
and type = 1
and ".((Settings::get('OC_BRANCH') == 'oc.pl') ? "deleted = 0" : "true")."
");
$user['found'] = array();
while (list($cache_id) = mysql_fetch_row($rs))
$user['found'][$cache_id] = true;
# Own caches.
$rs = Db::query("
select distinct cache_id
from caches
where user_id = '".mysql_real_escape_string($request->token->user_id)."'
");
$user['own'] = array();
while (list($cache_id) = mysql_fetch_row($rs))
$user['own'][$cache_id] = true;
Cache::set($cache_key, $user, 30);
}
# Add extra flags to geocaches.
foreach ($rows as &$row_ref)
{
# Add the "found" flag (to indicate that this cache needs
# to be drawn as found) and the "own" flag (to indicate that
# the current user is the owner).
if (isset($user['found'][$row_ref[0]]))
$row_ref[6] |= TileTree::$FLAG_FOUND; # $row[6] is "flags"
if (isset($user['own'][$row_ref[0]]))
$row_ref[6] |= TileTree::$FLAG_OWN; # $row[6] is "flags"
}
}
# Compute the image hash/fingerprint. This will be used both for ETags
# and internal cache ($cache_key).
$tile = new DefaultTileRenderer($zoom, $rows);
$image_fingerprint = $tile->get_unique_hash();
# Start creating response.
$response = new OkapiHttpResponse();
$response->content_type = $tile->get_content_type();
$response->cache_control = "Cache-Control: private, max-age=600";
$response->etag = 'W/"'.$image_fingerprint.'"';
# Check if the request didn't include the same ETag.
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointB", null,
microtime(true) - $checkpointB_started);
$checkpointC_started = microtime(true);
if (self::$USE_ETAGS_CACHE && ($request->etag == $response->etag))
{
# Hit. Report the content was unmodified.
$response->etag = null;
$response->status = "304 Not Modified";
return $response;
}
# Check if the image was recently rendered and is kept in image cache.
$cache_key = "tile/".$image_fingerprint;
$response->body = self::$USE_IMAGE_CACHE ? Cache::get($cache_key) : null;
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointC", null,
microtime(true) - $checkpointC_started);
$checkpointD_started = microtime(true);
if ($response->body !== null)
{
# Hit. We will use the cached version of the image.
return $response;
}
# Miss. Render the image. Cache the result.
$response->body = $tile->render();
Cache::set_scored($cache_key, $response->body);
OkapiServiceRunner::save_stats_extra("caches/map/tile/checkpointD", null,
microtime(true) - $checkpointD_started);
return $response;
}
}