Wednesday, October 13, 2010

IP-based geolocation can also be accurate

I always thought that IP-based geolocation is not as accurate as it should be until quite recently I ran into this HTML5 demo page (Geolocation API is one of the new features in HTML5), and now I have to admit that it was my prejudice.
If you open the demo page in browser and assume you don’t have a GPS device connected to your machine, then all this web app does is to detect your location based on your IP address and add a maker on Google Maps. The source code is also as simple as a “HelloWorld” sample of W3C Geolocation API:
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(success, error);
} else {
error('not supported');
}
function success(position) {
// ...non-related code omitted...
var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);  
var map = new google.maps.Map(document.getElementById("mapcanvas"), myOptions);
var marker = new google.maps.Marker({
position: latlng, 
map: map, 
title:"You are here!"
});
}
function error(msg) {
// ...non-related code omitted...
}

I tried this geolocation demo app twice at my bedroom, one using Firefox 3.6 and the other is using Google Chrome, and below are how the app locates me:

Untitled
Firefox 3.6 Successfully locate me in my land lot

Untitled
Google Chrome is a little off my land lot, but still quite accurate

The results of both are very accurate to where I actually was. That’s just amazing! And I guess the conclusion is that IP-based Geolocation can also be really accurate.

Thursday, August 19, 2010

NASA Earth Observation (NEO) Website is awesome

I recently came cross this NEO (NASA Earth Observation) website that contains a lot of recent and historical satellite image data related to environment and climate change. Unlike most other websites of the same type, NEO’s simple and pretty web GUI design and user experience really surprises me.

neo

Screenshot taken from NASA Earth Observation site at http://neo.sci.gsfc.nasa.gov/Search.html

The dataset hosted by NEO are divided into categories like ocean, atmosphere , energy, land and life. For each of those categories users can browse, search, preview, download, and analysis which is pretty much all you may want to do with satellite data. What’s really awesome is that you can do all of those things in a single ajax-based web page (you see only one screenshot is enough).

Discover data

To find the data, you can select one of the main categories, and then either search or look through the full list. Once it’s found you will be able to see the brief description and preview it in an interactive web map (support zoom in/out)

Download option

Most of the data can be downloaded as PNG, JPEG, GeoTiff, CSV, KML/KMZ directly through NEO home page, and whenever applicable you can also download data in original format e.g. HDF on the same page of data search result.

Web Map Service

NEO also provides WMS access to its data, which is a super plus for those who don't want to download and host data themselves.  NEO’s WMS links:

http://neowms.sci.gsfc.nasa.gov/wms/wms?version=1.1.1&service=WMS&request=GetCapabilities

http://neowms.sci.gsfc.nasa.gov/wms/wms?version=1.3.0&service=WMS&request=GetCapabilities

image

Consume NEO WMS in QGIS

NASA Earth Observation site is just awesome!!!

Wednesday, August 11, 2010

Mysterious “BBOX” parameter in Web Feature Service (WFS)

“BBOX” is a standard parameter defined as part of the OGC Web Feature Service (WFS) keyword-value encoding which is used in GetFeature requests to request features within a specific extent. So “…&BBOX=-180.0,-90.0,180.0,90.0…” means getting all the features in the extent defined by lower corner coordinate (-180.0,-90.0) and upper corner coordinate (180.0,90.0). Not as straight forward as it seems to be to most people, I actually find it quite confusing when trying to answer this question below:

Is it possible to set BBOX to coordinate values in other projection?

The answer to this question depends on whether it’s WFS 1.0.0 (No) or WFS 1.1.0 (Yes), and the yes for WFS 1.1.0 even introduces some more complexity. So in the rest of this article I am going to clear things up based on the definition and statement in following OGC specifications:

BBOX in WFS 1.0.0

First of all, it is a little less confusing for WFS version 1.0.0 that  you CAN’T specify BBOX in coordinates of any other projections. This is based on two facts in specs:

(1) WFS 1.0.0 spec (02-058), section 13.3.3, where it says:

