diff --git a/README.md b/README.md index 91ea5f5..f462eca 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,13 @@ Once you have the couchapp utility working, git clone this repo and ### [Spatial Views](https://github.com/couchbase/geocouch) -#### points.js +#### basic.js A spatial view that additionally emits the original GeoJSON value (doc.geometry) Example: - $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/points?bbox=80,88,90,90' + $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/basic?bbox=80,88,90,90' { "update_seq":203, "rows":[ @@ -56,13 +56,13 @@ Example: ] } -#### pointsFull.js +#### full.js -A spatial view that emits both GeoJSON and the full document (as value). +A spatial view that emits both GeoJSON and the full document (as value). Example: - $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/pointsFull?bbox=80,88,90,90' + $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/full?bbox=80,88,90,90' { "update_seq":203, "rows":[ @@ -89,13 +89,13 @@ Example: ] } -#### pointsOnly.js +#### minimal.js A spatial view that only emits GeoJSON and no additional value. Example: - $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/pointsOnly?bbox=80,88,90,90' + $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/minimal?bbox=80,88,90,90' { "update_seq":203, "rows":[ @@ -178,7 +178,7 @@ This list function generates a simple KML feed Example: - $ curl http://localhost:5984/yourdb/_design/geo/_spatiallist/kml/points?bbox=0,0,45,45 + $ curl http://localhost:5984/yourdb/_design/geo/_spatial/_list/kml/basic?bbox=0,0,45,45 @@ -198,7 +198,7 @@ This function outputs a GeoJSON FeatureCollection (compatible with OpenLayers). Example: - $curl -X GET 'http://localhost:5984/yourdb/_design/geo/_spatiallist/geojson/points?bbox=80,88,90,90' + $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/_list/geojson/basic?bbox=80,88,90,90' { "type":"FeatureCollection", "features":[ @@ -223,11 +223,9 @@ Example: This will take the centroid of the bbox parameter and a supplied radius parameter in meters and filter the rectangularly shaped bounding box result set by circular radius. -**WARNING** This only works with on points, not lines or polygons yet - Example: - $ curl -X GET http://localhost:5984/yourdb/_design/geo/_spatiallist/radius/points?bbox=-122.67,45.52,-122.67,45.52&radius=50 + $ curl 'http://localhost:5984/yourdb/_design/geo/_spatial/_list/radius/basic?bbox=-122.67,45.52,-122.67,45.52&radius=50' { "type":"FeatureCollection", "features":[ diff --git a/couchapp/_attachments/script/geojson-utils.js b/couchapp/_attachments/script/geojson-utils.js index 5f1e671..3fbf393 100755 --- a/couchapp/_attachments/script/geojson-utils.js +++ b/couchapp/_attachments/script/geojson-utils.js @@ -139,6 +139,30 @@ return { 'type': 'Point', 'coordinates': [y/f, x/f] }; }, + // checks if geometry lies entirely within a circle + // works with Point, LineString, Polygon + gju.geometryWithinRadius = function (geometry, center, radius) { + if (geometry.type == 'Point') { + return gju.pointDistance(geometry, center) <= radius; + } else if (geometry.type == 'LineString' || geometry.type == 'Polygon') { + var point = {}; + var coordinates; + if (geometry.type == 'Polygon') { + // it's enough to check the exterior ring of the Polygon + coordinates = geometry.coordinates[0]; + } else { + coordinates = geometry.coordinates; + } + for (var i in coordinates) { + point.coordinates = coordinates[i]; + if (gju.pointDistance(point, center) > radius) { + return false; + } + } + } + return true; + } + gju.simplify = function (source, kink) { /* source[] array of geojson points */ /* kink in metres, kinks above this depth kept */ diff --git a/couchapp/lists/radius.js b/couchapp/lists/radius.js index 032c01e..dc4bbc6 100755 --- a/couchapp/lists/radius.js +++ b/couchapp/lists/radius.js @@ -16,7 +16,6 @@ function(head, req) { "coordinates": [[[bbox[0], bbox[1]], [bbox[2], bbox[3]]]] }), callback = req.query.callback, - circle = gju.drawCircle(radius, center), startedOutput = false; if (req.headers.Accept.indexOf('application/json') != -1) @@ -27,7 +26,7 @@ function(head, req) { if ('callback' in req.query) send(req.query['callback'] + "("); send('{"type": "FeatureCollection", "features":['); while (row = getRow()) { - if (gju.pointInPolygon(row.value.geometry, circle)) { + if (gju.geometryWithinRadius(row.value.geometry, center, radius)) { if (startedOutput) send(",\n"); out = '{"type": "Feature", "geometry": ' + JSON.stringify(row.value.geometry); delete row.value.geometry; diff --git a/couchapp/rewrites.json b/couchapp/rewrites.json index bd21df1..16e96b1 100644 --- a/couchapp/rewrites.json +++ b/couchapp/rewrites.json @@ -20,7 +20,7 @@ "from": "" }, { - "to": "/_spatiallist/geojson/full", + "to": "/_spatial/_list/geojson/full", "from": "/data" }, { diff --git a/couchapp/vendor/geojson-utils.js b/couchapp/vendor/geojson-utils.js index 5f1e671..3fbf393 100644 --- a/couchapp/vendor/geojson-utils.js +++ b/couchapp/vendor/geojson-utils.js @@ -139,6 +139,30 @@ return { 'type': 'Point', 'coordinates': [y/f, x/f] }; }, + // checks if geometry lies entirely within a circle + // works with Point, LineString, Polygon + gju.geometryWithinRadius = function (geometry, center, radius) { + if (geometry.type == 'Point') { + return gju.pointDistance(geometry, center) <= radius; + } else if (geometry.type == 'LineString' || geometry.type == 'Polygon') { + var point = {}; + var coordinates; + if (geometry.type == 'Polygon') { + // it's enough to check the exterior ring of the Polygon + coordinates = geometry.coordinates[0]; + } else { + coordinates = geometry.coordinates; + } + for (var i in coordinates) { + point.coordinates = coordinates[i]; + if (gju.pointDistance(point, center) > radius) { + return false; + } + } + } + return true; + } + gju.simplify = function (source, kink) { /* source[] array of geojson points */ /* kink in metres, kinks above this depth kept */ diff --git a/misc/geocouch-filler-js/geocouch-filler.js b/misc/geocouch-filler-js/geocouch-filler.js index eff0c40..f8809de 100644 --- a/misc/geocouch-filler-js/geocouch-filler.js +++ b/misc/geocouch-filler-js/geocouch-filler.js @@ -6,71 +6,69 @@ var http = require('http'); var url = require('url'); var path = require('path'); - -require.paths.unshift(path.join(__dirname, 'lib')); var Barrier = require('barrier').Barrier; var httpClientPoolSize = 16; -var host,port,db; +var host, port, db; -if(process.argv.length < 5){ - console.log("\n\tUsage: node geocouch-filler.js <[bbox]> \n\n") - process.exit(1); +if (process.argv.length < 5) { + console.log("\n\tUsage: node geocouch-filler.js <[bbox]> \n\n") + process.exit(1); }; var uri = url.parse(process.argv[2] || "http://localhost:5984/gc-utils"); -var bbox = JSON.parse(process.argv[3] || "[-180,-90,180,90]") ; +var bbox = JSON.parse(process.argv[3] || "[-180,-90,180,90]"); var documentCount = parseInt(process.argv[4]) || 10; -if(uri && bbox && bbox.length === 4 && documentCount){ - host = uri.hostname; - port = uri.port || 5984; - db = uri.pathname.split("/")[1] || "gc-utils"; +if (uri && bbox && bbox.length === 4 && documentCount) { + host = uri.hostname; + port = uri.port || 5984; + db = uri.pathname.split("/")[1] || "gc-utils"; } -var randomArbitrary = function(min, max) { +var randomArbitrary = function (min, max) { return Math.random() * (max - min) + min; -}; + }; //create client pool to speed var clients = []; for (var i = 0; i < httpClientPoolSize; i++) { - clients.push(http.createClient(port, host)); + clients.push(http.createClient(port, host)); } //barrier allows to exit when queries have been executed -var b = new Barrier(documentCount, function() { - console.log("Insertion completed"); - process.exit(0); +var b = new Barrier(documentCount, function () { + console.log("Insertion completed"); + process.exit(0); }); var ptr = 0; for (var i = 0; i < documentCount; i++) { - var client = clients[ptr++ % httpClientPoolSize]; + var client = clients[ptr++ % httpClientPoolSize]; - var entity = { - "geometry" : { - "type" : "Point", - "coordinates": [randomArbitrary(bbox[0],bbox[2]),randomArbitrary(bbox[1],bbox[3])] - } - }; + var entity = { + "geometry": { + "type": "Point", + "coordinates": [randomArbitrary(bbox[0], bbox[2]), randomArbitrary(bbox[1], bbox[3])] + } + }; - var request = client.request('POST', "/" + db, { - "Content-Type": "application/json", - "Connection": "keep-alive" - }); - request.write(JSON.stringify(entity)); - request.end(); - request.once('response', function (response) { - if(response.statusCode === 201){ - console.log("Document "+response.headers['location']+" created"); - b.submit(); - } - else{ - console.log("POST caused "+response.statusCode+"!"); - b.submit(); - } - }); -} + var request = client.request('POST', "/" + db, { + "Content-Type": "application/json", + "Connection": "keep-alive" + }); + request.write(JSON.stringify(entity)); + request.end(); + request.once('response', function (response) { + + if (response.statusCode === 201) { + console.log("Document " + response.headers['location'] + " created"); + b.submit(); + } else { + console.log("POST caused " + response.statusCode + "!"); + b.submit(); + } + }); +} \ No newline at end of file diff --git a/misc/geocouch-filler-js/lib/barrier.js b/misc/geocouch-filler-js/lib/barrier.js deleted file mode 100644 index 59275ea..0000000 --- a/misc/geocouch-filler-js/lib/barrier.js +++ /dev/null @@ -1,57 +0,0 @@ -/** -* A simple barrier point coordination class for node.js -* -* See http://www.ioexception.de/2010/07/05/barrier-points-in-node-js/ for more details. -* -* @author Benjamin Erb | http://www.benjamin-erb.de -* -*/ - -/** -* @class -* -* Creates a new barrier for the given amount of parties. -* @param parties -* @param barrierCallback -* @param abortCallback -* @return -*/ -var Barrier = exports.Barrier = function(parties, barrierCallback, abortCallback) -{ -this.parties = parties; -this.barrierCallback = barrierCallback; -this.abortCallback = abortCallback; - -this.running = true; -this.count = 0; -}; - -/** -* Signals a completion of one of the parties. -* @return -*/ -Barrier.prototype.submit = function() -{ -if (++this.count === this.parties && this.running) -{ -this.barrierCallback(); -} -}; - -/** -* Signals an abort by one of the parties. If not callback is passed, the default abort callback will be executed. -* @param customAbortCallback Optional callback that should be executed due to the abort. -* @return -*/ -Barrier.prototype.abort = function(customAbortCallback) -{ -if (this.running && customAbortCallback) -{ -customAbortCallback(); -} -else if (this.running && this.abortCallback) -{ -this.abortCallback(); -} -this.running = false; -}; diff --git a/misc/geocouch-filler-js/node_modules/barrier.js b/misc/geocouch-filler-js/node_modules/barrier.js new file mode 100644 index 0000000..705fce7 --- /dev/null +++ b/misc/geocouch-filler-js/node_modules/barrier.js @@ -0,0 +1,41 @@ +/** + * A simple barrier point coordination class for node.js + * + * See http://www.ioexception.de/2010/07/05/barrier-points-in-node-js/ for more details. + * + * @class Creates a new barrier for the given amount of parties. + * @param parties + * @param barrierCallback + * @param abortCallback + * @author Benjamin Erb | http://www.benjamin-erb.de + */ +var Barrier = exports.Barrier = function (parties, barrierCallback, abortCallback) { + this.parties = parties; + this.barrierCallback = barrierCallback; + this.abortCallback = abortCallback; + + this.running = true; + this.count = 0; + }; + +/** + * Signals a completion of one of the parties. + */ +Barrier.prototype.submit = function () { + if (++this.count === this.parties && this.running) { + this.barrierCallback(); + } +}; + +/** + * Signals an abort by one of the parties. If not callback is passed, the default abort callback will be executed. + * @param customAbortCallback Optional callback that should be executed due to the abort. + */ +Barrier.prototype.abort = function (customAbortCallback) { + if (this.running && customAbortCallback) { + customAbortCallback(); + } else if (this.running && this.abortCallback) { + this.abortCallback(); + } + this.running = false; +}; \ No newline at end of file