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 |
|