The SRS of the bounding box must be the same as the SRS of the feature type(s) in a request. The SRS of a feature type is advertised by a WFS in the capabilities document. If more than one feature type is specified in a request, the feature types must all be in the same SRS and the BBOX must be specified in the common SRS as well.

(2) In WFS 1.0.0 schema, a WFS’s capabilities response can have only one “SRS” allowed for each feature type.

But I did see people get confused and thus argue about one thing: Can I specify BBOX in EPSG:4326?

Based on the statement above, if a WFS serves out feature types in a non-EPSG4326 then it should be valid and compliant to just reject GetFeature request with BBOX in EPSG:4326. But another paragraph in WFS 1.0.0 spec (same section 13.3.3) seems to indicate a yes on it:

If a request contains a Bounding Box whose area does not overlap at all with the LatLongBoundingBox(s) advertised in the Capabilities XML for the requested geodata object, the server should return empty content (e.g. null feature set) for that element. Any elements that are partly or entirely contained in the Bounding Box should be returned in the appropriate format.

In this case we don’t really have to strictly follow the spec, and I won’t be surprised to see most WFS 1.0.0 implementations supports BBOX in EPSG:4326 no matter what SRS its feature types are in.

BBOX in WFS 1.1.0

Now for WFS 1.1.0, things get a little complicated because of the fact that there is an optional “crsurl” value you can append at the end of the BBOX coordinates to explicitly specify what coordinate reference system the BBOX is in. For example, ”…&BBOX=minX,minY,maxX,maxY,EPSG:900913” means that those min/max X/Y values are in EPSG:900913. Another complexity is that in WFS 1.1.0 capabilities response there can be multiple supported SRS for each feature type.  So based the section 14.3.3 of WFS spec 1.1.0:

The KVP encoding for a bounding box is defined in subclause 10.2.3 of normative reference [15]. The general form of the parameter is:
BBOX=lcc1,lcc2,…,lccN,ucc1,ucc2,…uccN[,crsuri]…


…If the crsuri is not specified then the 2-D coordinates shall be specified using decimal degrees and WGS84 as described in [15].

Note: The WFS spec is reference OWS 1.1.0 spec as [15] here

Here I can actually get two conclusions:

(1) You can specify BBOX in any coordinate reference system as long as you provide a “crsuri”.

Although not described in spec, I would believe you will get correct and useful response only if you request one of those supported coordinate reference system listed in a WFS’s capabilities.

(2) If “crsuri” is missing, then the coordinate values specified in BBOX should be considered as WGS84 as described in OWS 1.1.0 spec as below:

A WGS 84 bounding box shall be KVP encoded in a corresponding parameter value list, with the ordered listed values for the quantities:
LowerCorner longitude, in decimal degrees
LowerCorner latitude, in decimal degrees
UpperCorner longitude, in decimal degrees
UpperCorner latitude, in decimal degrees
crs URI = “urn:ogc:def:crs:OGC:1.3:CRS84” (optional)

In another word, if you set BBOX parameter in request without a “crsurl”, the BBOX value should always be in syntax “min_lon, min_lat, max_lon, max_lat”.

EPSG:4326 versus. CRS84

Now one finally thing people get confused very often is the order of longitude and latitude they should put in BBOX parameter for EPSG:4326 and CRS:84. Originally and as always, official EPSG:4326 defines coordinates in order of “latitude” followed by “longitude”, but unfortunately almost everyone speaks and uses the reverse order as “longitude” followed by “latitude”, which is why old spec like WMS 1.1.1 and WFS 1.0.0 also uses this order for EPSG:4326. But in most recent specifications OGC is trying to fix this common mistaken usage of EPSG:4326 by creating a brand new CRS:84, which based on my understanding is identical to EPSG:4326 except that it defines coordinates in order of “longitude” followed by “latitude”. So as far as WFS goes, always follow the rules below:

