Statistics
| Revision:

gvsig-raster / org.gvsig.raster.wms / branches / org.gvsig.raster.wms_dataaccess_refactoring / org.gvsig.raster.wms.io / src / main / java / org / gvsig / raster / wms / io / time / TimeDimension.java @ 2378

History | View | Annotate | Download (15.9 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 */
22
package org.gvsig.raster.wms.io.time;
23

    
24
import java.util.ArrayList;
25
import java.util.Calendar;
26
import java.util.GregorianCalendar;
27
import java.util.List;
28

    
29
import org.gvsig.tools.ToolsLocator;
30
import org.gvsig.tools.dynobject.DynStruct;
31
import org.gvsig.tools.persistence.PersistenceManager;
32
import org.gvsig.tools.persistence.PersistentState;
33
import org.gvsig.tools.persistence.exception.PersistenceException;
34

    
35
/**
36
 * Class for WMS TIME dimension from a WMS. It allows you to handle the correct
37
 * values for this kind of dimension.
38
 * <br>
39
 * <p>
40
 * At the moment this class was written the WMS TIME dimension is defined as the
41
 * ISO8601 standard for expressing times.
42
 * </p>
43
 * <br>
44
 * <p>
45
 * As far as this class implements IFMapWMSDimension it uses the same interface
46
 * and documentation.
47
 * </p>
48
 *
49
 * @author jaume dominguez faus - jaume.dominguez@iver.es
50
 */
51
public class TimeDimension implements RemoteTimeDimension {
52
        static private final byte   YEAR_FORMAT           = 1;
53
        static private final byte   YEAR_TO_MONTH_FORMAT  = 2;
54
        static private final byte   YEAR_TO_DAY_FORMAT    = 3;
55
        static private final byte   FULL_FORMAT           = 4;
56

    
57
    static private final long   millisXsec            = 1000;
58
    static private final long   millisXminute         = 60 * millisXsec;
59
    static private final long   millisXhour           = 60 * millisXminute;
60
    static private final long   millisXday            = 24 * millisXhour;
61
    static private final long   millisXmonth          = 30 * millisXday;
62
    //(1 year = 365 days 6 hours 9 minutes 9,7 seconds)
63
    static private final long   millisXyear           = (365*millisXday) + (6*millisXhour) + (9*millisXminute) + 9700;
64

    
65
    static private final String digit                 = "[0-9]";
66
    static private final String nonZeroDigit          = "[1-9]";
67
    static private final String seconds               = "([0-5]" + digit + "((\\.|,)" + digit + digit+ "?" + digit + "?" + ")?)";
68
    static private final String minutes               = "([0-5]" + digit + ")";
69
    static private final String hours                 = "(0" + digit + "|1" + digit + "|2[0-3])";
70
    static private final String time                  = hours + ":" + minutes + "(:" + seconds + ")?";
71
    static private final String days                  = "(0?" + nonZeroDigit + "|1" + digit + "|2" + digit + "|30|31)";
72
    static private final String months                = "(0?" + nonZeroDigit + "|10|11|12)";
73
    static private final String year                  = "(" + digit + digit + ")";
74
    static private final String century               = "(" + digit + digit + ")";
75
    static private final String floatingPointNumber   = "(" + digit + "+(\\." + digit + "+)?)";
76

    
77
    private String              unit;
78
        private String              unitSymbol;
79
        private List<GregorianCalendar>   
80
                                    valueList;
81
        private boolean             compiled;
82
        private boolean             isGeologic;
83
        private String              expression;
84
        private int                 type;
85
        private byte                format               = 0;
86

    
87
    static private final String regexDateExtendedForBCE1 = "B?"+century+year;
88
    static private final String regexDateExtendedForBCE2 = "B?"+century+year+"-"+months;
89
    static private final String regexDateExtendedForBCE3 = "B?"+century+year+"-"+months+"-"+days;
90
    static private final String regexDateExtendedForBCE4 = "B?"+century+year+"-"+months+"-"+days+"(T| )"+time+"Z";
91
    // Note: in WMS 1.1.0 the suffix Z is optional
92

    
93
    static private final String regexDateExtendedForBCE =
94
        "(" +  regexDateExtendedForBCE1  + "|"
95
            +  regexDateExtendedForBCE2  + "|"
96
            +  regexDateExtendedForBCE3  + "|"
97
            +  regexDateExtendedForBCE4  +      ")";
98

    
99
    static private final String periodMagnitude = "(Y|M|D)";
100
    static private final String p1 = "(("+digit+")+"+periodMagnitude+")";
101

    
102
    static private final String timeMagnitude = "(H|M|S)";
103
    static private final String p2 = "("+floatingPointNumber+timeMagnitude+")";
104
    static private final String regexPeriod = "P(("+p1+"+"+"(T"+p2+")*)|("+p1+"*"+"(T"+p2+")+))";
105

    
106
    static private final String regexIntervalTimeDimension =
107
        "("+regexDateExtendedForBCE+")/("+regexDateExtendedForBCE+")/("+regexPeriod+")";
108

    
109
    static private final String geologicDatasets = "(K|M|G)";
110
    static private final String regexDateForGeologicDatasets = geologicDatasets+floatingPointNumber;
111

    
112
        @SuppressWarnings("unchecked")
113
        public void loadFromState(PersistentState state) throws PersistenceException {
114
                this.unit = state.getString("unit");
115
                this.unitSymbol = state.getString("unitSymbol");
116
                
117
                List<String> list = (List<String>)state.getList("valueList");
118
                valueList = convertStringListToGregorianCalendarList(list);
119
                
120
                this.compiled = state.getBoolean("compiled");
121
                this.isGeologic = state.getBoolean("isGeologic");
122
                this.expression = state.getString("expression");
123
                this.type = state.getInt("type");
124
                this.format = (byte)state.getInt("format");
125
        }
126
        
127
        public void saveToState(PersistentState state) throws PersistenceException {
128
                state.set("unit", unit);
129
                state.set("unitSymbol", unitSymbol);
130
                state.set("valueList", convertGregorianCalendarListToStringList(valueList));
131
                state.set("compiled", compiled);
132
                state.set("isGeologic", isGeologic);
133
                state.set("expression", expression);
134
                state.set("type", type);
135
                state.set("format", format);
136
        }        
137
        
138
        private List<String> convertGregorianCalendarListToStringList(List<GregorianCalendar> gList) {
139
                List<String> l = new ArrayList<String>();
140
                for (int i = 0; i < gList.size(); i++) {
141
                        l.add(toString(gList.get(i)));
142
                }
143
                return l;
144
        }
145
        
146
        private List<GregorianCalendar> convertStringListToGregorianCalendarList(List<String> sList) {
147
                List<GregorianCalendar> l = new ArrayList<GregorianCalendar>();
148
                for (int i = 0; i < sList.size(); i++) {
149
                        l.add(valueOf(sList.get(i)));
150
                }
151
                return l;
152
        }
153
        
154
        public static void registerPersistent() {
155
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
156
                DynStruct definition = manager.getDefinition("TimeDimension_Persistent");
157
                if( definition == null ) {
158
                        definition = manager.addDefinition(
159
                                        TimeDimension.class,
160
                                        "TimeDimension_Persistent",
161
                                        "TimeDimension Persistence",
162
                                        null, 
163
                                        null
164
                        );
165
                }
166
                
167
                definition.addDynFieldString("unit").setMandatory(false);
168
                definition.addDynFieldString("unitSymbol").setMandatory(false);
169
                definition.addDynFieldList("valueList").setClassOfItems(String.class).setMandatory(false);
170
                definition.addDynFieldBoolean("compiled").setMandatory(false);
171
                definition.addDynFieldBoolean("isGeologic").setMandatory(false);
172
                definition.addDynFieldString("expression").setMandatory(false);
173
                definition.addDynFieldInt("type").setMandatory(false);
174
                definition.addDynFieldInt("format").setMandatory(false);
175
        }
176
        
177
        public TimeDimension() {}
178
        
179
    /**
180
     * Creates a new instance of TimeDimension.
181
     * @param units
182
     * @param unitSymbol
183
     * @param expression
184
     */
185
    public TimeDimension(String _units, String _unitSymbol, String _dimensionExpression) {
186
        this.unit = _units;
187
        this.unitSymbol = _unitSymbol;
188
        setExpression(_dimensionExpression);
189
    }
190

    
191
        public String getName() {
192
        return "TIME";
193
    }
194

    
195
    public String getUnit() {
196
        return unit;
197
    }
198

    
199
    public String getUnitSymbol() {
200
        return unitSymbol;
201
    }
202

    
203
        public String getLowLimit() {
204
                return valueAt(0);
205
        }
206

    
207
        public String getHighLimit() {
208
                return valueAt(valueList.size() - 1);
209
        }
210

    
211

    
212
        public String getResolution() {
213
                return null;
214
        }
215

    
216
        public boolean isValidValue(String value) {
217
                return (value.matches(regexDateForGeologicDatasets) || value.matches(regexDateExtendedForBCE));
218
        }
219

    
220
        public GregorianCalendar valueOf(String value) throws IllegalArgumentException {
221
                if (compiled) {
222
                    String myValue = value.toUpperCase();
223
                    if (isValidValue(myValue)) {
224
                            GregorianCalendar val = null;
225
                            if (!isGeologic){
226
                                    // This is a normal date
227
                                    int myYear;
228
                                    int myMonth;
229
                                    int myDay;
230
                                    int myHour;
231
                                    int myMinute;
232
                                    float mySecond;
233
                                    String[] s = myValue.split("-");
234
                                    myYear = (s[0].charAt(0)=='B') ? -Integer.parseInt(s[0].substring(1, 5)) : Integer.parseInt(s[0].substring(0, 4));
235
                                    myMonth = (s.length>1) ? Integer.parseInt(s[1])-1 : 0;
236
                                    if (myValue.matches(regexDateExtendedForBCE4)){
237
                                            if (s[2].endsWith("Z"))
238
                                                    s[2] = s[2].substring(0,s[2].length()-1);
239
                                            s = (s[2].indexOf('T')!=-1) ? s[2].split("T") : s[2].split(" ");
240
                                            myDay = Integer.parseInt(s[0]);
241

    
242
                                            // Go with the time
243
                                            s = s[1].split(":");
244
                                            myHour = Integer.parseInt(s[0]);
245
                                            myMinute = (s.length>1) ? Integer.parseInt(s[1]) : 0;
246
                                            mySecond = (s.length>2) ? Float.parseFloat(s[2]) : 0;
247
                                    } else {
248
                                            myDay = (s.length>2) ? Integer.parseInt(s[2]) : 1;
249

    
250
                                            myHour = 0;
251
                                            myMinute = 0;
252
                                            mySecond = 0;
253
                                    }
254
                                    GregorianCalendar cal = new GregorianCalendar(myYear, myMonth, myDay, myHour, myMinute, (int)mySecond);
255
                                    val = cal;
256
                            } else{
257
                                    // this is a geological date >:-(
258
                            }
259
                            return val;
260
                    } else throw new IllegalArgumentException(myValue);
261
            }
262
            return null;
263
        }
264

    
265
        public String valueAt(int pos) throws ArrayIndexOutOfBoundsException {
266
                return toString((GregorianCalendar) valueList.get(pos));
267
        }
268

    
269
        public int valueCount() {
270
                return valueList.size();
271
        }
272

    
273
        public String getExpression() {
274
                return expression;
275
        }
276

    
277
        public void setExpression(String expr) {
278
                this.expression = expr;
279
        }
280

    
281
        public int getType() {
282
                return type;
283
        }
284

    
285
        public void compile() throws IllegalArgumentException {
286
                compiled = true;
287
                valueList = new ArrayList<GregorianCalendar>();
288
                String[] items = expression.split(",");
289
                for (int i = 0; i < items.length; i++) {
290
                        // Each iteration is a value of a comma-separated list
291
                        // which can be a single date or even an expression defining
292
                        // an interval.
293
                        items[i] = items[i].trim();
294
                        if (items[i].matches(regexDateExtendedForBCE1)) {
295
                                upgradeFormat(YEAR_FORMAT);
296
                                valueList.add(valueOf(items[i]));
297
                        } else if (items[i].matches(regexDateExtendedForBCE2)) {
298
                                upgradeFormat(YEAR_TO_MONTH_FORMAT);
299
                                valueList.add(valueOf(items[i]));
300
                        } else if (items[i].matches(regexDateExtendedForBCE3)) {
301
                                upgradeFormat(YEAR_TO_DAY_FORMAT);
302
                                valueList.add(valueOf(items[i]));
303
                        } else if (items[i].matches(regexDateExtendedForBCE4)) {
304
                                upgradeFormat(FULL_FORMAT);
305
                                valueList.add(valueOf(items[i]));
306
                        } else if (items[i].matches(regexIntervalTimeDimension)) {
307
                                // Analyze and transform this interval expression to a list
308
                                // of values.
309

    
310
                                // min value
311
                                String[] s = items[i].split("/");
312
                                if (s[0].matches(regexDateExtendedForBCE1)) {
313
                                        upgradeFormat(YEAR_FORMAT);
314
                                } else if (s[0].matches(regexDateExtendedForBCE2)) {
315
                                        upgradeFormat(YEAR_TO_MONTH_FORMAT);
316
                                } else if (s[0].matches(regexDateExtendedForBCE3)) {
317
                                        upgradeFormat(YEAR_TO_DAY_FORMAT);
318
                                } else if (s[0].matches(regexDateExtendedForBCE4)) {
319
                                        upgradeFormat(FULL_FORMAT);
320
                                }
321
                                GregorianCalendar minValue = valueOf(s[0]);
322

    
323
                                // max value
324
                                if (s[0].matches(regexDateExtendedForBCE1)) {
325
                                        upgradeFormat(YEAR_FORMAT);
326
                                } else if (s[1].matches(regexDateExtendedForBCE2)) {
327
                                        upgradeFormat(YEAR_TO_MONTH_FORMAT);
328
                                } else if (s[1].matches(regexDateExtendedForBCE3)) {
329
                                        upgradeFormat(YEAR_TO_DAY_FORMAT);
330
                                } else if (s[1].matches(regexDateExtendedForBCE4)) {
331
                                        upgradeFormat(FULL_FORMAT);
332
                                }
333
                                GregorianCalendar maxValue = valueOf(s[1]);
334

    
335
                                String period = s[2];
336

    
337
                                if (period == null) {
338
                                        valueList.add(minValue);
339
                                        valueList.add(maxValue);
340
                                        continue;
341
                                }
342

    
343
                                long x1 = ((GregorianCalendar) maxValue).getTimeInMillis();
344
                                long x0 = ((GregorianCalendar) minValue).getTimeInMillis();
345
                                long distance = x1-x0;
346
                                long step = 0;
347

    
348
                                boolean isTimeField = false;
349
                                String val = "";
350

    
351
                                for (int j = 0; j < period.length(); j++) {
352
                                        if (period.charAt(j) == 'P')
353
                                                continue;
354
                                        if (period.charAt(j) == 'T') {
355
                                                isTimeField = true;
356
                                                continue;
357
                                        }
358
                                        switch (period.charAt(j)) {
359
                                        case 'Y':
360
                                                step += Integer.parseInt(val) * millisXyear;
361
                                                val = "";
362
                                                break;
363
                                        case 'M':
364
                                                if (isTimeField)
365
                                                        step += Integer.parseInt(val) * millisXminute;
366
                                                else
367
                                                        step += Integer.parseInt(val) * millisXmonth;
368
                                                val = "";
369
                                                break;
370
                                        case 'D':
371
                                                step += Integer.parseInt(val) * millisXday;
372
                                                val = "";
373
                                                break;
374
                                        case 'H':
375
                                                step += Integer.parseInt(val) * millisXhour;
376
                                                val = "";
377
                                                break;
378
                                        case 'S':
379
                                                step += Integer.parseInt(val) * 1000;
380
                                                val = "";
381
                                                break;
382
                                        default:
383
                                                val += period.charAt(j);
384
                                                break;
385
                                        }
386
                                }
387
                                // Now that we know the earliest and the latest date and the period
388
                                // between two dates, I'm going to populate the list of values
389
                                // considering these values.
390
                                int valueCount = (int)(distance/step) + 1; // + 1 for the initial point
391
                                valueList.add(minValue);
392
                                for (int pos = 1; pos < valueCount; pos++) {
393
                                        long newTime = ((GregorianCalendar) minValue).getTimeInMillis();
394
                                    newTime += (step*pos);
395
                                    GregorianCalendar cal = new GregorianCalendar();
396
                                    cal.setTimeInMillis(newTime);
397
                                    if (cal.after(maxValue) || cal.before(minValue))
398
                                            continue;
399

    
400
                                    valueList.add(cal);
401
                                }
402
                                valueList.add(maxValue);
403
                        } else {
404
                                compiled = false;
405
                                throw new IllegalArgumentException();
406
                        }
407
                }
408
    }
409

    
410
        /**
411
         * Prints a GregorianCalendar value in WMS1.1.1 format.
412
         * @param cal
413
         * @return
414
         */
415
        private String toString(GregorianCalendar cal) {
416
                int iYear   = cal.get(Calendar.YEAR);
417
                int iMonth  = cal.get(Calendar.MONTH) + 1;
418
                int iDay    = cal.get(Calendar.DAY_OF_MONTH);
419
                int iHour   = cal.get(Calendar.HOUR_OF_DAY);
420
                int iMinute = cal.get(Calendar.MINUTE);
421
                int iSecond = cal.get(Calendar.SECOND);
422
                String myYear;
423
                if (iYear<10)
424
                        myYear = "200"+iYear;
425
                else if (iYear<100)
426
                        myYear = "20"+iYear;
427
                else if (iYear<1000)
428
                        myYear = "2"+iYear;
429
                else
430
                        myYear = ""+iYear;
431
                String myMonth       = (iMonth<10) ? "0"+iMonth  : ""+iMonth;
432
                String myDay         = (iDay<10)   ? "0"+iDay    : ""+iDay;
433
                String myHour        = (iHour<10)  ? "0"+iHour   : ""+iHour;
434
                String myMinute      = (iMinute<10)? "0"+iMinute : ""+iMinute;
435
                String mySecond      = (iSecond<10)? "0"+iSecond : ""+iSecond;
436
                int myMilliSecond = cal.get(Calendar.MILLISECOND);
437

    
438

    
439
                String s = null;
440
                if (format == YEAR_FORMAT)
441
                        s = myYear+"";
442
                else if (format == YEAR_TO_MONTH_FORMAT)
443
                        s = myYear+"-"+myMonth;
444
                else if (format == YEAR_TO_DAY_FORMAT)
445
                        s = myYear+"-"+myMonth+"-"+myDay;
446
                else if (format == FULL_FORMAT)
447
                        s = myYear+"-"+myMonth+"-"+myDay+"T"+myHour+":"+myMinute+":"+mySecond+"."+(myMilliSecond/10)+"Z";
448
                if (iYear<0)
449
                        s = "B"+s;
450
                return s;
451
        }
452

    
453
        private void upgradeFormat(byte sFormat) {
454
                switch (sFormat) {
455
                case YEAR_FORMAT:
456
                        if (format < YEAR_FORMAT)
457
                                format = YEAR_FORMAT;
458
                        break;
459
                case YEAR_TO_MONTH_FORMAT:
460
                        if (format < YEAR_TO_MONTH_FORMAT)
461
                                format = YEAR_TO_MONTH_FORMAT;
462
                        break;
463
                case YEAR_TO_DAY_FORMAT:
464
                        if (format < YEAR_TO_DAY_FORMAT)
465
                                format = YEAR_TO_DAY_FORMAT;
466
                        break;
467
                case FULL_FORMAT:
468
                        if (format < FULL_FORMAT)
469
                                format = FULL_FORMAT;
470
                        break;
471
                }
472
        }
473
}
474

    
475