1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching support file: KML superoverlay service
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 #include "mapcache.h"
31 #include <apr_strings.h>
32 #include "mapcache_services.h"
33 #include <math.h>
34
35
36 /** \addtogroup services */
37 /** @{ */
38
39
_create_capabilities_kml(mapcache_context * ctx,mapcache_request_get_capabilities * req,char * url,char * path_info,mapcache_cfg * cfg)40 void _create_capabilities_kml(mapcache_context *ctx, mapcache_request_get_capabilities *req, char *url, char *path_info, mapcache_cfg *cfg)
41 {
42 mapcache_request_get_capabilities_kml *request = (mapcache_request_get_capabilities_kml*)req;
43 char *caps;
44 const char *onlineresource = apr_table_get(cfg->metadata,"url");
45 int i, j;
46 if(!onlineresource) {
47 onlineresource = url;
48 }
49 request->request.mime_type = apr_pstrdup(ctx->pool,"application/vnd.google-earth.kml+xml");
50
51 assert(request->tile || (request->grid && request->tileset));
52
53 /* if we have no specific tile, create a kml document referencing all the tiles of the first level in the grid*/
54 if(!request->tile) {
55 mapcache_extent extent = request->grid->restricted_extent?*(request->grid->restricted_extent):request->grid->grid->extent;
56 caps = apr_psprintf(ctx->pool, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
57 "<kml xmlns=\"http://earth.google.com/kml/2.1\">\n"
58 " <Document>\n"
59 " <Region>\n"
60 " <Lod>\n"
61 " <minLodPixels>128</minLodPixels><maxLodPixels>512</maxLodPixels>\n"
62 " </Lod>\n"
63 " <LatLonAltBox>\n"
64 " <north>%f</north><south>%f</south>\n"
65 " <east>%f</east><west>%f</west>\n"
66 " </LatLonAltBox>\n"
67 " </Region>\n",
68 extent.maxy,extent.miny,extent.maxx,extent.minx);
69 for(i=request->grid->grid_limits[0].minx; i<request->grid->grid_limits[0].maxx; i++) {
70 for(j=request->grid->grid_limits[0].miny; j<request->grid->grid_limits[0].maxy; j++) {
71
72 mapcache_tile *t = mapcache_tileset_tile_create(ctx->pool, request->tileset, request->grid);
73 mapcache_extent bb;
74 t->x = i;
75 t->y = j;
76 t->z = 0;
77 mapcache_grid_get_tile_extent(ctx, t->grid_link->grid,
78 t->x, t->y, t->z, &bb);
79
80 caps = apr_psprintf(ctx->pool, "%s"
81 " <NetworkLink>\n"
82 " <name>%d%d%d</name>\n"
83 " <Region>\n"
84 " <Lod>\n"
85 " <minLodPixels>128</minLodPixels><maxLodPixels>-1</maxLodPixels>\n"
86 " </Lod>\n"
87 " <LatLonAltBox>\n"
88 " <north>%f</north><south>%f</south>\n"
89 " <east>%f</east><west>%f</west>\n"
90 " </LatLonAltBox>\n"
91 " </Region>\n"
92 " <Link>\n"
93 " <href>%s/kml/%s@%s/%d/%d/%d.kml</href>\n"
94 " <viewRefreshMode>onRegion</viewRefreshMode>\n"
95 " </Link>\n"
96 " </NetworkLink>\n",
97 caps, t->x, t->y, t->z,
98 bb.maxy, bb.miny, bb.maxx, bb.minx,
99 onlineresource, request->tileset->name, request->grid->grid->name,
100 t->z, t->x, t->y);
101 }
102 }
103 caps = apr_pstrcat(ctx->pool, caps, " </Document>\n</kml>\n", NULL);
104 } else {
105 mapcache_extent bbox;
106
107 mapcache_grid_get_tile_extent(ctx, request->tile->grid_link->grid,
108 request->tile->x, request->tile->y, request->tile->z, &bbox);
109
110
111 caps = apr_psprintf(ctx->pool, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
112 "<kml xmlns=\"http://earth.google.com/kml/2.1\">\n"
113 " <Document>\n"
114 " <Region>\n"
115 " <Lod>\n"
116 " <minLodPixels>128</minLodPixels><maxLodPixels>%d</maxLodPixels>\n"
117 " </Lod>\n"
118 " <LatLonAltBox>\n"
119 " <north>%f</north><south>%f</south>\n"
120 " <east>%f</east><west>%f</west>\n"
121 " </LatLonAltBox>\n"
122 " </Region>\n"
123 " <GroundOverlay>\n"
124 " <drawOrder>0</drawOrder>\n"
125 " <Icon>\n"
126 " <href>%s/tms/1.0.0/%s@%s/%d/%d/%d.%s</href>\n"
127 " </Icon>\n"
128 " <LatLonBox>\n"
129 " <north>%f</north><south>%f</south>\n"
130 " <east>%f</east><west>%f</west>\n"
131 " </LatLonBox>\n"
132 " </GroundOverlay>\n",
133 (request->tile->z == request->tile->grid_link->grid->nlevels - 1) ? -1 : 512,
134 bbox.maxy, bbox.miny, bbox.maxx, bbox.minx,
135 onlineresource, request->tile->tileset->name, request->tile->grid_link->grid->name,
136 request->tile->z, request->tile->x, request->tile->y,
137 (request->tile->tileset->format) ? request->tile->tileset->format->extension : "png",
138 bbox.maxy, bbox.miny, bbox.maxx, bbox.minx);
139
140 if (request->tile->z < request->tile->grid_link->grid->nlevels - 1) {
141 for (i = 0; i <= 1; i++) {
142 for (j = 0; j <= 1; j++) {
143 /* compute the addresses of the child tiles */
144 mapcache_tile *t = mapcache_tileset_tile_create(ctx->pool, request->tile->tileset, request->tile->grid_link);
145 mapcache_extent bb;
146 t->x = (request->tile->x << 1) + i;
147 t->y = (request->tile->y << 1) + j;
148 t->z = request->tile->z + 1;
149 mapcache_grid_get_tile_extent(ctx, t->grid_link->grid,
150 t->x, t->y, t->z, &bb);
151
152 caps = apr_psprintf(ctx->pool, "%s"
153 " <NetworkLink>\n"
154 " <name>%d%d%d</name>\n"
155 " <Region>\n"
156 " <Lod>\n"
157 " <minLodPixels>128</minLodPixels><maxLodPixels>-1</maxLodPixels>\n"
158 " </Lod>\n"
159 " <LatLonAltBox>\n"
160 " <north>%f</north><south>%f</south>\n"
161 " <east>%f</east><west>%f</west>\n"
162 " </LatLonAltBox>\n"
163 " </Region>\n"
164 " <Link>\n"
165 " <href>%s/kml/%s@%s/%d/%d/%d.kml</href>\n"
166 " <viewRefreshMode>onRegion</viewRefreshMode>\n"
167 " </Link>\n"
168 " </NetworkLink>\n",
169 caps, t->x, t->y, t->z,
170 bb.maxy, bb.miny, bb.maxx, bb.minx,
171 onlineresource, request->tile->tileset->name, request->tile->grid_link->grid->name,
172 t->z, t->x, t->y);
173 }
174 }
175 }
176
177 caps = apr_pstrcat(ctx->pool, caps, " </Document>\n</kml>\n", NULL);
178 }
179 request->request.capabilities = caps;
180
181
182 }
183
184 /**
185 * \brief parse a KML request
186 * \private \memberof mapcache_service_kml
187 * \sa mapcache_service::parse_request()
188 */
_mapcache_service_kml_parse_request(mapcache_context * ctx,mapcache_service * this,mapcache_request ** request,const char * cpathinfo,apr_table_t * params,mapcache_cfg * config)189 void _mapcache_service_kml_parse_request(mapcache_context *ctx, mapcache_service *this, mapcache_request **request,
190 const char *cpathinfo, apr_table_t *params, mapcache_cfg *config)
191 {
192 int index = 0;
193 char *last, *key, *endptr;
194 mapcache_tileset *tileset = NULL;
195 mapcache_grid_link *grid_link = NULL;
196 char *pathinfo = NULL;
197 int x=-1,y=-1,z=-1;
198
199 if(cpathinfo) {
200 pathinfo = apr_pstrdup(ctx->pool,cpathinfo);
201 /* parse a path_info like /layer@grid/0/0/0.kml */
202 for (key = apr_strtok(pathinfo, "/", &last); key != NULL;
203 key = apr_strtok(NULL, "/", &last)) {
204 if(!*key) continue; /* skip an empty string, could happen if the url contains // */
205 switch(++index) {
206 case 1: /* layer name */
207 tileset = mapcache_configuration_get_tileset(config,key);
208 if(!tileset) {
209 /*tileset not found directly, test if it was given as "name@grid" notation*/
210 char *tname = apr_pstrdup(ctx->pool,key);
211 char *gname = tname;
212 char*ext;
213 int i;
214 while(*gname) {
215 if(*gname == '@') {
216 *gname = '\0';
217 gname++;
218 break;
219 }
220 gname++;
221 }
222 if(!*gname) {
223 ctx->set_error(ctx,404, "received kml request with invalid layer %s", key);
224 return;
225 }
226
227 /* is this the first request, eg tileset@grid.kml? in that case reome the .kml
228 from the grid name */
229 ext = strstr(gname,".kml");
230 if(ext) *ext = '\0';
231
232 tileset = mapcache_configuration_get_tileset(config,tname);
233 if(!tileset) {
234 ctx->set_error(ctx,404, "received kml request with invalid layer %s", tname);
235 return;
236 }
237 for(i=0; i<tileset->grid_links->nelts; i++) {
238 mapcache_grid_link *sgrid = APR_ARRAY_IDX(tileset->grid_links,i,mapcache_grid_link*);
239 if(!strcmp(sgrid->grid->name,gname)) {
240 grid_link = sgrid;
241 break;
242 }
243 }
244 if(!grid_link) {
245 ctx->set_error(ctx,404, "received kml request with invalid grid %s", gname);
246 return;
247 }
248
249 } else {
250 grid_link = APR_ARRAY_IDX(tileset->grid_links,0,mapcache_grid_link*);
251 }
252 break;
253 case 2:
254 z = (int)strtol(key,&endptr,10);
255 if(*endptr != 0) {
256 ctx->set_error(ctx,404, "received kml request %s with invalid z %s", pathinfo, key);
257 return;
258 }
259 break;
260 case 3:
261 x = (int)strtol(key,&endptr,10);
262 if(*endptr != 0) {
263 ctx->set_error(ctx,404, "received kml request %s with invalid x %s", pathinfo, key);
264 return;
265 }
266 break;
267 case 4:
268 y = (int)strtol(key,&endptr,10);
269 if(*endptr != '.') {
270 ctx->set_error(ctx,404, "received kml request %s with invalid y %s", pathinfo, key);
271 return;
272 }
273 endptr++;
274 if(strcmp(endptr,"kml")) {
275 ctx->set_error(ctx,404, "received kml request with invalid extension %s", pathinfo, endptr);
276 return;
277 }
278 break;
279 default:
280 ctx->set_error(ctx,404, "received kml request %s with invalid parameter %s", pathinfo, key);
281 return;
282 }
283 }
284 }
285 if(index == 4) {
286 mapcache_request_get_capabilities_kml *req = (mapcache_request_get_capabilities_kml*)apr_pcalloc(
287 ctx->pool,sizeof(mapcache_request_get_capabilities_kml));
288 req->request.request.type = MAPCACHE_REQUEST_GET_CAPABILITIES;
289 req->tile = mapcache_tileset_tile_create(ctx->pool, tileset, grid_link);
290 req->tile->x = x;
291 req->tile->y = y;
292 req->tile->z = z;
293 mapcache_tileset_tile_validate(ctx,req->tile);
294 GC_CHECK_ERROR(ctx);
295 *request = (mapcache_request*)req;
296 return;
297 } else if(index==1) {
298 mapcache_request_get_capabilities_kml *req = (mapcache_request_get_capabilities_kml*)apr_pcalloc(
299 ctx->pool,sizeof(mapcache_request_get_capabilities_kml));
300 req->request.request.type = MAPCACHE_REQUEST_GET_CAPABILITIES;
301 req->tile = NULL;
302 req->tileset = tileset;
303 req->grid = grid_link;
304 *request = (mapcache_request*)req;
305 return;
306 } else {
307 ctx->set_error(ctx,404, "received kml request %s with wrong number of arguments", pathinfo);
308 return;
309 }
310 }
311
mapcache_service_kml_create(mapcache_context * ctx)312 mapcache_service* mapcache_service_kml_create(mapcache_context *ctx)
313 {
314 mapcache_service_kml* service = (mapcache_service_kml*)apr_pcalloc(ctx->pool, sizeof(mapcache_service_kml));
315 if(!service) {
316 ctx->set_error(ctx, 500, "failed to allocate kml service");
317 return NULL;
318 }
319 service->service.url_prefix = apr_pstrdup(ctx->pool,"kml");
320 service->service.name = apr_pstrdup(ctx->pool,"kml");
321 service->service.type = MAPCACHE_SERVICE_KML;
322 service->service.parse_request = _mapcache_service_kml_parse_request;
323 service->service.create_capabilities_response = _create_capabilities_kml;
324 return (mapcache_service*)service;
325 }
326
327 /** @} */
328 /* vim: ts=2 sts=2 et sw=2
329 */
330