Friday, 21 December 2012

CLIDB-SOS: A collaborative summer internship with NIWA

The National Institute of Water and Atmospheric Research (NIWA) operates a network of weather and climate and other measurement stations, equipped with different sensors, observing a wide range of environmental properties – from temperature over rainfall to wind speed and directions. These high frequency measurements are processed and stored in the NIWA climate database (CLIDB), which is also listed as a national significant database. NIWA has already a web interface in place, where data can be queried and downloaded. Yet this is a manual process.

The Open Geospatial Consortium (OGC) is an international consortium comprising of organisations from industry and research that develops geospatial data transfer and encoding standards and specifications in an open and consensus based process. The OGC Sensor Observation Service (SOS) is a web service interface specification describing access to sensor and time series data – the observations – measured or observed primarily by sensors.


NIWA intends that SOS services become the primary delivery mechanism of its climate, hydrometric and other time-series data to internal research staff, and also central, regional and local government, businesses, NGO's, utilities and the general public via appropriate SOS web clients. Such services provide a database independent, standards compliant approach to data discovery and delivery. Furthermore the NIWA National Climate Database (CliDB) contains a Nationally Significant Database.
With a Sensor Observation Service in place, the CLIDB could actually be queried like a database, but through the web, automagically, from within an application – based on an international standard. Plugins for e.g. R Statistics (sos4r), OpenLayers (OL sos demo) or ArcGIS (ArcHydro and 52°North ArcGIS SOS Extension) are already available to demonstrate data access from SOS servers.

In this Summer of eReseach project I will build a custom connector to source CLIDB directly, based on the sophisticated 52°North SOS server. But the devil is in the details. Although I have already worked with this software I will have to learn more about the database structure of the CLIDB and immerse in the latest development version of the 52°North SOS server, which will support Hibernate, the quite new OGC SOS 2.0 specification and exchangeable output encodings (e.g. CSV, O&MWaterML2.0 and maybe JSON). Additionally we will discuss and demonstrate ways to include SOS data services in a client application.

A lot of work, but worth the effort. I am looking forward to create something new and useful. Besides the apparent advantages for New Zealand’s researchers, also consultancies, commercial organisations and governmental agencies will profit from such a dynamic, standards-driven and web-based geospatial data delivery service that could serve as another good example for environmental data providers.

The benefits of the project include well managed freshwater use & potential impacts of climate change are critical for any country to plan & manage its natural resources. One of the foundations of any effort in these domains is ready access to climate (including rainfall) information.
NIWA is collaborating with a wide range of New Zealand and international agencies to determine strategies and appropriate standards to provide interoperable discovery and delivery facilities for data managed by NIWA, as well as to develop systems to implement those strategies. These include: CSIRO, BOM (Bureau of Meteorology), AODC (Australian Ocean Data Centre), IMOS(Integrated Marine Observing System) in Australia. Iquest and Kisters (New Zealand and internationally), 52o North.
Furthermore collaboration with GNS Science in the NZ-EU cooperative groundwater project SMART (www.smart-project.infohttp://www.gns.cri.nz/) is intended, to support those who seek to incorporate climate data for their research towards characterizing New Zealand’s aquifers.

Monday, 10 December 2012

A Sensor Observation Service for the NIWA climate database

The National Institute of Water and Atmospheric Research (NIWA) operates a network of weather and climate and other measurement stations, equipped with different sensors, observing a wide range of environmental properties – from temperature over rainfall to wind speed and directions. These high frequency measurements are processed and stored in the NIWA climate database (CLIDB), which is also listed as a national significant database. NIWA has already a web interface in place, where data can be queried and downloaded. Yet this is a manual process.

The Open Geospatial Consortium (OGC) is an international consortium comprising of organisations from industry and research that develops geospatial data transfer and encoding standards and specifications in an open and consensus based process. The OGC Sensor Observation Service (SOS) is a web service interface specification describing access to sensor and time series data – the observations – measured or observed primarily by sensors.

With a Sensor Observation Service in place, the CLIDB could actually be queried like a database, but through the web, automagically, from within an application – based on an international standard. Plugins for e.g. R Statistics (sos4r), OpenLayers (OL sos demo) or ArcGIS (ArcHydro and 52°North ArcGIS SOS Extension) are already available to demonstrate data access from SOS servers.

In this Summer of eReseach project I will build a custom connector to source CLIDB directly, based on the sophisticated 52°North SOS server. But the devil is in the details. Although I have already worked with this software I will have to learn more about the database structure of the CLIDB and immerse in the latest development version of the 52°North SOS server, which will support Hibernate, the quite new OGC SOS 2.0 specification and exchangeable output encodings (e.g. CSV, O&M, WaterML2.0 and maybe JSON). Additionally we will discuss and demonstrate ways to include SOS data services in a client application.

A lot of work, but worth the effort. I am looking forward to create something new and useful. Besides the apparent advantages for New Zealand’s researchers, also consultancies, commercial organisations and governmental agencies will profit from such a dynamic, standards-driven and web-based geospatial data delivery service that could serve as another good example for environmental data providers.

Link to the eResearch blog post

Friday, 16 November 2012

Starting to play with spatial-temporal data

Thinking  the big picture is a different thing to actually implement it :-) Well, who doesn't know that. Having some hydrological time-series (groundwater levels) in place in a sensor observation service (SOS), the hydrogeological all-in-one-wonder-portal is going to get a glance of the next level :-p
The last weeks I started to play around with the R environment for statistical computing and visualization (The R Project). 52°North developed a neat R toolkit to access and digest SOS time-series - sos4R.

