These demos only use a small amount of data, but in the real world GIS data can soon get to be very large. If the user is only interested in a few of the route sections, it wastes their time and bandwidth forcing them to download all the linestrings whether they're interested in them or not. Better is to give a list and then let them choose which to view in detail.
Simplifying geometries
One way of doing this is by displaying not the full geometries, but a simplified version. This could be done, for example, by applying the Douglas-Peucker algorithm to the lines on the server before transmitting them to the browser. On my live maps, I have a route overview option, in which the initial display shows a bbox for each linestring, represented as a polygon; these are on one layer. The user then clicks on those they're interested in and the details are fetched and displayed on a second layer. This means the fixed strategy is now for the bbox polygons, whereas the routeline layer no longer has any strategies.
The first layer then looks like this. The protocol has a different url, and the layer has a different style with 0 opacity so the boxes are transparent:
map.addLayer(new OpenLayers.Layer.Vector("Bboxes", {
style: {
strokeColor: "#ff00ff",
strokeWidth: 3,
fillOpacity: 0,
cursor: "pointer"
},
eventListeners: {
"featuresadded": dataLoaded
},
projection: wgs84,
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "bboxes.json",
format: new OpenLayers.Format.GeoJSON({
ignoreExtraDims: true
})
})
}));
The lines layer looks like this: still the same style and protocol, but there is no fixed strategy, as we will be calling read() dynamically. Because there's no strategy to transform the coordinates, we must in this case define internal and external projection on the format.
var linesLayer = new OpenLayers.Layer.Vector("Route Sections", {
style: {
strokeColor: "blue",
strokeWidth: 3,
cursor: "pointer"
},
protocol: new OpenLayers.Protocol.HTTP({
format: new OpenLayers.Format.GeoJSON({
ignoreExtraDims: true,
internalProjection: map.baseLayer.projection,
externalProjection: wgs84
})
})
});
map.addLayer(linesLayer);
We also need to change the onSelect() function: if the feature clicked is a polygon, it uses the protocol to read() the url with a callback that adds the features in the response to linesLayer. It then removes the selected feature, i.e the polygon:
selectControl.onSelect = function(feature) {
if (feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
linesLayer.protocol.read({
url: feature.fid+'.json',
scope: linesLayer,
callback: function(resp){this.addFeatures(resp.features)}
});
feature.destroy();
hideTooltip();
} else {
if (feature.attributes.clickable != 'off') alert('Feature!')
}
};
Changing layer visibility
The example above is a good way of representing large numbers of linestrings (and something similar could be done for polygons) that are all in the same layer. Another way of delaying the loading of the geometries is to put them in different layers and let the user pick which they want in the layerswitcher. This is good where there are logical ways of dividing the features into categories or whatever. I do this with my point data and will demonstrate this on the next page, but in the meantime here's an example of how to do this with a layer with one linestring:
map.addLayer(new OpenLayers.Layer.Vector("Alternative route", {
style: {
strokeColor: "blue",
strokeWidth: 3,
cursor: "pointer"
},
visibility: false,
projection: wgs84,
strategies: [new OpenLayers.Strategy.Fixed()],
protocol: new OpenLayers.Protocol.HTTP({
url: "185.json",
format: new OpenLayers.Format.GeoJSON({
ignoreExtraDims: true
})
})
}));
The key difference here is the "visibility: false" option, which tells the strategy this layer is not to be loaded initially, but only when the visibility is changed to true. This can be done programatically or by the layer being clicked by the user in the layerswitcher, in this case the layer called 'Alternative route'.
Other latency issues
Improving latency is a large topic that requires a rethink on many of the traditional ways of doing things. For example, it is better not to load non-essential text such as help text with the initial page; better is to load it via AJAX only if the user clicks on the help button. Similarly, those used to Google's Maps API will often load all features together with all descriptive text even if there are a large number of these and the user is only interested in a few; better is to load only what the user requests - as above - and to only load descriptive text when requested, via AJAX. In general, as much logic as possible should be done on the server not the browser; not only is the browser slower but each piece of code has to be transmitted to each browser before it can be executed. Variables calculated on the server can be passed to the browser via a JS object - a namespace, which I call ServerVars - in a script tag.

