The internal architecture of the 52°North SOS server is generally well structured. Based on the user request, the respective Listener will get the data from the data backend and send it to the corresponding Encoder. The response document, very likely a collection of observations, is then returned to the user in the requested encoding, also referred to as the response format. The typical application flow of such a request is depicted in the comic strip below. Available response formats can be queried from SOS server and are listed in the capabilities document. To date the two integrated encoders return XML documents based on the OGC© O&M schema and the OGC© SOS versions.
Saturday, 30 June 2012
Dynamic Output Formats for the Sensor Observation Service
As one of the 52°North Google Summer of Code projects, “Exchangeable Encodings for the 52°North Sensor Observation Service (SOS)” aims to implement a customisable result set encoding mechanism. The users would then be able to add their own compiled libraries (like plugins), which contain all the necessary code to provide the requested data in different formats, such as CSV or GeoJSON, to the SOS. When that mechanism works, we can easily add more encodings, e.g. the planned WaterML2.0 – but for now, where to start?
Wednesday, 30 May 2012
The Devil is in the Details
As one of the 52°North Google Summer of Code projects, “Exchangeable Encodings for SOS” aims to implement a customisable result set encoding mechanism. If that mechanism works, users could add an own pre-compiled .jar-file to the SOS that contains all the necessary code to encode the requested data in simpler formats like CSV or GeoJSON – but for now, where to start?
The architecture of the SOS server is generally well structured. Based on the request – GetCapabilities, GetObservation – and given parameters, like Offering, FeatureOfInterest and/or time the RequestOperator calls the respective Listener. The Listener calls the DAO, which fills an internal response object and gives it back to the Listener. The response object, most likely a SosObservationCollection is then sent to the respective encoding module actually based on the SOS version from the request. Then the Listener decides, which Encoder will be used - to date the decision is mainly based on the requested SOS protocol version. The Encoder now creates an XML object based on the OGC O&M schema and gives it back to the Listener. Finally the Listener puts the textual representation of that XML object into the response document that will be returned via http. This is good, because other encodings are likely to be plain/text, too.
There are different entry points to dynamacally add encodings, either as additional full-fledged SOS server modules or as another responseFormat internally be evaluated and the respective encoder selected through the SosConfigurator instance. But following constraints are to be regarded:
- The SOS protocol versions 1.0 and 2.0 must be honoured
- responseFormat O&M 2.0 ships as standard
- Listener and Encoder are coupled through the SOS protocol version, the response encoding needs to be decoupled and dynamically selectable
- Other formats / encoders need to be “dynamically” registered in the SoSConfigurator and to be identified and allocated based on the requested ResponseFormat value from the request
![]() |
| Simple sequence diagram of a GetObservation request to the SOS server |
Finally one of the challenges I suppose, is the generation of a non-XML-response in this XML dominated workflow. As the SOS internal response object is merely of the String datatype, a simple encoder would need to implement the ISosResponse for the return value and should accept the SOS internal data collection representation as delivered from the DAO backend. Here in between we will put the lever to enhance the versatility of the 52 North SOS server.
Saturday, 28 April 2012
Exposing geographic information with Geoserver and OpenLayers
Exposing geographic information
Visualising geographic data especially on a map has been scientific subject for ages :-) But one of the basics is actually still colouring and labeling features as means of symbolisation to recognise and distinguish geographic features and to compare specific attribute values.
The OpenGIS® Styled Layer Descriptor (SLD) Profile of the OpenGIS® Web Map Service (WMS) Encoding Standard [http://www.opengeospatial.org/standards/wms] defines an encoding that extends the WMS standard to allow user-defined symbolization and coloring of geographic feature[http://www.opengeospatial.org/ogc/glossary/f] and coverage[http://www.opengeospatial.org/ogc/glossary/c] data. SLD addresses the need for users and software to be able to control the visual portrayal of the geospatial data. The ability to define styling rules requires a styling language that the client and server can both understand. The OpenGIS® Symbology Encoding Standard (SE) [http://www.opengeospatial.org/standards/symbol] provides this language, while the SLD profile of WMS enables application of SE to WMS layers using extensions of WMS operations. Additionally, SLD defines an operation for standardized access to legend symbols. (http://www.opengeospatial.org/standards/sld)
I would like to show three nice examples I used in the SMART web mapping application. The first is a a point style - a neat triangle with a label, the 2nd is colouring of contour lines and labeling and the use of Geoserver's FeatureInfo template system.
The first example is a point feature, a State of the Environment monitoring well in New Zealand. Besides other data fields It has an ID, which is actually an officially assigned number.
<?xml version="1.0" encoding="UTF-8"?>The main things I do here, are essentially defining the text representation and then the point symbolisation by giving it the shape of small orange triangle with no surrounding line. For the text symbolisation I define, what data field shall the label represent, the font, the label placement in relation to the actual point and I use the halo-tag to give the font I nice colourful background "glow" :-)
<StyledLayerDescriptor version="1.0.0"
xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns="http://www.opengis.net/sld"
xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NamedLayer>
<Name>soe_gwl_monitoring_wells</Name>
<UserStyle>
<Name>soe_gwl_monitoring_wells:withLabel</Name>
<Title>withLabel</Title>
<FeatureTypeStyle>
<Name>triangleWithLabel</Name>
<Rule>
<Name>triangleWithLabel</Name>
<MinScaleDenominator>0</MinScaleDenominator>
<MaxScaleDenominator>9999999</MaxScaleDenominator>
<TextSymbolizer>
<Label>
<ogc:PropertyName>ID</ogc:PropertyName>
</Label>
<Font>
<CssParameter name="font-family">Sans-Serif</CssParameter>
<CssParameter name="font-style">italic</CssParameter>
<CssParameter name="font-size">10</CssParameter>
<CssParameter name="font-color">#000000</CssParameter>
</Font>
<LabelPlacement>
<PointPlacement>
<AnchorPoint>
<AnchorPointX>
<ogc:Literal>0.0</ogc:Literal>
</AnchorPointX>
<AnchorPointY>
<ogc:Literal>0.0</ogc:Literal>
</AnchorPointY>
</AnchorPoint>
<Displacement>
<DisplacementX>
<ogc:Literal>2.0</ogc:Literal>
</DisplacementX>
<DisplacementY>
<ogc:Literal>2.0</ogc:Literal>
</DisplacementY>
</Displacement>
<Rotation>
<ogc:Literal>0.0</ogc:Literal>
</Rotation>
</PointPlacement>
</LabelPlacement>
<Halo>
<Fill>
<CssParameter name="fill">#FF4000</CssParameter>
<CssParameter name="fill-opacity">0.3</CssParameter>
</Fill>
</Halo>
<Fill>
<CssParameter name="fill">#000000</CssParameter>
</Fill>
</TextSymbolizer>
<PointSymbolizer>
<Graphic>
<Mark>
<WellKnownName>triangle</WellKnownName>
<Fill>
<CssParameter name="fill">
<ogc:Literal>#FF4000</ogc:Literal>
</CssParameter>
</Fill>
</Mark>
<Opacity>
<ogc:Literal>1.0</ogc:Literal>
</Opacity>
<Size>
<ogc:Literal>10</ogc:Literal>
</Size>
</Graphic>
</PointSymbolizer>
</Rule>
</FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
The next is a contour line clouring description. The contour lines represent mean annual rainfall and regarding the data sets I used a Jenks classification with 5 classes, which I hard-coded in the SLD document.
<?xml version="1.0" encoding="ISO-8859-1"?>
<StyledLayerDescriptor version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- a Named Layer is the basic building block of an SLD document -->
<NamedLayer>
<Name>horowhenua_mean_rainfall_contours</Name>
<UserStyle>
<Name>horowhenua_mean_rainfall_contours:withContourLabel</Name>
<Title>withCountourLabels</Title>
<Abstract>withCountourLabels</Abstract>
<FeatureTypeStyle>
<Name>withCountourLabels</Name>
<Title>withCountourLabels</Title>
<!-- 5 breaks natural jenks, 900-1200, 1201-1700, 1701-2200, 2201-2700, 2701-3200 -->
<Rule>
<Name>900-1200</Name>
<Title>900-1200</Title>
<Abstract>900-1200</Abstract>
<ogc:Filter>
<ogc:PropertyIsLessThanOrEqualTo>
<ogc:PropertyName>CONTOUR</ogc:PropertyName>
<ogc:Literal>1200</ogc:Literal>
</ogc:PropertyIsLessThanOrEqualTo>
</ogc:Filter>
<MinScaleDenominator>0</MinScaleDenominator>
<MaxScaleDenominator>9999999</MaxScaleDenominator>
<TextSymbolizer>
<Label>
<ogc:PropertyName>CONTOUR</ogc:PropertyName>
</Label>
<LabelPlacement>
<LinePlacement />
</LabelPlacement>
<Halo>
<Fill>
<CssParameter name="fill">#CEF6F5</CssParameter>
<CssParameter name="fill-opacity">0.6</CssParameter>
</Fill>
</Halo>
<Fill>
<CssParameter name="fill">#0B0B3B</CssParameter>
</Fill>
<VendorOption name="followLine">true</VendorOption>
</TextSymbolizer>
<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">#2ECCFA</CssParameter>
<CssParameter name="stroke-opacity">0.7</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>3</ogc:Literal>
</CssParameter>
</Stroke>
</LineSymbolizer>
</Rule>
<!-- 5 breaks natural jenks, 900-1200, 1201-1700, 1701-2200, 2201-2700, 2701-3200 -->
<Rule>
<Name>1201-1700</Name>
<Title>1201-1700</Title>
<Abstract>1201-1700</Abstract>
<ogc:Filter>
<ogc:And>
<ogc:PropertyIsGreaterThan>
<ogc:PropertyName>CONTOUR</ogc:PropertyName>
<ogc:Literal>1201</ogc:Literal>
</ogc:PropertyIsGreaterThan>
<ogc:PropertyIsLessThanOrEqualTo>
<ogc:PropertyName>CONTOUR</ogc:PropertyName>
<ogc:Literal>1700</ogc:Literal>
</ogc:PropertyIsLessThanOrEqualTo>
</ogc:And>
</ogc:Filter>
<MinScaleDenominator>0</MinScaleDenominator>
<MaxScaleDenominator>9999999</MaxScaleDenominator>
<TextSymbolizer>
<Label>
<ogc:PropertyName>CONTOUR</ogc:PropertyName>
</Label>
<LabelPlacement>
<LinePlacement />
</LabelPlacement>
<Halo>
<Fill>
<CssParameter name="fill">#CEF6F5</CssParameter>
<CssParameter name="fill-opacity">0.6</CssParameter>
</Fill>
</Halo>
<Fill>
<CssParameter name="fill">#0B0B3B</CssParameter>
</Fill>
<VendorOption name="followLine">true</VendorOption>
</TextSymbolizer>
<LineSymbolizer>
<Stroke>
<CssParameter name="stroke">#2E9AFE</CssParameter>
<CssParameter name="stroke-opacity">0.7</CssParameter>
<CssParameter name="stroke-width">
<ogc:Literal>3</ogc:Literal>
</CssParameter>
</Stroke>
</LineSymbolizer>
</Rule>
<!-- 5 breaks natural jenks, 900-1200, 1201-1700, 1701-2200, 2201-2700, 2701-3200 -->
<Rule>
<Name>1701-2200</Name>
<Title>1701-2200</Title>
<Abstract>1701-2200</Abstract>
...
<!-- 5 breaks natural jenks, 900-1200, 1201-1700, 1701-2200, 2201-2700, 2701-3200 -->
<Rule>
<Name>2201-2700</Name>
<Title>2201-2700</Title>
<Abstract>2201-2700</Abstract>
...
<!-- 5 breaks natural jenks, 900-1200, 1201-1700, 1701-2200, 2201-2700, 2701-3200 -->
<Rule>
<Name>2701-3200</Name>
<Title>2701-3200</Title>
<Abstract>2701-3200</Abstract>
...
</Rule>
</FeatureTypeStyle>
</UserStyle>
</NamedLayer>
</StyledLayerDescriptor>
I cut the last three declarations, as anybody might derive that from the first two ones, I think :-) Here I used the OGC filter encoding specification (http://www.opengeospatial.org/standards/filter) to define that only these lines, which have values of a specific range in the named attribute. Additionally the vendor-option <VendorOption name="followLine">true</VendorOption> followLine aligns the contour's elevation labels along the contour lines :-). A nice fact is that if you use not only the name-tag within the rule, but also title and/or abstract (I am actually not 100% sure :-p ) that naming will be represented in the WMS layer legend.
Finally I would like to shortly introduce my first experiences with Geoserver's freemarker template engine (http://docs.geoserver.org/latest/en/user/tutorials/GetFeatureInfo/index.html and http://geoserver.org/display/GEOS/Templates) to customise featureInfo output with OpenLayers. The first code example is a pretty basic template that fetches attribute names and values from the Geoserver provided featureCollection and then fetches a hydrograph picture based on the ID of the current feature, to demonstrate the flexibilty. In future developments a bit more dynamic functionality like rendering such a graph online by requesting the time-series from a SOS server would way cooler :-)
<#--
Body section of the GetFeatureInfo template, it's provided with one feature collection, and
will be called multiple times if there are various feature collections
-->
<table class="featureInfo">
<caption class="featureInfo">SOE Wells</caption>
<tr>
<#list type.attributes as attribute>
<#if !attribute.isGeometry>
<th >${attribute.name}</th>
</#if>
</#list>
</tr>
<#assign odd = false>
<#list features as feature>
<#if odd>
<tr class="odd">
<#else>
<tr>
</#if>
<#assign odd = !odd>
<#list feature.attributes as attribute>
<#if !attribute.isGeometry>
<td>${attribute.value}</td>
</#if>
</#list>
</tr>
<tr>
<td colspan="4">
<img src="demorequests/${feature.attributes["ID"].value}.png" width="400" height="216" alt="SOE Well ID ${feature.attributes["ID"].value} Hydrograph">
</td>
</tr>
</#list>
</table>
<br/>
In the JavaScript implementation of your OpenLayers map-object you fetch this template "auomagically" filled with data from Geoserver. You need to patch together following things:
- featureInfo = OpenLayers.Control.WMSGetFeatureInfo()
- map.addControl(featureInfo)
- featureInfo.activate()
- document.getElementById('map').style.cursor='pointer';
And finally you could display that generated html via an Ext.Window
The JavaScript stuff is a big hassle in my opinion. It gives you a lot of flexibility and nice cool things directly in the client browser, but it costs a lot of nerves. But maybe I am just not the real JavaScript developer at all :-p
Subscribe to:
Posts (Atom)