It is well documented and pretty easy to connect to a SOS server, and query observations. So for the fun of it and to demonstrate the general feasibilty, I quickly queried the groundwater levels of the Horowhenua area in New Zealand, where I got some sample data (courtesy by the regional council).

With the R sos4R, fields and akima packages from the CRAN R packages archive I (quite coarsely) interpolated the groundwater surfaces for the years 1991-2009 and put the images together as an animated gif (meters above mean sea level over time).

Discussion

I am aware of the total uselessness of this particualr way presenting :-) No years, the scale changes slightly, and the exact spatial extent and north orientiation are not reliable :-p

Nevertheless, for just playing around, this was a motivating simple first shot to easily visualise changes over time.

I would like to play around with the gstat and spacetime R package, integrate a more sophisticated script as a 52°North WPS process and have those things happening automagically in the interwebz.


Monday, 27 August 2012

GSoC and Master Thesis completed

Busy summer, well or winter, depends on the hemisphere and the full time extent. Last 2-3 months have been packed with work and included about 25.000 air miles (which means concatenated spending several days in airplanes).
I finally managed to finish my Master Thesis "Towards a 4D WebGIS using harmonised datasets: Examined on a New Zealand Example" which examines in the first part existing groundwater-related geoportal projects and scientific applications around the globe, existing and useful, web-based technologies and standards (e.g. OGC) to implement such a portal, a draft architecture. In the second part actual prototypical implementations of a selected subset of the examined standards and and technologies demonstrate their feasibility for the SMART project. I also hold a talk at the GI_Forum conference in Salzburg (proceedings/conference paper).
Furthermore I successfully completed my Google Summer of Code (GSoC) project with the 52°North Initiative, where I implemented an exchangeable encodings mechanism for their Sensor Observation Service (SOS). I additionally implemented a plugin that provides SOS time-series output in the WaterML2.0 format. I could acquire the latest schema, which will be published soon.. we were supposed to write a series of blog articles to document or progress in the project:

After flying back to New Zealand I was lucky to visit the GeoSciML Face-to-Face-Meeting at GNS Science in Wellington. It was really exciting to meet these people, working on the international standard of a Geoscience Markup Language. also issues around the OneGeology portal have been disussed, as it uses GeoSciML to build up a world geology map. Additionally I could get in touch with one of the inspiring driving persons behind the Canadian Groundwater Information Network and their its sematic foundation, the Groundwater Markup Language (GWML), which itself is derived from GeoSciML.
Now I will dedicate my research to the development of a New Zealand groundwater geoportal within the SMART project, using OGC webservices, GeoSciML and/or GWML, WaterML2.0 and X3D as its foundations to transform and consume all available data relevant to aquifer characterisation and create a completely new, visually appealing 3D/4D view on New Zealand's groundwater resources.

Exciting ;-)

Thursday, 23 August 2012

Demonstrating Exchangeable Encodings for SOS - A Quantum Leap

The final sprint towards the end of the Google Summer of Code (GSoC) is over now. After fixing bugs, working on documentation and actually producing time-series in different formats, the “Exchangeable Encodings for SOS”
project met most of its ambitious goals – and it is demonstrated on the 52°North Google summer of Code Demo Server. Users and developers documenation on the encoding mechanism has also found a place in the 52°North wiki.

We have integrated a plugin mechanism into an SOS server (source code branch), which is based on the main 52°North SOS development line (source code trunk).

Link to the article on the 52°North blog

Sunday, 15 July 2012

First X3D example to visualise geological layers in the web

