1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching: tiled tiff filesytem cache backend.
6 * Author: Thomas Bonfort
7 * Frank Warmerdam
8 * Even Rouault
9 * MapServer team.
10 *
11 ******************************************************************************
12 * Copyright (c) 2011-2017 Regents of the University of Minnesota.
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in
22 * all copies of this Software or works derived from this Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
31 *****************************************************************************/
32
33 #include "mapcache.h"
34 #ifdef USE_TIFF
35
36 #include <apr_file_info.h>
37 #include <apr_strings.h>
38 #include <apr_file_io.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <tiffio.h>
43
44 #ifdef USE_GDAL
45 #include "cpl_vsi.h"
46 #include "cpl_conv.h"
47 #define CPL_SERV_H_INCLUDED
48 #endif
49
50 #ifdef USE_GEOTIFF
51 #include "xtiffio.h"
52 #include "geovalues.h"
53 #include "geotiff.h"
54 #include "geo_normalize.h"
55 #include "geo_tiffp.h"
56 #include "geo_keyp.h"
57 #define MyTIFFOpen XTIFFOpen
58 #define MyTIFFClose XTIFFClose
59 #else
60 #define MyTIFFOpen TIFFOpen
61 #define MyTIFFClose TIFFClose
62 #endif
63
64 typedef enum
65 {
66 MAPCACHE_TIFF_STORAGE_FILE,
67 MAPCACHE_TIFF_STORAGE_REST,
68 MAPCACHE_TIFF_STORAGE_GOOGLE
69 } mapcache_cache_tiff_storage_type;
70
71
72 typedef struct mapcache_cache_tiff mapcache_cache_tiff;
73
74 struct mapcache_cache_tiff {
75 mapcache_cache cache;
76 char *filename_template;
77 char *x_fmt,*y_fmt,*z_fmt,*inv_x_fmt,*inv_y_fmt,*div_x_fmt,*div_y_fmt,*inv_div_x_fmt,*inv_div_y_fmt;
78 int count_x;
79 int count_y;
80 mapcache_image_format_jpeg *format;
81 mapcache_locker *locker;
82 struct {
83 mapcache_cache_tiff_storage_type type;
84 int connection_timeout;
85 int timeout;
86 char *header_file;
87 union
88 {
89 struct
90 {
91 char* access;
92 char* secret;
93 } google;
94 } u;
95 } storage;
96 };
97
98 #ifdef USE_GDAL
99
100 static tsize_t
_tiffReadProc(thandle_t th,tdata_t buf,tsize_t size)101 _tiffReadProc( thandle_t th, tdata_t buf, tsize_t size )
102 {
103 VSILFILE* fp = (VSILFILE*)th;
104 return VSIFReadL( buf, 1, size, fp );
105 }
106
107 static tsize_t
_tiffWriteProc(thandle_t th,tdata_t buf,tsize_t size)108 _tiffWriteProc( thandle_t th, tdata_t buf, tsize_t size )
109 {
110 VSILFILE* fp = (VSILFILE*)th;
111 return VSIFWriteL( buf, 1, size, fp );
112 }
113
114 static toff_t
_tiffSeekProc(thandle_t th,toff_t off,int whence)115 _tiffSeekProc( thandle_t th, toff_t off, int whence )
116 {
117 VSILFILE* fp = (VSILFILE*)th;
118 if( VSIFSeekL( fp, off, whence ) != 0 )
119 {
120 TIFFErrorExt( th, "_tiffSeekProc", "%s", VSIStrerror( errno ) );
121 return (toff_t)( -1 );
122 }
123 return VSIFTellL( fp );
124 }
125
126 static int
_tiffCloseProc(thandle_t th)127 _tiffCloseProc( thandle_t th )
128 {
129 VSILFILE* fp = (VSILFILE*)th;
130 VSIFCloseL(fp);
131 return 0;
132 }
133
134 static toff_t
_tiffSizeProc(thandle_t th)135 _tiffSizeProc( thandle_t th )
136 {
137 vsi_l_offset old_off;
138 toff_t file_size;
139 VSILFILE* fp = (VSILFILE*)th;
140
141 old_off = VSIFTellL( fp );
142 (void)VSIFSeekL( fp, 0, SEEK_END );
143
144 file_size = (toff_t) VSIFTellL( fp );
145 (void)VSIFSeekL( fp, old_off, SEEK_SET );
146
147 return file_size;
148 }
149
150 static int
_tiffMapProc(thandle_t th,tdata_t * pbase,toff_t * psize)151 _tiffMapProc( thandle_t th, tdata_t* pbase, toff_t* psize )
152 {
153 (void)th;
154 (void)pbase;
155 (void)psize;
156 /* Unimplemented */
157 return 0;
158 }
159
160 static void
_tiffUnmapProc(thandle_t th,tdata_t base,toff_t size)161 _tiffUnmapProc( thandle_t th, tdata_t base, toff_t size )
162 {
163 (void)th;
164 (void)base;
165 (void)size;
166 /* Unimplemented */
167 }
168
set_conf_value(const char * key,const char * value)169 static char* set_conf_value(const char* key, const char* value)
170 {
171 const char* old_val_const;
172 char* old_val = NULL;
173 old_val_const = CPLGetConfigOption(key, NULL);
174 if( old_val_const != NULL )
175 old_val = strdup(old_val_const);
176 /* Prevent a directory listing to be done */
177 CPLSetConfigOption(key, value);
178 return old_val;
179 }
180
restore_conf_value(const char * key,char * old_val)181 static void restore_conf_value(const char* key, char* old_val)
182 {
183 CPLSetConfigOption(key, old_val);
184 free(old_val);
185 }
186
187 typedef struct
188 {
189 char* old_val_disable_readdir;
190 char* old_val_headerfile;
191 char* old_val_secret;
192 char* old_val_access;
193 } mapache_gdal_env_context;
194
set_gdal_context(mapcache_cache_tiff * cache,mapache_gdal_env_context * pcontext)195 static void set_gdal_context(mapcache_cache_tiff *cache,
196 mapache_gdal_env_context* pcontext)
197 {
198 memset(pcontext, 0, sizeof(mapache_gdal_env_context));
199 /* Prevent a directory listing to be done */
200 pcontext->old_val_disable_readdir =
201 set_conf_value("GDAL_DISABLE_READDIR_ON_OPEN", "YES");
202
203 if( cache->storage.header_file ) {
204 pcontext->old_val_headerfile = set_conf_value("GDAL_HTTP_HEADER_FILE",
205 cache->storage.header_file);
206 }
207 if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE ) {
208 pcontext->old_val_secret = set_conf_value("GS_SECRET_ACCESS_KEY",
209 cache->storage.u.google.secret);
210 pcontext->old_val_access = set_conf_value("GS_ACCESS_KEY_ID",
211 cache->storage.u.google.access);
212 }
213 }
214
restore_gdal_context(mapcache_cache_tiff * cache,mapache_gdal_env_context * pcontext)215 static void restore_gdal_context(mapcache_cache_tiff *cache,
216 mapache_gdal_env_context* pcontext)
217 {
218
219 restore_conf_value("GDAL_DISABLE_READDIR_ON_OPEN",
220 pcontext->old_val_disable_readdir);
221 if( cache->storage.header_file ) {
222 restore_conf_value("GDAL_HTTP_HEADER_FILE",
223 pcontext->old_val_headerfile);
224 }
225 if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE ) {
226 restore_conf_value("GS_SECRET_ACCESS_KEY",
227 pcontext->old_val_secret);
228 restore_conf_value("GS_ACCESS_KEY_ID",
229 pcontext->old_val_access);
230 }
231 }
232
mapcache_cache_tiff_vsi_stat(mapcache_cache_tiff * cache,const char * name,VSIStatBufL * pstat)233 static int mapcache_cache_tiff_vsi_stat(
234 mapcache_cache_tiff *cache,
235 const char* name,
236 VSIStatBufL* pstat)
237 {
238 mapache_gdal_env_context context;
239 int ret;
240
241 set_gdal_context(cache, &context);
242 ret = VSIStatL(name, pstat);
243 restore_gdal_context(cache, &context);
244
245 return ret;
246 }
247
mapcache_cache_tiff_vsi_open(mapcache_cache_tiff * cache,const char * name,const char * mode)248 static VSILFILE* mapcache_cache_tiff_vsi_open(
249 mapcache_cache_tiff *cache,
250 const char* name, const char* mode )
251 {
252 mapache_gdal_env_context context;
253 VSILFILE* fp;
254
255 set_gdal_context(cache, &context);
256 fp = VSIFOpenL(name, mode);
257 restore_gdal_context(cache, &context);
258
259 return fp;
260 }
261
mapcache_cache_tiff_open(mapcache_context * ctx,mapcache_cache_tiff * cache,const char * name,const char * mode)262 static TIFF* mapcache_cache_tiff_open(mapcache_context *ctx,
263 mapcache_cache_tiff *cache,
264 const char* name, const char* mode )
265 {
266 char chDummy;
267 VSILFILE* fp;
268
269 /* If writing or using a regular filename, then use standard */
270 /* libtiff/libgeotiff I/O layer */
271 if( strcmp(mode, "r") != 0 || strncmp(name, "/vsi", 4) != 0 )
272 {
273 return MyTIFFOpen(name, mode);
274 }
275
276 fp = mapcache_cache_tiff_vsi_open(cache, name, mode);
277 if( fp == NULL )
278 return NULL;
279
280 if( strcmp(mode, "r") == 0 )
281 {
282 /* But then the file descriptor may point to an invalid resource */
283 /* so try reading a byte from it */
284 if(VSIFReadL(&chDummy, 1, 1, fp) != 1)
285 {
286 VSIFCloseL(fp);
287 return NULL;
288 }
289 VSIFSeekL(fp, 0, SEEK_SET);
290 }
291
292 return
293 #ifdef USE_GEOTIFF
294 XTIFFClientOpen
295 #else
296 TIFFClientOpen
297 #endif
298 ( name, mode,
299 (thandle_t) fp,
300 _tiffReadProc, _tiffWriteProc,
301 _tiffSeekProc, _tiffCloseProc, _tiffSizeProc,
302 _tiffMapProc, _tiffUnmapProc );
303 }
304
mapcache_cache_tiff_gdal_error_handler(CPLErr eErr,int error_num,const char * pszMsg)305 static void CPL_STDCALL mapcache_cache_tiff_gdal_error_handler(CPLErr eErr,
306 int error_num,
307 const char* pszMsg)
308 {
309 #ifdef DEBUG
310 mapcache_context *ctx = (mapcache_context *) CPLGetErrorHandlerUserData();
311 ctx->log(ctx,MAPCACHE_DEBUG,"GDAL %s, %d: %s",
312 (eErr == CE_Failure) ? "Failure":
313 (eErr == CE_Warning) ? "Warning":
314 "Debug",
315 error_num,
316 pszMsg);
317 #endif
318 }
319
320
321 #else
322
mapcache_cache_tiff_open(mapcache_context * ctx,mapcache_cache_tiff * cache,const char * name,const char * mode)323 static TIFF* mapcache_cache_tiff_open(mapcache_context *ctx,
324 mapcache_cache_tiff *cache,
325 const char* name, const char* mode )
326 {
327 (void)ctx;
328 (void)cache;
329 return MyTIFFOpen(name, mode);
330
331 }
332
333 #endif /* USE_GDAL */
334
335 #undef MyTIFFOpen
336
337 /**
338 * \brief return filename for given tile
339 *
340 * \param tile the tile to get the key from
341 * \param path pointer to a char* that will contain the filename
342 * \param r
343 * \private \memberof mapcache_cache_tiff
344 */
_mapcache_cache_tiff_tile_key(mapcache_context * ctx,mapcache_cache_tiff * cache,mapcache_tile * tile,char ** path)345 static void _mapcache_cache_tiff_tile_key(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, char **path)
346 {
347 if( cache->storage.type == MAPCACHE_TIFF_STORAGE_REST ) {
348 *path = apr_pstrcat(ctx->pool, "/vsicurl/", cache->filename_template, NULL);
349 }
350 else if( cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE &&
351 strncmp(cache->filename_template,
352 "https://storage.googleapis.com/",
353 strlen("https://storage.googleapis.com/")) == 0) {
354 *path = apr_pstrcat(ctx->pool, "/vsigs/",
355 cache->filename_template +
356 strlen("https://storage.googleapis.com/"), NULL);
357 }
358 else {
359 *path = apr_pstrdup(ctx->pool, cache->filename_template);
360 }
361
362 /*
363 * generic template substitutions
364 */
365 if(strstr(*path,"{tileset}"))
366 *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}",
367 tile->tileset->name);
368 if(strstr(*path,"{grid}"))
369 *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}",
370 tile->grid_link->grid->name);
371 if(tile->dimensions && strstr(*path,"{dim")) {
372 char *dimstring="";
373 int i = tile->dimensions->nelts;
374 while(i--) {
375 mapcache_requested_dimension *rdim = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
376 const char *dimval = mapcache_util_str_sanitize(ctx->pool,rdim->cached_value,"/.",'#');
377 char *dim_key = apr_pstrcat(ctx->pool,"{dim:",rdim->dimension->name,"}",NULL);
378 dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL);
379 if(strstr(*path,dim_key)) {
380 *path = mapcache_util_str_replace(ctx->pool,*path, dim_key, dimval);
381 }
382 }
383 *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
384 }
385
386
387 while(strstr(*path,"{z}"))
388 *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
389 apr_psprintf(ctx->pool,cache->z_fmt,tile->z));
390 /*
391 * x and y replacing, when the tiff files are numbered with an increasing
392 * x,y scheme (adjacent tiffs have x-x'=1 or y-y'=1
393 */
394 while(strstr(*path,"{div_x}"))
395 *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}",
396 apr_psprintf(ctx->pool,cache->div_x_fmt,tile->x/cache->count_x));
397 while(strstr(*path,"{div_y}"))
398 *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}",
399 apr_psprintf(ctx->pool,cache->div_y_fmt,tile->y/cache->count_y));
400 while(strstr(*path,"{inv_div_y}"))
401 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}",
402 apr_psprintf(ctx->pool,cache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y));
403 while(strstr(*path,"{inv_div_x}"))
404 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}",
405 apr_psprintf(ctx->pool,cache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x));
406
407 /*
408 * x and y replacing, when the tiff files are numbered with the index
409 * of their bottom-left tile
410 * adjacent tiffs have x-x'=count_x or y-y'=count_y
411 */
412 while(strstr(*path,"{x}"))
413 *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
414 apr_psprintf(ctx->pool,cache->x_fmt,tile->x/cache->count_x*cache->count_x));
415 while(strstr(*path,"{y}"))
416 *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
417 apr_psprintf(ctx->pool,cache->y_fmt,tile->y/cache->count_y*cache->count_y));
418 while(strstr(*path,"{inv_y}"))
419 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
420 apr_psprintf(ctx->pool,cache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/cache->count_y*cache->count_y));
421 while(strstr(*path,"{inv_x}"))
422 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
423 apr_psprintf(ctx->pool,cache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/cache->count_x*cache->count_y));
424 if(!*path) {
425 ctx->set_error(ctx,500, "failed to allocate tile key");
426 }
427 }
428
429 #ifdef DEBUG
check_tiff_format(mapcache_context * ctx,mapcache_cache_tiff * cache,mapcache_tile * tile,TIFF * hTIFF,const char * filename)430 static void check_tiff_format(mapcache_context *ctx, mapcache_cache_tiff *cache, mapcache_tile *tile, TIFF *hTIFF, const char *filename)
431 {
432 uint32 imwidth,imheight,tilewidth,tileheight;
433 int16 planarconfig,orientation;
434 uint16 compression;
435 uint16 photometric;
436 int rv;
437 mapcache_grid_level *level;
438 int ntilesx;
439 int ntilesy;
440 TIFFGetField( hTIFF, TIFFTAG_IMAGEWIDTH, &imwidth );
441 TIFFGetField( hTIFF, TIFFTAG_IMAGELENGTH, &imheight );
442 TIFFGetField( hTIFF, TIFFTAG_TILEWIDTH, &tilewidth );
443 TIFFGetField( hTIFF, TIFFTAG_TILELENGTH, &tileheight );
444
445 /* Test that the TIFF is tiled and not stripped */
446 if(!TIFFIsTiled(hTIFF)) {
447 ctx->set_error(ctx,500,"TIFF file \"%s\" is not tiled", filename);
448 return;
449 }
450
451 /* check we have jpeg compression */
452 rv = TIFFGetField( hTIFF, TIFFTAG_COMPRESSION, &compression );
453 if(rv == 1 && compression != COMPRESSION_JPEG) {
454 ctx->set_error(ctx,500,"TIFF file \"%s\" is not jpeg compressed",
455 filename);
456 return;
457 }
458
459 /* tiff must be pixel interleaved, not with a single image per band */
460 rv = TIFFGetField( hTIFF, TIFFTAG_PLANARCONFIG, &planarconfig );
461 if(rv == 1 && planarconfig != PLANARCONFIG_CONTIG) {
462 ctx->set_error(ctx,500,"TIFF file \"%s\" is not pixel interleaved",
463 filename);
464 return;
465 }
466
467 /* is this test needed once we now we have JPEG ? */
468 rv = TIFFGetField( hTIFF, TIFFTAG_PHOTOMETRIC, &photometric );
469 if(rv == 1 && (photometric != PHOTOMETRIC_RGB && photometric != PHOTOMETRIC_YCBCR)) {
470 ctx->set_error(ctx,500,"TIFF file \"%s\" is not RGB: %d",
471 filename);
472 return;
473 }
474
475 /* the default is top-left, but check just in case */
476 rv = TIFFGetField( hTIFF, TIFFTAG_ORIENTATION, &orientation );
477 if(rv == 1 && orientation != ORIENTATION_TOPLEFT) {
478 ctx->set_error(ctx,500,"TIFF file \"%s\" is not top-left oriented",
479 filename);
480 return;
481 }
482
483 /* check that the tiff internal tiling aligns with the mapcache_grid we are using:
484 * - the tiff tile size must match the grid tile size
485 * - the number of tiles in each direction in the tiff must match what has been
486 * configured for the cache
487 */
488 level = tile->grid_link->grid->levels[tile->z];
489 ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
490 ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
491 if( tilewidth != tile->grid_link->grid->tile_sx ||
492 tileheight != tile->grid_link->grid->tile_sy ||
493 imwidth != tile->grid_link->grid->tile_sx * ntilesx ||
494 imheight != tile->grid_link->grid->tile_sy * ntilesy ) {
495 ctx->set_error(ctx,500,"TIFF file %s imagesize (%d,%d) and tilesize (%d,%d).\
496 Expected (%d,%d),(%d,%d)",filename,imwidth,imheight,tilewidth,tileheight,
497 tile->grid_link->grid->tile_sx * ntilesx,
498 tile->grid_link->grid->tile_sy * ntilesy,
499 tile->grid_link->grid->tile_sx,
500 tile->grid_link->grid->tile_sy);
501 return;
502 }
503
504 /* TODO: more tests ? */
505
506 /* ok, success */
507 }
508 #endif
509
_mapcache_cache_tiff_has_tile(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)510 static int _mapcache_cache_tiff_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
511 {
512 char *filename;
513 TIFF *hTIFF;
514 mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
515 _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
516 if(GC_HAS_ERROR(ctx)) {
517 return MAPCACHE_FALSE;
518 }
519
520 #ifdef USE_GDAL
521 CPLPushErrorHandlerEx(mapcache_cache_tiff_gdal_error_handler, ctx);
522 #endif
523
524 hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r");
525
526 if(hTIFF) {
527 do {
528 uint32 nSubType = 0;
529 int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
530 int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
531
532 mapcache_grid_level *level;
533 int ntilesx;
534 int ntilesy;
535 toff_t *offsets=NULL, *sizes=NULL;
536
537 if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) )
538 nSubType = 0;
539
540 /* skip overviews and masks */
541 if( (nSubType & FILETYPE_REDUCEDIMAGE) ||
542 (nSubType & FILETYPE_MASK) )
543 continue;
544
545
546 #ifdef DEBUG
547 check_tiff_format(ctx,cache,tile,hTIFF,filename);
548 if(GC_HAS_ERROR(ctx)) {
549 MyTIFFClose(hTIFF);
550 #ifdef USE_GDAL
551 CPLPopErrorHandler();
552 #endif
553 return MAPCACHE_FALSE;
554 }
555 #endif
556 level = tile->grid_link->grid->levels[tile->z];
557 ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
558 ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
559
560 /* x offset of the tile along a row */
561 tiff_offx = tile->x % ntilesx;
562
563 /*
564 * y offset of the requested row. we inverse it as the rows are ordered
565 * from top to bottom, whereas the tile y is bottom to top
566 */
567 tiff_offy = ntilesy - (tile->y % ntilesy) -1;
568 tiff_off = tiff_offy * ntilesx + tiff_offx;
569
570 if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets )) {
571 MyTIFFClose(hTIFF);
572 #ifdef USE_GDAL
573 CPLPopErrorHandler();
574 #endif
575 return MAPCACHE_FALSE;
576 }
577 if(1 != TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes )) {
578 MyTIFFClose(hTIFF);
579 #ifdef USE_GDAL
580 CPLPopErrorHandler();
581 #endif
582 return MAPCACHE_FALSE;
583 }
584 MyTIFFClose(hTIFF);
585 if( offsets[tiff_off] > 0 && sizes[tiff_off] > 0 ) {
586 #ifdef USE_GDAL
587 CPLPopErrorHandler();
588 #endif
589 return MAPCACHE_TRUE;
590 } else {
591 #ifdef USE_GDAL
592 CPLPopErrorHandler();
593 #endif
594 return MAPCACHE_FALSE;
595 }
596 } while( TIFFReadDirectory( hTIFF ) );
597 /* TIFF only contains overviews ? */
598 }
599 #ifdef USE_GDAL
600 CPLPopErrorHandler();
601 #endif
602 return MAPCACHE_FALSE;
603 }
604
_mapcache_cache_tiff_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)605 static void _mapcache_cache_tiff_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
606 {
607 ctx->set_error(ctx,500,"TIFF cache tile deleting not implemented");
608 }
609
610
611 /**
612 * \brief get file content of given tile
613 *
614 * fills the mapcache_tile::data of the given tile with content stored in the file
615 * \private \memberof mapcache_cache_tiff
616 * \sa mapcache_cache::tile_get()
617 */
_mapcache_cache_tiff_get(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)618 static int _mapcache_cache_tiff_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
619 {
620 char *filename;
621 TIFF *hTIFF = NULL;
622 int rv;
623 mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
624 _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
625 if(GC_HAS_ERROR(ctx)) {
626 return MAPCACHE_FALSE;
627 }
628 #ifdef DEBUG
629 ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) => filename %s)",
630 tile->x,tile->y,tile->z,filename);
631 #endif
632
633 #ifdef USE_GDAL
634 CPLPushErrorHandlerEx(mapcache_cache_tiff_gdal_error_handler, ctx);
635 #endif
636
637 hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r");
638
639 /*
640 * we currrently have no way of knowing if the opening failed because the tif
641 * file does not exist (which is not an error condition, as it only signals
642 * that the requested tile does not exist in the cache), or if an other error
643 * that should be signaled occurred (access denied, not a tiff file, etc...)
644 *
645 * we ignore this case here and hope that further parts of the code will be
646 * able to detect what's happening more precisely
647 */
648
649 if(hTIFF) {
650 do {
651 uint32 nSubType = 0;
652 int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
653 int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
654
655 mapcache_grid_level *level;
656 int ntilesx;
657 int ntilesy;
658 toff_t *offsets=NULL, *sizes=NULL;
659
660 if( !TIFFGetField(hTIFF, TIFFTAG_SUBFILETYPE, &nSubType) )
661 nSubType = 0;
662
663 /* skip overviews */
664 if( nSubType & FILETYPE_REDUCEDIMAGE )
665 continue;
666
667
668 #ifdef DEBUG
669 check_tiff_format(ctx,cache,tile,hTIFF,filename);
670 if(GC_HAS_ERROR(ctx)) {
671 MyTIFFClose(hTIFF);
672 #ifdef USE_GDAL
673 CPLPopErrorHandler();
674 #endif
675 return MAPCACHE_FAILURE;
676 }
677 #endif
678 /*
679 * compute the width and height of the full tiff file. This
680 * is not simply the tile size times the number of tiles per
681 * file for lower zoom levels
682 */
683 level = tile->grid_link->grid->levels[tile->z];
684 ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
685 ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
686
687 /* x offset of the tile along a row */
688 tiff_offx = tile->x % ntilesx;
689
690 /*
691 * y offset of the requested row. we inverse it as the rows are ordered
692 * from top to bottom, whereas the tile y is bottom to top
693 */
694 tiff_offy = ntilesy - (tile->y % ntilesy) -1;
695 tiff_off = tiff_offy * ntilesx + tiff_offx;
696
697 /* get the offset of the jpeg data from the start of the file for each tile */
698 rv = TIFFGetField( hTIFF, TIFFTAG_TILEOFFSETS, &offsets );
699 if( rv != 1 ) {
700 ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile offsets",
701 filename);
702 MyTIFFClose(hTIFF);
703 #ifdef USE_GDAL
704 CPLPopErrorHandler();
705 #endif
706 return MAPCACHE_FAILURE;
707 }
708
709 /* get the size of the jpeg data for each tile */
710 rv = TIFFGetField( hTIFF, TIFFTAG_TILEBYTECOUNTS, &sizes );
711 if( rv != 1 ) {
712 ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" tile sizes",
713 filename);
714 MyTIFFClose(hTIFF);
715 #ifdef USE_GDAL
716 CPLPopErrorHandler();
717 #endif
718 return MAPCACHE_FAILURE;
719 }
720
721 /*
722 * the tile data exists for the given tiff_off if both offsets and size
723 * are not zero for that index.
724 * if not, the tiff file is sparse and is missing the requested tile
725 */
726 if( offsets[tiff_off] > 0 && sizes[tiff_off] >= 2 ) {
727 apr_file_t *f;
728 apr_finfo_t finfo;
729 apr_status_t ret;
730
731 /* read the jpeg header (common to all tiles) */
732 uint32 jpegtable_size = 0;
733 unsigned char* jpegtable_ptr;
734 rv = TIFFGetField( hTIFF, TIFFTAG_JPEGTABLES, &jpegtable_size, &jpegtable_ptr );
735 if( rv != 1 || !jpegtable_ptr || jpegtable_size < 2) {
736 /* there is no common jpeg header in the tiff tags */
737 ctx->set_error(ctx,500,"Failed to read TIFF file \"%s\" jpeg table",
738 filename);
739 MyTIFFClose(hTIFF);
740 #ifdef USE_GDAL
741 CPLPopErrorHandler();
742 #endif
743 return MAPCACHE_FAILURE;
744 }
745
746 /*
747 * open the tiff file directly to access the jpeg image data with the given
748 * offset
749 */
750 #ifdef USE_GDAL
751 if( cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
752 {
753 char *bufptr;
754 apr_off_t off;
755 apr_size_t bytes_to_read;
756 size_t bytes_read;
757 VSIStatBufL sStat;
758 VSILFILE* fp;
759
760 fp = mapcache_cache_tiff_vsi_open(cache, filename, "r");
761 if( fp == NULL )
762 {
763 /*
764 * shouldn't usually happen. we managed to open the file before,
765 * nothing much to do except bail out.
766 */
767 ctx->set_error(ctx,500,
768 "VSIFOpenL() failed on already open tiff "
769 "file \"%s\", giving up .... ",
770 filename);
771 MyTIFFClose(hTIFF);
772 CPLPopErrorHandler();
773 return MAPCACHE_FAILURE;
774 }
775
776 if( mapcache_cache_tiff_vsi_stat(cache, filename, &sStat) == 0 ) {
777 /*
778 * extract the file modification time. this isn't guaranteed to be the
779 * modification time of the actual tile, but it's the best we can do
780 */
781 tile->mtime = sStat.st_mtime;
782 }
783
784 #ifdef DEBUG
785 ctx->log(ctx,MAPCACHE_DEBUG,"tile (%d,%d,%d) => mtime = %d)",
786 tile->x,tile->y,tile->z,tile->mtime);
787 #endif
788
789
790 /* create a memory buffer to contain the jpeg data */
791 tile->encoded_data = mapcache_buffer_create(
792 (jpegtable_size+sizes[tiff_off]-4),ctx->pool);
793
794 /*
795 * copy the jpeg header to the beginning of the memory buffer,
796 * omitting the last 2 bytes
797 */
798 memcpy(tile->encoded_data->buf,jpegtable_ptr,(jpegtable_size-2));
799
800 /* advance the data pointer to after the header data */
801 bufptr = ((char *)tile->encoded_data->buf) + (jpegtable_size-2);
802
803
804 /* go to the specified offset in the tiff file, plus 2 bytes */
805 off = offsets[tiff_off]+2;
806 VSIFSeekL(fp, (vsi_l_offset)off, SEEK_SET);
807
808 /*
809 * copy the jpeg body at the end of the memory buffer, accounting
810 * for the two bytes we omitted in the previous step
811 */
812 bytes_to_read = sizes[tiff_off]-2;
813 bytes_read = VSIFReadL(bufptr, 1, bytes_to_read, fp);
814
815 /* check we have correctly read the requested number of bytes */
816 if(bytes_to_read != bytes_read) {
817 ctx->set_error(ctx,500,"failed to read jpeg body in \"%s\".\
818 (read %d of %d bytes)",
819 filename,(int)bytes_read,(int)sizes[tiff_off]-2);
820 VSIFCloseL(fp);
821 MyTIFFClose(hTIFF);
822 CPLPopErrorHandler();
823 return MAPCACHE_FAILURE;
824 }
825
826 tile->encoded_data->size = (jpegtable_size+sizes[tiff_off]-4);
827
828 VSIFCloseL(fp);
829 CPLPopErrorHandler();
830 return MAPCACHE_SUCCESS;
831 }
832 else
833 #endif
834 if((ret=apr_file_open(&f, filename,
835 APR_FOPEN_READ|APR_FOPEN_BUFFERED|APR_FOPEN_BINARY,APR_OS_DEFAULT,
836 ctx->pool)) == APR_SUCCESS) {
837 char *bufptr;
838 apr_off_t off;
839 apr_size_t bytes;
840 ret = apr_file_info_get(&finfo, APR_FINFO_MTIME, f);
841 if(ret == APR_SUCCESS) {
842 /*
843 * extract the file modification time. this isn't guaranteed to be the
844 * modification time of the actual tile, but it's the best we can do
845 */
846 tile->mtime = finfo.mtime;
847 }
848
849 /* create a memory buffer to contain the jpeg data */
850 tile->encoded_data = mapcache_buffer_create((jpegtable_size+sizes[tiff_off]-4),ctx->pool);
851
852 /*
853 * copy the jpeg header to the beginning of the memory buffer,
854 * omitting the last 2 bytes
855 */
856 memcpy(tile->encoded_data->buf,jpegtable_ptr,(jpegtable_size-2));
857
858 /* advance the data pointer to after the header data */
859 bufptr = ((char *)tile->encoded_data->buf) + (jpegtable_size-2);
860
861
862 /* go to the specified offset in the tiff file, plus 2 bytes */
863 off = offsets[tiff_off]+2;
864 apr_file_seek(f,APR_SET,&off);
865
866 /*
867 * copy the jpeg body at the end of the memory buffer, accounting
868 * for the two bytes we omitted in the previous step
869 */
870 bytes = sizes[tiff_off]-2;
871 apr_file_read(f,bufptr,&bytes);
872
873 /* check we have correctly read the requested number of bytes */
874 if(bytes != sizes[tiff_off]-2) {
875 ctx->set_error(ctx,500,"failed to read jpeg body in \"%s\".\
876 (read %d of %d bytes)", filename,bytes,sizes[tiff_off]-2);
877 apr_file_close(f);
878 MyTIFFClose(hTIFF);
879 #ifdef USE_GDAL
880 CPLPopErrorHandler();
881 #endif
882 return MAPCACHE_FAILURE;
883 }
884
885 tile->encoded_data->size = (jpegtable_size+sizes[tiff_off]-4);
886
887 /* finalize and cleanup */
888 apr_file_close(f);
889 MyTIFFClose(hTIFF);
890 #ifdef USE_GDAL
891 CPLPopErrorHandler();
892 #endif
893 return MAPCACHE_SUCCESS;
894 } else {
895 /* shouldn't usually happen. we managed to open the file with TIFFOpen,
896 * but apr_file_open failed to do so.
897 * nothing much to do except bail out.
898 */
899 ctx->set_error(ctx,500,"apr_file_open failed on already open tiff file \"%s\", giving up .... ",
900 filename);
901 MyTIFFClose(hTIFF);
902 #ifdef USE_GDAL
903 CPLPopErrorHandler();
904 #endif
905 return MAPCACHE_FAILURE;
906 }
907 } else {
908 /* sparse tiff file without the requested tile */
909 MyTIFFClose(hTIFF);
910 #ifdef USE_GDAL
911 CPLPopErrorHandler();
912 #endif
913 return MAPCACHE_CACHE_MISS;
914 }
915 } /* loop through the tiff directories if there are multiple ones */
916 while( TIFFReadDirectory( hTIFF ) );
917
918 /*
919 * should not happen?
920 * finished looping through directories and didn't find anything suitable.
921 * does the file only contain overviews?
922 */
923 MyTIFFClose(hTIFF);
924 }
925 #ifdef USE_GDAL
926 CPLPopErrorHandler();
927 #endif
928 /* failed to open tiff file */
929 return MAPCACHE_CACHE_MISS;
930 }
931
932 /**
933 * \brief write tile data to tiff
934 *
935 * writes the content of mapcache_tile::data to tiff.
936 * \returns MAPCACHE_FAILURE if there is no data to write, or if the tile isn't locked
937 * \returns MAPCACHE_SUCCESS if the tile has been successfully written to tiff
938 * \private \memberof mapcache_cache_tiff
939 * \sa mapcache_cache::tile_set()
940 */
_mapcache_cache_tiff_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)941 static void _mapcache_cache_tiff_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
942 {
943 #ifdef USE_TIFF_WRITE
944 char *filename;
945 TIFF *hTIFF = NULL;
946 int rv;
947 void *lock;
948 int create;
949 mapcache_cache_tiff *cache;
950 mapcache_image_format_jpeg *format;
951 int tilew;
952 int tileh;
953 unsigned char *rgb;
954 int r,c;
955 apr_finfo_t finfo;
956 mapcache_grid_level *level;
957 int ntilesx;
958 int ntilesy;
959 int tiff_offx, tiff_offy; /* the x and y offset of the tile inside the tiff image */
960 int tiff_off; /* the index of the tile inside the list of tiles of the tiff image */
961
962 cache = (mapcache_cache_tiff*)pcache;
963 if( cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
964 {
965 ctx->set_error(ctx,500,"tiff cache %s is read-only\n",pcache->name);
966 return;
967 }
968 _mapcache_cache_tiff_tile_key(ctx, cache, tile, &filename);
969 format = (mapcache_image_format_jpeg*) cache->format;
970 if(GC_HAS_ERROR(ctx)) {
971 return;
972 }
973 #ifdef DEBUG
974 ctx->log(ctx,MAPCACHE_DEBUG,"tile write (%d,%d,%d) => filename %s)",
975 tile->x,tile->y,tile->z,filename);
976 #endif
977
978 /*
979 * create the directory where the tiff file will be stored
980 */
981 mapcache_make_parent_dirs(ctx,filename);
982 GC_CHECK_ERROR(ctx);
983
984 tilew = tile->grid_link->grid->tile_sx;
985 tileh = tile->grid_link->grid->tile_sy;
986
987 if(!tile->raw_image) {
988 tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
989 GC_CHECK_ERROR(ctx);
990 }
991
992 /* remap xrgb to rgb */
993 rgb = (unsigned char*)malloc(tilew*tileh*3);
994 for(r=0; r<tile->raw_image->h; r++) {
995 unsigned char *imptr = tile->raw_image->data + r * tile->raw_image->stride;
996 unsigned char *rgbptr = rgb + r * tilew * 3;
997 for(c=0; c<tile->raw_image->w; c++) {
998 rgbptr[0] = imptr[2];
999 rgbptr[1] = imptr[1];
1000 rgbptr[2] = imptr[0];
1001 rgbptr += 3;
1002 imptr += 4;
1003 }
1004 }
1005
1006 /*
1007 * aquire a lock on the tiff file.
1008 */
1009
1010 while(mapcache_lock_or_wait_for_resource(ctx,(cache->locker?cache->locker:ctx->config->locker),filename, &lock) == MAPCACHE_FALSE);
1011
1012 /* check if the tiff file exists already */
1013 rv = apr_stat(&finfo,filename,0,ctx->pool);
1014 if(!APR_STATUS_IS_ENOENT(rv)) {
1015 hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"r+");
1016 create = 0;
1017 } else {
1018 hTIFF = mapcache_cache_tiff_open(ctx,cache,filename,"w+");
1019 create = 1;
1020 }
1021 if(!hTIFF) {
1022 ctx->set_error(ctx,500,"failed to open/create tiff file %s\n",filename);
1023 goto close_tiff;
1024 }
1025
1026
1027 /*
1028 * compute the width and height of the full tiff file. This
1029 * is not simply the tile size times the number of tiles per
1030 * file for lower zoom levels
1031 */
1032 level = tile->grid_link->grid->levels[tile->z];
1033 ntilesx = MAPCACHE_MIN(cache->count_x, level->maxx);
1034 ntilesy = MAPCACHE_MIN(cache->count_y, level->maxy);
1035 if(create) {
1036 #ifdef USE_GEOTIFF
1037 double adfPixelScale[3], adfTiePoints[6];
1038 mapcache_extent bbox;
1039 GTIF *gtif;
1040 int x,y;
1041 #endif
1042 /* populate the TIFF tags if we are creating the file */
1043
1044 TIFFSetField( hTIFF, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT );
1045 TIFFSetField( hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
1046 TIFFSetField( hTIFF, TIFFTAG_BITSPERSAMPLE, 8 );
1047 TIFFSetField( hTIFF, TIFFTAG_COMPRESSION, COMPRESSION_JPEG );
1048 TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
1049 TIFFSetField( hTIFF, TIFFTAG_TILEWIDTH, tilew );
1050 TIFFSetField( hTIFF, TIFFTAG_TILELENGTH, tileh );
1051 TIFFSetField( hTIFF, TIFFTAG_IMAGEWIDTH, ntilesx * tilew );
1052 TIFFSetField( hTIFF, TIFFTAG_IMAGELENGTH, ntilesy * tileh );
1053 TIFFSetField( hTIFF, TIFFTAG_SAMPLESPERPIXEL,3 );
1054
1055 #ifdef USE_GEOTIFF
1056 gtif = GTIFNew(hTIFF);
1057 if(gtif) {
1058
1059 GTIFKeySet(gtif, GTRasterTypeGeoKey, TYPE_SHORT, 1,
1060 RasterPixelIsArea);
1061
1062 GTIFKeySet( gtif, GeographicTypeGeoKey, TYPE_SHORT, 1,
1063 0 );
1064 GTIFKeySet( gtif, GeogGeodeticDatumGeoKey, TYPE_SHORT,
1065 1, 0 );
1066 GTIFKeySet( gtif, GeogEllipsoidGeoKey, TYPE_SHORT, 1,
1067 0 );
1068 GTIFKeySet( gtif, GeogSemiMajorAxisGeoKey, TYPE_DOUBLE, 1,
1069 0.0 );
1070 GTIFKeySet( gtif, GeogSemiMinorAxisGeoKey, TYPE_DOUBLE, 1,
1071 0.0 );
1072 switch(tile->grid_link->grid->unit) {
1073 case MAPCACHE_UNIT_FEET:
1074 GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1,
1075 Linear_Foot );
1076 break;
1077 case MAPCACHE_UNIT_METERS:
1078 GTIFKeySet( gtif, ProjLinearUnitsGeoKey, TYPE_SHORT, 1,
1079 Linear_Meter );
1080 break;
1081 case MAPCACHE_UNIT_DEGREES:
1082 GTIFKeySet(gtif, GeogAngularUnitsGeoKey, TYPE_SHORT, 0,
1083 Angular_Degree );
1084 break;
1085 default:
1086 break;
1087 }
1088
1089 GTIFWriteKeys(gtif);
1090 GTIFFree(gtif);
1091
1092 adfPixelScale[0] = adfPixelScale[1] = level->resolution;
1093 adfPixelScale[2] = 0.0;
1094 TIFFSetField( hTIFF, TIFFTAG_GEOPIXELSCALE, 3, adfPixelScale );
1095
1096
1097 /* top left tile x,y */
1098 x = (tile->x / cache->count_x)*(cache->count_x);
1099 y = (tile->y / cache->count_y)*(cache->count_y) + ntilesy - 1;
1100
1101 mapcache_grid_get_tile_extent(ctx, tile->grid_link->grid,
1102 x,y,tile->z,&bbox);
1103 adfTiePoints[0] = 0.0;
1104 adfTiePoints[1] = 0.0;
1105 adfTiePoints[2] = 0.0;
1106 adfTiePoints[3] = bbox.minx;
1107 adfTiePoints[4] = bbox.maxy;
1108 adfTiePoints[5] = 0.0;
1109 TIFFSetField( hTIFF, TIFFTAG_GEOTIEPOINTS, 6, adfTiePoints );
1110 }
1111
1112 #endif
1113 }
1114 TIFFSetField(hTIFF, TIFFTAG_JPEGQUALITY, format->quality);
1115 if(format->photometric == MAPCACHE_PHOTOMETRIC_RGB) {
1116 TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
1117 } else {
1118 TIFFSetField( hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_YCBCR);
1119 }
1120 TIFFSetField( hTIFF, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
1121
1122 /* x offset of the tile along a row */
1123 tiff_offx = tile->x % ntilesx;
1124
1125 /*
1126 * y offset of the requested row. we inverse it as the rows are ordered
1127 * from top to bottom, whereas the tile y is bottom to top
1128 */
1129 tiff_offy = ntilesy - (tile->y % ntilesy) -1;
1130 tiff_off = tiff_offy * ntilesx + tiff_offx;
1131
1132
1133 rv = TIFFWriteEncodedTile(hTIFF, tiff_off, rgb, tilew*tileh*3);
1134 free(rgb);
1135 if(!rv) {
1136 ctx->set_error(ctx,500,"failed TIFFWriteEncodedTile to %s",filename);
1137 goto close_tiff;
1138 }
1139 rv = TIFFWriteCheck( hTIFF, 1, "cache_set()");
1140 if(!rv) {
1141 ctx->set_error(ctx,500,"failed TIFFWriteCheck %s",filename);
1142 goto close_tiff;
1143 }
1144
1145 if(create) {
1146 rv = TIFFWriteDirectory(hTIFF);
1147 if(!rv) {
1148 ctx->set_error(ctx,500,"failed TIFFWriteDirectory to %s",filename);
1149 goto close_tiff;
1150 }
1151 }
1152
1153 close_tiff:
1154 if(hTIFF)
1155 MyTIFFClose(hTIFF);
1156 mapcache_unlock_resource(ctx,cache->locker?cache->locker:ctx->config->locker, lock);
1157 #else
1158 ctx->set_error(ctx,500,"tiff write support disabled by default");
1159 #endif
1160
1161 }
1162
1163 /**
1164 * \private \memberof mapcache_cache_tiff
1165 */
_mapcache_cache_tiff_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * pcache,mapcache_cfg * config)1166 static void _mapcache_cache_tiff_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
1167 {
1168 ezxml_t cur_node;
1169 mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
1170 char * format_name;
1171 mapcache_image_format *pformat;
1172 if ((cur_node = ezxml_child(node,"template")) != NULL) {
1173 char *fmt;
1174 cache->filename_template = apr_pstrdup(ctx->pool,cur_node->txt);
1175 fmt = (char*)ezxml_attr(cur_node,"x_fmt");
1176 if(fmt && *fmt) {
1177 cache->x_fmt = apr_pstrdup(ctx->pool,fmt);
1178 }
1179 fmt = (char*)ezxml_attr(cur_node,"y_fmt");
1180 if(fmt && *fmt) {
1181 cache->y_fmt = apr_pstrdup(ctx->pool,fmt);
1182 }
1183 fmt = (char*)ezxml_attr(cur_node,"z_fmt");
1184 if(fmt && *fmt) {
1185 cache->z_fmt = apr_pstrdup(ctx->pool,fmt);
1186 }
1187 fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt");
1188 if(fmt && *fmt) {
1189 cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt);
1190 }
1191 fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt");
1192 if(fmt && *fmt) {
1193 cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt);
1194 }
1195 fmt = (char*)ezxml_attr(cur_node,"div_x_fmt");
1196 if(fmt && *fmt) {
1197 cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt);
1198 }
1199 fmt = (char*)ezxml_attr(cur_node,"div_y_fmt");
1200 if(fmt && *fmt) {
1201 cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt);
1202 }
1203 fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt");
1204 if(fmt && *fmt) {
1205 cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt);
1206 }
1207 fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt");
1208 if(fmt && *fmt) {
1209 cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
1210 }
1211 }
1212 cur_node = ezxml_child(node,"xcount");
1213 if(cur_node && cur_node->txt && *cur_node->txt) {
1214 char *endptr;
1215 cache->count_x = (int)strtol(cur_node->txt,&endptr,10);
1216 if(*endptr != 0) {
1217 ctx->set_error(ctx,400,"failed to parse xcount value %s for tiff cache %s", cur_node->txt,pcache->name);
1218 return;
1219 }
1220 }
1221 cur_node = ezxml_child(node,"ycount");
1222 if(cur_node && cur_node->txt && *cur_node->txt) {
1223 char *endptr;
1224 cache->count_y = (int)strtol(cur_node->txt,&endptr,10);
1225 if(*endptr != 0) {
1226 ctx->set_error(ctx,400,"failed to parse ycount value %s for tiff cache %s", cur_node->txt,pcache->name);
1227 return;
1228 }
1229 }
1230 cur_node = ezxml_child(node,"format");
1231 if(cur_node && cur_node->txt && *cur_node->txt) {
1232 format_name = cur_node->txt;
1233 } else {
1234 format_name = "JPEG";
1235 }
1236 pformat = mapcache_configuration_get_image_format(
1237 config,format_name);
1238 if(!pformat) {
1239 ctx->set_error(ctx,500,"TIFF cache %s references unknown image format %s",
1240 pcache->name, format_name);
1241 return;
1242 }
1243 if(pformat->type != GC_JPEG) {
1244 ctx->set_error(ctx,500,"TIFF cache %s can only reference a JPEG image format",
1245 pcache->name);
1246 return;
1247 }
1248 cache->format = (mapcache_image_format_jpeg*)pformat;
1249
1250 cur_node = ezxml_child(node,"locker");
1251 if(cur_node) {
1252 mapcache_config_parse_locker(ctx, cur_node, &cache->locker);
1253 }
1254
1255 cache->storage.type = MAPCACHE_TIFF_STORAGE_FILE;
1256 cur_node = ezxml_child(node,"storage");
1257 if (cur_node) {
1258 ezxml_t child_node;
1259 const char *type = ezxml_attr(cur_node,"type");
1260 if( !type ) {
1261 ctx->set_error(ctx,400,
1262 "<storage> with no \"type\" attribute in cache (%s)",
1263 pcache->name);
1264 return;
1265 }
1266
1267 if( strcmp(type, "rest") == 0 ) {
1268 cache->storage.type = MAPCACHE_TIFF_STORAGE_REST;
1269 }
1270 else if( strcmp(type, "google") == 0 ) {
1271 cache->storage.type = MAPCACHE_TIFF_STORAGE_GOOGLE;
1272 if ((child_node = ezxml_child(cur_node,"access")) != NULL) {
1273 cache->storage.u.google.access =
1274 apr_pstrdup(ctx->pool, child_node->txt);
1275 } else if ( getenv("GS_ACCESS_KEY_ID")) {
1276 cache->storage.u.google.access =
1277 apr_pstrdup(ctx->pool,getenv("GS_ACCESS_KEY_ID"));
1278 } else {
1279 ctx->set_error(ctx,400,
1280 "google storage in cache (%s) is missing "
1281 "required <access> child", pcache->name);
1282 return;
1283 }
1284 if ((child_node = ezxml_child(cur_node,"secret")) != NULL) {
1285 cache->storage.u.google.secret =
1286 apr_pstrdup(ctx->pool, child_node->txt);
1287 } else if ( getenv("GS_SECRET_ACCESS_KEY")) {
1288 cache->storage.u.google.access =
1289 apr_pstrdup(ctx->pool,getenv("GS_SECRET_ACCESS_KEY"));
1290 } else {
1291 ctx->set_error(ctx,400,
1292 "google storage in cache (%s) is missing "
1293 "required <secret> child", pcache->name);
1294 return;
1295 }
1296 }
1297 else {
1298 ctx->set_error(ctx, 400, "unknown cache type %s for cache \"%s\"",
1299 type, pcache->name);
1300 return;
1301 }
1302
1303 if ((child_node = ezxml_child(cur_node,"connection_timeout")) != NULL) {
1304 char *endptr;
1305 cache->storage.connection_timeout = (int)strtol(child_node->txt,&endptr,10);
1306 if(*endptr != 0 || cache->storage.connection_timeout<1) {
1307 ctx->set_error(ctx,400,"invalid rest cache <connection_timeout> "
1308 "\"%s\" (positive integer expected)",
1309 child_node->txt);
1310 return;
1311 }
1312 } else {
1313 cache->storage.connection_timeout = 30;
1314 }
1315
1316 if ((child_node = ezxml_child(cur_node,"timeout")) != NULL) {
1317 char *endptr;
1318 cache->storage.timeout = (int)strtol(child_node->txt,&endptr,10);
1319 if(*endptr != 0 || cache->storage.timeout<1) {
1320 ctx->set_error(ctx,400,"invalid rest cache <timeout> \"%s\" "
1321 "(positive integer expected)",
1322 child_node->txt);
1323 return;
1324 }
1325 } else {
1326 cache->storage.timeout = 120;
1327 }
1328
1329 if ((child_node = ezxml_child(cur_node,"header_file")) != NULL) {
1330 cache->storage.header_file = apr_pstrdup(ctx->pool, child_node->txt);
1331 }
1332
1333 }
1334
1335 }
1336
1337 /**
1338 * \private \memberof mapcache_cache_tiff
1339 */
_mapcache_cache_tiff_configuration_post_config(mapcache_context * ctx,mapcache_cache * pcache,mapcache_cfg * cfg)1340 static void _mapcache_cache_tiff_configuration_post_config(mapcache_context *ctx, mapcache_cache *pcache,
1341 mapcache_cfg *cfg)
1342 {
1343 mapcache_cache_tiff *cache = (mapcache_cache_tiff*)pcache;
1344 /* check all required parameters are configured */
1345 if((!cache->filename_template || !strlen(cache->filename_template))) {
1346 ctx->set_error(ctx, 400, "tiff cache %s has no template pattern",cache->cache.name);
1347 return;
1348 }
1349 if(cache->count_x <= 0 || cache->count_y <= 0) {
1350 ctx->set_error(ctx, 400, "tiff cache %s has invalid count (%d,%d)",cache->count_x,cache->count_y);
1351 return;
1352 }
1353
1354 #ifdef USE_GDAL
1355 if(cache->storage.type == MAPCACHE_TIFF_STORAGE_REST &&
1356 strncmp(cache->filename_template, "http://", 6) != 0 &&
1357 strncmp(cache->filename_template, "https://", 7) != 0 ) {
1358 ctx->set_error(ctx, 400, "tiff cache %s template pattern should begin with http:// or https://",cache->cache.name);
1359 return;
1360 }
1361
1362 if(cache->storage.type == MAPCACHE_TIFF_STORAGE_GOOGLE &&
1363 strncmp(cache->filename_template, "https://storage.googleapis.com/",
1364 strlen("https://storage.googleapis.com/")) != 0 &&
1365 strncmp(cache->filename_template, "/vsigs/",
1366 strlen("/vsigs/")) != 0 ) {
1367 ctx->set_error(ctx, 400, "tiff cache %s template pattern should begin "
1368 "with https://storage.googleapis.com/ or /vsigs/",cache->cache.name);
1369 return;
1370 }
1371 #else
1372 if(cache->storage.type != MAPCACHE_TIFF_STORAGE_FILE )
1373 {
1374 ctx->set_error(ctx, 400, "tiff cache %s cannot use a network based "
1375 "storage due to mising GDAL dependency",
1376 cache->cache.name);
1377 return;
1378 }
1379 #endif
1380 }
1381
1382 /**
1383 * \brief creates and initializes a mapcache_tiff_cache
1384 */
mapcache_cache_tiff_create(mapcache_context * ctx)1385 mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx)
1386 {
1387 mapcache_cache_tiff *cache = apr_pcalloc(ctx->pool,sizeof(mapcache_cache_tiff));
1388 if(!cache) {
1389 ctx->set_error(ctx, 500, "failed to allocate tiff cache");
1390 return NULL;
1391 }
1392 cache->cache.metadata = apr_table_make(ctx->pool,3);
1393 cache->cache.type = MAPCACHE_CACHE_TIFF;
1394 cache->cache._tile_delete = _mapcache_cache_tiff_delete;
1395 cache->cache._tile_get = _mapcache_cache_tiff_get;
1396 cache->cache._tile_exists = _mapcache_cache_tiff_has_tile;
1397 cache->cache._tile_set = _mapcache_cache_tiff_set;
1398 cache->cache.configuration_post_config = _mapcache_cache_tiff_configuration_post_config;
1399 cache->cache.configuration_parse_xml = _mapcache_cache_tiff_configuration_parse_xml;
1400 cache->count_x = 10;
1401 cache->count_y = 10;
1402 cache->x_fmt = cache->y_fmt = cache->z_fmt
1403 = cache->inv_x_fmt = cache->inv_y_fmt
1404 = cache->div_x_fmt = cache->div_y_fmt
1405 = cache->inv_div_x_fmt = cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,"%d");
1406 #ifndef DEBUG
1407 TIFFSetWarningHandler(NULL);
1408 TIFFSetErrorHandler(NULL);
1409 #endif
1410 return (mapcache_cache*)cache;
1411 }
1412
1413 #else
1414
mapcache_cache_tiff_create(mapcache_context * ctx)1415 mapcache_cache* mapcache_cache_tiff_create(mapcache_context *ctx) {
1416 ctx->set_error(ctx,400,"TIFF support not compiled in this version");
1417 return NULL;
1418 }
1419
1420 #endif
1421
1422 /* vim: ts=2 sts=2 et sw=2
1423 */
1424