svn-gvsig-desktop / tags / v1_1_Build_1012 / extensions / extWMS / src / com / iver / cit / gvsig / fmap / layers / TimeDimension.java @ 12987
History | View | Annotate | Download (26.3 KB)
1 | 4222 | jaume | /* gvSIG. Sistema de Informaci�n Geogr�fica de la Generalitat Valenciana
|
---|---|---|---|
2 | 3746 | jaume | *
|
3 | * Copyright (C) 2005 IVER T.I. and Generalitat Valenciana.
|
||
4 | *
|
||
5 | * This program is free software; you can redistribute it and/or
|
||
6 | * modify it under the terms of the GNU General Public License
|
||
7 | * as published by the Free Software Foundation; either version 2
|
||
8 | * of the License, or (at your option) any later version.
|
||
9 | *
|
||
10 | * This program is distributed in the hope that it will be useful,
|
||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
13 | * GNU General Public License for more details.
|
||
14 | *
|
||
15 | * You should have received a copy of the GNU General Public License
|
||
16 | * along with this program; if not, write to the Free Software
|
||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,USA.
|
||
18 | *
|
||
19 | * For more information, contact:
|
||
20 | *
|
||
21 | * Generalitat Valenciana
|
||
22 | * Conselleria d'Infraestructures i Transport
|
||
23 | 4222 | jaume | * Av. Blasco Ib��ez, 50
|
24 | 3746 | jaume | * 46010 VALENCIA
|
25 | * SPAIN
|
||
26 | *
|
||
27 | * +34 963862235
|
||
28 | * gvsig@gva.es
|
||
29 | * www.gvsig.gva.es
|
||
30 | *
|
||
31 | * or
|
||
32 | *
|
||
33 | * IVER T.I. S.A
|
||
34 | * Salamanca 50
|
||
35 | * 46005 Valencia
|
||
36 | * Spain
|
||
37 | *
|
||
38 | * +34 963163400
|
||
39 | * dac@iver.es
|
||
40 | */
|
||
41 | |||
42 | /* CVS MESSAGES:
|
||
43 | *
|
||
44 | * $Id$
|
||
45 | * $Log$
|
||
46 | 10664 | jaume | * Revision 1.7.6.1 2007-03-08 12:06:28 jaume
|
47 | * fixed bug in YYYY date formats
|
||
48 | *
|
||
49 | * Revision 1.7 2006/05/12 07:47:39 jaume
|
||
50 | 5158 | jaume | * removed unnecessary imports
|
51 | *
|
||
52 | * Revision 1.6 2006/04/21 11:53:04 jaume
|
||
53 | 3749 | jaume | * *** empty log message ***
|
54 | *
|
||
55 | 4920 | jaume | * Revision 1.5 2006/02/28 15:25:14 jaume
|
56 | * *** empty log message ***
|
||
57 | *
|
||
58 | 4222 | jaume | * Revision 1.3.2.6 2006/02/28 12:54:12 jaume
|
59 | * *** empty log message ***
|
||
60 | *
|
||
61 | * Revision 1.3.2.5 2006/02/23 10:36:30 jaume
|
||
62 | * *** empty log message ***
|
||
63 | *
|
||
64 | * Revision 1.3.2.4 2006/02/10 13:22:35 jaume
|
||
65 | * now analyzes dimensions on demand
|
||
66 | *
|
||
67 | * Revision 1.3.2.3 2006/01/31 16:25:24 jaume
|
||
68 | * correcciones de bugs
|
||
69 | *
|
||
70 | * Revision 1.4 2006/01/26 16:07:14 jaume
|
||
71 | * *** empty log message ***
|
||
72 | *
|
||
73 | 3805 | jaume | * Revision 1.3.2.1 2006/01/26 12:59:32 jaume
|
74 | * 0.5
|
||
75 | *
|
||
76 | * Revision 1.3 2006/01/24 18:01:17 jaume
|
||
77 | * *** empty log message ***
|
||
78 | *
|
||
79 | 3749 | jaume | * Revision 1.2 2006/01/24 14:36:33 jaume
|
80 | 3746 | jaume | * This is the new version
|
81 | *
|
||
82 | * Revision 1.1.2.11 2006/01/20 15:59:13 jaume
|
||
83 | * *** empty log message ***
|
||
84 | *
|
||
85 | * Revision 1.1.2.10 2006/01/20 15:22:46 jaume
|
||
86 | * *** empty log message ***
|
||
87 | *
|
||
88 | * Revision 1.1.2.9 2006/01/20 08:50:52 jaume
|
||
89 | * handles time dimension for the NASA Jet Propulsion Laboratory WMS
|
||
90 | *
|
||
91 | * Revision 1.1.2.8 2006/01/19 16:09:30 jaume
|
||
92 | * *** empty log message ***
|
||
93 | *
|
||
94 | * Revision 1.1.2.7 2006/01/11 12:20:30 jaume
|
||
95 | * *** empty log message ***
|
||
96 | *
|
||
97 | * Revision 1.1.2.6 2006/01/10 11:33:31 jaume
|
||
98 | * Time dimension working against Jet Propulsion Laboratory's WMS server
|
||
99 | *
|
||
100 | * Revision 1.1.2.5 2006/01/09 18:10:38 jaume
|
||
101 | * casi con el time dimension
|
||
102 | *
|
||
103 | * Revision 1.1.2.4 2006/01/05 23:15:53 jaume
|
||
104 | * *** empty log message ***
|
||
105 | *
|
||
106 | * Revision 1.1.2.3 2006/01/04 18:09:02 jaume
|
||
107 | * Time dimension
|
||
108 | *
|
||
109 | * Revision 1.1.2.2 2006/01/04 16:49:44 jaume
|
||
110 | * Time dimensios
|
||
111 | *
|
||
112 | * Revision 1.1.2.1 2006/01/03 18:08:40 jaume
|
||
113 | * *** empty log message ***
|
||
114 | *
|
||
115 | *
|
||
116 | */
|
||
117 | /**
|
||
118 | 10664 | jaume | *
|
119 | 3746 | jaume | */
|
120 | package com.iver.cit.gvsig.fmap.layers; |
||
121 | |||
122 | 4222 | jaume | import java.util.ArrayList; |
123 | 3746 | jaume | import java.util.GregorianCalendar; |
124 | |||
125 | /**
|
||
126 | * Class for WMS TIME dimension from a WMS. It allows you to handle the correct
|
||
127 | * values for this kind of dimension.
|
||
128 | * <br>
|
||
129 | * <p>
|
||
130 | * At the moment this class was written the WMS TIME dimension is defined as the
|
||
131 | * ISO8601 standard for expressing times.
|
||
132 | * </p>
|
||
133 | * <br>
|
||
134 | * <p>
|
||
135 | * As far as this class implements IFMapWMSDimension it uses the same interface
|
||
136 | * and documentation.
|
||
137 | * </p>
|
||
138 | 10664 | jaume | *
|
139 | 3746 | jaume | * @author jaume dominguez faus - jaume.dominguez@iver.es
|
140 | */
|
||
141 | public class TimeDimension implements IFMapWMSDimension { |
||
142 | 4222 | jaume | static private final byte YEAR_FORMAT = 1; |
143 | static private final byte YEAR_TO_MONTH_FORMAT = 2; |
||
144 | static private final byte YEAR_TO_DAY_FORMAT = 3; |
||
145 | static private final byte FULL_FORMAT = 4; |
||
146 | 10664 | jaume | |
147 | 3746 | jaume | static private final long millisXsec = 1000; |
148 | static private final long millisXminute = 60 * millisXsec; |
||
149 | 3749 | jaume | static private final long millisXhour = 60 * millisXminute; |
150 | static private final long millisXday = 24 * millisXhour; |
||
151 | static private final long millisXmonth = 30 * millisXday; |
||
152 | // according on the Wikipedia (1 year = 365 days 6 hours 9 minutes 9,7 seconds)
|
||
153 | 10664 | jaume | static private final long millisXyear = (365*millisXday) + (6*millisXhour) + (9*millisXminute) + 9700; |
154 | |||
155 | 3746 | jaume | static private final String digit = "[0-9]"; |
156 | static private final String nonZeroDigit = "[1-9]"; |
||
157 | 4222 | jaume | static private final String seconds = "([0-5]"+digit+"((\\.|,)"+digit+digit+")?)"; |
158 | static private final String minutes = "([0-5]"+digit+")"; |
||
159 | static private final String hours = "(0"+digit+"|1"+digit+"|2[0-3])"; |
||
160 | static private final String time = hours+":"+minutes+"(:"+seconds+")?"; |
||
161 | static private final String days = "(0?"+nonZeroDigit+"|1"+digit+"|2"+digit+"|30|31)"; |
||
162 | static private final String months = "(0?"+nonZeroDigit+"|10|11|12)"; |
||
163 | static private final String year = "("+digit+digit+")"; |
||
164 | static private final String century = "("+digit+digit+")"; |
||
165 | static private final String floatingPointNumber = "("+digit+"+(\\."+digit+"+)?)"; |
||
166 | 10664 | jaume | |
167 | 4920 | jaume | private String unit; |
168 | 4222 | jaume | private String unitSymbol; |
169 | private ArrayList valueList; |
||
170 | private boolean compiled; |
||
171 | private boolean isGeologic; |
||
172 | private String expression; |
||
173 | private int type; |
||
174 | private byte format = 0; |
||
175 | |||
176 | 10664 | jaume | static private final String regexDateExtendedForBCE1 = "B?"+century+year; |
177 | 4222 | jaume | static private final String regexDateExtendedForBCE2 = "B?"+century+year+"-"+months; |
178 | static private final String regexDateExtendedForBCE3 = "B?"+century+year+"-"+months+"-"+days; |
||
179 | static private final String regexDateExtendedForBCE4 = "B?"+century+year+"-"+months+"-"+days+"(T| )"+time+"Z"; |
||
180 | // Note: in WMS 1.1.0 the suffix Z is optional
|
||
181 | 10664 | jaume | |
182 | static private final String regexDateExtendedForBCE = |
||
183 | 4222 | jaume | "(" + regexDateExtendedForBCE1 + "|" |
184 | + regexDateExtendedForBCE2 + "|"
|
||
185 | + regexDateExtendedForBCE3 + "|"
|
||
186 | + regexDateExtendedForBCE4 + ")";
|
||
187 | 10664 | jaume | |
188 | 4222 | jaume | static private final String periodMagnitude = "(Y|M|D)"; |
189 | static private final String p1 = "(("+digit+")+"+periodMagnitude+")"; |
||
190 | 10664 | jaume | |
191 | 4222 | jaume | static private final String timeMagnitude = "(H|M|S)"; |
192 | static private final String p2 = "("+floatingPointNumber+timeMagnitude+")"; |
||
193 | 10664 | jaume | static private final String regexPeriod = "P(("+p1+"+"+"(T"+p2+")*)|("+p1+"*"+"(T"+p2+")+))"; |
194 | |||
195 | 4222 | jaume | static private final String regexIntervalTimeDimension = |
196 | "("+regexDateExtendedForBCE+")/("+regexDateExtendedForBCE+")/("+regexPeriod+")"; |
||
197 | 10664 | jaume | |
198 | 4222 | jaume | static private final String geologicDatasets = "(K|M|G)"; |
199 | static private final String regexDateForGeologicDatasets = geologicDatasets+floatingPointNumber; |
||
200 | 10664 | jaume | |
201 | 4222 | jaume | /**
|
202 | * Creates a new instance of TimeDimension.
|
||
203 | * @param units
|
||
204 | * @param unitSymbol
|
||
205 | * @param expression
|
||
206 | */
|
||
207 | public TimeDimension(String _units, String _unitSymbol, String _dimensionExpression) { |
||
208 | this.unit = _units;
|
||
209 | this.unitSymbol = _unitSymbol;
|
||
210 | setExpression(_dimensionExpression); |
||
211 | } |
||
212 | 10664 | jaume | |
213 | 4222 | jaume | public String getName() { |
214 | 4920 | jaume | return "TIME"; |
215 | 4222 | jaume | } |
216 | 10664 | jaume | |
217 | 4222 | jaume | public String getUnit() { |
218 | return unit;
|
||
219 | } |
||
220 | |||
221 | public String getUnitSymbol() { |
||
222 | return unitSymbol;
|
||
223 | } |
||
224 | |||
225 | public String getLowLimit() { |
||
226 | return (String) valueList.get(0); |
||
227 | } |
||
228 | |||
229 | public String getHighLimit() { |
||
230 | return (String) valueList.get(valueList.size()-1); |
||
231 | } |
||
232 | |||
233 | 10664 | jaume | |
234 | 4222 | jaume | public String getResolution() { |
235 | return null; |
||
236 | } |
||
237 | |||
238 | public boolean isValidValue(String value) { |
||
239 | return (value.matches(regexDateForGeologicDatasets) || value.matches(regexDateExtendedForBCE));
|
||
240 | } |
||
241 | |||
242 | public Object valueOf(String value) throws IllegalArgumentException { |
||
243 | if (compiled) {
|
||
244 | // TODO Missing geological dates
|
||
245 | String myValue = value.toUpperCase();
|
||
246 | if (isValidValue(myValue)) {
|
||
247 | Object val = null; |
||
248 | if (!isGeologic){
|
||
249 | // This is a normal date
|
||
250 | int myYear;
|
||
251 | int myMonth;
|
||
252 | int myDay;
|
||
253 | int myHour;
|
||
254 | int myMinute;
|
||
255 | float mySecond;
|
||
256 | String[] s = myValue.split("-"); |
||
257 | myYear = (s[0].charAt(0)=='B') ? -Integer.parseInt(s[0].substring(1, 5)) : Integer.parseInt(s[0].substring(0, 4)); |
||
258 | 10664 | jaume | myMonth = (s.length>1) ? Integer.parseInt(s[1])-1 : 0; |
259 | 4222 | jaume | if (myValue.matches(regexDateExtendedForBCE4)){
|
260 | if (s[2].endsWith("Z")) |
||
261 | s[2] = s[2].substring(0,s[2].length()-1); |
||
262 | s = (s[2].indexOf('T')!=-1) ? s[2].split("T") : s[2].split(" "); |
||
263 | myDay = Integer.parseInt(s[0]); |
||
264 | 10664 | jaume | |
265 | 4222 | jaume | // Go with the time
|
266 | s = s[1].split(":"); |
||
267 | myHour = Integer.parseInt(s[0]); |
||
268 | myMinute = (s.length>1) ? Integer.parseInt(s[1]) : 0; |
||
269 | mySecond = (s.length>2) ? Float.parseFloat(s[2]) : 0; |
||
270 | } else {
|
||
271 | myDay = (s.length>2) ? Integer.parseInt(s[2]) : 1; |
||
272 | 10664 | jaume | |
273 | 4222 | jaume | myHour = 0;
|
274 | myMinute = 0;
|
||
275 | mySecond = 0;
|
||
276 | } |
||
277 | GregorianCalendar cal = new GregorianCalendar(myYear, myMonth, myDay, myHour, myMinute, (int)mySecond); |
||
278 | val = cal; |
||
279 | } else{
|
||
280 | // this is a geological date >:-(
|
||
281 | } |
||
282 | 10664 | jaume | return val;
|
283 | 4222 | jaume | } else throw new IllegalArgumentException(myValue); |
284 | } |
||
285 | return null; |
||
286 | } |
||
287 | |||
288 | public String valueAt(int pos) throws ArrayIndexOutOfBoundsException { |
||
289 | return toString((GregorianCalendar) valueList.get(pos)); |
||
290 | } |
||
291 | |||
292 | public int valueCount() { |
||
293 | return valueList.size();
|
||
294 | } |
||
295 | |||
296 | public String getExpression() { |
||
297 | return expression;
|
||
298 | } |
||
299 | |||
300 | public void setExpression(String expr) { |
||
301 | this.expression = expr;
|
||
302 | } |
||
303 | |||
304 | public int getType() { |
||
305 | return type;
|
||
306 | } |
||
307 | |||
308 | public void compile() throws IllegalArgumentException { |
||
309 | compiled = true;
|
||
310 | valueList = new ArrayList(); |
||
311 | String[] items = expression.split(","); |
||
312 | for (int i = 0; i < items.length; i++) { |
||
313 | // Each iteration is a value of a comma-separated list
|
||
314 | // which can be a single date or even an expression defining
|
||
315 | // an interval.
|
||
316 | if (items[i].matches(regexDateExtendedForBCE1)) {
|
||
317 | upgradeFormat(YEAR_FORMAT); |
||
318 | valueList.add(valueOf(items[i])); |
||
319 | } else if (items[i].matches(regexDateExtendedForBCE2)) { |
||
320 | upgradeFormat(YEAR_TO_MONTH_FORMAT); |
||
321 | valueList.add(valueOf(items[i])); |
||
322 | } else if (items[i].matches(regexDateExtendedForBCE3)) { |
||
323 | upgradeFormat(YEAR_TO_DAY_FORMAT); |
||
324 | valueList.add(valueOf(items[i])); |
||
325 | 10664 | jaume | } else if (items[i].matches(regexDateExtendedForBCE4)) { |
326 | 4222 | jaume | upgradeFormat(FULL_FORMAT); |
327 | valueList.add(valueOf(items[i])); |
||
328 | } else if (items[i].matches(regexIntervalTimeDimension)) { |
||
329 | // Analyze and transform this interval expression to a list
|
||
330 | // of values.
|
||
331 | 10664 | jaume | |
332 | 4222 | jaume | // min value
|
333 | String[] s = items[i].split("/"); |
||
334 | if (s[0].matches(regexDateExtendedForBCE1)) { |
||
335 | upgradeFormat(YEAR_FORMAT); |
||
336 | } else if (s[0].matches(regexDateExtendedForBCE2)) { |
||
337 | upgradeFormat(YEAR_TO_MONTH_FORMAT); |
||
338 | } else if (s[0].matches(regexDateExtendedForBCE3)) { |
||
339 | upgradeFormat(YEAR_TO_DAY_FORMAT); |
||
340 | 10664 | jaume | } else if (s[0].matches(regexDateExtendedForBCE4)) { |
341 | 4222 | jaume | upgradeFormat(FULL_FORMAT); |
342 | } |
||
343 | Object minValue = valueOf(s[0]); |
||
344 | 10664 | jaume | |
345 | 4222 | jaume | // max value
|
346 | if (s[0].matches(regexDateExtendedForBCE1)) { |
||
347 | upgradeFormat(YEAR_FORMAT); |
||
348 | } else if (s[1].matches(regexDateExtendedForBCE2)) { |
||
349 | upgradeFormat(YEAR_TO_MONTH_FORMAT); |
||
350 | } else if (s[1].matches(regexDateExtendedForBCE3)) { |
||
351 | upgradeFormat(YEAR_TO_DAY_FORMAT); |
||
352 | 10664 | jaume | } else if (s[1].matches(regexDateExtendedForBCE4)) { |
353 | 4222 | jaume | upgradeFormat(FULL_FORMAT); |
354 | } |
||
355 | Object maxValue = valueOf(s[1]); |
||
356 | 10664 | jaume | |
357 | 4222 | jaume | String period = s[2]; |
358 | 10664 | jaume | |
359 | 4222 | jaume | if (period == null) { |
360 | valueList.add(minValue); |
||
361 | valueList.add(maxValue); |
||
362 | continue;
|
||
363 | } |
||
364 | 10664 | jaume | |
365 | 4222 | jaume | long x1 = ((GregorianCalendar) maxValue).getTimeInMillis(); |
366 | long x0 = ((GregorianCalendar) minValue).getTimeInMillis(); |
||
367 | long distance = x1-x0;
|
||
368 | long step = 0; |
||
369 | 10664 | jaume | |
370 | 4222 | jaume | boolean isTimeField = false; |
371 | String val = ""; |
||
372 | 10664 | jaume | |
373 | 4222 | jaume | for (int j = 0; j < period.length(); j++) { |
374 | if (period.charAt(j) == 'P') |
||
375 | continue;
|
||
376 | if (period.charAt(j) == 'T') { |
||
377 | isTimeField = true;
|
||
378 | continue;
|
||
379 | } |
||
380 | switch (period.charAt(j)) {
|
||
381 | case 'Y': |
||
382 | step += Integer.parseInt(val) * millisXyear;
|
||
383 | val = "";
|
||
384 | break;
|
||
385 | case 'M': |
||
386 | if (isTimeField)
|
||
387 | step += Integer.parseInt(val) * millisXminute;
|
||
388 | else
|
||
389 | step += Integer.parseInt(val) * millisXmonth;
|
||
390 | val = "";
|
||
391 | break;
|
||
392 | case 'D': |
||
393 | step += Integer.parseInt(val) * millisXday;
|
||
394 | val = "";
|
||
395 | break;
|
||
396 | case 'H': |
||
397 | step += Integer.parseInt(val) * millisXhour;
|
||
398 | val = "";
|
||
399 | break;
|
||
400 | case 'S': |
||
401 | step += Integer.parseInt(val) * 1000; |
||
402 | val = "";
|
||
403 | break;
|
||
404 | default:
|
||
405 | val += period.charAt(j); |
||
406 | break;
|
||
407 | 10664 | jaume | } |
408 | 4222 | jaume | } |
409 | // Now that we know the earliest and the latest date and the period
|
||
410 | // between two dates, I'm going to populate the list of values
|
||
411 | // considering these values.
|
||
412 | int valueCount = (int)(distance/step) + 1; // + 1 for the initial point |
||
413 | valueList.add(minValue); |
||
414 | for (int pos = 1; pos < valueCount; pos++) { |
||
415 | long newTime = ((GregorianCalendar) minValue).getTimeInMillis(); |
||
416 | newTime += (step*pos); |
||
417 | GregorianCalendar cal = new GregorianCalendar(); |
||
418 | cal.setTimeInMillis(newTime); |
||
419 | if (cal.after(maxValue) || cal.before(minValue))
|
||
420 | continue;
|
||
421 | |||
422 | valueList.add(cal); |
||
423 | } |
||
424 | valueList.add(maxValue); |
||
425 | } else {
|
||
426 | compiled = false;
|
||
427 | throw new IllegalArgumentException(); |
||
428 | } |
||
429 | } |
||
430 | } |
||
431 | 10664 | jaume | |
432 | 4222 | jaume | /**
|
433 | * Prints a GregorianCalendar value in WMS1.1.1 format.
|
||
434 | * @param cal
|
||
435 | * @return
|
||
436 | */
|
||
437 | private String toString(GregorianCalendar cal) { |
||
438 | int iYear = cal.get(cal.YEAR);
|
||
439 | int iMonth = cal.get(cal.MONTH) + 1; |
||
440 | int iDay = cal.get(cal.DAY_OF_MONTH);
|
||
441 | int iHour = cal.get(cal.HOUR_OF_DAY);
|
||
442 | int iMinute = cal.get(cal.MINUTE);
|
||
443 | int iSecond = cal.get(cal.SECOND);
|
||
444 | String myYear;
|
||
445 | if (iYear<10) |
||
446 | myYear = "200"+iYear;
|
||
447 | else if (iYear<100) |
||
448 | myYear = "20"+iYear;
|
||
449 | else if (iYear<1000) |
||
450 | myYear = "2"+iYear;
|
||
451 | else
|
||
452 | myYear = ""+iYear;
|
||
453 | String myMonth = (iMonth<10) ? "0"+iMonth : ""+iMonth; |
||
454 | String myDay = (iDay<10) ? "0"+iDay : ""+iDay; |
||
455 | String myHour = (iHour<10) ? "0"+iHour : ""+iHour; |
||
456 | String myMinute = (iMinute<10)? "0"+iMinute : ""+iMinute; |
||
457 | String mySecond = (iSecond<10)? "0"+iSecond : ""+iSecond; |
||
458 | int myMilliSecond = cal.get(cal.MILLISECOND);
|
||
459 | 10664 | jaume | |
460 | |||
461 | 4222 | jaume | String s = null; |
462 | if (format == YEAR_FORMAT)
|
||
463 | s = myYear+"";
|
||
464 | else if (format == YEAR_TO_MONTH_FORMAT) |
||
465 | s = myYear+"-"+myMonth;
|
||
466 | else if (format == YEAR_TO_DAY_FORMAT) |
||
467 | s = myYear+"-"+myMonth+"-"+myDay; |
||
468 | else if (format == FULL_FORMAT) |
||
469 | s = myYear+"-"+myMonth+"-"+myDay+"T"+myHour+":"+myMinute+":"+mySecond+"."+(myMilliSecond/10)+"Z"; |
||
470 | if (iYear<0) |
||
471 | s = "B"+s;
|
||
472 | return s;
|
||
473 | } |
||
474 | |||
475 | private void upgradeFormat(byte sFormat) { |
||
476 | switch (sFormat) {
|
||
477 | case YEAR_FORMAT:
|
||
478 | if (format < YEAR_FORMAT)
|
||
479 | format = YEAR_FORMAT; |
||
480 | break;
|
||
481 | case YEAR_TO_MONTH_FORMAT:
|
||
482 | if (format < YEAR_TO_MONTH_FORMAT)
|
||
483 | format = YEAR_TO_MONTH_FORMAT; |
||
484 | break;
|
||
485 | case YEAR_TO_DAY_FORMAT:
|
||
486 | if (format < YEAR_TO_DAY_FORMAT)
|
||
487 | format = YEAR_TO_DAY_FORMAT; |
||
488 | break;
|
||
489 | case FULL_FORMAT:
|
||
490 | if (format < FULL_FORMAT)
|
||
491 | format = FULL_FORMAT; |
||
492 | break;
|
||
493 | } |
||
494 | } |
||
495 | } |
||
496 | |||
497 | |||
498 | /*
|
||
499 | 10664 | jaume | |
500 | 4222 | jaume | // an old (and good one) version. However, the new version is a bit better.
|
501 | 10664 | jaume | |
502 | 4222 | jaume | public class TimeDimension implements IFMapWMSDimension {
|
503 | static private final long millisXsec = 1000;
|
||
504 | static private final long millisXminute = 60 * millisXsec;
|
||
505 | static private final long millisXhour = 60 * millisXminute;
|
||
506 | static private final long millisXday = 24 * millisXhour;
|
||
507 | static private final long millisXmonth = 30 * millisXday;
|
||
508 | // according on the Wikipedia (1 year = 365 days 6 hours 9 minutes 9,7 seconds)
|
||
509 | 10664 | jaume | static private final long millisXyear = (365*millisXday) + (6*millisXhour) + (9*millisXminute) + 9700;
|
510 | |||
511 | 4222 | jaume | static private final String digit = "[0-9]";
|
512 | static private final String nonZeroDigit = "[1-9]";
|
||
513 | 3746 | jaume | static private final String letter = "[a-zA-Z]";
|
514 | static private final String seconds = "([0-5]"+digit+"((\\.|,)"+digit+digit+")?)";
|
||
515 | static private final String minutes = "([0-5]"+digit+")";
|
||
516 | static private final String hours = "(0"+digit+"|1"+digit+"|2[0-3])";
|
||
517 | static private final String time = hours+":"+minutes+"(:"+seconds+")?";
|
||
518 | static private final String days = "(0?"+nonZeroDigit+"|1"+digit+"|2"+digit+"|30|31)";
|
||
519 | static private final String months = "(0?"+nonZeroDigit+"|10|11|12)";
|
||
520 | static private final String year = "("+digit+digit+")";
|
||
521 | static private final String century = "("+digit+digit+")";
|
||
522 | 10664 | jaume | |
523 | 3746 | jaume | static private final String geologicDatasets = "(K|M|G)";
|
524 | static private final String floatingPointNumber = "("+digit+"+(\\."+digit+"+)?)";
|
||
525 | 10664 | jaume | |
526 | |||
527 | 3749 | jaume | static private final String regexDateExtendedForBCE1 = "B?"+century+year+"-";
|
528 | 3746 | jaume | static private final String regexDateExtendedForBCE2 = "B?"+century+year+"-"+months;
|
529 | static private final String regexDateExtendedForBCE3 = "B?"+century+year+"-"+months+"-"+days;
|
||
530 | static private final String regexDateExtendedForBCE4 = "B?"+century+year+"-"+months+"-"+days+"(T| )"+time+"Z";
|
||
531 | // Note: in WMS 1.1.0 the suffix Z is optional
|
||
532 | // TODO truncated values not yet allowed
|
||
533 | 10664 | jaume | |
534 | static private final String regexDateExtendedForBCE =
|
||
535 | 3746 | jaume | "(" + regexDateExtendedForBCE1 + "|"
|
536 | + regexDateExtendedForBCE2 + "|"
|
||
537 | + regexDateExtendedForBCE3 + "|"
|
||
538 | + regexDateExtendedForBCE4 + ")";
|
||
539 | 10664 | jaume | |
540 | |||
541 | 3746 | jaume | static private final String regexDateForGeologicDatasets = geologicDatasets+floatingPointNumber;
|
542 | 10664 | jaume | |
543 | 3746 | jaume | static private final String periodMagnitude = "(Y|M|D)";
|
544 | static private final String p1 = "(("+digit+")+"+periodMagnitude+")";
|
||
545 | 10664 | jaume | |
546 | 3746 | jaume | static private final String timeMagnitude = "(H|M|S)";
|
547 | static private final String p2 = "("+floatingPointNumber+timeMagnitude+")";
|
||
548 | 10664 | jaume | static private final String regexPeriod = "P(("+p1+"+"+"(T"+p2+")*)|("+p1+"*"+"(T"+p2+")+))";
|
549 | |||
550 | 3746 | jaume | static private final String regexTimeDimension =
|
551 | "("+regexDateExtendedForBCE+"(,"+regexDateExtendedForBCE+")*|("+regexDateExtendedForBCE+")/("+regexDateExtendedForBCE+")/("+regexPeriod+"))";
|
||
552 | 10664 | jaume | |
553 | 3746 | jaume | private String name = "TIME";
|
554 | private String unit;
|
||
555 | private String unitSymbol;
|
||
556 | private String expression;
|
||
557 | private Object minValue;
|
||
558 | private Object maxValue;
|
||
559 | private boolean isGeologic = false;
|
||
560 | private Integer valueCount;
|
||
561 | private String period;
|
||
562 | private long step; // Distance between two points in milliseconds.
|
||
563 | private int type;
|
||
564 | 4222 | jaume | private boolean compiled = false;
|
565 | 10664 | jaume | |
566 | 3746 | jaume | /**
|
567 | * Creates a new instance of TimeDimension.
|
||
568 | * @param units
|
||
569 | * @param unitSymbol
|
||
570 | * @param expression
|
||
571 | 4222 | jaume | * /
|
572 | 3746 | jaume | public TimeDimension(String _units, String _unitSymbol, String _dimensionExpression) {
|
573 | this.unit = _units;
|
||
574 | this.unitSymbol = _unitSymbol;
|
||
575 | setExpression(_dimensionExpression);
|
||
576 | }
|
||
577 | |||
578 | public String getName() {
|
||
579 | return name;
|
||
580 | }
|
||
581 | 10664 | jaume | |
582 | 3746 | jaume | public String getUnit() {
|
583 | return unit;
|
||
584 | }
|
||
585 | |||
586 | public String getUnitSymbol() {
|
||
587 | return unitSymbol;
|
||
588 | }
|
||
589 | |||
590 | |||
591 | public String getLowLimit() {
|
||
592 | String separator = (type == INTERVAL) ? "/" : ",";
|
||
593 | return expression.split(separator)[0];
|
||
594 | }
|
||
595 | |||
596 | public String getHighLimit() {
|
||
597 | if (type == INTERVAL) {
|
||
598 | String[] s = expression.split("/");
|
||
599 | return (s.length > 1) ? s[1] : s[0];
|
||
600 | } else if (type == MULTIPLE_VALUE) {
|
||
601 | String[] s = expression.split(",");
|
||
602 | return s[s.length-1];
|
||
603 | } else {
|
||
604 | return expression;
|
||
605 | }
|
||
606 | 10664 | jaume | |
607 | 3746 | jaume | }
|
608 | |||
609 | public String getResolution() {
|
||
610 | if (type == INTERVAL) {
|
||
611 | String[] s = expression.split("/");
|
||
612 | return (s.length == 1) ? s[3] : null;
|
||
613 | } else return null;
|
||
614 | }
|
||
615 | |||
616 | public boolean isValidValue(String value) {
|
||
617 | return (value.matches(regexDateForGeologicDatasets) || value.matches(regexDateExtendedForBCE));
|
||
618 | }
|
||
619 | 10664 | jaume | |
620 | 3746 | jaume | public Object valueOf(String value) throws IllegalArgumentException {
|
621 | 4222 | jaume | if (compiled) {
|
622 | // TODO Missing geological dates
|
||
623 | String myValue = value.toUpperCase();
|
||
624 | if (isValidValue(myValue)) {
|
||
625 | Object val = null;
|
||
626 | if (!isGeologic){
|
||
627 | // This is a normal date
|
||
628 | int myYear;
|
||
629 | int myMonth;
|
||
630 | int myDay;
|
||
631 | int myHour;
|
||
632 | int myMinute;
|
||
633 | float mySecond;
|
||
634 | String[] s = myValue.split("-");
|
||
635 | myYear = (s[0].charAt(0)=='B')? -Integer.parseInt(s[0].substring(1, 5)) : Integer.parseInt(s[0].substring(0, 4));
|
||
636 | 10664 | jaume | myMonth = (s.length>1) ? Integer.parseInt(s[1])-1 : 0;
|
637 | 4222 | jaume | if (myValue.matches(regexDateExtendedForBCE4)){
|
638 | if (s[2].endsWith("Z"))
|
||
639 | s[2] = s[2].substring(0,s[2].length()-1);
|
||
640 | s = (s[2].indexOf('T')!=-1) ? s[2].split("T") : s[2].split(" ");
|
||
641 | myDay = Integer.parseInt(s[0]);
|
||
642 | 10664 | jaume | |
643 | 4222 | jaume | // Go with the time
|
644 | s = s[1].split(":");
|
||
645 | myHour = Integer.parseInt(s[0]);
|
||
646 | myMinute = (s.length>1) ? Integer.parseInt(s[1]) : 0;
|
||
647 | mySecond = (s.length>2) ? Float.parseFloat(s[2]) : 0;
|
||
648 | } else {
|
||
649 | myDay = (s.length>2) ? Integer.parseInt(s[2]) : 1;
|
||
650 | 10664 | jaume | |
651 | 4222 | jaume | myHour = 0;
|
652 | myMinute = 0;
|
||
653 | mySecond = 0;
|
||
654 | }
|
||
655 | GregorianCalendar cal = new GregorianCalendar(myYear, myMonth, myDay, myHour, myMinute, (int)mySecond);
|
||
656 | val = cal;
|
||
657 | } else{
|
||
658 | // this is a geological date >:-(
|
||
659 | }
|
||
660 | 10664 | jaume | return val;
|
661 | 4222 | jaume | } else throw new IllegalArgumentException(myValue);
|
662 | }
|
||
663 | return null;
|
||
664 | 3746 | jaume | }
|
665 | 10664 | jaume | |
666 | 3746 | jaume | public String valueAt(int pos) throws ArrayIndexOutOfBoundsException {
|
667 | 10664 | jaume | if (compiled) {
|
668 | 4222 | jaume | if (pos<0 || pos>valueCount())
|
669 | throw new ArrayIndexOutOfBoundsException(pos+"(must be >= 0 and <="+valueCount()+")");
|
||
670 | 10664 | jaume | |
671 | 4222 | jaume | if (type == SINGLE_VALUE)
|
672 | return expression;
|
||
673 | 10664 | jaume | |
674 | 4222 | jaume | if (type == MULTIPLE_VALUE)
|
675 | return expression.split(",")[pos];
|
||
676 | 10664 | jaume | |
677 | 4222 | jaume | if (!isGeologic){
|
678 | long newTime = ((GregorianCalendar) minValue).getTimeInMillis();
|
||
679 | newTime += (step*pos);
|
||
680 | GregorianCalendar cal = new GregorianCalendar();
|
||
681 | cal.setTimeInMillis(newTime);
|
||
682 | if (cal.after(maxValue))
|
||
683 | return toString((GregorianCalendar) maxValue);
|
||
684 | else if (cal.before(minValue))
|
||
685 | return toString((GregorianCalendar) minValue);
|
||
686 | else
|
||
687 | return toString(cal);
|
||
688 | }
|
||
689 | }
|
||
690 | 3746 | jaume | return null;
|
691 | }
|
||
692 | |||
693 | /**
|
||
694 | * Prints a GregorianCalendar value in WMS1.1.1 format.
|
||
695 | * @param cal
|
||
696 | * @return
|
||
697 | 4222 | jaume | * /
|
698 | 3746 | jaume | private String toString(GregorianCalendar cal) {
|
699 | int iYear = cal.get(cal.YEAR);
|
||
700 | int iMonth = cal.get(cal.MONTH) + 1;
|
||
701 | int iDay = cal.get(cal.DAY_OF_MONTH);
|
||
702 | int iHour = cal.get(cal.HOUR_OF_DAY);
|
||
703 | int iMinute = cal.get(cal.MINUTE);
|
||
704 | int iSecond = cal.get(cal.SECOND);
|
||
705 | String myYear;
|
||
706 | if (iYear<10)
|
||
707 | myYear = "200"+iYear;
|
||
708 | else if (iYear<100)
|
||
709 | myYear = "20"+iYear;
|
||
710 | else if (iYear<1000)
|
||
711 | myYear = "2"+iYear;
|
||
712 | else
|
||
713 | myYear = ""+iYear;
|
||
714 | String myMonth = (iMonth<10) ? "0"+iMonth : ""+iMonth;
|
||
715 | String myDay = (iDay<10) ? "0"+iDay : ""+iDay;
|
||
716 | String myHour = (iHour<10) ? "0"+iHour : ""+iHour;
|
||
717 | String myMinute = (iMinute<10)? "0"+iMinute : ""+iMinute;
|
||
718 | String mySecond = (iSecond<10)? "0"+iSecond : ""+iSecond;
|
||
719 | int myMilliSecond = cal.get(cal.MILLISECOND);
|
||
720 | 10664 | jaume | |
721 | |||
722 | 3746 | jaume | String s = myYear+"-"+myMonth+"-"+myDay+"T"+myHour+":"+myMinute+":"+mySecond+"."+(myMilliSecond/10)+"Z";
|
723 | if (iYear<0)
|
||
724 | s = "B"+s;
|
||
725 | return s;
|
||
726 | }
|
||
727 | |||
728 | public int valueCount() {
|
||
729 | 4222 | jaume | if (compiled) {
|
730 | if (valueCount==null){
|
||
731 | if (type == MULTIPLE_VALUE) {
|
||
732 | return expression.split(",").length;
|
||
733 | } else if (type == INTERVAL) {
|
||
734 | if (period == null) {
|
||
735 | valueCount = new Integer(0);
|
||
736 | return valueCount.intValue();
|
||
737 | }
|
||
738 | 10664 | jaume | |
739 | 4222 | jaume | if (!isGeologic){
|
740 | 10664 | jaume | |
741 | 4222 | jaume | long x1 = ((GregorianCalendar) maxValue).getTimeInMillis();
|
742 | long x0 = ((GregorianCalendar) minValue).getTimeInMillis();
|
||
743 | long distance = x1-x0;
|
||
744 | step = 0;
|
||
745 | 10664 | jaume | |
746 | 4222 | jaume | boolean isTimeField = false;
|
747 | String val = "";
|
||
748 | 10664 | jaume | |
749 | 4222 | jaume | for (int i = 0; i < period.length(); i++) {
|
750 | if (period.charAt(i) == 'P')
|
||
751 | continue;
|
||
752 | if (period.charAt(i) == 'T'){
|
||
753 | isTimeField = true;
|
||
754 | continue;
|
||
755 | }
|
||
756 | switch (period.charAt(i)){
|
||
757 | case 'Y':
|
||
758 | step += Integer.parseInt(val) * millisXyear;
|
||
759 | val = "";
|
||
760 | break;
|
||
761 | case 'M':
|
||
762 | if (isTimeField)
|
||
763 | step += Integer.parseInt(val) * millisXminute;
|
||
764 | else
|
||
765 | step += Integer.parseInt(val) * millisXmonth;
|
||
766 | val = "";
|
||
767 | break;
|
||
768 | case 'D':
|
||
769 | step += Integer.parseInt(val) * millisXday;
|
||
770 | val = "";
|
||
771 | break;
|
||
772 | case 'H':
|
||
773 | step += Integer.parseInt(val) * millisXhour;
|
||
774 | val = "";
|
||
775 | break;
|
||
776 | case 'S':
|
||
777 | step += Integer.parseInt(val) * 1000;
|
||
778 | val = "";
|
||
779 | break;
|
||
780 | default:
|
||
781 | val += period.charAt(i);
|
||
782 | break;
|
||
783 | 10664 | jaume | }
|
784 | 4222 | jaume | }
|
785 | valueCount = new Integer((int)(distance/step) + 1); // + 1 for the initial point
|
||
786 | }
|
||
787 | } else {
|
||
788 | // this is a single value expression
|
||
789 | valueCount = new Integer(1);
|
||
790 | return valueCount.intValue();
|
||
791 | }
|
||
792 | }
|
||
793 | 10664 | jaume | |
794 | 4222 | jaume | return valueCount.intValue();
|
795 | }
|
||
796 | return -1;
|
||
797 | 3746 | jaume | }
|
798 | |||
799 | 4222 | jaume | public void setExpression(String expr) {
|
800 | 3746 | jaume | expression = expr.toUpperCase();
|
801 | 4222 | jaume | }
|
802 | |||
803 | public String getExpression() {
|
||
804 | return expression;
|
||
805 | }
|
||
806 | |||
807 | public int getType() {
|
||
808 | return type;
|
||
809 | }
|
||
810 | |||
811 | public void compile() {
|
||
812 | if (expression.matches(regexTimeDimension)){
|
||
813 | 3746 | jaume | isGeologic = false;
|
814 | } else if (expression.matches(regexDateExtendedForBCE)) {
|
||
815 | isGeologic = false;
|
||
816 | } else if (expression.matches(regexDateForGeologicDatasets)){
|
||
817 | isGeologic = true;
|
||
818 | } else {
|
||
819 | throw new IllegalArgumentException();
|
||
820 | }
|
||
821 | 10664 | jaume | String separator;
|
822 | |||
823 | 3746 | jaume | if (expression.indexOf("/")!=-1) {
|
824 | separator = "/";
|
||
825 | type = INTERVAL;
|
||
826 | } else if (expression.indexOf(",")!=-1) {
|
||
827 | separator = ",";
|
||
828 | type = MULTIPLE_VALUE;
|
||
829 | } else {
|
||
830 | separator = ",";
|
||
831 | type = SINGLE_VALUE;
|
||
832 | }
|
||
833 | 10664 | jaume | |
834 | 4222 | jaume | compiled = true;
|
835 | 3746 | jaume | String[] s = expression.split(separator);
|
836 | minValue = valueOf(s[0]);
|
||
837 | if (type == INTERVAL) {
|
||
838 | maxValue = (s.length>1) ? valueOf(s[1]) : valueOf(s[0]);
|
||
839 | period = (s.length>2 && s[2].matches(regexPeriod)) ? s[2] : null;
|
||
840 | } else if (type == MULTIPLE_VALUE) {
|
||
841 | maxValue = valueOf(s[s.length-1]);
|
||
842 | } else {
|
||
843 | maxValue = valueOf(s[0]);
|
||
844 | }
|
||
845 | }
|
||
846 | 4222 | jaume | }*/
|
847 | 3746 | jaume |