1
/******************************************************************************
5
* Purpose: MapCache tile caching support file: Mapserver Mapfile datasource
6
* Author: Thomas Bonfort and the MapServer team.
8
******************************************************************************
9
* Copyright (c) 1996-2011 Regents of the University of Minnesota.
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:
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.
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
*****************************************************************************/
35
#include <apr_tables.h>
36
#include <apr_strings.h>
37
#ifdef APR_HAS_THREADS
38
#include <apr_thread_mutex.h>
41
#include <apr_reslist.h>
42
#include <mapserver.h>
44
/* hash table key = source->name, value = apr_reslist_t of mapObjs */
45
static apr_hash_t *mapobj_container = NULL;
49
mapcache_grid_link *grid_link;
53
static apr_status_t _ms_get_mapobj(void **conn_, void *params, apr_pool_t *pool)
55
mapcache_source_mapserver *src = (mapcache_source_mapserver*) params;
56
struct mc_mapobj *mcmap = calloc(1,sizeof(struct mc_mapobj));
58
mcmap->map = msLoadMap(src->mapfile,NULL);
60
errorObj *errors = NULL;
62
errors = msGetErrorObj();
63
mcmap->error = apr_psprintf(pool,"Failed to load mapfile '%s'. Mapserver reports: %s",src->mapfile, errors->message);
66
msMapSetLayerProjections(mcmap->map);
70
static apr_status_t _ms_free_mapobj(void *conn_, void *params, apr_pool_t *pool)
72
struct mc_mapobj *mcmap = (struct mc_mapobj*) conn_;
73
msFreeMap(mcmap->map);
78
static struct mc_mapobj* _get_mapboj(mapcache_context *ctx, mapcache_map *map) {
80
mapcache_source_mapserver *src = (mapcache_source_mapserver*) map->tileset->source;
81
struct mc_mapobj *mcmap;
82
apr_reslist_t *mapobjs = NULL;
83
if(!mapobj_container || NULL == (mapobjs = apr_hash_get(mapobj_container,src->source.name,APR_HASH_KEY_STRING))) {
84
#ifdef APR_HAS_THREADS
86
apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock);
88
if(!mapobj_container) {
89
mapobj_container = apr_hash_make(ctx->process_pool);
91
mapobjs = apr_hash_get(mapobj_container,src->source.name,APR_HASH_KEY_STRING);
94
rv = apr_reslist_create(&mapobjs,
98
6 * 1000000 /*6 seconds, ttl*/,
99
_ms_get_mapobj, /* resource constructor */
100
_ms_free_mapobj, /* resource destructor */
101
src, ctx->process_pool);
102
if (rv != APR_SUCCESS) {
103
ctx->set_error(ctx, 500, "failed to create mapobj connection pool for cache %s", src->source.name);
104
#ifdef APR_HAS_THREADS
106
apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
110
apr_hash_set(mapobj_container,src->source.name,APR_HASH_KEY_STRING,mapobjs);
113
#ifdef APR_HAS_THREADS
115
apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
118
rv = apr_reslist_acquire(mapobjs, (void **) &mcmap);
119
if (rv != APR_SUCCESS) {
120
ctx->set_error(ctx, 500, "failed to aquire mappObj instance: %s", mcmap->error);
126
static void _release_mapboj(mapcache_context *ctx, mapcache_map *map, struct mc_mapobj *mcmap)
128
mapcache_source_mapserver *src = (mapcache_source_mapserver*) map->tileset->source;
129
msFreeLabelCache(&mcmap->map->labelcache);
130
apr_reslist_t *mapobjs = apr_hash_get(mapobj_container,src->source.name, APR_HASH_KEY_STRING);
132
if (GC_HAS_ERROR(ctx)) {
133
apr_reslist_invalidate(mapobjs, (void*) mcmap);
135
apr_reslist_release(mapobjs, (void*) mcmap);
139
* \private \memberof mapcache_source_mapserver
140
* \sa mapcache_source::render_map()
142
void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *map)
144
errorObj *errors = NULL;
146
struct mc_mapobj *mcmap = _get_mapboj(ctx,map);
149
if(mcmap->grid_link != map->grid_link) {
150
if (msLoadProjectionString(&(mcmap->map->projection), map->grid_link->grid->srs) != 0) {
151
errors = msGetErrorObj();
152
ctx->set_error(ctx,500, "Unable to set projection on mapObj. MapServer reports: %s", errors->message);
153
_release_mapboj(ctx,map,mcmap);
156
switch(map->grid_link->grid->unit) {
157
case MAPCACHE_UNIT_DEGREES:
158
mcmap->map->units = MS_DD;
160
case MAPCACHE_UNIT_FEET:
161
mcmap->map->units = MS_FEET;
163
case MAPCACHE_UNIT_METERS:
164
mcmap->map->units = MS_METERS;
167
mcmap->grid_link = map->grid_link;
172
** WMS extents are edge to edge while MapServer extents are center of
173
** pixel to center of pixel. Here we try to adjust the WMS extents
174
** in by half a pixel.
177
dx = (map->extent.maxx - map->extent.minx) / (map->width*2);
178
dy = (map->extent.maxy - map->extent.miny) / (map->height*2);
180
mcmap->map->extent.minx = map->extent.minx + dx;
181
mcmap->map->extent.miny = map->extent.miny + dy;
182
mcmap->map->extent.maxx = map->extent.maxx - dx;
183
mcmap->map->extent.maxy = map->extent.maxy - dy;
184
msMapSetSize(mcmap->map, map->width, map->height);
186
imageObj *image = msDrawMap(mcmap->map, MS_FALSE);
188
errors = msGetErrorObj();
189
ctx->set_error(ctx,500, "MapServer failed to create image. MapServer reports: %s", errors->message);
190
_release_mapboj(ctx,map,mcmap);
195
if(image->format->vtable->supports_pixel_buffer) {
196
image->format->vtable->getRasterBufferHandle(image,&rb);
198
ctx->set_error(ctx,500,"format %s has no pixel export",image->format->name);
199
_release_mapboj(ctx,map,mcmap);
203
map->raw_image = mapcache_image_create(ctx);
204
map->raw_image->w = map->width;
205
map->raw_image->h = map->height;
206
map->raw_image->stride = 4 * map->width;
207
map->raw_image->data = malloc(map->width*map->height*4);
208
memcpy(map->raw_image->data,rb.data.rgba.pixels,map->width*map->height*4);
209
apr_pool_cleanup_register(ctx->pool, map->raw_image->data,(void*)free, apr_pool_cleanup_null);
211
_release_mapboj(ctx,map,mcmap);
215
void _mapcache_source_mapserver_query(mapcache_context *ctx, mapcache_feature_info *fi)
217
ctx->set_error(ctx,500,"mapserver source does not support queries");
221
* \private \memberof mapcache_source_mapserver
222
* \sa mapcache_source::configuration_parse()
224
void _mapcache_source_mapserver_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source)
227
mapcache_source_mapserver *src = (mapcache_source_mapserver*)source;
228
if ((cur_node = ezxml_child(node,"mapfile")) != NULL) {
229
src->mapfile = apr_pstrdup(ctx->pool,cur_node->txt);
234
* \private \memberof mapcache_source_mapserver
235
* \sa mapcache_source::configuration_check()
237
void _mapcache_source_mapserver_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg,
238
mapcache_source *source)
240
mapcache_source_mapserver *src = (mapcache_source_mapserver*)source;
241
/* check all required parameters are configured */
243
ctx->set_error(ctx, 400, "mapserver source %s has no <mapfile> configured",source->name);
246
ctx->set_error(ctx,400,"mapserver source \"%s\" has no mapfile configured",src->source.name);
252
/* do a test load to check the mapfile is correct */
253
mapObj *map = msLoadMap(src->mapfile, NULL);
255
msWriteError(stderr);
256
ctx->set_error(ctx,400,"failed to load mapfile \"%s\"",src->mapfile);
262
mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
264
mapcache_source_mapserver *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_mapserver));
266
ctx->set_error(ctx, 500, "failed to allocate mapserver source");
269
mapcache_source_init(ctx, &(source->source));
270
source->source.type = MAPCACHE_SOURCE_MAPSERVER;
271
source->source.render_map = _mapcache_source_mapserver_render_map;
272
source->source.configuration_check = _mapcache_source_mapserver_configuration_check;
273
source->source.configuration_parse_xml = _mapcache_source_mapserver_configuration_parse_xml;
274
source->source.query_info = _mapcache_source_mapserver_query;
275
return (mapcache_source*)source;
278
mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
280
ctx->set_error(ctx, 500, "mapserver source not configured for this build");
284
/* vim: ts=2 sts=2 et sw=2