~ubuntu-branches/ubuntu/trusty/mapcache/trusty

« back to all changes in this revision

Viewing changes to lib/source_mapserver.c

  • Committer: Package Import Robot
  • Author(s): Bas Couwenberg
  • Date: 2013-09-11 19:16:06 UTC
  • Revision ID: package-import@ubuntu.com-20130911191606-9aydo919w4dgjx9v
Tags: upstream-1.2.0
ImportĀ upstreamĀ versionĀ 1.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/* hash table key = source->name, value = apr_reslist_t of mapObjs */
 
45
static apr_hash_t *mapobj_container = NULL;
 
46
 
 
47
struct mc_mapobj {
 
48
  mapObj *map;
 
49
  mapcache_grid_link *grid_link;
 
50
  char *error;
 
51
};
 
52
 
 
53
static apr_status_t _ms_get_mapobj(void **conn_, void *params, apr_pool_t *pool)
 
54
{
 
55
  mapcache_source_mapserver *src = (mapcache_source_mapserver*) params;
 
56
  struct mc_mapobj *mcmap = calloc(1,sizeof(struct mc_mapobj));
 
57
  *conn_ = mcmap;
 
58
  mcmap->map = msLoadMap(src->mapfile,NULL);
 
59
  if(!mcmap->map) {
 
60
    errorObj *errors = NULL;
 
61
    msWriteError(stderr);
 
62
    errors = msGetErrorObj();
 
63
    mcmap->error = apr_psprintf(pool,"Failed to load mapfile '%s'. Mapserver reports: %s",src->mapfile, errors->message);
 
64
    return APR_EGENERAL;
 
65
  }
 
66
  msMapSetLayerProjections(mcmap->map);
 
67
  return APR_SUCCESS;
 
68
}
 
69
 
 
70
static apr_status_t _ms_free_mapobj(void *conn_, void *params, apr_pool_t *pool)
 
71
{
 
72
  struct mc_mapobj *mcmap = (struct mc_mapobj*) conn_;
 
73
  msFreeMap(mcmap->map);
 
74
  free(mcmap);
 
75
  return APR_SUCCESS;
 
76
}
 
77
 
 
78
static struct mc_mapobj* _get_mapboj(mapcache_context *ctx, mapcache_map *map) {
 
79
  apr_status_t rv;
 
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
 
85
    if(ctx->threadlock)
 
86
      apr_thread_mutex_lock((apr_thread_mutex_t*)ctx->threadlock);
 
87
#endif
 
88
    if(!mapobj_container) {
 
89
      mapobj_container = apr_hash_make(ctx->process_pool);
 
90
    }
 
91
    mapobjs = apr_hash_get(mapobj_container,src->source.name,APR_HASH_KEY_STRING);
 
92
    if(!mapobjs) {
 
93
      apr_status_t rv;
 
94
      rv = apr_reslist_create(&mapobjs,
 
95
                              0 /* min */,
 
96
                              1 /* soft max */,
 
97
                              30 /* hard max */,
 
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
 
105
        if(ctx->threadlock)
 
106
          apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
 
107
#endif
 
108
        return NULL;
 
109
      }
 
110
      apr_hash_set(mapobj_container,src->source.name,APR_HASH_KEY_STRING,mapobjs);
 
111
    }
 
112
    assert(mapobjs);
 
113
#ifdef APR_HAS_THREADS
 
114
    if(ctx->threadlock)
 
115
      apr_thread_mutex_unlock((apr_thread_mutex_t*)ctx->threadlock);
 
116
#endif
 
117
  }
 
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);
 
121
    return NULL;
 
122
  }
 
123
  return mcmap;
 
124
}
 
125
 
 
126
static void _release_mapboj(mapcache_context *ctx, mapcache_map *map, struct mc_mapobj *mcmap)
 
127
{
 
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);
 
131
  assert(mapobjs);
 
132
  if (GC_HAS_ERROR(ctx)) {
 
133
    apr_reslist_invalidate(mapobjs, (void*) mcmap);
 
134
  } else {
 
135
    apr_reslist_release(mapobjs, (void*) mcmap);
 
136
  }
 
137
}
 
138
/**
 
139
 * \private \memberof mapcache_source_mapserver
 
140
 * \sa mapcache_source::render_map()
 
141
 */
 
142
void _mapcache_source_mapserver_render_map(mapcache_context *ctx, mapcache_map *map)
 
