OpenLayers: Vector Layers

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.

An introduction to OpenLayers

The most mature, portable, and stable API for browser-based map applications is OpenLayers.  OpenLayers is an API that renders a map in a <div> tag on your HTML page, and allows you to manipulate all the minutia of adding data to the map, interacting with it, getting data out of it, and so forth.  In this post, you will be introduced to OpenLayers, learn about how to develop interactive applications with it, and learn a little bit about adding data.

 Getting a base map on the page

I usually start by including the most up-to-date development snapshot of OpenLayers.  The OpenLayers development snapshots are stable within reason, and contain a few features that are well above and beyond those you can find in the stable branch.

<html>
  <head>
    <script src="http://dev.openlayers.org/nightly/OpenLayers.js">     </script>
<!-- the next line is needed if you intend to use Google Maps -->
<script src="http://maps.google.com/maps/api/js?v=3.2&sensor=false"></script>
  </head>
  <body>
    <div id=‘map’></div>
  </body>
</html>

This gives you a blank page to work with that includes everything you need to get started with OpenLayers.  To actually add a map to the page, you will need to add an OpenLayers.Map object and a base layer.  Practically any kind of layer can be used as a base layer, but the more common base layers are from widely used tile mapping services: Google, Bing, and Open Streetmaps.

When these are not used, it is common to use an OGC WMS or TMS (Tile Mapping Service) feed to populate the base map of OpenLayers. The advantage to a WMS or TMS service, especially if you deploy them yourself, is that you can get them on more than one map projection.  All the popular tiling map services use the same map projection and are locked into that projection, known as “Spherical Mercator”.  Spherical Mercator, despite its name, is a cylindrical projection, which gives increasing distortion of area near the poles and cannot display the poles themselves at all.  Additionally, you must be able to retrieve any layers added onto a Spherical Mercator layer in Spherical Mercator.  This is not normally a problem, as most WMS and WFS map servers are capable of reprojecting to the Spherical Mercator coordinate system.  Older services and static feeds may not, however.

Google

Google maps are the most popular maps on the web both because they were available to make mashup-apps early, and because they are highly readable.  Google’s provides separate tilesets for satellite imagery, terrain bumpmaps, roads and cities, and finally a road/satellite hybrid tileset.

Which one to use depends largely on the application you are writing.  The terrain map provides the most neutral background, however it shows the least detail.  The road map tells you nothing about what kind of terrain a feature is found on, but gives accurate information about roads, parks, military installations (in the US), cities, towns, and landmarks.  The satellite and hybrid maps have great variability in background, limiting the color palette and size of features that you can display legibly, but they also show whether or not the data you’re overlaying is in the middle of the forest or in the middle of the desert – an important distinction in some cases!

So let’s look at how to create a map and add a Google layer to it.  All this code would be enclosed in a <script> tag or be stored in a separate Javascript file to be loaded after OpenLayers:

var map = new OpenLayers.Map(‘map’ // the id of the <div> tag
   {maxExtent: new OpenLayers.Bounds(
       -20037508, -20037508,
        20037508, 20037508.34),
   }
);

// The terrain map
var terrain = new OpenLayers.Layer.Google(“Google Terrain”,
   {type: google.maps.MapTypeId.TERRAIN, sphericalMercator: true});

// The roads map
var roads = new OpenLayers.Layer.Google(“Google Maps”,
   {sphericalMercator: true}); // no maptype needed since this is default.

// The hybrid map
var hybrid = new OpenLayers.Layer.Google(“Google Maps”,
   {type: google.maps.MapTypeId.HYBRID, sphericalMercator: true });

//The satellite imagery map.
var satellite = new OpenLayers.Layer.Google(“Google Satellite”,
   {type: google.maps.MapTypeId.SATELLITE, sphericalMercator: true });

// This control is one way to raise or lower base layers.
map.addControl(new OpenLayers.Control.LayerSwitcher());

// Layers are stacked from bottom to top, with the leftmost at the bottom.
map.addLayers([terrain, roads, hybrid, satellite]);

Any one of these layers could have been added individually.  Here we add them all and a layer switcher control (shown as the little blue tab with a plus sign on it at the top right of the map.  The layer switcher lets us switch between them at will.  Note that sphericalMercator is set to “true”.  This is always a good practice, as layering other maps or drawing your own features on top of a Google (or any commercially provided) map will require that this is set to “true”.

The map object we created is very basic; we will come to more complex map objects later on, however note the maxExtent parameter.  This is often essential when adding multiple layers.  If this is omitted, some versions of OpenLayers can fail to display data at all zoom levels.  The maxExtent used here covers the entire world in the spherical Mercator projection. These values, although arcane, are safe to use and copy wholesale into your project.

Bing

Bing maps are perhaps the strongest commercial alternative to Google.  If you want to give your application a look that distinguishes it from other applications, Microsoft’s Bing might just be the way to go.  Bing’s maps are still fairly readable.  Their default map looks a bit like a combined view of Google’s road and terrain maps.  At its most detailed level it is still a little sparser than Google, which may appeal depending on your application.  Finally the licensing terms are different.  Licensing turns out to be a key issue when dealing with a commercial map provider, be it Google, Yahoo, or Microsoft, and each providers terms are enough different that if you find yourself unable to develop the application you want (for instance, Google doesn’t let you track vehicles as of this writing), you may look at another company’s map product.

To get the Bing map, you will need to add another script tag to the page in the area before your source code is included or written:

<script src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&mkt=en-us"></script>

Then the map creation procedure is very similar to the procedure we followed for Google:

var map = new OpenLayers.Map(‘map’ // the id of the <div> tag.
  {maxExtent: new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508.34)
});

