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