143
{
 
144
  errorObj *errors = NULL;
 
145
 
 
146
  struct mc_mapobj *mcmap = _get_mapboj(ctx,map);
 
147
  GC_CHECK_ERROR(ctx);
 
148
 
 
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);
 
154
      return;
 
155
    }
 
156
    switch(map->grid_link->grid->unit) {
 
157
      case MAPCACHE_UNIT_DEGREES:
 
158
        mcmap->map->units = MS_DD;
 
159
        break;
 
160
      case MAPCACHE_UNIT_FEET:
 
161
        mcmap->map->units = MS_FEET;
 
162
        break;
 
163
      case MAPCACHE_UNIT_METERS:
 
164
        mcmap->map->units = MS_METERS;
 
165
        break;
 
166
    }
 
167
    mcmap->grid_link = map->grid_link;
 
168
  }
 
169
 
 
170
 
 
171
  /*
 
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.
 
175
  */
 
176
  double dx, dy;
 
177
  dx = (map->extent.maxx - map->extent.minx) / (map->width*2);
 
178
  dy = (map->extent.maxy - map->extent.miny) / (map->height*2);
 
179
 
 
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);
 
185
 
 
186
  imageObj *image = msDrawMap(mcmap->map, MS_FALSE);
 
187
  if(!image) {
 
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);
 
191
    return;
 
192
  }
 
193
  rasterBufferObj rb;
 
194
 
 
195
  if(image->format->vtable->supports_pixel_buffer) {
 
196
    image->format->vtable->getRasterBufferHandle(image,&rb);
 
197
  } else {
 
198
    ctx->set_error(ctx,500,"format %s has no pixel export",image->format->name);
 
199
    _release_mapboj(ctx,map,mcmap);
 
200
    return;
 
201
  }
 
202
 
 
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);
 
210
  msFreeImage(image);
 
211
  _release_mapboj(ctx,map,mcmap);
 
212
 
 
213
}
 
214
 
 
215
void _mapcache_source_mapserver_query(mapcache_context *ctx, mapcache_feature_info *fi)
 
216
{
 
217
  ctx->set_error(ctx,500,"mapserver source does not support queries");
 
218
}
 
219
 
 
220
/**
 
221
 * \private \memberof mapcache_source_mapserver
 
222
 * \sa mapcache_source::configuration_parse()
 
223
 */
 
224
void _mapcache_source_mapserver_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_source *source)
 
225
{
 
226
  ezxml_t cur_node;
 
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);
 
230
  }
 
231
}
 
232
 
 
233
/**
 
234
 * \private \memberof mapcache_source_mapserver
 
235
 * \sa mapcache_source::configuration_check()
 
236
 */
 
237
void _mapcache_source_mapserver_configuration_check(mapcache_context *ctx, mapcache_cfg *cfg,
 
238
    mapcache_source *source)
 
239
{
 
240
  mapcache_source_mapserver *src = (mapcache_source_mapserver*)source;
 
241
  /* check all required parameters are configured */
 
242
  if(!src->mapfile) {
 
243
    ctx->set_error(ctx, 400, "mapserver source %s has no <mapfile> configured",source->name);
 
244
  }
 
245
  if(!src->mapfile) {
 
246
    ctx->set_error(ctx,400,"mapserver source \"%s\" has no mapfile configured",src->source.name);
 
247
    return;
 
248
  }
 
249
 
 
250
  msSetup();
 
251
 
 
252
  /* do a test load to check the mapfile is correct */
 
253
  mapObj *map = msLoadMap(src->mapfile, NULL);
 
254
  if(!map) {
 
255
    msWriteError(stderr);
 
256
    ctx->set_error(ctx,400,"failed to load mapfile \"%s\"",src->mapfile);
 
257
    return;
 
258
  }
 
259
  msFreeMap(map);
 
260
}
 
261
 
 
262
mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
 
263
{
 
264
  mapcache_source_mapserver *source = apr_pcalloc(ctx->pool, sizeof(mapcache_source_mapserver));
 
265
  if(!source) {
 
266
    ctx->set_error(ctx, 500, "failed to allocate mapserver source");
 
267
    return NULL;
 
268
  }
 
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;
 
276
}
 
277
#else
 
278
mapcache_source* mapcache_source_mapserver_create(mapcache_context *ctx)
 
279
{
 
280
  ctx->set_error(ctx, 500, "mapserver source not configured for this build");
 
281
}
 
282
#endif
 
283
 
 
284
/* vim: ts=2 sts=2 et sw=2
 
285
*/