How to deal with complex dimension dependencies - a proposal
The problem
Assume a layer, wind, with three modelruns. Each modelrun contains 48
forecasts at 1 hour intervals. In this situation, the available combinations of TIME and MODELRUN_TIME will be these:
|
TIME |
2009-11-26 |
2009-11-27 |
2009-11-28 |
MODELRUN_TIME |
00-12 |
12-00 |
00-12 |
12-00 |
00-12 |
12-00 |
2009-11-26T00:00 |
* |
* |
* |
* |
2009-11-26T12:00 |
* |
* |
* |
* |
2009-11-27T00:00 |
* |
* |
* |
* |
In this case, the normal capabilities document would contain these
values for the dimensions modelruntime and time
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0" default="2009-11-27T00:00">
2009-11-26T00:00/2009-11-27T00:00/PT12H
</Dimension>
<Dimension name="TIME" units="ISO8601" nearestValue="0" default="2009-11-27T12:00">
2009-11-26T00:00/2009-11-29T00:00/PT1H
</Dimension>
This would make it seem like it is possible to select a combination
like MODELRUN_TIME=2009-11-27T00:00&TIME=2009-11-26T00:00, and the
goal is to find a way to express which combinations that are
(im)possible, so that a sufficiently advanced client can select any
combination of dimensions that will return an image.
Capabilities document unsuitable?
I've been thinking of different ways of expressing these combinations
as effectively as possible within the capabilities document, and I've
come to the conclusion that it's not possible. If a layer offers many
dimensions with many dependencies in other dimensions, you can't
present a complete set of combinations without creating a huge
document. Conversely, if you want to express the combinations
compactly, you must hide some of the possible combinations from the
client.
GetDimensions
I propose that we instead define a new WMS operation, called
GetDimensions. The purpose of this operation is to return only those
dimension values that are still available when the user has selected
values from the other dimensions.
Parameters should be LAYERS (list of comma separated layers), TIME
(currently selected TIME), ELEVATION (currently selected elevation)
and DIM_* (currently selected value of any sample dimension), plus the
standard WMS parameters, SERVICE, VERSION and REQUEST, of course.
Examples
http://host/wmsserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetDimensions&LAYERS=wind
This will return a list of dimensions almost identical to the one in
the GetCapabilities response.
<DimensionList>
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0" default="2009-11-27T00:00">
2009-11-26T00:00/2009-11-27T00:00/PT12H
</Dimension>
<Dimension name="TIME" units="ISO8601" nearestValue="0" default="2009-11-27T12:00">
2009-11-26T00:00/2009-11-29T00:00/PT1H
</Dimension>
</DimensionList>
The only difference is the parent element DimensionList
When the user selects a dimension value in the client, the client
must issue another GetDimensions request, to get the updated list of
available dimensions.
http://host/wmsserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetDimensions&LAYERS=wind&DIM_MODELRUN_TIME=2009-11-26T00:00
Now the TIME dimension has been truncated to only include the forecast
times available for MODELRUN_TIME=2009-11-26T00:00. Note that
MODELRUN_TIME still includes all available modelruns, and the
attribute userValue, which repeats the selected value.
<DimensionList>
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0"
default="2009-11-27T00:00" userValue="2009-11-26T00:00">
2009-11-26T00:00/2009-11-27T00:00/PT12H
</Dimension>
<Dimension name="TIME" units="ISO8601" nearestValue="0" default="2009-11-27T12:00">
2009-11-26T00:00/2009-11-28T00:00/PT1H
</Dimension>
</DimensionList>
When the user selects a TIME value as well, the client must issue
yet another GetDimensions request.
http://host/wmsserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetDimensions&LAYERS=wind&DIM_MODELRUN_TIME=2009-11-26T00:00&TIME=2009-11-26T15:00
The selected TIME is only available in two of the MODELRUN_TIMEs, so
this time the reply will look like this:
<DimensionList>
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0"
default="2009-11-27T00:00" userValue="2009-11-26T00:00">
2009-11-26T00:00/2009-11-26T12:00/PT12H
</Dimension>
<Dimension name="TIME" units="ISO8601" nearestValue="0"
default="2009-11-27T12:00" userValue="2009-11-26T15:00>
2009-11-26T00:00/2009-11-28T00:00/PT1H
</Dimension>
</DimensionList>
and so on. Every time the user changes the value of any dimension, or
selects a new layer, a new GetDimension request is issued by the
client. The returned XML-document will contain all values that the
client shall present to the user, and assuming the user only changes
one dimension value at a time, it will be impossible for the user to
select an impossible combination.
Mutually exclusive dimensions
This solution could also be extended to work in situations where a
layer offers multiple dimensions that are mutually exlusive. One
example is the use of hour offsets instead the forecast time. The
layer allows you to use both the TIME dimension and the OFFSET_TIME
dimension, but using both in the same request will not make sense.
This can be solved by using a new element to group dimensions. I've
called it DimensionGroup, but suggestions are welcome.
Examples
http://host/wmsserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetDimensions&LAYERS=wind
<DimensionList>
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0" default="2009-11-27T00:00">
2009-11-26T00:00/2009-11-27T00:00/PT12H
</Dimension>
<DimensionGroup default="TIME">
<Dimension name="TIME" units="ISO8601" nearestValue="0" default="2009-11-27T12:00">
2009-11-26T00:00/2009-11-29T00:00/PT1H
</Dimension>
<Dimension name="OFFSET_TIME" nearestValue="0" default="0">
0/48/1
</Dimension>
</DimensionGroup>
</DimensionList>
In this case, the client should present the user with an option to
choose between using TIME or OFFSET_TIME, and when the user has made
the selection, it'll be possible to select a value for that dimension.
When the user selects a value, another GetDimension request is issued,
just like in the other situations.
http://host/wmsserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetDimensions&LAYERS=wind&DIM_MODELRUN_TIME=2009-11-26T00:00&OFFSET_TIME=15
<DimensionList>
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0"
default="2009-11-27T00:00" userValue="2009-11-26T00:00">
2009-11-26T00:00/2009-11-27T00:00/PT12H
</Dimension>
<DimensionGroup default="TIME" userValue="OFFSET_TIME">
<Dimension name="TIME" units="ISO8601" nearestValue="0" default="2009-11-27T12:00">
2009-11-26T00:00/2009-11-28T00:00/PT1H
</Dimension>
<Dimension name="OFFSET_TIME" nearestValue="0" default="0" userValue="15">
0/48/1
</Dimension>
</DimensionGroup>
</DimensionList>
Note that the TIME dimension now contains all values available for the
selected MODELRUN_TIME, and completely ignores the OFFSET_TIME
dimension. When generating the list of available values the server
shall ignore any dimensions from the same dimension group. This way,
the client can allow the user to switch back from OFFSET_TIME to TIME,
and make a new TIME selection, based on the TIMEs available from the
currently selected MODELRUN_TIME.
Vertical dimensions
So far, I've only used temporal dimensions as examples, but this
mechanism should be powerful enough to allow for vertical dimensions
as well
Example
http://host/wmsserver?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetDimensions&LAYERS=wind
<DimensionList>
<Dimension name="MODELRUN_TIME" units="ISO8601" nearestValue="0" default="2009-11-27T00:00">
2009-11-26T00:00/2009-11-27T00:00/PT12H
</Dimension>
<DimensionGroup default="TIME">
<Dimension name="TIME" units="ISO8601" nearestValue="0" default="2009-11-27T12:00">
2009-11-26T00:00/2009-11-29T00:00/PT1H
</Dimension>
<Dimension name="OFFSET_TIME" nearestValue="0" default="0">
0/48/1
</Dimension>
</DimensionGroup>
<DimensionGroup default="PRESSURE">
<Dimension name="PRESSURE" units="hektoPascal" unitSymbol="hPa"
nearestValue="0" default="1000">
1000,925,850,700,500,400,300,250,200,150,100,70,50,30,10
</Dimension>
<Dimension name="ISENTROPIC_LEVEL" units="Kelvin" unitSymbol="K"
nearestValue="0" default="270">
270/370/5
</Dimension>
</DimensionGroup>
</DimensionList>
This DimensionGroup will work exactly as the TIME/OFFSET_TIME group.
Conclusion
I think this way of dealing with dealing with dimensions is flexible
enough for all our needs. It necessarily adds some complexity to the
client, but I think this is unavoidable anyway.
I also suggest that the capabilities document must contain all
possible values for all possible dimensions for each of the
layers. This will allow users of less sophisticated clients to still
access all available data. It will be possible for those users to
create request that asks for impossible combinations of dimensions,
but it's still better than not being able to access the data at all.
There's one thing I haven't made an effort to address, and that's the
situation when there's no complexity, and the use of GetDimensions is
unnecessary. This can vary from layer to layer, and I think it would
be a good idea to limit the use of GetDimensions to only those layers
that needs it.
I also think it could be an idea to add more attributes to the
elements. Maybe there should be a "name"-attribute to the
DimensionGroup element? Perhaps an attribute to Dimension that
indicates if the values are ascending or descending? An attribute that
declares if the unitSymbol is postfix or prefix could also be useful.
--
TrondMichelsen - 27 Nov 2009
Example URLs
I have implemented a very simple example of how this could work. This example will only demonstrate the request and response. It doesn't actually filter out impossible combinations yet.
http://wms1.oslo.dnmi.no/interrisk/diana.map?Service=WMS&Request=GetDimensions&layers=oildrift_interrisk_82_2
To make it simple to implement in a client, I've also added the option of returning a JSON data structure instead of XML
http://wms1.oslo.dnmi.no/interrisk/diana.map?Service=WMS&Request=GetDimensions&layers=oildrift_interrisk_82_2&format=application/json
A client that uses the JSON-version is available here:
http://wms1.oslo.dnmi.no/interrisk/
This client will only request dimension values when a layer is switched on or off, and my suggestion will require a new
GetDimension request every time the user changed the value of any dimension as well, but it shouldn't be too difficult to apply this change.
--
TrondMichelsen - 18 Jan 2010