229 lines
7.8 KiB
PHP
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;
|
|
}
|
|
}
|