Today we will look at some additional web mapping topics, specifically:
GeoJSON is a relatively new format for representing geographical data. As the name implies, it is based on JSON. It is typically supplied to clients by a web service; for instance, we might have a web service which supplies the location of points of interest close to a given latitude and longitude in GeoJSON format. Because GeoJSON is JSON based, it is easily parsed by JavaScript-based clients.
GeoJSON consists of a series of objects. These are:
Here is an example of some GeoJSON.
{
type: "FeatureCollection":
features:
[
{
type: "Feature",
geometry:
{
type: "Point",
coordinates: [-1, 51]
},
properties:
{
featureClass: "pub",
name: "The Red Lion"
}
},
{
type: "Feature",
geometry:
{
type: "Point",
coordinates: [-0.9, 51.1]
},
properties:
{
featureClass: "restaurant",
name: "Sams Burger Joint"
}
},
{
type: "Feature",
geometry:
{
type: "LineString",
coordinates: [
[-1, 51],
[-1.01, 50.99],
[-1.01, 50.98],
[-1.02, 50.97],
[-1.04, 50.96]
]
},
properties:
{
featureClass: "main road",
number: "A987",
name: "High Street"
}
}
]
}
Note how this GeoJSON consists of a FeatureCollection. The
FeatureCollection in turn contains an array of Feature objects,
each of which contains three fields:
Interpreting GeoJSON is the same as interpreting any other type of JSON. A client would typically send an AJAX request to a web service supplying GeoJSON, and then evaluate the GeoJSON returned to load it into a JavaScript variable, e.g. with evalJSON() within Prototype. We can then access the GeoJSON collection using JavaScript syntax. For example, in an AJAX callback:
function responseReceived(xmlHTTP)
{
var geojsonData=xmlHTTP.responseText.evalJSON();
alert(geojsonData.features[0].properties.name); // name of 1st feature
//type of geometry of 1st feature
alert(geojsonData.features[0].geometry.type);
//longitude of 1st feature, assuming it's a point
alert(geojsonData.features[0].geometry.coordinates[0]);
//latitude of 1st feature, assuming it's a point
alert(geojsonData.features[0].geometry.coordinates[1]);
// longitude and latitude of 1st point of 3rd feature, assuming it's
// a LineString
alert(geojsonData.features[2].geometry.coordinates[0][0]);
alert(geojsonData.features[2].geometry.coordinates[0][1]);
}
If we are using Leaflet, however, it's easier than that. Leaflet comes with GeoJSON parsing built-in. Within Leaflet, you can create a GeoJSON Layer. Here is an example of how you would handle GeoJSON from Leaflet. First you would add a GeoJSON layer in your init() function (note that geojsonLayer would need to be a global variable, declared outside of any function, so that the AJAX parsing function can access it):
geojsonLayer = new L.GeoJSON(); map.addLayer(geojsonLayer);Then, in your AJAX callback function, you simply do, for example:
function resultsReturned (xmlHTTP)
{
var geojson = xmlHTTP.responseText.evalJSON();
for(var i=0; i<geojson.features.length; i++)
{
geojsonLayer.addGeoJSON(geojson.features[i]);
}
}
On Edward, at http://edward/ewt/poi.php is a web service which generates GeoJSON of all features within a given area stored in the poi table in the dftitutorials database. (This data is taken from OpenStreetMap, data copyright OSM contributors, licenced under Creative Commons Attribution-ShareAlike Licence 2.0).
This takes one query string parameter, bbox (a bounding box). This consists of four values separated by commas: the western, southern, eastern and northern bounds of the area to query respectively. So if bbox is -1,51,0,52 for instance, only points of interest between longitude -1 and longitude 0 (1 West and 0) and latitude 51 North and 52 North will be returned. To call the script and instruct it to generate GeoJSON, use:
http://edward/ewt/poi.php?bbox=west,south,east,north&format=geojson
alert('Bounds: west=' + map.getBounds().getSouthWest().lng +
' south=' + map.getBounds().getSouthWest().lat +
' east=' + map.getBounds().getNorthEast().lng +
' north=' + map.getBounds().getNorthEast().lat );
You can also detect when a user stops dragging the map by reacting to the
dragend event, eg:
map.on("dragend", onDragEnd);
where onDragEnd would be a function to handle a drag end event.What would be useful is to display some information about the feature when the user clicks the marker. This can be approached via the featureparse event. The idea is that when a GeoJSON feature is added to the GeoJSON layer, a featureparse event is generated. We can write code to react to the featureparse event, so that, for example, we can attach a popup containing information about the feature. (The popup will remain hidden until the user clicks the feature). Here is how this could be done:
geojsonLayer.on("featureparse", function(e)
{
e.layer.bindPopup("Name=" + e.properties.name);
}
);
Note that this is an anonymous function, which you first encountered in the
jQuery topic. The variable e, automatically supplied to the function,
contains the GeoJSON feature being added, which we can query to obtain the
properties, as shown here. What the function is therefore doing is
attaching a popup to the layer associated with the feature, containing the
feature's name.
for(var i=0; i<geojson.features.length; i++)
{
var id=geojson.features[i].properties.id;
if (!indexedFeatures[id])
{
indexedFeatures[id] = geojson.features[i];
geojsonLayer.addGeoJSON(geojson.features[i]);
}
}
Implement this. The GeoJSON contains a record of each feature's ID.
You will need to make the array global, so that any function can access it, and
create memory for the array in your init() function with:
indexedFeatures=new Array();
On Edward, under the dftitutorials database, there is a table called poi representing points of interest. It contains lat, lon, name and type columns.
list($w,$s,$e,$n) = explode(",", $_GET["bbox"]);
This "explodes" the bbox variable into an array (the comma is the
separator character), and list() then extracts the four
elements of the array into the variables $w, $s, $e and $n.