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'))) { /* Okay, these two consumers can always access it. */ } elseif ($request->consumer->hasFlag(OkapiConsumer::FLAG_MAPTILE_ACCESS)) { /* If the Consumer is aware that it is not backward-compatible, then * he may be granted permission to access it. */ } else { 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', new OkapiInternalRequest($request->consumer, $request->token, $request->get_all_parameters_including_unknown())); $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 = Db::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("\n select cache_id\n from cache_ignore\n where user_id = '" . Db::escape_string($request->token->user_id) . "'\n "); $user['ignored'] = array(); while (list($cache_id) = Db::fetch_row($rs)) { $user['ignored'][$cache_id] = true; } # Found caches. $rs = Db::query("\n select distinct cache_id\n from cache_logs\n where\n user_id = '" . Db::escape_string($request->token->user_id) . "'\n and type = 1\n and " . (Settings::get('OC_BRANCH') == 'oc.pl' ? "deleted = 0" : "true") . "\n "); $user['found'] = array(); while (list($cache_id) = Db::fetch_row($rs)) { $user['found'][$cache_id] = true; } # Own caches. $rs = Db::query("\n select distinct cache_id\n from caches\n where user_id = '" . Db::escape_string($request->token->user_id) . "'\n "); $user['own'] = array(); while (list($cache_id) = Db::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 TileRenderer($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 . '"'; $response->allow_gzip = false; // images are usually compressed, prevent compression at Apache level # 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; }
/** * Return 64x26 bitmap with the caption (name) for the given geocache. */ private function get_caption($cache_id) { # Check cache. $cache_key = "tilecaption/" . self::$VERSION . "/" . $cache_id; $gd2 = self::$USE_CAPTIONS_CACHE ? Cache::get($cache_key) : null; if ($gd2 === null) { # We'll work with 16x bigger image to get smoother interpolation. $im = imagecreatetruecolor(64 * 4, 26 * 4); imagealphablending($im, false); $transparent = imagecolorallocatealpha($im, 255, 255, 255, 127); imagefilledrectangle($im, 0, 0, 64 * 4, 26 * 4, $transparent); imagealphablending($im, true); # Get the name of the cache. $name = Db::select_value("\n select name\n from caches\n where cache_id = '" . mysql_real_escape_string($cache_id) . "'\n "); # Split the name into a couple of lines. //$font = $GLOBALS['rootpath'].'util.sec/bt.ttf'; $font = $GLOBALS['rootpath'] . 'okapi/static/tilemap/tahoma.ttf'; $size = 25; $lines = explode("\n", self::wordwrap($font, $size, 64 * 4 - 6 * 2, $name)); # For each line, compute its (x, y) so that the text is centered. $y = 0; $positions = array(); foreach ($lines as $line) { $bbox = imagettfbbox($size, 0, $font, $line); $width = $bbox[2] - $bbox[0]; $x = 128 - ($width >> 1); $positions[] = array($x, $y); $y += 36; } $drawer = function ($x, $y, $color) use(&$lines, &$positions, &$im, &$size, &$font) { $len = count($lines); for ($i = 0; $i < $len; $i++) { $line = $lines[$i]; list($offset_x, $offset_y) = $positions[$i]; imagettftext($im, $size, 0, $offset_x + $x, $offset_y + $y, $color, $font, $line); } }; # Draw an outline. $outline_color = imagecolorallocatealpha($im, 255, 255, 255, 80); for ($x = 0; $x <= 12; $x += 3) { for ($y = $size - 3; $y <= $size + 9; $y += 3) { $drawer($x, $y, $outline_color); } } # Add a slight shadow effect (on top of the outline). $drawer(9, $size + 3, imagecolorallocatealpha($im, 0, 0, 0, 110)); # Draw the caption. $drawer(6, $size + 3, imagecolorallocatealpha($im, 150, 0, 0, 40)); # Resample. imagealphablending($im, false); $small = imagecreatetruecolor(64, 26); imagealphablending($small, false); imagecopyresampled($small, $im, 0, 0, 0, 0, 64, 26, 64 * 4, 26 * 4); # Cache it! ob_start(); imagegd2($small); $gd2 = ob_get_clean(); Cache::set_scored($cache_key, $gd2); } return imagecreatefromstring($gd2); }