Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

GeoCouch, a spatial index for CouchDB

Converting geocouch to CMake.

The following CMake targets exist:

"geocouch" -just compiles the geocouch source, using rebar as
always. ("make all" is the default, and "all" depend on "geocouch", so
"make" by itself will compile the source.)

"geocouch_makecheck" - same as old "compileforcheck" target.

"geocouch_check" - same as old "check" target; cleans, runs
geocouch_makecheck, and then runs the tests.

"realclean" - same as old "clean".

I specifically did not include .tar.gz or "dist" targets as they
support the old, undesirable way of creating source tarballs.

Change-Id: I9098c47db8b50fdde760130391b529f987e74d03
Reviewed-on: http://review.couchbase.org/35099
Reviewed-by: Volker Mische <volker.mische@gmail.com>
Tested-by: Volker Mische <volker.mische@gmail.com>
Reviewed-by: Chris Hillery <ceej@couchbase.com>
Tested-by: Chris Hillery <ceej@couchbase.com>
latest commit 78873365fd
Chris Hillery authored
Octocat-spinner-32 cmake_modules Converting geocouch to CMake. April 07, 2014
Octocat-spinner-32 etc Spatial index merging. December 04, 2011
Octocat-spinner-32 share Fix spatial design docs test July 12, 2012
Octocat-spinner-32 src MB-10138: Writing a header now also returns the position February 06, 2014
Octocat-spinner-32 test MB-6990: Add validation of spatial function October 24, 2012
Octocat-spinner-32 .gitignore Converting geocouch to CMake. April 07, 2014
Octocat-spinner-32 CMakeLists.txt Converting geocouch to CMake. April 07, 2014
Octocat-spinner-32 README.md MB-7013: There is a branch for Apache CouchDB 1.3.x now October 25, 2012
Octocat-spinner-32 rebar GC-3: Make COUCH_SRC parsing less fragile December 19, 2012
Octocat-spinner-32 rebar.config MB-100: Make GeoCouch safe for future rebar versions July 20, 2012
Octocat-spinner-32 rebar.config.script Converting geocouch to CMake. April 07, 2014
README.md

Welcome to the world of GeoCouch

GeoCouch is a spatial extension for Apache CouchDB and Couchbase.

Prerequisites

A working installation of CouchDB with corresponding source code. GeoCouch works best with Couchbase and the latest stable releases of CouchDB (should be >= 1.1.0).

Understanding the branches:

This repository contains several branches, please make sure you use the correct one:

  • master: works with the CouchDB master branch from Couchbase's repo (https://github.com/couchbase/couchdb)
  • couchdb1.1.x: works with Apache CouchDB 1.1.x
  • couchdb1.2.x: works with Apache CouchDB 1.2.x
  • couchdb1.3.x: works with Apache CouchDB 1.3.x

Installation

Get GeoCouch:

git clone https://github.com/couchbase/geocouch.git
cd geocouch

Compilation

Note: Always replace <vanilla-couch> with the path to your CouchDB source and <geocouch> with the location of the GeoCouch source.

Set the COUCH_SRC environment to the directory that contains the CouchDB core source (<vanilla-couch>/src/couchdb/).

export COUCH_SRC=<vanilla-couch>/src/couchdb

Run "make" in your directory

make

Copy the configuration file for GeoCouch from <geocouch>/etc/couchdb/default.d/ to <vanilla-couch>/etc/couchdb/default.d/

cp <geocouch>/etc/couchdb/default.d/geocouch.ini <vanilla-couch>/etc/couchdb/default.d/

Futon tests

To make sure your installation is working also copy the Futon tests over (from <geocouch>/share/www/script/test to <vanilla-couch>/share/www/script/test):

