1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: Mapserver Mapfile datasource
6  * Author:   Thomas Bonfort and the MapServer team.
7  *
8  ******************************************************************************
9  * Copyright (c) 1996-2011 Regents of the University of Minnesota.
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies of this Software or works derived from this Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  *****************************************************************************/
29 
30 
31 #include "mapcache.h"
32 #include "ezxml.h"
33 
34 #ifdef USE_MAPSERVER
35 #include <apr_tables.h>
36 #include <apr_strings.h>
37 #ifdef APR_HAS_THREADS
38 #include <apr_thread_mutex.h>
39 #endif
40 #include <apr_hash.h>
41 #include <apr_reslist.h>
42 #include <mapserver.h>
43 
44 /**\class mapcache_source_mapserver
45  * \brief WMS mapcache_source
46  * \implements mapcache_source
47  */
48 typedef struct mapcache_source_mapserver mapcache_source_mapserver;
49 struct mapcache_source_mapserver {
50   mapcache_source source;
51   char *mapfile;
52 };
53 
54 struct mc_mapobj {
55   mapObj *map;
56   mapcache_grid_link *grid_link;
57   char *error;
58 };
59 
mapcache_mapserver_connection_constructor(mapcache_context * ctx,void ** conn_,void * params)60 void mapcache_mapserver_connection_constructor(mapcache_context *ctx, void **conn_, void *params) {
61   mapcache_source_mapserver *src = (mapcache_source_mapserver*) params;
62   struct mc_mapobj *mcmap = calloc(1,sizeof(struct mc_mapobj));
63   mcmap->map = msLoadMap(src->mapfile,NULL);
64   if(!mcmap->map) {
65     errorObj *errors = NULL;
66     ctx->set_error(ctx, 500, "Failed to load mapfile '%s'",src->mapfile);
67     errors = msGetErrorObj();
68     while(errors) {
69       ctx->set_error(ctx, 500, "Failed to load mapfile '%s'. Mapserver reports: %s",src->mapfile, errors->message);
70       errors = errors->next;
71     }
72     return;
73   }
74   msMapSetLayerProjections(mcmap->map);
75   *conn_ = mcmap;
76 }
77 
mapcache_mapserver_connection_destructor(void * conn_)78 void mapcache_mapserver_connection_destructor(void *conn_) {
79   struct mc_mapobj *mcmap = (struct mc_mapobj*) conn_;
80   msFreeMap(mcmap->map);
81   free(mcmap);
82 }
83 
_mapserver_get_connection(mapcache_context * ctx,mapcache_map * map)84 static mapcache_pooled_connection* _mapserver_get_connection(mapcache_context *ctx, mapcache_map *map)
85 {
86   mapcache_pooled_connection *pc;
87   char *key = apr_psprintf(ctx->pool, "ms_src_%s", map->tileset->source->name);
88 
89   pc = mapcache_connection_pool_get_connection(ctx, key, mapcache_mapserver_connection_constructor,
90           mapcache_mapserver_connection_destructor, map->tileset->source);
91   if(!GC_HAS_ERROR(ctx) && pc && pc->connection) {
92   }
93 
94   return pc;
95 }
96 
97 
98 /**
99  * \private \memberof mapcache_source_mapserver
100  * \sa mapcache_source::render_map()
101  */
_mapcache_source_mapserver_render_map(mapcache_context * ctx,mapcache_map * map)102 void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *map)
103 {
104   errorObj *errors = NULL;
105   mapcache_pooled_connection *pc;
106   struct mc_mapobj *mcmap;
107   double dx, dy;
108   rasterBufferObj rb;
109   imageObj *image;
110 
111   pc = _mapserver_get_connection(ctx, map);
112   GC_CHECK_ERROR(ctx);
113 
114   mcmap = pc->connection;
115   GC_CHECK_ERROR(ctx);
116 
117   if(mcmap->grid_link != map->grid_link) {
118     if (msLoadProjectionString(&(mcmap->map->projection), map->grid_link->grid->srs) != 0) {
119       ctx->set_error(ctx,500, "Unable to set projection on mapObj.");
120       errors = msGetErrorObj();
121       while(errors) {
122         ctx->set_error(ctx,500, "Unable to set projection on mapObj. MapServer reports: %s", errors->message);
123         errors = errors->next;
124       }
125       mapcache_connection_pool_invalidate_connection(ctx,pc);
126       return;
127     }
128     switch(map->grid_link->grid->unit) {
129       case MAPCACHE_UNIT_DEGREES:
130         mcmap->map->units = MS_DD;
131         break;
132       case MAPCACHE_UNIT_FEET:
133         mcmap->map->units = MS_FEET;
134         break;
135       case MAPCACHE_UNIT_METERS:
136         mcmap->map->units = MS_METERS;
137         break;
138     }
139     mcmap->grid_link = map->grid_link;
140   }
141 
142 
143   /*
144   ** WMS extents are edge to edge while MapServer extents are center of
145   ** pixel to center of pixel.  Here we try to adjust the WMS extents
146   ** in by half a pixel.
147   */
148   dx = (map->extent.maxx - map->extent.minx) / (map->width*2);
149   dy = (map->extent.maxy - map->extent.miny) / (map->height*2);
150 
151   mcmap->map->extent.minx = map->extent.minx + dx;
152   mcmap->map->extent.miny = map->extent.miny + dy;
153   mcmap->map->extent.maxx = map->extent.maxx - dx;
154   mcmap->map->extent.maxy = map->extent.maxy - dy;
155   msMapSetSize(mcmap->map, map->width, map->height);
156 
157   image = msDrawMap(mcmap->map, MS_FALSE);
158   if(!image) {
159     ctx->set_error(ctx,500, "MapServer failed to create image.");
160     errors = msGetErrorObj();
161     while(errors) {
162       ctx->set_error(ctx,500, "MapServer reports: %s", errors->message);
163       errors = errors->next;
164     }
165     mapcache_connection_pool_invalidate_connection(ctx,pc);
166     return;
167   }
168 
169   if(image->format->vtable->supports_pixel_buffer) {
170     if( MS_SUCCESS != image->format->vtable->getRasterBufferHandle(image,&rb)) {
171       ctx->set_error(ctx,500,"failed to get mapserver raster buffer handle");
172       mapcache_connection_pool_invalidate_connection(ctx,pc);
173       return;
174     }
175   } else {
176     ctx->set_error(ctx,500,"format %s has no pixel export",image->format->name);
177     mapcache_connection_pool_invalidate_connection(ctx,pc);
178     return;
179   }
180 
181   map->raw_image = mapcache_image_create(ctx);
182   map->raw_image->w = map->width;
183   map->raw_image->h = map->height;
184   map->raw_image->stride = 4 * map->width;
185   map->raw_image->data = malloc(map->width*map->height*4);
186   memcpy(map->raw_image->data,rb.data.rgba.pixels,map->width*map->height*4);
187   apr_pool_cleanup_register(ctx->pool, map->raw_image->data,(void*)free, apr_pool_cleanup_null);
188   msFreeImage(image);
189   mapcache_connection_pool_release_connection(ctx,pc);
190 
191 }
192 
_mapcache_source_mapserver_query(mapcache_context * ctx,mapcache_source * psource,mapcache_feature_info * fi)193 void _mapcache_source_mapserver_query(mapcache_context *ctx, mapcache_source *psource, mapcache_feature_info *fi)
194 {
195   ctx->set_error(ctx,500,"mapserver source does not support queries");
196 }
197 
198 /**
199  * \private \memberof mapcache_source_mapserver
200  * \sa mapcache_source::configuration_parse()
201  */
_mapcache_source_mapserver_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_source * source)202 void _mapcache_source_mapserver_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source)
203 {
204   ezxml_t cur_node;
205   mapcache_source_mapserver *src = (mapcache_source_mapserver*)source;
206   if ((cur_node = ezxml_child(node,"mapfile")) != NULL) {
207     src->mapfile = apr_pstrdup(ctx->pool,cur_node->txt);
208   }
209 }
210 
211 /**
212  * \private \memberof mapcache_source_mapserver
213  * \sa mapcache_source::configuration_check()
214  */
_mapcache_source_mapserver_configuration_check(mapcache_context * ctx,mapcache_cfg * cfg,mapcache_source * source)215 void _mapcache_source_mapserver_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg,
216     mapcache_source *source)
217 {
218   mapcache_source_mapserver *src = (mapcache_source_mapserver*)source;
219   mapObj *map;
220   /* check all required parameters are configured */
221   if(!src->mapfile) {
222     ctx->set_error(ctx, 400, "mapserver source %s has no <mapfile> configured",source->name);
223   }
224   if(!src->mapfile) {
225     ctx->set_error(ctx,400,"mapserver source \"%s\" has no mapfile configured",src->source.name);
226     return;
227   }
228 
229   msSetup();
230 
231   /* do a test load to check the mapfile is correct */
232   map = msLoadMap(src->mapfile, NULL);
233   if(!map) {
234     msWriteError(stderr);
235     ctx->set_error(ctx,400,"failed to load mapfile \"%s\"",src->mapfile);
236     return;
237   }
238   msFreeMap(map);
239 }
240 
mapcache_source_mapserver_create(mapcache_context * ctx)241 mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
242 {
243   mapcache_source_mapserver *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_mapserver));
244   if(!source) {
245     ctx->set_error(ctx, 500, "failed to allocate mapserver source");
246     return NULL;
247   }
248   mapcache_source_init(ctx, &(source->source));
249   source->source.type = MAPCACHE_SOURCE_MAPSERVER;
250   source->source.render_map = _mapcache_source_mapserver_render_map;
251   source->source.configuration_check = _mapcache_source_mapserver_configuration_check;
252   source->source.configuration_parse_xml = _mapcache_source_mapserver_configuration_parse_xml;
253   source->source.query_info = _mapcache_source_mapserver_query;
254   return (mapcache_source*)source;
255 }
256 #else
mapcache_source_mapserver_create(mapcache_context * ctx)257 mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
258 {
259   ctx->set_error(ctx, 500, "mapserver source not configured for this build");
260   return NULL;
261 }
262 #endif
263 
264 /* vim: ts=2 sts=2 et sw=2
265 */
266