User:Mstriewe/OSMTileProxy
Jump to navigation
Jump to search
This is an extension of ProxySimplePHP5 written by User:Kgolding which in turn is an extension of ProxySimplePHP written by User:Lizard.
I made some changes to get rid of the retry loop and to add basic logging, filtering of requests based on HTTP referers and a status page.
tiles.php:
<?php
/* Original source https://wiki.openstreetmap.org/wiki/ProxySimplePHP
* and https://wiki.openstreetmap.org/wiki/ProxySimplePHP5
*/
// Config section
$cacheDays = 15;
$minZoom = 5;
$maxZoom = 15;
$operatorMail = "your@email.com";
$allowedReferers = array(
"https://www.your-domain.de",
"https://www.another-domain.de"
);
$server = array(
"a.tile.openstreetmap.org",
"b.tile.openstreetmap.org",
"c.tile.openstreetmap.org"
);
// Add a log handler
set_error_handler(function($errno, $errstr, $errfile, $errline){
if($errno === E_ERROR || $errno === E_WARNING){
$fp = fopen("log.txt", "a");
fwrite($fp, $errstr . "\r\n" . $errfile . " in line " . $errline . "\r\n");
fclose($fp);
}
});
// Read parameters
$referer = $_SERVER['HTTP_REFERER'];
$x = intval($_GET['x']);
$y = intval($_GET['y']);
$z = intval($_GET['z']);
// Start processing
if (!empty($referer) && !empty($x) && !empty($y) && !empty($z)) { // Request has all required parameters
// Check request
$allowedRequest = false;
foreach ($allowedReferers as $a) {
if (strpos($referer, $a) === 0) {
$allowedRequest = true;
break;
}
}
if (!$allowedRequest) {
echo "Access denied";
die;
}
if ($z < $minZoom || $z > $maxZoom) {
echo "Sorry, this tile proxy does not serve tiles for the requested zoom level";
die;
}
// Prepare for chache lookup
$file = "tiles/$z/$x/$y.png";
$ttl = 60 * 60 * 24 * $cacheDays; //cache timeout in seconds
$img = null;
if (is_file($file) && @filemtime($file) >= time()-$ttl) { // We can serve a sufficiently fresh tile from cache
$img = file_get_contents($file);
} else { // We don't have the tile or it is too old
$url = 'https://'.$server[array_rand($server)];
$url .= "/".$z."/".$x."/".$y.".png";
$options = array(
'http'=>array(
'method'=>"GET",
'header'=>"User-Agent: OSMTileProxy/1.0 (operated by " . $operatorMail . ") based on https://wiki.openstreetmap.org/wiki/ProxySimplePHP\r\n
Referer: " . $referer . "\r\n"
)
);
$context = stream_context_create($options);
$img = @file_get_contents($url, false, $context);
if ($img) { // We got a fresh tile, so we store it
if (!file_exists(dirname($file))) {
@mkdir(dirname($file), 0755, true);
}
$fp = fopen($file, "w");
fwrite($fp, $img);
fclose($fp);
} else if (is_file($file)) { // If we got no fresh tile but have an old one, we serve that
$img = file_get_contents($file);
}
}
if ($img) { // There is something to serve
$exp_gmt = gmdate("D, d M Y H:i:s", time() + $ttl * 2) ." GMT";
$mod_gmt = gmdate("D, d M Y H:i:s", filemtime($file)) ." GMT";
header("Expires: " . $exp_gmt);
header("Cache-Control: public, max-age=" . $ttl * 2);
// for MSIE 5
header("Cache-Control: pre-check=" . $ttl * 2, FALSE);
header("Last-Modified: " . $mod_gmt);
header('Content-Type: image/png');
echo $img;
$fp = fopen("lastServed.txt", "w");
fwrite($fp, $file);
fclose($fp);
} else { // We cannot answer the request, so we log an error
$fp = fopen("lastMissed.txt", "w");
fwrite($fp, $file);
fclose($fp);
}
} else { // Request misses something, so we show the status page instead
$ttl = 60 * 60 * 24 * $cacheDays; //cache timeout in seconds
$fileCount = array();
$fileSize = array();
$fileFreshCount = array();
$lastRefreshed['file'] = "";
$lastRefreshed['time'] = 0;
$freshLimit = time()-$ttl;
// Create file statistics
for ($z = $minZoom; $z <= $maxZoom; $z++) {
if (is_dir('tiles/' . $z)) {
$xFolders = array_diff(scandir('tiles/' . $z), array('..', '.'));
foreach ($xFolders as $x) {
$yFiles = array_diff(scandir('tiles/' . $z . '/' . $x), array('..', '.'));
$fileCount[$z] += sizeof($yFiles);
foreach ($yFiles as $y) {
$fileSize[$z] += filesize('tiles/' . $z . '/' . $x . '/' . $y);
$time = filemtime('tiles/' . $z . '/' . $x . '/' . $y);
if ($time >= $freshLimit) {
$fileFreshCount[$z]++;
}
if ($time > $lastRefreshed['time']) {
$lastRefreshed['time'] = $time;
$lastRefreshed['file'] = 'tiles/' . $z . '/' . $x . '/' . $y;
}
}
}
}
}
// Write page content
echo '<html><head><title>OSM Tile Proxy (v1.0) status page</title></head><body>';
echo '<h1>OSM Tile Proxy (v1.0) status page</h1>';
echo '<p>This tile proxy is operated by ' . $operatorMail . '.</p>';
if (is_file("log.txt") && filesize("log.txt") > 0) {
echo '<p>There are errors or warnings in the log file!</p>';
}
if (is_file("lastMissed.txt") && filesize("lastMissed.txt") > 0) {
echo '<p>Last tile that could not be refreshed: ' . file_get_contents("lastMissed.txt") . '. Check log file for details.</p>';
}
echo '<h2>Latest activity</h2>';
$lastServed = @file_get_contents("lastServed.txt");
echo '<table border="1"><tr>';
echo '<td style="text-align:center">Last tile delivered:<br /><img src="' . $lastServed . '"><br />' . $lastServed . '<br />at ' . date(DATE_ATOM, filemtime("lastServed.txt")) . '</td>';
echo '<td style="text-align:center">Last tile refreshed:<br /><img src="' . $lastRefreshed['file'] . '"><br />' . $lastRefreshed['file'] . '<br />at ' . date(DATE_ATOM, $lastRefreshed['time']) . '</td>';
echo '</tr></table>';
$totalFiles = 0;
$totalSize = 0;
$totalFresh = 0;
echo '<h2>File statistics</h2>';
echo '<table border="1"><tr><th> </th><th>#Files in cache</th><th>#Files not older<br />than ' . $cacheDays . ' days</th></tr>';
for ($i = $minZoom; $i <= $maxZoom; $i++) {
echo '<tr><td>Zoom level ' . $i . '</td><td>' . $fileCount[$i] . ' (' . round($fileSize[$i]*100/1024/1024)/100 . ' MB)</td><td>' . $fileFreshCount[$i] . ' (' . round($fileFreshCount[$i]*100/$fileCount[$i]) . ' %)</td></tr>';
$totalFiles += $fileCount[$i];
$totalSize += $fileSize[$i];
$totalFresh += $fileFreshCount[$i];
}
echo '<tr><td>Total</td><td>' . $totalFiles . ' (' . round($totalSize*100/1024/1024)/100 . ' MB)</td><td>' . $totalFresh . ' (' . round($totalFresh*100/$totalFiles) . ' %)</td></tr></table>';
echo '</body></html>';
}
?>
Usage instructions are as per ProxySimplePHP except you do not need to make the tiles directory. The directory where you place "tiles.php" must be writable!