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, &params);
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), &params);
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