1 /******************************************************************************
2  * $Id$
3  *
4  * Project:  MapServer
5  * Purpose:  MapCache tile caching support file: time dimension support
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 #include "mapcache.h"
30 #include <apr_time.h>
31 #include <apr_strings.h>
32 #include <time.h>
33 
34 typedef enum {
35   MAPCACHE_TINTERVAL_SECOND,
36   MAPCACHE_TINTERVAL_MINUTE,
37   MAPCACHE_TINTERVAL_HOUR,
38   MAPCACHE_TINTERVAL_DAY,
39   MAPCACHE_TINTERVAL_MONTH,
40   MAPCACHE_TINTERVAL_YEAR
41 } mapcache_time_interval_t;
42 
43 #ifndef HAVE_TIMEGM
timegm(struct tm * tm)44 time_t timegm(struct tm *tm)
45 {
46   time_t t, tdiff;
47   struct tm in, gtime, ltime;
48 
49   memcpy(&in, tm, sizeof(in));
50   t = mktime(&in);
51 
52   memcpy(&gtime, gmtime(&t), sizeof(gtime));
53   memcpy(&ltime, localtime(&t), sizeof(ltime));
54   gtime.tm_isdst = ltime.tm_isdst;
55   tdiff = t - mktime(&gtime);
56 
57   memcpy(&in, tm, sizeof(in));
58   return mktime(&in) + tdiff;
59 }
60 #endif
61 
62 
mapcache_ogc_strptime(const char * value,struct tm * ts,mapcache_time_interval_t * ti)63 char *mapcache_ogc_strptime(const char *value, struct tm *ts, mapcache_time_interval_t *ti) {
64   char *valueptr;
65   memset (ts, '\0', sizeof (*ts));
66   ts->tm_mday = 1;
67   valueptr = strptime(value,"%Y-%m-%dT%H:%M:%SZ",ts);
68   *ti = MAPCACHE_TINTERVAL_SECOND;
69   if(valueptr) return valueptr;
70   valueptr = strptime(value,"%Y-%m-%dT%H:%MZ",ts);
71   *ti = MAPCACHE_TINTERVAL_MINUTE;
72   if(valueptr) return valueptr;
73   valueptr = strptime(value,"%Y-%m-%dT%HZ",ts);
74   *ti = MAPCACHE_TINTERVAL_HOUR;
75   if(valueptr) return valueptr;
76   valueptr = strptime(value,"%Y-%m-%d",ts);
77   *ti = MAPCACHE_TINTERVAL_DAY;
78   if(valueptr) return valueptr;
79   valueptr = strptime(value,"%Y-%m",ts);
80   *ti = MAPCACHE_TINTERVAL_MONTH;
81   if(valueptr) return valueptr;
82   valueptr = strptime(value,"%Y",ts);
83   *ti = MAPCACHE_TINTERVAL_YEAR;
84   if(valueptr) return valueptr;
85   return NULL;
86 }
87 
mapcache_dimension_time_get_entries(mapcache_context * ctx,mapcache_dimension * dim,const char * dim_value,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid,time_t * intervals,int n_intervals)88 apr_array_header_t* mapcache_dimension_time_get_entries(mapcache_context *ctx, mapcache_dimension *dim, const char *dim_value,
89         mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid, time_t *intervals, int n_intervals) {
90   int i;
91   apr_array_header_t *time_ids = apr_array_make(ctx->pool,0,sizeof(char*));
92   if(!dim->_get_entries_for_time_range) {
93     ctx->set_error(ctx,500,"dimension does not support time queries");
94     return NULL;
95   }
96   for(i=0;i<n_intervals;i++) {
97       apr_array_header_t *interval_ids = dim->_get_entries_for_time_range(ctx, dim, dim_value,
98             intervals[i*2], intervals[i*2+1],
99             tileset, extent, grid);
100       if(GC_HAS_ERROR(ctx)) {
101         return NULL;
102       }
103       apr_array_cat(time_ids, interval_ids);
104   }
105   return time_ids;
106 }
107 
mapcache_dimension_time_get_entries_for_value(mapcache_context * ctx,mapcache_dimension * dimension,const char * value,mapcache_tileset * tileset,mapcache_extent * extent,mapcache_grid * grid)108 apr_array_header_t* mapcache_dimension_time_get_entries_for_value(mapcache_context *ctx, mapcache_dimension *dimension, const char *value,
109                                                                    mapcache_tileset *tileset, mapcache_extent *extent, mapcache_grid *grid) {
110 
111   /* look if supplied value is a predefined key */
112   /* split multiple values, loop */
113 
114   /* extract start and end values */
115   struct tm tm_start,tm_end;
116   time_t *intervals;
117   mapcache_time_interval_t tis,tie;
118   char *valueptr = apr_pstrdup(ctx->pool,value);
119   char *last,*key;
120   int count=1;
121   char * value_scan = (char *)value;
122 
123   /*count how many time entries were supplied*/
124   for(; *value_scan; value_scan++) if(*value_scan == ',') count++;
125 
126   intervals = apr_pcalloc(ctx->pool,2*count*sizeof(time_t));
127   count = 0;
128 
129 
130   /* Split the input on ',' */
131   for (key = apr_strtok(valueptr, ",", &last); key != NULL;
132        key = apr_strtok(NULL, ",", &last)) {
133     valueptr = mapcache_ogc_strptime(key,&tm_start,&tis);
134     if(!valueptr) {
135       ctx->set_error(ctx,400,"failed to parse time %s",value);
136       return NULL;
137     }
138 
139     if(*valueptr == '/' || (*valueptr == '-' && *(valueptr+1) == '-')) {
140       /* we have a second (end) time */
141       if (*valueptr == '/') {
142         valueptr++;
143       }
144       else {
145         valueptr += 2;
146       }
147       valueptr = mapcache_ogc_strptime(valueptr,&tm_end,&tie);
148       if(!valueptr) {
149         ctx->set_error(ctx,400,"failed to parse end time in %s",value);
150         return NULL;
151       }
152     } else if(*valueptr == 0) {
153       tie = tis;
154       tm_end = tm_start;
155     } else {
156       ctx->set_error(ctx,400,"failed (2) to parse time %s",value);
157       return NULL;
158     }
159     switch(tie) {
160     case MAPCACHE_TINTERVAL_SECOND:
161       tm_end.tm_sec += 1;
162       break;
163     case MAPCACHE_TINTERVAL_MINUTE:
164       tm_end.tm_min += 1;
165       break;
166     case MAPCACHE_TINTERVAL_HOUR:
167       tm_end.tm_hour += 1;
168       break;
169     case MAPCACHE_TINTERVAL_DAY:
170       tm_end.tm_mday += 1;
171       break;
172     case MAPCACHE_TINTERVAL_MONTH:
173       tm_end.tm_mon += 1;
174       break;
175     case MAPCACHE_TINTERVAL_YEAR:
176       tm_end.tm_year += 1;
177       break;
178     }
179     intervals[count*2] = timegm(&tm_start);
180     intervals[count*2+1] = timegm(&tm_end);
181     count++;
182   }
183   return mapcache_dimension_time_get_entries(ctx,dimension,value,tileset,extent,grid,intervals,count);
184   /* end loop */
185 }
186