1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching support file: GDAL datasource support (incomplete and disabled)
6 * Author: Thomas Bonfort, Even Rouault and the MapServer team.
7 *
8 ******************************************************************************
9 * Copyright (c) 1996-2011 Regents of the University of Minnesota.
10 * Copyright (c) 2004, Frank Warmerdam <warmerdam@pobox.com> (for GDALAutoCreateWarpedVRT who CreateWarpedVRT is derived from)
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included in
20 * all copies of this Software or works derived from this Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 *****************************************************************************/
30
31 #include "mapcache.h"
32 #include "ezxml.h"
33 #include <apr_tables.h>
34 #include <apr_strings.h>
35
36 #ifdef USE_GDAL
37
38 #include <gdal.h>
39 #include <cpl_conv.h>
40
41 #include "gdalwarper.h"
42 #include "cpl_string.h"
43 #include "ogr_srs_api.h"
44 #include "gdal_vrt.h"
45
46 #define MAPCACHE_DEFAULT_RESAMPLE_ALG GRA_Bilinear
47
48 /* Note: this will also work with GDAL >= 2.0 */
49 #if GDAL_VERSION_MAJOR < 2
50 #define USE_PRE_GDAL2_METHOD
51 #endif
52 /*#define USE_PRE_GDAL2_METHOD*/
53
54 typedef struct mapcache_source_gdal mapcache_source_gdal;
55
56 /**\class mapcache_source_gdal
57 * \brief GDAL mapcache_source
58 * \implements mapcache_source
59 */
60 struct mapcache_source_gdal {
61 mapcache_source source;
62 const char *datastr; /**< the gdal source string*/
63 const char *srs_wkt;
64 GDALResampleAlg eResampleAlg; /**< resampling algorithm */
65 const char *srcOvrLevel; /**< strategy to pickup source overview: AUTO, NONE,
66 AUTO-xxx, xxxx. See -ovr doc in http://www.gdal.org/gdalwarp.html.
67 Only used for GDAL >= 2.0 (could probably be made to work for USE_PRE_GDAL2_METHOD with more work) */
68 int bUseConnectionPool;
69 };
70
71 typedef struct {
72 mapcache_source_gdal *gdal;
73 const char *gdal_data;
74 const char *dst_srs;
75 } gdal_connection_params;
76
77 typedef struct {
78 GDALDatasetH hSrcDS;
79 char *dst_srs_wkt;
80 } gdal_connection;
81
mapcache_source_gdal_connection_constructor(mapcache_context * ctx,void ** conn_,void * params)82 void mapcache_source_gdal_connection_constructor(mapcache_context *ctx, void **conn_, void *params) {
83 gdal_connection_params *p = (gdal_connection_params*)params;
84 gdal_connection *c = malloc(sizeof(gdal_connection));
85 OGRSpatialReferenceH hDstSRS;
86
87 *conn_ = NULL;
88 /* -------------------------------------------------------------------- */
89 /* Open source dataset. */
90 /* -------------------------------------------------------------------- */
91 c->hSrcDS = GDALOpen( p->gdal_data, GA_ReadOnly );
92
93 if( c->hSrcDS == NULL ) {
94 ctx->set_error(ctx, 500, "Cannot open gdal source for %s .\n", p->gdal->source.name );
95 free(c);
96 return;
97 }
98
99 /* -------------------------------------------------------------------- */
100 /* Check that there's 3 or 4 raster bands. */
101 /* -------------------------------------------------------------------- */
102 if ( GDALGetRasterCount(c->hSrcDS) != 3 && GDALGetRasterCount(c->hSrcDS) != 4) {
103 ctx->set_error(ctx, 500, "Input gdal source for %s has %d raster bands, but only 3 or 4 are supported.\n",
104 p->gdal->source.name, GDALGetRasterCount(c->hSrcDS) );
105 GDALClose(c->hSrcDS);
106 free(c);
107 return;
108 }
109
110
111
112 hDstSRS = OSRNewSpatialReference( NULL );
113 if( OSRSetFromUserInput( hDstSRS, p->dst_srs ) == OGRERR_NONE ) {
114 c->dst_srs_wkt = NULL;
115 OSRExportToWkt( hDstSRS, &c->dst_srs_wkt );
116 }
117 else {
118 ctx->set_error(ctx,500,"failed to parse gdal srs %s",p->dst_srs);
119 GDALClose(c->hSrcDS);
120 free(c);
121 return;
122 }
123
124 OSRDestroySpatialReference( hDstSRS );
125 *conn_ = c;
126 }
127
mapcache_source_gdal_connection_destructor(void * conn_)128 void mapcache_source_gdal_connection_destructor(void *conn_) {
129 gdal_connection *c = (gdal_connection*)conn_;
130 CPLFree(c->dst_srs_wkt);
131 GDALClose(c->hSrcDS);
132 free(c);
133 }
134
_gdal_get_connection(mapcache_context * ctx,mapcache_source_gdal * gdal,const char * dst_srs,const char * gdal_data)135 static mapcache_pooled_connection* _gdal_get_connection(mapcache_context *ctx, mapcache_source_gdal *gdal, const char *dst_srs, const char *gdal_data)
136 {
137 mapcache_pooled_connection *pc;
138 gdal_connection_params params;
139 char *key;
140
141 params.gdal = gdal;
142 params.dst_srs = dst_srs;
143 params.gdal_data = gdal_data;
144
145 key = apr_pstrcat(ctx->pool, gdal_data, dst_srs, NULL);
146
147 pc = mapcache_connection_pool_get_connection(ctx,key,mapcache_source_gdal_connection_constructor,
148 mapcache_source_gdal_connection_destructor, ¶ms);
149 return pc;
150 }
151 #ifdef USE_PRE_GDAL2_METHOD
152 /* Creates a (virtual) dataset that matches an overview level of the source
153 dataset. This dataset has references on the source dataset, so it should
154 be closed before it.
155 */
CreateOverviewVRTDataset(GDALDatasetH hSrcDS,int iOvrLevel)156 static GDALDatasetH CreateOverviewVRTDataset(GDALDatasetH hSrcDS,
157 int iOvrLevel)
158 {
159 double adfSrcGeoTransform[6];
160 int iBand;
161 GDALRasterBandH hFirstBand = GDALGetRasterBand(hSrcDS, 1);
162 GDALRasterBandH hOvr = GDALGetOverview( hFirstBand, iOvrLevel );
163 int nFullResXSize = GDALGetRasterBandXSize(hFirstBand);
164 int nFullResYSize = GDALGetRasterBandYSize(hFirstBand);
165 int nVRTXSize = GDALGetRasterBandXSize(hOvr);
166 int nVRTYSize = GDALGetRasterBandYSize(hOvr);
167
168 VRTDatasetH hVRTDS = VRTCreate( nVRTXSize, nVRTYSize );
169
170 /* Scale geotransform */
171 if( GDALGetGeoTransform( hSrcDS, adfSrcGeoTransform) == CE_None )
172 {
173 adfSrcGeoTransform[1] *= (double)nFullResXSize / nVRTXSize;
174 adfSrcGeoTransform[2] *= (double)nFullResYSize / nVRTYSize;
175 adfSrcGeoTransform[4] *= (double)nFullResXSize / nVRTXSize;
176 adfSrcGeoTransform[5] *= (double)nFullResYSize / nVRTYSize;
177 GDALSetProjection( hVRTDS, GDALGetProjectionRef(hSrcDS) );
178 GDALSetGeoTransform( hVRTDS, adfSrcGeoTransform );
179 }
180
181 /* Scale GCPs */
182 if( GDALGetGCPCount(hSrcDS) > 0 )
183 {
184 const GDAL_GCP* pasGCPsMain = GDALGetGCPs(hSrcDS);
185 int nGCPCount = GDALGetGCPCount(hSrcDS);
186 GDAL_GCP* pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPsMain );
187 int i;
188 for(i = 0; i < nGCPCount; i++)
189 {
190 pasGCPList[i].dfGCPPixel *= (double)nVRTXSize / nFullResXSize;
191 pasGCPList[i].dfGCPLine *= (double)nVRTYSize / nFullResYSize;
192 }
193 GDALSetGCPs( hVRTDS, nGCPCount, pasGCPList, GDALGetGCPProjection(hSrcDS) );
194 GDALDeinitGCPs( nGCPCount, pasGCPList );
195 CPLFree( pasGCPList );
196 }
197
198 /* Create bands */
199 for(iBand = 1; iBand <= GDALGetRasterCount(hSrcDS); iBand++ )
200 {
201 GDALRasterBandH hSrcBand;
202 GDALRasterBandH hVRTBand;
203 int bNoDataSet = FALSE;
204 double dfNoData;
205
206 VRTAddBand( hVRTDS, GDT_Byte, NULL );
207 hVRTBand = GDALGetRasterBand(hVRTDS, iBand);
208 hSrcBand = GDALGetOverview(GDALGetRasterBand(hSrcDS, iBand), iOvrLevel);
209 dfNoData = GDALGetRasterNoDataValue(hSrcBand, &bNoDataSet);
210 if( bNoDataSet )
211 GDALSetRasterNoDataValue(hVRTBand, dfNoData);
212
213 /* Note: the consumer of this VRT is the warper, which doesn't do any */
214 /* subsampled RasterIO requests, so NEAR is fine */
215 VRTAddSimpleSource( hVRTBand, hSrcBand,
216 0, 0, nVRTXSize, nVRTYSize,
217 0, 0, nVRTXSize, nVRTYSize,
218 "NEAR", VRT_NODATA_UNSET );
219 }
220
221 return hVRTDS;
222 }
223 #endif
224
225 /* Derived from GDALAutoCreateWarpedVRT(), with various improvements. */
226 /* Returns a warped VRT that covers the passed extent, in pszDstWKT. */
227 /* The provided width and height are used, but the size of the returned dataset */
228 /* may not match those values. In the USE_PRE_GDAL2_METHOD, it should match them. */
229 /* In the non USE_PRE_GDAL2_METHOD case, it might be a multiple of those values. */
230 /* phTmpDS is an output parameter (a temporary VRT in the USE_PRE_GDAL2_METHOD case). */
231 static GDALDatasetH
CreateWarpedVRT(GDALDatasetH hSrcDS,const char * pszSrcWKT,const char * pszDstWKT,int width,int height,const mapcache_extent * extent,GDALResampleAlg eResampleAlg,double dfMaxError,char ** papszWarpOptions,GDALDatasetH * phTmpDS)232 CreateWarpedVRT( GDALDatasetH hSrcDS,
233 const char *pszSrcWKT,
234 const char *pszDstWKT,
235 int width, int height,
236 const mapcache_extent *extent,
237 GDALResampleAlg eResampleAlg,
238 double dfMaxError,
239 char** papszWarpOptions,
240 GDALDatasetH *phTmpDS )
241
242 {
243 int i;
244 GDALWarpOptions *psWO;
245 double adfDstGeoTransform[6];
246 GDALDatasetH hDstDS;
247 int nDstPixels, nDstLines;
248 CPLErr eErr;
249 int bHaveNodata = FALSE;
250 char** papszOptions = NULL;
251
252 /* -------------------------------------------------------------------- */
253 /* Populate the warp options. */
254 /* -------------------------------------------------------------------- */
255
256 psWO = GDALCreateWarpOptions();
257 psWO->papszWarpOptions = CSLDuplicate(papszWarpOptions);
258
259 psWO->eResampleAlg = eResampleAlg;
260
261 psWO->hSrcDS = hSrcDS;
262
263 psWO->nBandCount = GDALGetRasterCount( hSrcDS );
264 if( psWO->nBandCount == 4 )
265 {
266 psWO->nBandCount = 3;
267 psWO->nSrcAlphaBand = 4;
268 psWO->nDstAlphaBand = 4;
269 }
270 /* Due to the reprojection, we might get transparency in the edges */
271 else if( psWO->nBandCount == 3 )
272 psWO->nDstAlphaBand = 4;
273
274 psWO->panSrcBands = (int*)CPLMalloc( sizeof(int) * psWO->nBandCount );
275 psWO->panDstBands = (int*)CPLMalloc( sizeof(int) * psWO->nBandCount );
276
277 for( i = 0; i < psWO->nBandCount; i++ )
278 {
279 psWO->panSrcBands[i] = i+1;
280 psWO->panDstBands[i] = i+1;
281 }
282
283 /* -------------------------------------------------------------------- */
284 /* Set nodata values if existing */
285 /* -------------------------------------------------------------------- */
286 GDALGetRasterNoDataValue( GDALGetRasterBand(hSrcDS, 1), &bHaveNodata);
287 if( bHaveNodata )
288 {
289 psWO->padfSrcNoDataReal = (double *)
290 CPLMalloc(psWO->nBandCount*sizeof(double));
291 psWO->padfSrcNoDataImag = (double *)
292 CPLCalloc(psWO->nBandCount, sizeof(double)); /* zero initialized */
293
294 for( i = 0; i < psWO->nBandCount; i++ )
295 {
296 GDALRasterBandH hBand = GDALGetRasterBand( hSrcDS, i+1 );
297
298 double dfReal = GDALGetRasterNoDataValue( hBand, &bHaveNodata );
299
300 if( bHaveNodata )
301 {
302 psWO->padfSrcNoDataReal[i] = dfReal;
303 }
304 else
305 {
306 psWO->padfSrcNoDataReal[i] = -123456.789;
307 }
308 }
309 }
310
311 /* -------------------------------------------------------------------- */
312 /* Create the transformer. */
313 /* -------------------------------------------------------------------- */
314 psWO->pfnTransformer = GDALGenImgProjTransform;
315 psWO->pTransformerArg =
316 GDALCreateGenImgProjTransformer( psWO->hSrcDS, pszSrcWKT,
317 NULL, pszDstWKT,
318 TRUE, 1.0, 0 );
319
320 if( psWO->pTransformerArg == NULL )
321 {
322 GDALDestroyWarpOptions( psWO );
323 return NULL;
324 }
325
326 /* -------------------------------------------------------------------- */
327 /* Figure out the desired output bounds and resolution. */
328 /* -------------------------------------------------------------------- */
329 eErr =
330 GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer,
331 psWO->pTransformerArg,
332 adfDstGeoTransform, &nDstPixels, &nDstLines );
333 if( eErr != CE_None )
334 {
335 GDALDestroyTransformer( psWO->pTransformerArg );
336 GDALDestroyWarpOptions( psWO );
337 return NULL;
338 }
339
340 /* -------------------------------------------------------------------- */
341 /* To minimize the risk of extra resampling done by generic */
342 /* RasterIO itself and maximize resampling done in the wraper, */
343 /* adjust the resolution so that the overview factor of the output */
344 /* dataset that will indirectly query matches an exiting overview */
345 /* factor of the input dataset. */
346 /* -------------------------------------------------------------------- */
347 {
348 double dfDesiredXRes = (extent->maxx - extent->minx) / width;
349 double dfDesiredYRes = (extent->maxy - extent->miny) / height;
350 double dfDesiredRes = MIN( dfDesiredXRes, dfDesiredYRes );
351 double dfGuessedFullRes = MIN( adfDstGeoTransform[1],
352 fabs(adfDstGeoTransform[5]) );
353 double dfApproxDstOvrRatio = dfDesiredRes / dfGuessedFullRes;
354
355 GDALRasterBandH hFirstBand = GDALGetRasterBand(hSrcDS, 1);
356 int nOvrCount = GDALGetOverviewCount(hFirstBand);
357 int nSrcXSize = GDALGetRasterBandXSize(hFirstBand);
358 int i;
359 double dfSrcOvrRatio = 1.0;
360 #ifdef USE_PRE_GDAL2_METHOD
361 int iSelectedOvr = -1;
362 #endif
363 for( i = 0; *phTmpDS == NULL && i < nOvrCount; i ++)
364 {
365 GDALRasterBandH hOvr = GDALGetOverview(hFirstBand, i);
366 int nOvrXSize = GDALGetRasterBandXSize(hOvr);
367 double dfCurOvrRatio = (double)nSrcXSize / nOvrXSize;
368 if(dfCurOvrRatio > dfApproxDstOvrRatio+0.1 ) /* +0.1 to avoid rounding issues */
369 {
370 break;
371 }
372 dfSrcOvrRatio = dfCurOvrRatio;
373 #ifdef USE_PRE_GDAL2_METHOD
374 iSelectedOvr = i;
375 #endif
376 }
377
378 #ifdef USE_PRE_GDAL2_METHOD
379 if( iSelectedOvr >= 0 )
380 {
381 GDALDestroyTransformer( psWO->pTransformerArg );
382 GDALDestroyWarpOptions( psWO );
383
384 *phTmpDS = CreateOverviewVRTDataset(hSrcDS, iSelectedOvr);
385 return CreateWarpedVRT( *phTmpDS,
386 pszSrcWKT,
387 pszDstWKT,
388 width, height,
389 extent,
390 eResampleAlg,
391 dfMaxError,
392 papszWarpOptions,
393 phTmpDS );
394 }
395 #endif
396
397 adfDstGeoTransform[1] = dfDesiredXRes / dfSrcOvrRatio;
398 adfDstGeoTransform[5] = -dfDesiredYRes / dfSrcOvrRatio;
399 }
400
401 /* -------------------------------------------------------------------- */
402 /* Compute geotransform and raster dimension for our extent of */
403 /* interest. */
404 /* -------------------------------------------------------------------- */
405 adfDstGeoTransform[0] = extent->minx;
406 adfDstGeoTransform[2] = 0.0;
407 adfDstGeoTransform[3] = extent->maxy;
408 adfDstGeoTransform[4] = 0.0;
409 nDstPixels = (int)( (extent->maxx - extent->minx) / adfDstGeoTransform[1] + 0.5 );
410 nDstLines = (int)( (extent->maxy - extent->miny) / fabs(adfDstGeoTransform[5]) + 0.5 );
411 /*printf("nDstPixels=%d nDstLines=%d\n", nDstPixels, nDstLines);*/
412
413 /* -------------------------------------------------------------------- */
414 /* Update the transformer to include an output geotransform */
415 /* back to pixel/line coordinates. */
416 /* -------------------------------------------------------------------- */
417
418 GDALSetGenImgProjTransformerDstGeoTransform(
419 psWO->pTransformerArg, adfDstGeoTransform );
420
421 /* -------------------------------------------------------------------- */
422 /* Do we want to apply an approximating transformation? */
423 /* -------------------------------------------------------------------- */
424 if( dfMaxError > 0.0 )
425 {
426 psWO->pTransformerArg =
427 GDALCreateApproxTransformer( psWO->pfnTransformer,
428 psWO->pTransformerArg,
429 dfMaxError );
430 psWO->pfnTransformer = GDALApproxTransform;
431 GDALApproxTransformerOwnsSubtransformer(psWO->pTransformerArg, TRUE);
432 }
433
434 /* -------------------------------------------------------------------- */
435 /* Create the VRT file. */
436 /* -------------------------------------------------------------------- */
437
438 /* We could potentially used GDALCreateWarpedVRT() instead of this logic */
439 /* but GDALCreateWarpedVRT() in GDAL < 2.0.1 doesn't create the destination */
440 /* alpha band. */
441
442 papszOptions = CSLSetNameValue(NULL, "SUBCLASS", "VRTWarpedDataset");
443 hDstDS = GDALCreate( GDALGetDriverByName("VRT"), "", nDstPixels, nDstLines,
444 psWO->nBandCount + ((psWO->nDstAlphaBand != 0) ? 1 : 0),
445 GDT_Byte, papszOptions );
446 CSLDestroy(papszOptions);
447 if( hDstDS == NULL )
448 {
449 GDALDestroyWarpOptions( psWO );
450 return NULL;
451 }
452
453 psWO->hDstDS = hDstDS;
454
455 GDALSetGeoTransform( hDstDS, adfDstGeoTransform );
456 if( GDALInitializeWarpedVRT( hDstDS, psWO ) != CE_None )
457 {
458 GDALClose(hDstDS);
459 GDALDestroyWarpOptions( psWO );
460 return NULL;
461 }
462
463 GDALDestroyWarpOptions( psWO );
464
465 return hDstDS;
466 }
467
468
469 /**
470 * \private \memberof mapcache_source_gdal
471 * \sa mapcache_source::render_metatile()
472 */
_mapcache_source_gdal_render_metatile(mapcache_context * ctx,mapcache_source * psource,mapcache_map * map)473 void _mapcache_source_gdal_render_metatile(mapcache_context *ctx, mapcache_source *psource, mapcache_map *map)
474 {
475 mapcache_source_gdal *gdal = (mapcache_source_gdal*)psource;
476 gdal_connection *gdal_conn;
477 GDALDatasetH hDstDS;
478 GDALDatasetH hTmpDS = NULL;
479 mapcache_buffer *data;
480 unsigned char *rasterdata;
481 CPLErr eErr;
482 mapcache_pooled_connection *pc = NULL;
483 int bands_bgra[] = { 3, 2, 1, 4 }; /* mapcache buffer order is BGRA */
484
485 CPLErrorReset();
486
487 if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
488 pc = _gdal_get_connection(ctx, gdal, map->grid_link->grid->srs, gdal->datastr );
489 GC_CHECK_ERROR(ctx);
490 gdal_conn = (gdal_connection*) pc->connection;
491 } else {
492 gdal_connection_params params;
493 params.gdal = gdal;
494 params.dst_srs = map->grid_link->grid->srs;
495 params.gdal_data = gdal->datastr;
496 mapcache_source_gdal_connection_constructor(ctx, (void**)(&gdal_conn), ¶ms);
497 GC_CHECK_ERROR(ctx);
498 }
499
500
501
502 hDstDS = CreateWarpedVRT( gdal_conn->hSrcDS, gdal->srs_wkt, gdal_conn->dst_srs_wkt,
503 map->width, map->height,
504 &map->extent,
505 gdal->eResampleAlg, 0.125, NULL, &hTmpDS );
506
507 if( hDstDS == NULL ) {
508 ctx->set_error(ctx, 500,"CreateWarpedVRT() failed");
509 if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
510 mapcache_connection_pool_invalidate_connection(ctx,pc);
511 } else {
512 mapcache_source_gdal_connection_destructor(gdal_conn);
513 }
514 return;
515 }
516
517 if(GDALGetRasterCount(hDstDS) != 4) {
518 ctx->set_error(ctx, 500,"gdal did not create a 4 band image");
519 GDALClose(hDstDS); /* close first this one, as it references hSrcDS */
520 if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
521 mapcache_connection_pool_invalidate_connection(ctx,pc);
522 } else {
523 mapcache_source_gdal_connection_destructor(gdal_conn);
524 }
525 return;
526 }
527
528 data = mapcache_buffer_create(map->height*map->width*4,ctx->pool);
529 rasterdata = data->buf;
530
531 #if GDAL_VERSION_MAJOR >= 2
532 {
533 GDALRasterIOExtraArg sExtraArg;
534 INIT_RASTERIO_EXTRA_ARG(sExtraArg);
535 if( gdal->eResampleAlg == GRA_Bilinear )
536 sExtraArg.eResampleAlg = GRIORA_Bilinear;
537 else if( gdal->eResampleAlg == GRA_Cubic )
538 sExtraArg.eResampleAlg = GRIORA_Cubic;
539 else if( gdal->eResampleAlg == GRA_CubicSpline )
540 sExtraArg.eResampleAlg = GRIORA_CubicSpline;
541 else if( gdal->eResampleAlg == GRA_Lanczos )
542 sExtraArg.eResampleAlg = GRIORA_Lanczos;
543 else if( gdal->eResampleAlg == GRA_Average )
544 sExtraArg.eResampleAlg = GRIORA_Average;
545
546 if( gdal->srcOvrLevel != NULL )
547 {
548 /* If the user specified a particular strategy to choose the source */
549 /* overview level, apply it now */
550 GDALSetMetadataItem(hDstDS, "SrcOvrLevel",gdal->srcOvrLevel, NULL);
551 }
552
553 /* Hopefully, given how we adjust hDstDS resolution, we should query */
554 /* exactly at the resolution of one overview level of hDstDS, and not */
555 /* do extra resampling in generic RasterIO, but just in case specify */
556 /* the resampling alg in sExtraArg. */
557 eErr = GDALDatasetRasterIOEx( hDstDS, GF_Read,0,0,
558 GDALGetRasterXSize(hDstDS),
559 GDALGetRasterYSize(hDstDS),
560 rasterdata,map->width,map->height,GDT_Byte,
561 4, bands_bgra,
562 4,4*map->width,1, &sExtraArg );
563 }
564 #else
565 eErr = GDALDatasetRasterIO( hDstDS, GF_Read,0,0,
566 GDALGetRasterXSize(hDstDS),
567 GDALGetRasterYSize(hDstDS),
568 rasterdata,map->width,map->height,GDT_Byte,
569 4, bands_bgra,
570 4,4*map->width,1 );
571 #endif
572
573 if( eErr != CE_None ) {
574 ctx->set_error(ctx, 500,"GDAL I/O error occurred");
575 GDALClose(hDstDS); /* close first this one, as it references hTmpDS or hSrcDS */
576 if( hTmpDS )
577 GDALClose(hTmpDS); /* references hSrcDS, so close before */
578 if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
579 mapcache_connection_pool_invalidate_connection(ctx,pc);
580 } else {
581 mapcache_source_gdal_connection_destructor(gdal_conn);
582 }
583 return;
584 }
585
586 map->raw_image = mapcache_image_create(ctx);
587 map->raw_image->w = map->width;
588 map->raw_image->h = map->height;
589 map->raw_image->stride = map->width * 4;
590 map->raw_image->data = rasterdata;
591 map->raw_image->has_alpha = MC_ALPHA_UNKNOWN;
592
593 GDALClose( hDstDS ); /* close first this one, as it references hTmpDS or hSrcDS */
594 if( hTmpDS )
595 GDALClose(hTmpDS); /* references hSrcDS, so close before */
596 if(gdal->bUseConnectionPool == MAPCACHE_TRUE) {
597 mapcache_connection_pool_release_connection(ctx,pc);
598 } else {
599 mapcache_source_gdal_connection_destructor(gdal_conn);
600 }
601 }
602
603 /**
604 * \private \memberof mapcache_source_gdal
605 * \sa mapcache_source::configuration_parse()
606 */
_mapcache_source_gdal_configuration_parse(mapcache_context * ctx,ezxml_t node,mapcache_source * source,mapcache_cfg * config)607 void _mapcache_source_gdal_configuration_parse(mapcache_context *ctx, ezxml_t node, mapcache_source *source, mapcache_cfg *config)
608 {
609 ezxml_t cur_node;
610 mapcache_source_gdal *src = (mapcache_source_gdal*)source;
611
612 if ((cur_node = ezxml_child(node,"data")) != NULL) {
613 src->datastr = apr_pstrdup(ctx->pool,cur_node->txt);
614 }
615 if ((cur_node = ezxml_child(node,"connection_pooled")) != NULL) {
616 if(!strcasecmp(cur_node->txt,"false")) {
617 src->bUseConnectionPool = MAPCACHE_FALSE;
618 } else if(!strcasecmp(cur_node->txt,"true")) {
619 src->bUseConnectionPool = MAPCACHE_TRUE;
620 } else {
621 ctx->set_error(ctx,400,"failed to parse <connection_pooled> (%s). Expecting true or false",cur_node->txt);
622 return;
623 }
624 }
625
626 if ((cur_node = ezxml_child(node,"resample")) != NULL && *cur_node->txt) {
627 if( EQUALN( cur_node->txt, "NEAR", 4) )
628 src->eResampleAlg = GRA_NearestNeighbour;
629 else if( EQUAL( cur_node->txt, "BILINEAR") )
630 src->eResampleAlg = GRA_Bilinear;
631 else if( EQUAL( cur_node->txt, "CUBIC") )
632 src->eResampleAlg = GRA_Cubic;
633 else if( EQUAL( cur_node->txt, "CUBICSPLINE") )
634 src->eResampleAlg = GRA_CubicSpline;
635 else if( EQUAL( cur_node->txt, "LANCZOS") )
636 src->eResampleAlg = GRA_Lanczos;
637 #if GDAL_VERSION_MAJOR >= 2
638 else if( EQUAL( cur_node->txt, "AVERAGE") )
639 src->eResampleAlg = GRA_Average;
640 #endif
641 else {
642 ctx->set_error(ctx, 500, "unsupported gdal <resample>: %s", cur_node->txt);
643 return;
644 }
645 }
646
647 if ((cur_node = ezxml_child(node,"overview-strategy")) != NULL && *cur_node->txt) {
648 src->srcOvrLevel = apr_pstrdup(ctx->pool,cur_node->txt);
649 }
650 }
651
652 /**
653 * \private \memberof mapcache_source_gdal
654 * \sa mapcache_source::configuration_check()
655 */
_mapcache_source_gdal_configuration_check(mapcache_context * ctx,mapcache_cfg * cfg,mapcache_source * source)656 void _mapcache_source_gdal_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg,
657 mapcache_source *source)
658 {
659 mapcache_source_gdal *src = (mapcache_source_gdal*)source;
660 GDALDatasetH hDataset;
661
662 /* check all required parameters are configured */
663 if( src->datastr == NULL || !strlen(src->datastr)) {
664 ctx->set_error(ctx, 500, "gdal source %s has no data",source->name);
665 return;
666 }
667 hDataset = GDALOpen(src->datastr,GA_ReadOnly);
668 if( hDataset == NULL ) {
669 ctx->set_error(ctx, 500, "gdalOpen failed on data %s", src->datastr);
670 return;
671 }
672 if( GDALGetProjectionRef( hDataset ) != NULL
673 && strlen(GDALGetProjectionRef( hDataset )) > 0 ) {
674 src->srs_wkt = apr_pstrdup(ctx->pool,GDALGetProjectionRef( hDataset ));
675 } else if( GDALGetGCPProjection( hDataset ) != NULL
676 && strlen(GDALGetGCPProjection(hDataset)) > 0
677 && GDALGetGCPCount( hDataset ) > 1 ) {
678 src->srs_wkt = apr_pstrdup(ctx->pool,GDALGetGCPProjection( hDataset ));
679 } else {
680 ctx->set_error(ctx, 500, "Input gdal source for %s has no defined SRS\n", source->name );
681 GDALClose(hDataset);
682 return;
683 }
684 GDALClose(hDataset);
685
686 }
687 #endif //USE_GDAL
688
mapcache_source_gdal_create(mapcache_context * ctx)689 mapcache_source* mapcache_source_gdal_create(mapcache_context *ctx)
690 {
691 #ifdef USE_GDAL
692 mapcache_source_gdal *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_gdal));
693 if(!source) {
694 ctx->set_error(ctx, 500, "failed to allocate gdal source");
695 return NULL;
696 }
697 mapcache_source_init(ctx, &(source->source));
698 source->source.type = MAPCACHE_SOURCE_GDAL;
699 source->source._render_map = _mapcache_source_gdal_render_metatile;
700 source->source.configuration_check = _mapcache_source_gdal_configuration_check;
701 source->source.configuration_parse_xml = _mapcache_source_gdal_configuration_parse;
702 source->eResampleAlg = MAPCACHE_DEFAULT_RESAMPLE_ALG;
703 source->bUseConnectionPool = MAPCACHE_TRUE;
704 GDALAllRegister();
705 return (mapcache_source*)source;
706 #else
707 ctx->set_error(ctx, 400, "failed to create gdal source, GDAL support is not compiled in this version");
708 return NULL;
709 #endif
710 }
711
712
713
714 /* vim: ts=2 sts=2 et sw=2
715 */
716