cp <geocouch>/share/www/script/test/* <vanilla-couch>/share/www/script/test/

Add the test to <vanilla-couch>/share/www/script/couch_tests.js

loadTest("spatial.js");
loadTest("list_spatial.js");
loadTest("etags_spatial.js");
loadTest("multiple_spatial_rows.js");
loadTest("spatial_compaction.js");
loadTest("spatial_design_docs.js");
loadTest("spatial_bugfixes.js");
loadTest("spatial_merging.js");
loadTest("spatial_offsets.js");

Run CouchDB with GeoCouch

The compiled beam files from GeoCouch need to be in Erlang's path, which can be set with the ERL_FLAGS environment variable:

export ERL_FLAGS="-pa <geocouch>/ebin"

If you run a dev instance with CouchDB's ./utils/run you can also define it on startup:

ERL_FLAGS="-pa <geocouch>/ebin" <vanilla-couch>/utils/run

Using GeoCouch

Create a database:

curl -X PUT http://127.0.0.1:5984/places

Add a Design Document with a spatial function:

curl -X PUT -d '{"spatial":{"points":"function(doc) {\n    if (doc.loc) {\n        emit({\n            type: \"Point\",\n            coordinates: [doc.loc[0], doc.loc[1]]\n        }, [doc._id, doc.loc]);\n    }};"}}' http://127.0.0.1:5984/places/_design/main

Put some data into it:

curl -X PUT -d '{"loc": [-122.270833, 37.804444]}' http://127.0.0.1:5984/places/oakland
curl -X PUT -d '{"loc": [10.898333, 48.371667]}' http://127.0.0.1:5984/places/augsburg

Make a bounding box request:

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=0,0,180,90'

It should return:

{"update_seq":3,"rows":[
{"id":"augsburg","bbox":[10.898333,48.371667,10.898333,48.371667],"geometry":{"type":"Point","coordinates":[10.898333,48.371667]},"value":["augsburg",[10.898333,48.371667]]}
]}

The Design Document Function

function(doc) { if (doc.loc) { emit({ type: "Point", coordinates: [doc.loc[0], doc.loc[1]] }, [doc._id, doc.loc]); }};"

It uses the emit() from normal views. The key is a GeoJSON geometry, the value is any arbitrary JSON. All geometry types (even GemetryCollections) are supported.

If the GeoJSON geometry contains a bbox property it will be used instead of calculating it from the geometry (even if it's wrong, i.e. is not the actual bounding box).

Bounding box search and the date line

A common problem when performing bounding box searches is the date line/poles. As the bounding box follows the GeoJSON specification, where the first two numbers are the lower left coordinate, the last two numbers the upper right coordinate, it is easy to map it over the date line/poles. The lower coordinate would have a higher value than the upper one. Such a bounding box has a seems invalid at first glance, but isn't. For example a bounding box like 110,-60,-30,15 would include Australia and South America, but not Africa.

GeoCouch operates on a plane and doesn't perform spherical calculations. Therefore the bounds of the plane needs to be set explicitly with the plane_bounds parameter. If bounding boxes are flipped, a search across those bounds will be performed automatically. Give it a try (with the same Design Document as above). Insert some Documents:

curl -X PUT -d '{"loc": [17.15, -22.566667]}' http://127.0.0.1:5984/places/namibia
curl -X PUT -d '{"loc": [135, -25]}' http://127.0.0.1:5984/places/australia
curl -X PUT -d '{"loc": [-52.95, -10.65]}' http://127.0.0.1:5984/places/brasilia

And request only Australia and Brasilia:

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=110,-60,-30,15&plane_bounds=-180,-90,180,90'

The result is as expected:

{"update_seq":6,"rows":[
{"id":"australia","bbox":[135,-25,135,-25],"geometry":{"type":"Point","coordinates":[135,-25]},"value":["australia",[135,-25]]},
{"id":"brasilia","bbox":[-52.95,-10.65,-52.95,-10.65],"geometry":{"type":"Point","coordinates":[-52.95,-10.65]},"value":["brasilia",[-52.95,-10.65]]}
]}

The bounding with the same numbers, but different order (-30,-60,110,15) would only return Namibia:

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=-30,-60,110,15&plane_bounds=-180,-90,180,90'

{"update_seq":6,"rows":[
{"id":"namibia","bbox":[17.15,-22.566667,17.15,-22.566667],"geometry":{"type":"Point","coordinates":[17.15,-22.566667]},"value":["namibia",[17.15,-22.566667]]}
]}

List function support

GeoCouch supports List functions just as CouchDB does for Views. This way you can output any arbitrary format, e.g. GeoRSS.

As an example we output the points as WKT. Add a new Design Document with an additional List function (the rest is the same as above). Make sure you use the right _rev:

curl -X PUT -d '{"_rev": "1-121efc747b00743b8c7621ffccf1ac40", "lists": {"wkt": "function(head, req) {\n    var row;\n    while (row = getRow()) {\n        send(\"POINT(\" + row.geometry.coordinates.join(\" \") + \")\\n\");\n    }\n};"}, "spatial":{"points":"function(doc) {\n    if (doc.loc) {\n        emit({\n            type: \"Point\",\n            coordinates: [doc.loc[0], doc.loc[1]]\n        }, [doc._id, doc.loc]);\n    }};"}}' http://127.0.0.1:5984/places/_design/main

Now you can request this List function as you would do for CouchDB, though with a different Design handler (_spatial/_list instead of _list ):

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/_list/wkt/points?bbox=-180,-90,180,90'

The result is:

POINT(10.898333 48.371667)
POINT(-122.270833 37.804444)
POINT(17.15 -22.566667)
POINT(135 -25)
POINT(-52.95 -10.65)

Using List functions from Design Documents other than the one containing the Spatial functions is supported as well. This time we add the Document ID in parenthesis:

curl -X PUT -d '{"lists": {"wkt": "function(head, req) {\n    var row;\n    while (row = getRow()) {\n        send(\"POINT(\" + row.geometry.coordinates.join(\" \") + \") (\" + row.id + \")\\n\");\n    }\n};"}}' http://127.0.0.1:5984/places/_design/listfunonly

curl -X GET 'http://localhost:5984/places/_design/listfunonly/_spatial/_list/wkt/main/points?bbox=-180,-90,180,90'

Other supported query arguments

stale

stale=ok is supported. The spatial index won't be rebuilt even if new Documents were added. It works for normal spatial queries as well as for the spatial List functions.

count

count is a boolean. count=true will only return the number of geometries the query will return, not the geometry themselves.

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=0,0,180,90&count=true'

{"count":1}

limit

With limit you can limit the number of rows that should be returned.

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=-180,-90,180,90&limit=2'

{"update_seq":8,"rows":[
{"id":"augsburg","bbox":[10.898333,48.371667,10.898333,48.371667],"geometry":{"type":"Point","coordinates":[10.898333,48.371667]},"value":["augsburg",[10.898333,48.371667]]},
{"id":"oakland","bbox":[-122.270833,37.804444,-122.270833,37.804444],"geometry":{"type":"Point","coordinates":[-122.270833,37.804444]},"value":["oakland",[-122.270833,37.804444]]}
]}

skip

With skip you start to return the results at a certain offset.

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=-180,-90,180,90&skip=3'

{"update_seq":8,"rows":[
{"id":"australia","bbox":[135,-25,135,-25],"geometry":{"type":"Point","coordinates":[135,-25]},"value":["australia",[135,-25]]},
{"id":"brasilia","bbox":[-52.95,-10.65,-52.95,-10.65],"geometry":{"type":"Point","coordinates":[-52.95,-10.65]},"value":["brasilia",[-52.95,-10.65]]}
]}

Compaction, cleanup and info

The API of GeoCouch's spatial indexes is similar to the one for the Views. Compaction of spatial indexes is per Design Document, thus:

curl -X POST 'http://localhost:5984/places/_design/main/_spatial/_compact' -H 'Content-Type: application/json'

To cleanup spatial indexes that are no longer in use (this is per database):

curl -X POST 'http://localhost:5984/places/_spatial_cleanup' -H 'Content-Type: application/json'

To get information about the spatial indexes of a certain Design Document use the the _info handler:

curl -X GET 'http://localhost:5984/places/_design/main/_spatial/_info'
Something went wrong with that request. Please try again.