// API key for http://openlayers.org. Please get your own at
// http://bingmapsportal.com/ and use that instead.
var apiKey = "AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf";
        
var roads = new OpenLayers.Layer.Bing(“Roads”,
   {type: "Road", apiKey:apiKey, sphericalMercator:true });

var hybrid = new OpenLayers.Layer.Bing("Hybrid",
   {type: "AerialWithLabels", apiKey:apiKey, sphericalMercator:true});

var aerial = new OpenLayers.Layer.Bing("Aerial",
   {type: "Aerial", apiKey:apiKey, sphericalMercator:true});

map.addControl(new OpenLayers.Control.LayerSwitcher());
map.addLayers([shaded, hybrid, aerial]);

OpenStreetMap

OpenStreetMap is a crowd-sourced mapping project similar in scope and intent to Wikipedia, but for navigation. Its strength lies in two areas: licensing and coverage.  OpenStreetMaps is released via a Creative Commons Share-alike license.  You can develop turn-by-turn directions, vehicle tracking, and all kinds of other applications that the commercial providers restrict you from.  You can download the data, which is vector formatted, and render your own map tiles to your liking. Additionally, in countries where US Trade restrictions abound, OpenStreetMaps commonly has whole towns that Google, Yahoo, and Bing just don’t have.  For instance, you might go to the Haitian port of Legoane in Google and OpenStreetMaps and compare notes.  Or try rural Cuba.  OpenStreetMap was used to great effect in the early days of the Haitian earthquake relief effort.

There is only one possible view to the openstreetmap.org’s OpenStreetMap, so creating an OpenStreetMap layer is perhaps the simplest of all.  No extra script tag to include APIs is required:

var map = new OpenLayers.Map(‘map’ // the string is the id of the <div> tag.
   {maxExtent: new OpenLayers.Bounds(
      -20037508, -20037508,20037508, 20037508.34)});

layer = new OpenLayers.Layer.OSM( "Simple OSM Map”, {sphericalMercator: true});
map.addLayer(layer);

WMS

WMS stands for Web Map Service, and is a standard produced by the OGC.  We will go into detail into WMS in a couple of posts, but for now, we’ll stick to showing how to add a WMS service to a base map, and why you would want to.  WMS is by far the most versatile mapping solution.  The open source Geoserver and MapServer projects are just two examples of servers you can setup for yourself to serve WMS tiles.  RENCI’s Geoanalytics Framework contains a highly flexible implementation of WMS for Python/Django, which operates on top of any of the supported data models.

WMS has its drawbacks. Many WMS servers render tiles on the fly, so many require an additional caching service to run on the server to keep up performance.  WMS servers may also be quite varied in the projections they support, so it would be good to do a little research on the service you intend to use before you actually go about using it.  We will go into how to do this programmatically later, but for now, let’s stay focused on getting the data into the map:

var map = new OpenLayers.Map(‘map’,{ // the string is the id of the <div> tag.
  maxExtent: new OpenLayers.Bounds(-180,-90,180,90),
  projection: “EPSG:4326”
});

var layer = new OpenLayers.Layer.WMS("OpenLayers WMS",
    "http://vmap0.tiles.osgeo.org/wms/vmap0", {
    layers: ‘basic’,
});

map.addLayer(layer);

Here we see a couple of new parameters: layers and srs.  Layers describes to the WMS what layers to composite together into a single image.  Compositing in WMS is done server side, so if you are sure you always want a few layers composited together, you can save client side performance and have the server do it.  This parameter is required.  The second parameter is optional, but without it you are at the mercy of whatever the projection the layer has set as default.  It should generally be the map projection.

To use a WMS layer as the base layer, be sure to set the projection and extents, and then add “baseLayer: true” to the options object.

The srs parameter stands for spatial reference system, and is usually but not always a code like that seen above.  “EPSG:4326” is a common, (but often misused) basic latitude-longitude projection.  “EPSG:3785” (or “EPSG:900913” on older systems) is the Google/Bing/Yahoo/OSM Spherical Mercator projection. There are a number of common ones, and not all may be supported by your particular map server.  We will cover in detail in a future post how to find out for your server which spatial reference systems are supported.  A full search engine and large database of spatial reference systems can be found online at SpatialReference.org.  This is a crowdsourced wiki for spatial reference systems, and not all spatial reference systems may be found there.