For WFS 1.0.0, uses EPSG:4326 and always set BBOX coordinate values as “longitude” followed by “latitude”. For WFS 1.1.0, specify coordinates in BBOX as longitude followed by latitude if you’re setting CRS:84 as the coordinate reference system for BBOX (e.g. “…&BBOX=–180.0,-90.0,180.0,90.0,urn:ogc:def:crs:OGC:1.3:CRS84…”), or set it the other way if you’re setting EPSG:4326 for BBOX (e.g. “…&BBOX=-90.0,-180.0,90.0,180.0,urn:x-ogc:def:crs:EPSG:6.9:4326…), but again if you’re omittng “crsuri” at the end please always stick to the order where longitude is ahead of latitude.

Summary

“BBOX” parameter looks easy and straight forward to use, but it also gets complicated when different versions of WFS implementations and EPSG:4326/WGS84 are involved. Hopefully this article clear things up a little.

Monday, May 3, 2010

Expo 2010 Shanghai China

To celebrate the opening of Expo 2010 Shanghai China, I just had a peek at where the Expo Axis should locate in Shanghai in those popular online map services (Google Maps, OSM, Bing Maps, and Yahoo Maps). See what I get:

OSM Maps
osm
Not bad, but that is actually what I expected to see.

Google Maps
google
It seems like Google Maps really needs to catch up with this big event. But if you’re laughing at Google Maps now, you better check out Bing Maps and Yahoo Maps below.

Bing Maps
bing
What can I say…just like the map…

Yahoo Maps
yahoo
You know what? I zoomed out on purpose because I can’t zoom in any more…

Wednesday, April 14, 2010

Several approaches to do Geolocation by IP address programmatically

Geolocation is the process of identifying the real-world geographic location based on a device’s IP address (either connected through cable or WiFi access point), GSM/CDMA cells IDs or GPS and so on. The result of Geolocation is essentially a pair of coordinate (lat and lon) on earth, and if combined with other service like Geocoding the result can also be translated to a well-known place name or a physical address.
Geolocation by IP address although  may not be as accurate as GPS sometimes, does have great value in many of today’s location aware web applications. One of the typical examples is that when you do search on internet the search engine will detect the location by your IP address and automatically filter and sort the results based on that information. In this article I will be looking at some existing approaches I’ve played with to do Geolocation by IP address.

1. W3C Geolocation API
If you’re in JavaScript environment, W3C Geolocation API is definitely the approach you should follow because it is the standard. Independent of the actual source of the location information which is not just limited to IP address but can also be WiFi and Bluetooth MAC address, GSM/CDMA cell IDs, GPS or so, it actually defines a high-level interface to location associated information with devices hosting the implementation. Other than that W3C Geolocation API also defines the way you request repeated position updates through callback mechanism as well as requesting an aged position.
Mozilla Firefox is a very good example of such implementation. The code example below illustrate the common work flow to get your current location in a JavaScript application:
if(navigator.geolocation) {  
/* geolocation is available */  
navigator.geolocation.getCurrentPosition(
function(position) { // success callback  
//alert(position.coords.latitude + "," + position.coords.longitude);
alert(position.address.city);                
},
function() { // failure callback
// error handling
},
{
// other options
timeout:30000
}
);    
} else {      
// browser does not support Geolocation API
} 
In Firefox’s implementation, it gives you not only the lat and lon value of your location but also gives you the address associated with that coordinate. Under the hood Firefox takes advantage of Google Location Service which I believe provided all necessary infrastructure.

2. Google Geolocation API in Gears
Google Gears Geolocation API implements the same W3c Geolocation interfaces but in a more device independent, location information source independent, and browser independent way. It’s online doc is very good and concise for people who would like to use it in their web application.

3. Google Location Service
Google Location Service is far more than an interfaces that only provides geolocation function, but it’s totally valid to use that as a Geolocation service. Without even realizing, it is definitely behind a lot of its own services like Search and Latitude which sometimes you don’t even realize. It is also backing up many other Geolocation API implementations like Firefox. But unfortunately I was able to find out an explicit native Geolocation service API either in JavaScript or in REST flavor. So it’s probably impossible to directly communication with Google Location Service by feeding an IP address and getting a coordinate back.


