-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
tiles.php
299 lines (286 loc) · 18.6 KB
/
tiles.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<?php
ob_start(); // попробуем перехватить любой вывод скрипта
/* By the http://wiki.openstreetmap.org/wiki/ProxySimplePHP
Берёт тайл из кеша и сразу отдаёт-показывает.
Потом, если надо - скачивает
Если полученный тайл ещё не показывали (он новый) - показываем.
Version 2.10.1
История History:
2.10.0 - map's function PrepareTileFile for tilefromsource.php with support for uploading more them one tile
2.9.0 - map's $bounds support
*/
//$nowTime = microtime(TRUE);
chdir(__DIR__); // задаем директорию выполнение скрипта
$freshOnly = FALSE; // показывать тайлы, даже если они протухли
require('fcommon.php');
require('params.php'); // пути и параметры (без указания пути оно сперва ищет в include_path, а он не обязан начинаться с .)
$x = filter_var($_REQUEST['x'],FILTER_SANITIZE_NUMBER_INT);
$y = filter_var($_REQUEST['y'],FILTER_SANITIZE_URL); // 123456.png
$z = filter_var($_REQUEST['z'],FILTER_SANITIZE_NUMBER_INT);
$r = filter_var($_REQUEST['r'],FILTER_SANITIZE_FULL_SPECIAL_CHARS);
if(($x===false) OR ($y===false) OR ($z===false) OR ($r===false)) {
showTile(NULL); // покажем 404
error_log("Incorrect tile info: $r/$z/$x/$y");
goto END;
}
$x = intval($x);
$y = intval($y);
$z = intval($z);
// FOR TEST
//$x=19;$y=11;$z=5;$r="world-coastline";
//$x=19;$y=11;$z=5;$r="osmmapMapnik";
//$x=4822;$y=6161;$z=14;$r="NOAA_USA_ENC";
//$x=5167;$y=2872;$z=13;$r="RosreestrTopo";
//// FOR TEST
// определимся с источником карты
$sourcePath = explode('/',$r); // $r может быть с путём до конкретного кеша, однако - никогда абсолютным
$sourceName = $sourcePath[0];
if($pos=strpos($sourceName,'_COVER')) { // нужно показать покрытие, а не саму карту
require("$mapSourcesDir/common_COVER"); // файл, описывающий источник тайлов покрытия, используемые ниже переменные - оттуда.
}
else {
require('mapsourcesVariablesList.php'); // потому что в файле источника они могут быть не все, и для новой карты останутся старые
$res=include("$mapSourcesDir/$sourceName.php"); // файл, описывающий источник, используемые ниже переменные - оттуда
if(!$res){
showTile(NULL); // покажем 404
error_log("Incorrect map name: $sourceName");
goto END;
};
};
if(($z<$minZoom)or($z>$maxZoom)){
showTile(NULL); // покажем 404
//error_log("Out of zoom: 404");
goto END;
};
//error_log("function_exists('getTile'):".function_exists('getTile'));
if($functionGetTileFile){ // у карты есть собственная функция получения тайла
eval($functionGetTileFile); // создаём функцию получения данных
extract(getTileFile($r,$z,$x,$y),EXTR_OVERWRITE); // выполняем функцию получения даных. Обычно она возвращает массив ['img',value], и extract присваивает $img=value
}
else {
// возьмём тайл
$path_parts = pathinfo($y); //
$y = $path_parts['filename'];
// Расширение из конфига имеет преимущество!
if(!$ext) $ext = $path_parts['extension']; // в конфиге источника не указано расширение -- используем из запроса
if(!$ext) $ext='png'; // совсем нет -- умолчальное
$fileName = "$tileCacheDir/$r/$z/$x/$y.$ext"; // из кэша
//echo "file=$fileName; <br>\n";
/*
Нужно понять -- тайл сначала показывать, потом скачивать, или сначала скачивать, а потом показывать
если тайла нет -- если есть, чем скачивать - сперва скачивать, потом показывать, иначе 404
если тайл есть, он не нулевой длины, и не протух -- просто показать
если тайл есть, он не нулевой длины, и протух, и указано протухшие не показывать -- скачать, если есть, чем скачивать, потом показать, иначе 404
если тайл есть, он не нулевой длины, и протух, и не указано протухшие не показывать -- показать, потом скачать, если есть чем, иначе - просто показать
если файл есть, но нулевой длины -- использовать специальное время протухания
*/
/* $showTHENloading
0; // только показывать
1; // сперва показывать, потом скачивать
2; //сперва скачивать, потом показывать
*/
$showTHENloading = 0; // только показывать
//clearstatcache();
$imgFileTime = @filemtime($fileName); // файла может не быть
//echo "tiles.php: $r/$z/$x/$y tile exist:$imgFileTime, and expired to ".(time()-(filemtime($fileName)+$ttl))."sec. и имеет дату модификации ".date('d.m.Y H:i',$imgFileTime)."<br>\n";
if($imgFileTime) { // файл есть
//error_log("tiles.php: $r/$z/$x/$y Tile exists.");
if(($imgFileTime+$ttl) < time()) { // файл протух. Таким образом, файлы нулевой длины могут протухнуть раньше, но не позже.
//error_log("tiles.php: $r/$z/$x/$y tile expired to ".(time()-(filemtime($fileName)+$ttl))."sec. freshOnly=$freshOnly; maxZoom=$maxZoom;");
if($freshOnly) { // протухшие не показывать
if($functionGetURL) $showTHENloading = 2; //сперва скачивать, потом показывать
else {
showTile(NULL); // покажем 404
goto END;
};
}
else { // протухшие показывать
$img = file_get_contents($fileName); // берём тайл из кеша, возможно, за приделами разрешённых масштабов
//error_log("tiles.php: Get rotten tile $r/$z/$x/$y.$ext : ".strlen($img)." bytes from cache");
if($functionGetURL) $showTHENloading = 1; // сперва показывать, потом скачивать
}
}
else { // файл свежий
$img = file_get_contents($fileName); // берём тайл из кеша, возможно, за приделами разрешённых масштабов
//error_log("tiles.php: Get fresh tile $r/$z/$x/$y.$ext : ".strlen($img)." bytes from cache");
if(!$img) { // файл нулевой длины
if($noTileReTry) $ttl= $noTileReTry; // если указан специальный срок протухания для файла нулевой длины -- им обозначается перманентная проблема скачивания
if(($imgFileTime+$ttl) < time()) { // файл протух
if($freshOnly) { // протухшие не показывать
if($functionGetURL) $showTHENloading = 2; //сперва скачивать, потом показывать
}
else {
if($functionGetURL) $showTHENloading = 1; // сперва показывать, потом скачивать
};
};
};
};
}
elseif($functionGetURL) { // файла нет, но в описании карты указано, где взять
//error_log("tiles.php: $r/$z/$x/$y Tile not exists.");
if(checkInBounds($z,$x,$y,$bounds)){ // тайл вообще должен быть?
$showTHENloading = 2; //сперва скачивать, потом показывать
}
else{ // тайла и не должно быть
showTile(NULL); // покажем 404
goto END;
};
}
else{ // файла нет, и негде взять
showTile(NULL); // покажем 404
goto END;
};
};
// Так или иначе - тайл получен или не получен, и решение, что делать - выработано
//echo "|$functionPrepareTileImg|\n";
if($functionPrepareTileImg) eval($functionPrepareTileImg); // определим функцию обработки картинки
//error_log("tiles.php: $r/$z/$x/$y showTHENloading=$showTHENloading;");
//echo "tiles.php: $r/$z/$x/$y showTHENloading=$showTHENloading;<br>\n";
//$file_info = finfo_open(FILEINFO_MIME); // подготовимся к определению mime-type
//$file_type = finfo_buffer($file_info,$img);
//echo "$file_type\n";
switch($showTHENloading){
case 1: // сперва показывать, потом скачивать
showTile($img,$ContentType,$content_encoding,$ext); // тайл есть, возможно, пустой
//exec("$phpCLIexec tilefromsource.php $fileName > /dev/null 2>&1 &"); // exec не будет ждать завершения
// вместо получения - положим в очередь
if($z >= $aheadLoadStartZoom) createJob($sourceName,$z,$x,$y); //echo "поставим задание на получение всех нижележащих тайлов, если этот тайл удачно скачался<br>\n";
else createJob($sourceName,$z,$x,$y,TRUE); // скачать только этот тайл
break;
case 2: //echo "сперва скачивать, потом показывать<br>\n";
$execStr = "$phpCLIexec tilefromsource.php $fileName";
if(thisRun($execStr)) sleep(1); // Предотвращает множественную загрузку одного тайла одновременно, если у proxy больше одного клиента. Не сильно тормозит?
else exec($execStr); // exec будет ждать завершения
// покажем тайл
$img = @file_get_contents($fileName); // попробуем взять тайл из кеша, скачивание могло плохо кончиться
//if($img) echo "Тайл скачался <br>\n"; else echo "Тайл не скачался <br>\n";
showTile($img,$ContentType,$content_encoding,$ext); //покажем тайл
// Опережающее скачивание при показе - должно помочь с крупными масштабами
if($img and ($z >= $aheadLoadStartZoom)) { //echo "поставим задание на получение всех нижележащих тайлов, если этот тайл удачно скачался<br>\n";
//echo "$sourceName,$z,$x,$y<br>\n";
createJob($sourceName,$z,$x,$y); // скачать начиная с этого тайла
}
break;
default: // только показывать
showTile($img,$ContentType,$content_encoding,$ext); // тайл есть, возможно, пустой
}
END:
ob_clean(); // очистим, если что попало в буфер
return;
function showTile($img,$mime_type='',$content_encoding='',$ext='') {
/*
Отдаёт тайл. Считается, что только эта функция что-то показывает клиенту
https://gist.github.com/bubba-h57/32593b2b970366d24be7
*/
//global $nowTime;
//apache_setenv('no-gzip', '1'); // отключить сжатие вывода
set_time_limit(0); // Cause we are clever and don't want the rest of the script to be bound by a timeout. Set to zero so no time limit is imposed from here on out.
ignore_user_abort(true); // чтобы выполнение не прекратилось после разрыва соединения
ob_end_clean(); // очистим, если что попало в буфер
ob_start();
header("Connection: close"); // Tell the client to close connection
if($img) { // тайла могло не быть в кеше, и его не удалось получить или его попортила функция prepareTile
if(function_exists('prepareTileImg')) { // обработка картинки, если таковая указана в описании источника
$prepared = prepareTileImg($img);
//echo "tiles.php [showTile] prepared:"; print_r($prepared); echo "\n";
if($prepared['img']) extract($prepared);
unset($prepared);
}
// FOR TEST
//file_put_contents('test.png',$img);
//return;
//// FOR TEST
//$exp_gmt = gmdate("D, d M Y H:i:s", time() + 60*60) ." GMT"; // Тайл будет стопудово кешироваться браузером 1 час
//header("Expired: " . $exp_gmt);
//$mod_gmt = gmdate("D, d M Y H:i:s", filemtime($fileName)) ." GMT"; // слишком долго?
//header("Last-Modified: " . $mod_gmt);
//header("Cache-Control: public, max-age=3600"); // Тайл будет стопудово кешироваться браузером 1 час
if(($ext == 'pbf') or ($mime_type == 'application/x-protobuf')){
if(!$content_encoding){
$file_info = finfo_open(FILEINFO_MIME_TYPE); // подготовимся к определению mime-type
$file_type = finfo_buffer($file_info,$img);
//header("X-Debug: $file_type");
if($file_type == 'application/x-gzip') $content_encoding = 'gzip';
}
header ("Content-Type: application/x-protobuf");
}
elseif($mime_type) header ("Content-Type: $mime_type");
elseif($ext) header ("Content-Type: image/$ext");
else{
$file_info = finfo_open(FILEINFO_MIME_TYPE); // подготовимся к определению mime-type
$mime_type = finfo_buffer($file_info,$img);
if($mime_type) header ("Content-Type: $mime_type");
}
if($content_encoding) header ("Content-encoding: $content_encoding");
}
else {
header("X-Debug: Not found if no tile");
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Дата в прошлом
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
}
echo $img; // теперь в output buffer только тайл
$content_lenght = ob_get_length(); // возьмём его размер
header("Content-Length: $content_lenght"); // завершающий header
header("Access-Control-Allow-Origin: *"); // эта пурга их какой-то горбатой безопасности, смысл которой я так и не уловил
//header("Access-Control-Expose-Headers: *"); // эта пурга должна позволить показывать заголовки запросов, но они и так показываются?
//header("X-CacheTiming: ".($nowTime-microtime(TRUE)));
ob_end_flush(); // отправляем тело - собственно картинку и прекращаем буферизацию
@ob_flush();
flush(); // Force php-output-cache to flush to browser.
ob_start(); // попробуем перехватить любой вывод скрипта
}
function createJob($mapSourcesName,$z,$x,$y,$oneOnly=FALSE) {
/* Создаёт задание для загрузчика
для скачивания карты с именем $mapSourcesName
начиная с тайла $z,$x,$y
или если $oneOnly -- только этот тайл
*/
global $jobsDir,$jobsInWorkDir,$phpCLIexec,$aheadLoadStartZoom,$loaderMaxZoom,$maxZoom; // из params.php и параметров карты
//echo "mapSourcesName=$mapSourcesName; z=$z; jobsDir=$jobsDir; phpCLIexec=$phpCLIexec, aheadLoadStartZoom=$aheadLoadStartZoom, loaderMaxZoom=$loaderMaxZoom\n";
if(($z > $loaderMaxZoom) OR ($z < $aheadLoadStartZoom)) $oneOnly=TRUE; // масштаб вне указанного для планировщика
$jobName = "$mapSourcesName.$z"; // имя файла задания
$umask = umask(0); // сменим на 0777 и запомним текущую
//error_log("tiles.php createJob: Update loader job $jobsInWorkDir/$jobName by $x,$y");
if($oneOnly) { // нужно загрузить только один тайл
// нужно положить задание в каталог заданий для загрузчика, есть там такое, или нет
file_put_contents("$jobsInWorkDir/$jobName", "$x,$y\n",FILE_APPEND); // создадим/добавим файл задания для загрузчика
@chmod("$jobsDir/$jobName",0666); // чтобы запуск от другого юзера
}
else { // нужно загрузить всё
// нужно добавить к заданию для загрузчика, если там такое есть
// ибо если просто дать его планировщику, то оно исчезнет
if(file_exists("$jobsInWorkDir/$jobName")){
file_put_contents("$jobsInWorkDir/$jobName", "$x,$y\n",FILE_APPEND); // создадим/добавим файл задания для загрузчика
@chmod("$jobsDir/$jobName",0666); // чтобы запуск от другого юзера
}
// дадим задание планировщику
file_put_contents("$jobsDir/$jobName", "$x,$y\n",FILE_APPEND); // создадим/добавим файл задания для планировщика. Понимаем, что этот тайл не будет скачан, если задание существует и скачивается. Будут скачаны только тайлы следующего масштаба. Но выше мы положили то же самое в задание для загрузчика.
@chmod("$jobsDir/$jobName",0666); // чтобы запуск от другого юзера
}
umask($umask); // Вернём. Зачем? Но umask глобальна вообще для всех юзеров веб-сервера
if(!glob("$jobsDir/*.slock")) { // если не запущено ни одного планировщика
//error_log("tiles.php createJob: Need scheduler for zoom $z; phpCLIexec=$phpCLIexec;");
exec("$phpCLIexec loaderSched.php > /dev/null 2>&1 &"); // асинхронно. если запускать сам файл, ему нужны права
}
} // end function createJob
function thisRun($exec) {
/**/
$pid = getmypid();
exec("ps -A w | grep '$exec'",$psList);
if(!$psList) exec("ps w | grep '$exec'",$psList); // for OpenWRT. For others -- let's hope so all run from one user
//print_r($psList); //
$run = FALSE;
foreach($psList as $str) {
//error_log("$str;\n");
if(strpos($str,(string)$pid)!==FALSE) continue;
if(strpos($str,'grep')!==FALSE) continue;
if(strpos($str,$exec)!==FALSE){
$run=TRUE;
break;
}
}
//error_log("tiles.php thisRun:$run;\n");
return $run;
}
?>