Recently I have played around with tools to visualise geological layers in 3D in the web, preferably without any browser plugins. With HTML5 and WebGL some really cool possibilities arise. WebGL is not supported by every browser, but apparently  all newer cool browsers like Chrome or Firefox, as well as Safari and Opera support WebGL at least experimentally. What a surprise that Microsoft Internet Explorer does not net yet support neither HTML5 canvas nor WebGL. But luckily there is the Chromeframe plugin :-)

If anyone has ever been working with OpenGL for 3D stuff probably in C or C++, well, WebGL is quite arcane, too, but in JavaScript ^^

Nevertheless, X3D for the rescue. X3D is a) an ISO standard (ISO/IEC 19775-1.2:2008 ), b) the successor of the working, but not really successful VRML97 (ISO/IEC 14772-1.2:1997) and c) a fully XML-based scenegraph declarative language. And the final ingredient is the Fraunhofer IGD experimental open source framework x3dom that thrives to integrate X3D content into HTML5.

Well, to the actual task. I am supposed to visualise a 3D geological model that originally has been designed with EarthVision(c). EarthVision(c) has its own binary format to store the 3D models, but they van be exported to a simpe XYZ-ASCII file:

2696105 6047205 150 20 11
2696605 6047205 150 21 11
2697105 6047205 150 22 11
2697605 6047205 150 23 11
2701105 6047205 150 30 11

...

The first two columns are Easting and Northing - implicitly known that the spatial reference system is New Zealand Map Grid. Third column is the height value and the further columns represent some additional attribute data. Based on the resolution, the surfaces from the roundabout 20 km  by 30 km range from 50KB (500m), 1MB (100m) to 20MB (20m) per layer (5 layers altogether).
X3D provides two easy (point set based) possibilities to show surfaces (right now the geological layers are represented as surfaces, they are not described as full bodies).

Furthermore there are the NURBS and extrusion implementations, which describes surfaces through splines. But that's rather complicated for the first shot :-) ElevationGrid requires an evenly spaced grid of height values, whereas IndexedFaceSet  notes all point coordinates and then defines coordinate indexes to define a mesh of single surfaces (similar to TINs, but not necessarily triangles).
I decided for the ElevationGrid. X3D has a geospatial extension (X3D Earth), which can geographically reference and place 3D objects in a defined spatial reference system. I didn't try this feature yet. And apparently it does not make sense to load 100MB for a 3D model into the browser. Therefore the 500m grid has been used here. The following example outlines a rather simple definition of such an elevation grid in X3D:

<shape> 

    <elevationgrid colorpervertex="false" creaseangle="3.14" def="Greywacke_top_500" 
        normalPerVertx="true" colorPerVertex='false' xDimension='37'
        zDimension='44' xSpacing='500' zSpacing='500' creaseAngle='3.14' solid='false'
        height='
150 150 150 ...
           '>
    </elevationgrid> 
    <appearance>
        <material ambientintensity="0.1" diffusecolor="red" id="Greywacke" 
            shininess="0.2" specularcolor="lightred" transparency="0.0">
        </material> 
    </appearance>
<shape>

To prepare that grid, you need to take care of some things:

  • you need to know the extent and resolution of the dataset, from that you calculate and define the x- and zSpacing (how many values will be filled, because you only need the height values)
  • the coordinate system orientation of the X3D 3-dimensional space is probably from the 2/2,5 coordinate system from the source dataset
  • the source datasets only contained points with actual values, to fill the ElevationGrid properly, NODATA values need to added
Finally I got my data sorted on built a neat first little demo :-) There might happen some improvement, as we are intending to visualise wells, bores and other hydro(geo)logical data in such a scene.

Fig. 1: Preliminary (X)3D  model with five layers and a (not aligned) image as an underlayer

Wednesday, 4 July 2012

Exchangeable Encodings Getting in Shape

The time is flying by and soon we reach midway of the Google Summer of Code (GSoC) with 52°North. It is the typical time in between – I already managed to meet the first milestones, but there is no proper product yet. Catching up with the changes from the SOS main development branch is a constant challenge, but the infrastructural changes within the “Exchangeable Encodings for SOS” project are slowly but continuously leading towards a robust and standardized, yet limited, encoding plugin API (application programming interface).

The main use case is the request for observations (GetObservation request) that could be delivered in CSV (comma separated values) format for legacy applications, WaterML2.0, an upcoming OGC (Open Geospatial Consortium)
data transfer standard for the hydrological domain, possibly netCDF (Network Common Data Form), also an OGC standard for meteorological applications, and in O&M (Observations & Measurements) for the generic use within sensor networks and spatial data infrastructures.

Link to the article on the 52°North blog

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?

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.

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"?>
<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 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" :-)
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</VendorOptionfollowLine 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