4. Other Geolocation by IP service providers
HostIP, mentioned in Andrew Turner’s Introduction to Neogeography, HostIP has the largest free database and service for GeoIP (the term Andrew uses for geolocation by IP address). The best part of HostIP is that it freely distribute it’s database with regular updates. Since I don’t find any information regarding license and term of use for HostIP database I would assume it is in public domain. Programmatically HostIP provides a simple and lightweight RESTful API for people to locate themselves by IP. It doesn’t do much but it’s good at what it does. Because of its REST nature, it is not limited to any program environment except that you might need a proxy in your JavaScript web application. Below is an example:
http://api.hostip.info/country.php
US
http://api.hostip.info/get_html.php?ip=12.215.42.19
Country: UNITED STATES (US)
City: Sugar Grove, IL
IP: 12.215.42.19
http://api.hostip.info/get_html.php?ip=12.215.42.19&position=true
Country: UNITED STATES (US)
City: Sugar Grove, IL
Latitude: 41.7696
Longitude: -88.4588
IP: 12.215.42.19
http://api.hostip.info/?ip=12.215.42.19
[use the URL above for an example - XML too long to paste below]
Finally its website itself provides a simple browser based GUI for people who don’t want to bother coding their own app.

GeoIP: MaxMind has a free GeoIP database and API called GeoLite City. MaxMind and it GeoIP product suite does not have a publicly hosted RESTful service or API for directly use but it allows you to download their database in either binary format or CSV format (mainly for you to load data into SQL database) to host locally. The API for accessing the local database is available in many platforms (C, C#, MS COM, PHP, Java, Perl Python, Ruby etc.). The workflow is to first download the database to local disk and uncompress it, then use any of the API available the query the database. Below is an example I tried out using its Java API:
public void lookupServiceTest() {  
try {
LookupService cl = new LookupService("E:\\projects\\geoip\\geolite-city-database\\GeoLiteCity.dat", LookupService.GEOIP_MEMORY_CACHE);
Location location = cl.getLocation("xxx.xxx.xxx.xxx");
System.out.println(" countryCode: " + location.countryCode +
"\n countryName: " + location.countryName +
"\n region: " + location.region +
"\n regionName: " + regionName.regionNameByCode(location.countryCode, location.region) +
"\n city: " + location.city +
"\n postalCode: " + location.postalCode +
"\n latitude: " + location.latitude +
"\n longitude: " + location.longitude +
"\n metro code: " + location.metro_code +
"\n area code: " + location.area_code +
"\n timezone: " + timeZone.timeZoneByCountryAndRegion(location.countryCode, location.region));
cl.close();
} catch (IOException e) {
System.out.println("IO Exception");
}  
}
GeoIP API is free and open source under GPL/LGPL license, and the GeoLite City database itself is also free under its database license. MaxMind also provides an advanced commercial version called GeoIP City, which is functionally equal to the free one but has more accuracy, different redistribution license and more frequent updates.

Sunday, March 28, 2010

Using Java Map Projection Library in Android

Recently I was writing a little mapping application on Android, which basically takes Open Street Map tiles as base layer, and also has the ability to overlay some vector features on top. In this application there is need to convert coordinates back and forth between WGS84 and Mercator projection, so I need certain library to do the job. Given the popularity of PROJ.4, the pure Java port of it, Java Map Projection Library (name it “PROJ4Java” below), seems to be an obvious choice so I spend some hours during the weekend to see how it works.
First you can get the latest version (1.0.6) of PROJ4Java from this link. The size of the library is very small and lightweight with only 1.17MB for complete source code and 247KB for compiled jar file. If you’re familiar with PROJ.4 itself then this Java version should be quite straight forward, otherwise there isn’t much sample code or tutorial on its homepage. But the basic workflow is quite simple as described by its project homepage:
To use the library, you need to create a projection, either directly by calling its constructor or via one of the factory methods. Once you have a Projection, you can use it to convert between latitude/longitude and projected map units (by default, metres) by calling the transform and transformInverse methods.
A major issue of using PROJ4Java on Android is that out of box it can’t be run due to the fact that it is using some classes in java.awt.geom.* which is not available in Android SDK. But since the only major AWT class used by PROJ4Java is Point2D, it’s not difficult at all to work around by replacing it with your own 2D point class. In my case I use the point class from JTS library. After taking out all Java awt class references from source code and replacing them with JTS class, everything works pretty well on Android.
Another thing I modifies on the source code is to get rid of those projection implementations I don’t need. Mercator projection is the only one I need so far so that I am able to reduce the size of compile jar file to only 168KB. Here is a list of classes I extracted from original source code to support only Mercator projection:
    • com.jhlabs.map.*
    • com.jhlabs.map.proj.CylindricalProjection
    • com.jhlabs.map.proj.Ellipsoid
    • com.jhlabs.map.proj.MercatorProjection
    • com.jhlabs.map.proj.Projection
    • com.jhlabs.map.proj.ProjectionFactory
    • com.jhlabs.map.proj.ProjectionException
    • com.jhlabs.map.proj.Ellipsoid
ProjectionFactory class is the entry point for you to use PROJ4Java, which provides several static methods to create an instance of Projection. Below are some use cases I figured out by briefly reading through the source code:
(1) ProjectionFactory.getNamedPROJ4CoordinateSystem(String name) takes a well-known projection code like “epsg:4326” and returns a Projection:
1: String name = "epsg:3785";    
2: Projection proj = ProjectionFactory.getNamedPROJ4CoordinateSystem(name);
(2) ProjectionFactory.fromPROJ4Specification(String[] params) takes a set of PROJ.4 parameters and returns a Projection:
1: String[] params = {
2:     "proj=tmerc",
3:     "lat_0=37.5",
4:     "lon_0=-85.66666666666667",
5:     "k=0.999966667",
6:     "x_0=99999.99989839978",
7:     "y_0=249999.9998983998",
8:     "ellps=GRS80",
9:     "datum=NAD83",
10:     "to_meter=0.3048006096012192",
11:     "no_defs"
12: };
13: ProjectionFactory.fromPROJ4Specification(params);
(3) Create an instance of customized projection. All supported projection definition strings (PROJ.4 parameters for ) for PROJ4Java are listed in text files under folder “nad” that is coming with the library. These files are “epsg”, “esri”, “nad27”, “nad83”, and “world”, and a sample projection definition string is like below:
1: <2965> +proj=tmerc +lat_0=37.5 +lon_0=-85.66666666666667 +k=0.999966667 +x_0=99999.99989839978 +y_0=249999.9998983998 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs
2: 
To add a new customized projection just create a definition string (usually modify certain parameter values upon an existing projection) and add it into one of those files, or create a new text file under “nad” folder with the definition string, then use the code below:
1: Projection proj = null;
2: try {
3:     // assume that "epsg:900913" projection defintion 
4:     //   has been added in text file "others"
5:     proj = ProjectionFactory.readProjectionFile("others", "900913");    
6: } catch(IOException e) {
7:     e.printStackTrace();
8: }
(4) Transform between lat/lon and projection units with a Projection instance:
1: Projection epsg3785 = ProjectionFactory.getNamedPROJ4CoordinateSystem("epsg:3785");    
2:               
3: System.out.println("transform from latlon to epsg:3785");
4: System.out.println("latlon: -117.5931084, 34.1063989");
5: Point pEpsg3785 = epsg3785.transform(-117.5931084, 34.1063989);
6: System.out.println("epsg:3785: " + pEpsg3785.getX() + ", " + pEpsg3785.getY());
7:     
8: System.out.println("transform from epsg:3785 to latlon");
9: System.out.println("epsg:3785: " + pEpsg3785.getX() + ", " + pEpsg3785.getY());
10: Point latlon = epsg3785.inverseTransform(pEpsg3785);
11: System.out.println("latlon: " + latlon.getX() + ", " + latlon.getY());
12: 
13:   

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.