According to this interesting BBC page on population, the number of humans on Earth has more than doubled in my lifetime.
Anyone who has spent time trying to speed up an O(n^2) algorithm knows this can't be a good thing...
Thursday, 27 October 2011
Tuesday, 25 October 2011
Using a web service for Elevation from JEQL
MapQuest has a whole range of useful and freely available web services for geospatial queries. One of these is the Open Elevation Service. I thought it would be interesting to try it out using JEQL's capabilities of querying URLs and plotting datasets as images.
The Open Elevation Service is extremely simple to use. You just submit a URL containing the location:
and you get back a JSON (or XML) response with the elevation:
foo({
"shapePoints":[39.740112,-104.984856],
"elevationProfile":[{"distance":0,"height":1616}],
"info":{"copyright":{"text":"© 2011 MapQuest, Inc.","imageUrl":"http://tile21.mqcdn.com/res/mqlogo.gif","imageAltText":"© 2011 MapQuest, Inc."},
"statuscode":0,
"messages":[]}
});
JEQL doesn't (yet) do JSON parsing, but it's easy to pick out the height attribute with a simple RegEx:
height = RegEx.extract(elevJson, \'"height":(-?\d+)\}');
Querying over a grid of locations provides a dataset which is a gridded Digital Elevation Model (DEM). JEQL provides a function to generate a table which is a grid of integers, so it's easy to produce a grid of lat/long locations:
loc = select baseLat + i * cellSize lat, baseLon + j * cellSize lon
from Generate.grid(1, gridSize, 1, gridSize);
Once the elevations for all locations are retrieved, the locations are used to compute square cells (polygons) which cover the area, and the cells are symbolized with a fill value from a suitable color map based on elevation.
Here's the final result. It's slightly squashed in aspect ratio, since the data grid is in the lat/long coordinate system. The obvious data dropouts seem to be hard errors in the MapQuest service, which is slightly surprising. But's it's recognizably Mt. St. Helens - you can even see the lava dome in the crater!
Not surprisingly, this isn't quick to generate - the service runs at about 10 queries per second. (Actually, that seems pretty reasonable for a free web service!). The service does support querying multiple points per request - I might try to see if this decreases the latency (although this might be a bit complex to express in JEQL).
Here's the whole script:
The Open Elevation Service is extremely simple to use. You just submit a URL containing the location:
http://open.mapquestapi.com/elevation/v1/getElevationProfile?callback=foo&shapeFormat=raw&latLngCollection=39.740112,-104.984856
and you get back a JSON (or XML) response with the elevation:
foo({
"shapePoints":[39.740112,-104.984856],
"elevationProfile":[{"distance":0,"height":1616}],
"info":{"copyright":{"text":"© 2011 MapQuest, Inc.","imageUrl":"http://tile21.mqcdn.com/res/mqlogo.gif","imageAltText":"© 2011 MapQuest, Inc."},
"statuscode":0,
"messages":[]}
});
JEQL doesn't (yet) do JSON parsing, but it's easy to pick out the height attribute with a simple RegEx:
height = RegEx.extract(elevJson, \'"height":(-?\d+)\}');
Querying over a grid of locations provides a dataset which is a gridded Digital Elevation Model (DEM). JEQL provides a function to generate a table which is a grid of integers, so it's easy to produce a grid of lat/long locations:
loc = select baseLat + i * cellSize lat, baseLon + j * cellSize lon
from Generate.grid(1, gridSize, 1, gridSize);
Once the elevations for all locations are retrieved, the locations are used to compute square cells (polygons) which cover the area, and the cells are symbolized with a fill value from a suitable color map based on elevation.
Here's the final result. It's slightly squashed in aspect ratio, since the data grid is in the lat/long coordinate system. The obvious data dropouts seem to be hard errors in the MapQuest service, which is slightly surprising. But's it's recognizably Mt. St. Helens - you can even see the lava dome in the crater!
Not surprisingly, this isn't quick to generate - the service runs at about 10 queries per second. (Actually, that seems pretty reasonable for a free web service!). The service does support querying multiple points per request - I might try to see if this decreases the latency (although this might be a bit complex to express in JEQL).
Here's the whole script:
//--------------------------------------
//
// Plots a DEM generated using the MapQuest Elevation service
//
//--------------------------------------
baseLat = 46.16 ; baseLon = -122.25; // Mt St Helens
areaSize = 0.10;
gridSize = 100;
cellSize = areaSize / gridSize;
//---- Generate location grid
loc = select baseLat + i * cellSize lat,
baseLon + j * cellSize lon
from Generate.grid(1, gridSize, 1, gridSize);
//---- Query elevation service for each grid point
telev = select lat, lon, elevJson, elev
with {
url = $"http://open.mapquestapi.com/elevation/v1/getElevationProfile?callback=foo&shapeFormat=raw&latLngCollection=${lat},${lon}";
elevJson = Net.readURLnoEOL(url);
height = RegEx.extract(elevJson, \'"height":(-?\d+)\}');
elev = Val.toInt(height);
}
from loc;
Mem telev;
//---- Create raster cell boxes, symbolize with color map based on elevation, and plot
minElev = 800.0; maxElev = 2500.0;
tplot = select Geom.createBoxExtent(lon, lat, cellSize, cellSize) cell,
Color.interpolate("00a000", "ffb000", "aaaaaa", "ffffff", index) style_fillColor
with {
index = (elev - minElev) / (maxElev - minElev);
}
from telev;
textent = select geomExtent(cell) from tplot;
Plot extent: val(textent)
data: tplot
file: "dem.png";
Monday, 24 October 2011
(r-i-p (john mccarthy))
John McCarthy, inventor of LISP, has died at age 84.
Having finally balanced his parentheses, his atoms can now be returned to the free list.
Jobs, Ritchie, McCarthy - so many of the greats passing....
Having finally balanced his parentheses, his atoms can now be returned to the free list.
Jobs, Ritchie, McCarthy - so many of the greats passing....
Friday, 7 October 2011
Extracting GTFS data using JEQL
Once you understand the General Transit Feed Specification (GTFS) data model, the next step is to transform the data into a format usable in GIS tools. As usual, that means shapefiles (sigh...). Here's a JEQL script that extracts the geometry of all unique trips (aka "route patterns"), as well as the stop locations.
Some interesting aspects of the script:
Some interesting aspects of the script:
- the GTFS spatial data is in geographic coordinates (lat/long). The script projects this into BC-Albers (EPGS code 3005) using the CRS.project() function. Many other coordinate systems could be used (or none - e.g. for output to KML)
- the geomConnect()aggregate function connects Points into LineStrings
/*=======================================================
Extracts Trips (with shapes) and stops from from a GTFS dataset.
Includes shape, trip and route information.
Spatial data is converted from Geographic to BC-ALbers
Usage Notes:
- indir: directory containing the input GTFS files
========================================================*/
indir = ""; outdir = "";
//========================================================
// Extract Trips with unique linework, with route information
//========================================================
CSVReader tshape hasColNames: file: indir+"shapes.txt";
CSVReader ttrip hasColNames: file: indir+"trips.txt";
CSVReader troutes hasColNames: file: indir+"routes.txt";
Mem troutes;
crs_BCAlbers = "EPSG:3005";
//--- extract unique shapes
trips = select distinct route_id, trip_headsign, shape_id
from ttrip;
//--- extract trip pts and order them in sequence
tpt = select shape_id, Val.toInt(shape_pt_sequence) seq,
Geom.createPoint(Val.toDouble(shape_pt_lon),
Val.toDouble(shape_pt_lat) ) pt
from tshape
order by shape_id, seq;
//--- connect pts into lines and project
tline = select shape_id,
CRS.project(Geom.toMulti(geomConnect(pt)), crs_BCAlbers) geom
from tpt
group by shape_id;
//--- join route info to trip shape
trouteLines = select r.route_id, r.agency_id,
r.route_short_name, r.route_long_name,
r.route_type, r.route_url,
trip_headsign, l.*
from tline l
join trips t on t.shape_id == l.shape_id
join troutes r on r.route_id == t.route_id;
ShapefileWriter trouteLines file: outdir+"gtfs_trips.shp";
//========================================================
// Extract Stop points
//========================================================
CSVReader tstopsRaw hasColNames: file: indir+"stops.txt";
tstop = select stop_id,stop_code,stop_name,stop_desc,
CRS.project(Geom.createPoint(
Val.toDouble(stop_lon),
Val.toDouble(stop_lat) ), crs_BCAlbers) pt,
zone_id
from tstopsRaw;
ShapefileWriter tstop file: outdir+"gtfs_stops.shp";
Using this script on the GTFS data for Metro Vancouver (available from TransLink here) produces a dataset that looks like this:JTS & GEOS Wordles
For my JTS talk at FOSS4G I created a couple of Wordles showing systems which use JTS and GEOS. Here they are for posterity (and because I love seeing Wordles...)