Adding Features by Hand
The most straightforward if un-modular way of creating a feature layer in OpenLayers is to create individual features by hand and add them. This can be quite tedious for complex features, but for the simplest of layers, it may work well. Use the OpenLayers.Feature.Vector and OpenLayers.Geometry.* references to create these features.
New geometry types can be defined by inheriting from OpenLayers.Geometry. Currently supported geometry types include:
- Collection – A collection of geometries of different types.
- Curve – A smooth spline curve.
- LinearRing – A string of line segments where the last segment connects with the first.
- LineString – A string of line segments.
- MultiLineString – A collection of strings of line segments where the collection members aren’t contiguous.
- Point – A single point
- MultiPoint – A collection of points. If you have more than 50 or so points, it is best to use this and not create multiple Point geometries. It is much more efficient.
- Polygon – A polygon, possibly with holes.
- MultiPolygon – A set of polygons.
- Rectangle – A single rectangle.
GeoJSON
JSON is both a subset of Javascript’s object syntaxanda subset of Python’s (much of GIS code is written in Python these days) dictionary literal syntax. It is much simpler and easier to read than XML while remaining as general as OGC’s GML for expressing geographical data. The GeoJSON standard is the JSON standard for feature data. GeoJSON can be used directly by OpenLayers using the HTTP protocol object and the GeoJSON format object.
At the top level of a GeoJSON document is a “type” property that is either “Feature” or “FeatureCollection”. If the document is a feature, then it contains one geometry object and one set of properties containing the data. If it’s a feature collection, then the document will contain a “features” property that contains an array of GeoJSON features or nested feature collections, which are themselves objects.
All features in GeoJSON are objects containing properties for ”type”, ”geometry”, and ”properties”. “properties” is entirely arbitrary. It is the data associated with each feature, and can be any valid JSON object. The “geometry” property contains the actual geometry itself.
A geometry object describes the physical shape of the feature in the world. The “type” property contains the geometry type, and is generally one of the following values: ”Point”, “LineString”, “LinearRing”, “Polygon”, or ”GeometryCollection”. There are other kinds of geometries, but for now we’ll focus on these, because they are core and OpenLayers supports them well. These feature types correspond to the OpenLayers.Feature.* classes in the OpenLayers class hierarchy. The “coordinates” property will vary according to the type of geometry. Points will contain a single array of two or three values: x, y, and possibly z. LineStrings will contain an array of point arrays. Polygons will contain arrays of LineStrings that describe something called a linear ring. A linear ring is simply a LineString whose last point is always also the first point. It can either be explicitly specified, or if not, the first point is appended for you. A GeometryCollection is simply a way of nesting geometries. Instead of a “coordinates” property, it contains a “geometries” property whose contents are other geometry (or nested geometry collections) objects.
Here are some examples of GeoJSON:
// Representing a point in GeoJSON { “type” : “Feature”, “geometry” : { “type” : “Point”, “coordinates” : [-85,35] }, “properties” : { “name” : “Sir Lancelot” } } // Representing a line string in GeoJSON { “type” : “LineString”, “geometry” : { “type” : “LineString”, “coordinates” : [[-85,35], [-84,35.357], [-83,35.789]] }, “properties” : { “name” : “The Tale of Sir Lancelot” } } // Representing complex feature collections in GeoJSON { “type” : “FeatureCollection”, “features” : [ { “type” : “Feature”, “geometry” : { “type” : “Point”, “coordinates” : [-85, 35], }, "properties" : { “name” : “The Tale of Sir Lancelot” } } ] }
GeoJSON is probably the most popular way to write feature data by hand, because it provides the best balance between flexibility and simplicity. GML, by contrast is a complex but complete standard for representing features in XML, and is meant to be generated entirely by machine.
To load a GeoJSON file as a layer, we create an HTTP protocol object in OpenLayers and use it to access the file. Then we pass this into the GeoJSON Format object and the format object turns it into OpenLayers features:
var layer = new OpenLayers.Layer.Vector("Photos", { strategies: [new OpenLayers.Strategy.BBOX()], protocol: new OpenLayers.Protocol.HTTP({ url: “http://jeffersonheard.com/examples/geojson/data.json”, params: {}, format: new OpenLayers.Format.GeoJSON() }), styleMap: { externalGraphic : “${marker}” } }); map.addLayers([layer]);
WFS
The Open Geographic Consortium’s WFS is the most widely adopted standard for transmitting feature data across the wire. WFS stands for Web Feature Service and its reference implementation is the open source Geoserver although it is also implemented by commercial tools such as ArcGIS and GO Publisher. It is composed of two components. One is a standard for how to query a geographic web-service, consisting of a set of methods and parameters for those methods. The other is a standard for how data comes back. In the case of the feature data itself, data, by convention is returned as GML, an XML standard for conveying geographic data.
The two most important methods for using WFS are GetFeature and GetCapabilities. The former method actually retrieves the features in the layer. The latter provides the user or another program the list of what layers are available and how to query them. GetCapabilites is highly useful when you want to explore a feature server that you don’t have administrative access to.
The most common open source software for serving up WFS are Mapserver and Geoserver. Geoserver is a Java-based solution and runs in a standard Java web container, like Tomcat. Mapserver is native-compiled. Both integrate with Apache well.
Integration with Apache is important when using WMS and WFS with OpenLayers, because OpenLayers uses the XMLHttpRequest method (the AJAX method) to retrieve certain data on the fly. The advantage of this is a nice smooth interface that doesn’t require reloading the page to get more feature data. The disadvantage of it is that all XMLHttpRequests must be directed to the same server (and port) of the the page that the map data is displayed on.
This means one of two things: To use WFS with Geoserver, either you must deploy your web applications in the same container as Geoserver or somehow via MapServer, or a much cleaner solution is to use Apache’s mod_proxy, mod_rewrite, or other integration to treat Mapserver/Geoserver as part of the same package. mod_proxy can also be used to proxy in requests to other WFS servers from third party providers (such as government geography, weather, or census sites or commercial providers). As a last resort. OpenLayers.org provides a proxy CGI-script written in Python. It is neither terribly efficient nor terribly secure, however, and is best used for testing purposes only.
OpenLayers consumes WFS via the OpenLayers.Layer.Vector class and the OpenLayers.Protocol.WFS object. Combining the two like this:
var layer = new OpenLayers.Layer.Vector("WFS", { strategies: [new OpenLayers.Strategy.BBOX(), new OpenLayers.Strategy.Cluster()], protocol: new OpenLayers.Protocol.WFS({ url: “http://localhost:8080/geoserver/wfs”, featureType: “census_centroids”, featureNS: “http://nc.gov”, srsName: “EPSG:900913”, version: “1.1.0” }) }); map.addLayers([layer]);
will query your local Geoserver for the layer entitled census_centroids. Geoserver layer names are usually of two parts, however, a part before the colon and a part after. The first part is a shorthand for the namespace of the feature collection in geoserver. For your layer, this can either be determined in the administrative interface for Geoserver or as part of the results of a GetCapabilities request. The fully qualified namespace name goes into “featureNS”. Of special importance is the “strategies parameter which tells OpenLayers how to query the WFS server. Common strategies include:
- Cluster – A strategy that groups features that are too close together to show distinctly on the map at the current zoom level.
- BBOX – A strategy that loads new features when the viewing box is changed
- Filter – A strategy that filters the features that will show on the map.
Adding the layer to our map, we see a bunch of yellow dots show up over North Carolina. Not too extraordinary right now, but we’ll get there later.
In the next post, we will use OpenLayers to style our features.