xref: /2.1.1/geocouch/README.md (revision 5f6e9f19)
1Welcome to the world of GeoCouch
2================================
3
4GeoCouch is a spatial extension for Apache CouchDB and Couchbase.
5
6Prerequisites
7-------------
8
9A working installation of CouchDB with corresponding source
10code. GeoCouch works best with Couchbase and the latest stable releases of
11CouchDB (should be >= 1.1.0).
12
13### Understanding the branches:
14
15This repository contains several branches, please make sure you use
16the correct one:
17
18 - master: works with the CouchDB master branch from Couchbase's repo
19   (https://github.com/couchbase/couchdb)
20 - couchdb1.1.x: works with Apache CouchDB 1.1.x
21 - couchdb1.2.x: works with Apache CouchDB 1.2.x
22 - there is currently no branch for Apache CouchDB 1.3.x
23
24Installation
25------------
26
27### Get GeoCouch:
28
29    git clone https://github.com/couchbase/geocouch.git
30    cd geocouch
31
32### Compilation
33
34Note: Always replace `<vanilla-couch>` with the path to your CouchDB
35source and `<geocouch>` with the location of the GeoCouch source.
36
37Set the `COUCH_SRC` environment to the directory that contains the
38CouchDB core source (`<vanilla-couch>/src/couchdb/`).
39
40    export COUCH_SRC=<vanilla-couch>/src/couchdb
41
42Run "make" in your <geocouch> directory
43
44    make
45
46Copy the configuration file for GeoCouch from
47`<geocouch>/etc/couchdb/default.d/` to
48`<vanilla-couch>/etc/couchdb/default.d/`
49
50    cp <geocouch>/etc/couchdb/default.d/geocouch.ini <vanilla-couch>/etc/couchdb/default.d/
51
52### Futon tests
53
54To make sure your installation is working also copy the Futon tests
55over (from `<geocouch>/share/www/script/test` to
56`<vanilla-couch>/share/www/script/test`):
57
58    cp <geocouch>/share/www/script/test/* <vanilla-couch>/share/www/script/test/
59
60Add the test to `<vanilla-couch>/share/www/script/couch_tests.js`
61
62    loadTest("spatial.js");
63    loadTest("list_spatial.js");
64    loadTest("etags_spatial.js");
65    loadTest("multiple_spatial_rows.js");
66    loadTest("spatial_compaction.js");
67    loadTest("spatial_design_docs.js");
68    loadTest("spatial_bugfixes.js");
69    loadTest("spatial_merging.js");
70    loadTest("spatial_offsets.js");
71    loadTest("spatial_opensearch.js");
72
73### Run CouchDB with GeoCouch
74
75The compiled beam files from GeoCouch need to be in Erlang's path,
76which can be set with the `ERL_FLAGS` environment variable:
77
78    export ERL_FLAGS="-pa <geocouch>/ebin"
79
80If you run a dev instance with CouchDB's `./utils/run` you can also
81define it on startup:
82
83    ERL_FLAGS="-pa <geocouch>/ebin" <vanilla-couch>/utils/run
84
85
86Using GeoCouch
87--------------
88
89Create a database:
90
91    curl -X PUT http://127.0.0.1:5984/places
92
93Add a Design Document with a spatial function:
94
95    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
96
97Put some data into it:
98
99    curl -X PUT -d '{"loc": [-122.270833, 37.804444]}' http://127.0.0.1:5984/places/oakland
100    curl -X PUT -d '{"loc": [10.898333, 48.371667]}' http://127.0.0.1:5984/places/augsburg
101
102Make a bounding box request:
103
104    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=0,0,180,90'
105
106It should return:
107
108    {"update_seq":3,"rows":[
109    {"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]]}
110    ]}
111
112The Design Document Function
113----------------------------
114
115function(doc) {
116    if (doc.loc) {
117        emit({
118            type: "Point",
119            coordinates: [doc.loc[0], doc.loc[1]]
120        }, [doc._id, doc.loc]);
121    }};"
122
123It uses the emit() from normal views. The key is a
124[GeoJSON](http://geojson.org) geometry, the value is any arbitrary JSON. All
125geometry types (even GemetryCollections) are supported.
126
127If the GeoJSON geometry contains a `bbox` property it will be used instead
128of calculating it from the geometry (even if it's wrong, i.e. is not
129the actual bounding box).
130
131
132Bounding box search and the date line
133-------------------------------------
134
135A common problem when performing bounding box searches is the date
136line/poles. As the bounding box follows the GeoJSON specification,
137where the first two numbers are the lower left coordinate, the last
138two numbers the upper right coordinate, it is easy to map it over the
139date line/poles. The lower coordinate would have a higher value than
140the upper one. Such a bounding box has a seems invalid at first
141glance, but isn't. For example a bounding box like `110,-60,-30,15`
142would include Australia and South America, but not Africa.
143
144GeoCouch operates on a plane and doesn't perform spherical
145calculations. Therefore the bounds of the plane needs to be set
146explicitly with the `plane_bounds` parameter. If bounding boxes are
147flipped, a search across those bounds will be performed
148automatically. Give it a try (with the same Design Document as
149above). Insert some Documents:
150
151    curl -X PUT -d '{"loc": [17.15, -22.566667]}' http://127.0.0.1:5984/places/namibia
152    curl -X PUT -d '{"loc": [135, -25]}' http://127.0.0.1:5984/places/australia
153    curl -X PUT -d '{"loc": [-52.95, -10.65]}' http://127.0.0.1:5984/places/brasilia
154
155And request only Australia and Brasilia:
156
157    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=110,-60,-30,15&plane_bounds=-180,-90,180,90'
158
159The result is as expected:
160
161    {"update_seq":6,"rows":[
162    {"id":"australia","bbox":[135,-25,135,-25],"geometry":{"type":"Point","coordinates":[135,-25]},"value":["australia",[135,-25]]},
163    {"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]]}
164    ]}
165
166The bounding with the same numbers, but different order
167(`-30,-60,110,15`) would only return Namibia:
168
169    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=-30,-60,110,15&plane_bounds=-180,-90,180,90'
170
171    {"update_seq":6,"rows":[
172    {"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]]}
173    ]}
174
175List function support
176---------------------
177
178GeoCouch supports List functions just as CouchDB does for Views. This way
179you can output any arbitrary format, e.g. GeoRSS.
180
181As an example we output the points as WKT. Add a new Design Document
182with an additional List function (the rest is the same as above). Make
183sure you use the right `_rev`:
184
185    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
186
187Now you can request this List function as you would do for CouchDB,
188though with a different Design handler (`_spatial/_list` instead of
189`_list` ):
190
191    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/_list/wkt/points?bbox=-180,-90,180,90'
192
193The result is:
194
195    POINT(10.898333 48.371667)
196    POINT(-122.270833 37.804444)
197    POINT(17.15 -22.566667)
198    POINT(135 -25)
199    POINT(-52.95 -10.65)
200
201Using List functions from Design Documents other than the one containing the
202Spatial functions is supported as well. This time we add the Document
203ID in parenthesis:
204
205    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
206
207    curl -X GET 'http://localhost:5984/places/_design/listfunonly/_spatial/_list/wkt/main/points?bbox=-180,-90,180,90'
208
209
210Geometry search
211---------------
212
213The most common geometry search is probably polygon search, though all
214geometries as specified in the OpenSearch Geo (Draft 2) Specification [1]
215((Multi)Point, (Multi)LineString, (Multi)Polygon) are supported.
216
217Here's an example request with a polygon, spaces are encoded as "+".
218
219    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?geometry=POLYGON((-21.0+58.9,+21.0+-61.1,+113.9+-54.3,+150.4+72.289067198883,+-21.0+58.9))'
220
221    {"update_seq":8,"rows":[
222    {"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]]},
223    {"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]]}
224    ]}
225
226If you want to make a polygon request over the date line or poles, split it
227and make it a MultiPolygon.
228
229
230Other supported query arguments
231-------------------------------
232
233### stale ###
234`stale=ok` is supported. The spatial index won't be rebuilt even if
235new Documents were added. It works for normal spatial queries as well
236as for the spatial List functions.
237
238### count ###
239`count` is a boolean. `count=true` will only return the number of geometries
240the query will return, not the geometry themselves.
241
242    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=0,0,180,90&count=true'
243
244    {"count":1}
245
246### limit ###
247With `limit` you can limit the number of rows that should be returned.
248
249    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=-180,-90,180,90&limit=2'
250
251    {"update_seq":8,"rows":[
252    {"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]]},
253    {"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]]}
254    ]}
255
256### skip ###
257With `skip` you start to return the results at a certain offset.
258
259    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/points?bbox=-180,-90,180,90&skip=3'
260
261    {"update_seq":8,"rows":[
262    {"id":"australia","bbox":[135,-25,135,-25],"geometry":{"type":"Point","coordinates":[135,-25]},"value":["australia",[135,-25]]},
263    {"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]]}
264    ]}
265
266
267Compaction, cleanup and info
268----------------------------
269
270The API of GeoCouch's spatial indexes is similar to the one for the
271Views. Compaction of spatial indexes is per Design Document, thus:
272
273    curl -X POST 'http://localhost:5984/places/_design/main/_spatial/_compact' -H 'Content-Type: application/json'
274
275To cleanup spatial indexes that are no longer in use (this is per database):
276
277    curl -X POST 'http://localhost:5984/places/_spatial_cleanup' -H 'Content-Type: application/json'
278
279To get information about the spatial indexes of a certain Design
280Document use the the `_info` handler:
281
282    curl -X GET 'http://localhost:5984/places/_design/main/_spatial/_info'
283
284
285References
286----------
287
288[1] http://www.opensearch.org/Specifications/OpenSearch/Extensions/Geo/1.0/Draft_2
289