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