Tuesday, March 9, 2010

WMS TIME support in MapServer

In context of my last blog, I’m looking at the WMS TIME support in OSGeo MapServer. MapServer has a very concise and helpful doc on the scope and usage of WMS TIME in its implementation. It is always a good idea to briefly go through the documentation first before you spend a lot time trying something that may not be even  implemented.
My test data (in shapefile format) is very simple such that the picture below tells all about it:
0
Each point feature has a field “TIME” which has time value in syntax “YYYY-MM-DD HH:MM:SS”. This is valid ISO8601 time format and supported by MapServer. All points have a time stamp within day 2010-02-16. To enable time on WMS in MapServer is straight forward given that you have some experience of publishing shapefile as a WMS in MapServer. The only extra thing you need to configure is to specify time field, time extent and default time value in map file. That’s it and MapServer documentation is very clear on how to do that.
1: LAYER
2:     NAME 'points'
3:     METADATA
4:         "ows_title" "points"
5:         "gml_include_items"   "all"
6:         "wms_include_items"   "all"
7:         # TIME Related
8:         "wms_title"          "points"
9:         "wms_timeextent"     "2010-02-16T00:00:00Z/2010-02-17T00:00:00Z/P1H"
10:         "wms_timeitem"       "TIME"
11:         "wms_timedefault"     "2010-02-16T12:00:00Z,2010-02-16T18:00:00Z"
12:     END
13: ...
14: END
After saving the map file and refresh the GetCapabilities response you will notice information of time dimension is advertised.
1: <Dimension name="time" units="ISO8601" default="2010-02-16T12:00:00Z,2010-02-16T18:00:00Z" nearestValue="0">
2:     2010-02-16T00:00:00Z/2010-02-17T00:00:00Z/P1H
3: </Dimension>
4: 
The time extent value in <Dimension> is whatever you specified in “wms_timeextent” so it doesn’t smart enough to calculate the extent from data, and neither does MapServer do any validation on the time extent string. A trick I did in map file is to provide a special default time value “2010-02-16T12:00:00Z, 2010-02-16T18:00:00Z,”, which will inform me if certain time string is not correctly processed by MapServer thus default value is used.

Now it’s time to try a few requests against this time enabled WMS. According to doc the supported request syntax for time are “t”, “t1,t2…”, “t1/t2”, and “t1/t2,t3/t4…” and “t” should be any valid ISO8601 time string. I skipped the time zone because there is nowhere indicating it is implemented (you can not configure time zone at server-side thus it makes no sense to request from client). Below is a list of different time values that have been tried:
  • …&TIME=2010 (not recognized, default time value is returned)
  • …&TIME=2010-02 (not recognized, default time value is returned)
  • …&TIME=2010-02-16 (recognized, only point of “2010-02-16 00:00:00” returned)
  • …&TIME=2010-02-16T12 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T12:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T12:00:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T14:59:59 (recognized, only point of “2010-02-16 14:59:59” returned)
  • …&TIME=2010-02-16T15:00:01 (recognized, only point of “2010-02-16 15:00:01” returned)
  • …&TIME=2010-02-16T15:00:00.000 (recognized, only point of “2010-02-16 15:00:00” returned)
  • …&TIME=2009/2010 (not recognized, default time value is returned)
  • …&TIME=2010/2011 (not recognized, default time value is returned)
  • …&TIME=2010-02/2010-03 (not recognized, default time value is returned)
  • …&TIME=2010-02-16/2010-02-17 (recognized, all points returned)
The test results above actually proves the time interpretation described in the documentation:
When MapServer receives a request with a TIME parameter, it transforms the time requests into valid expressions that are assigned to the filter parameter on layers that are time-aware.
Here are some examples of how different types of requests are treated (wms_timeitem is defined here as being “time_field”):
single value (2004-10-12) transforms to (`[time_field]` eq `2004-10-12`)
multiple values (2004-10-12, 2004-10-13) transform to (`[time_field]` eq `2004-10-12` OR `[time_field]` eq `2004-10-13`)
single range : 2004-10-12/2004-10-13 transforms to ((`[time_field]` ge `2004-10-12`) AND (`[time_field]` le `2004-10-13`))
multiple ranges (2004-10-12/2004-10-13, 2004-10-15/2004-10-16) transform to ((`[time_field]` ge `2004-10-12` AND `[time_field]` le `2004-10-13`) OR (`[time_field]` ge `2004-10-15` AND `[time_field]` le `2004-10-16`))
As shown in the above examples, all fields and values are written inside back tics (`) - this is the general way of specifying time expressions inside MapServer.
Overall it’s doing a good job and the only thing missing point is that requests like “TIME=2010” and “TIME=2010/2011” should be properly handled.

If you read carefully, MapServer documentation has another slightly different time interpretation for PostGIS layer, to test that I followed up and applied the same requests on a PostGIS layer.

Things I need to configure before I can finally send the same set of WMS time requests to PostGIS layer are listed below:
  • Use “shp2pgsql” command line to convert shape file into pgsql file;
  • Use “psql” command line to load pgsql file into PostGIS database;
  • Add another new column of type “timestamp without time zone”;
  • Copy time value from original column to the new column;
  • Modify map file to use new column of of type “timestamp without time zone” for WMS time;
  • Modify the map file to point to PostGIS table instead of shape file;
All of above are straight forward except that a WMS layer based on PostGIS doesn’t seem to recognize the temporal field if it is of type “character varying”, which force me to add an extra column in type “timestamp without time zone” and copy over those original time values in string. After all of those it worked and below are the results of same WMS TIME requests on shapefile layer:
  • …&TIME=2010 (not recognized, default time value is returned)
  • …&TIME=2010-02 (not recognized, default time value is returned)
  • …&TIME=2010-02-16 (recognized, all points with time stamp within 2010-02-16 returned)
  • …&TIME=2010-02-16T12 (recognized, points between 12:00:00 and 13:00:00 on 2010-02-16 returned, not including points on 13:00:00)
  • …&TIME=2010-02-16T12:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T12:00:00 (recognized, only point of “2010-02-16 12:00:00” returned)
  • …&TIME=2010-02-16T14:59:59 (recognized, only point of “2010-02-16 14:59:59” returned)
  • …&TIME=2010-02-16T15:00:01 (recognized, only point of “2010-02-16 15:00:01” returned)
  • …&TIME=2010-02-16T15:00:00.000 (recognized, only point of “2010-02-16 15:00:00” returned)
  • …&TIME=2009/2010 (not recognized, default time value is returned)
  • …&TIME=2010/2011 (not recognized, default time value is returned)
  • …&TIME=2010-02/2010-03 (not recognized, default time value is returned)
  • …&TIME=2010-02-16/2010-02-17 (recognized, all points returned)
So it proves what it says in the doc about PostGIS layer:
For PostGIS/PostgreSQL layers, the time expression built uses the date_trunc function available in PostgreSQL. For example, if the user passes a time value of ‘2004-10-12’, the expression set in the filter is date_trunc(‘day’, time_field) = ‘2004-10-12’. The use of the date_trunc function allows requests to use the concept of time resolution. In the example above, for a request of ‘2004-10-12’, MapServer determines that the resolution is “day” by parsing the time string and the result gives all records matching the date 2004-10-12 regardless of the values set for Hours/Minutes/Seconds in the database. For more information on the date_trunc function, please refer to the PostgreSQL documentation.