{"_id":"_design/gc-utils","_rev":"3-b2829e04b1329d844f40e9b1f8da1fb2","vendor":{"clustering":{"KNNCluster":"/**\n * kNN-based Clustering\n */\n(function() {\n\tvar cluster = this.cluster = {};\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tmodule.exports = cluster;\n\t}\n\t\n\t//TODO\n})();","ProximityCluster":"/**\n * Proximity based clustering\n */\n(function() {\n\n var proxcluster = this.proxcluster = {};\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tmodule.exports = proxcluster;\n\t}\n\n var gju = require('vendor/geojson-js-utils/geojson-utils');\n\n proxcluster.PointCluster = function(threshold){\n this.clusters = [];\n this.points = [];\n this.distanceThreshold = threshold;\n\n };\n \n proxcluster.PointCluster.prototype.addToClosestCluster = function(point) {\n var distance = 40000; // Some large number\n var clusterToAddTo = null;\n var pos = point.geometry;\n for (var i = 0, cluster; cluster = this.clusters[i]; i++) {\n var center = cluster.center;\n \n if (center) {\n var d = gju.pointDistance(center, pos)/1000; //convert to km\n if ( d < this.distanceThreshold) {\n distance = d;\n clusterToAddTo = cluster;\n }\n }\n }\n\n if (clusterToAddTo) {\n clusterToAddTo.addPoint(point);\n } else {\n var cluster = new proxcluster.Cluster(this);\n cluster.addPoint(point);\n this.clusters.push(cluster);\n }\n };\n\n proxcluster.PointCluster.prototype.getClusters = function(){\n\n var clusterdata = [];\n for (var i = 0, cluster; cluster = this.clusters[i]; i++) {\n clusterdata.push({\"center\":cluster.center, \"points\":cluster.getPoints(), \"size\": cluster.getSize()});\n }\n return clusterdata;\n }\n\n /**\n * A cluster that contains points.\n *\n * @param {PointCluster} The PointCluster that this\n * cluster is associated with.\n * @constructor\n * @ignore\n */\n proxcluster.Cluster = function (PointCluster) {\n this.pointCluster = PointCluster;\n this.center = null;\n this.points = [];\n }\n\n /**\n * Add a point the cluster.\n *\n * @param {point} The point to add.\n * @return {boolean} True if the point was added.\n */\n proxcluster.Cluster.prototype.addPoint = function(point) {\n if (!this.center) {\n this.center = point.geometry;\n } else {\n var l = this.points.length + 1;\n var lat = (this.center.coordinates[0] * (l-1) + point.geometry.coordinates[0]) / l;\n var lng = (this.center.coordinates[1] * (l-1) + point.geometry.coordinates[1]) / l;\n this.center = {\"type\": \"Point\", \"coordinates\":[lat, lng]};\n }\n\n \n this.points.push(point);\n\n return true;\n };\n\n\n /**\n * Returns points in the cluster\n *\n * @return {Array.} The points\n */\n proxcluster.Cluster.prototype.getPoints = function() {\n return this.points;\n };\n\n /**\n * Returns number of points\n *\n * @return {int} number of points\n */\n proxcluster.Cluster.prototype.getSize = function() {\n return this.points.length;\n };\n\n\n})();"},"geojson-js-utils":{"README":"h1. GeoJSON Utilities for JavaScript\n\nHere you will find some functions to help you manipulate and work with GeoJSON objects.\n\nSome algorithms adapted from \"bjwbell/canvas-geolib\":https://github.com/bjwbell/canvas-geolib\n\nh2. How to use!\n\nLoad up in a browser OR with your favorite \"CommonJS module implementation\":http://wiki.commonjs.org/wiki/Modules/1.1:\n\n
\n  \n    var gju = require('geojson-utils.js');\n  
\n\n\nYou now have an object named gju that contains all of the helper functions.\n\nRemember, GeoJSON coordinates are ordered [y,x] to comply with the Open Geospatial Consortium's recommendation!\n\nh2. Line intersections\n\n
\n  \n    gju.linesIntersect({ \"type\": \"LineString\", \"coordinates\": [[0, 2], [5, 2]] },\n                     { \"type\": \"LineString\", \"coordinates\": [[3, 0], [3, 4], [4,4], [4,0]] })\n    => [{\"type\":\"Point\",\"coordinates\":[2,3]},{\"type\":\"Point\",\"coordinates\":[2,4]}]\n\n    gju.linesIntersect({ \"type\": \"LineString\", \"coordinates\": [[0, 2], [5, 2]] },\n                     { \"type\": \"LineString\", \"coordinates\": [[0, 0], [5, 0]] })\n    => false\n  \n
\n\nh2. Point in polygon\n\n
\n  \n    gju.pointInPolygon({\"type\":\"Point\",\"coordinates\":[3,3]}, \n                     {\"type\":\"Polygon\", \"coordinates\":[[[0,0],[6,0],[6,6],[0,6]]]})\n    => [{\"type\":\"Point\",\"coordinates\":[3,3]}]\n    gju.pointInPolygon({\"type\":\"Point\",\"coordinates\":[-1,-1]}, \n                     {\"type\":\"Polygon\", \"coordinates\":[[[0,0],[6,0],[6,6],[0,6]]]})\n    => false\n  \n
\n\nh2. Radius filtering\n\nWARNING: Only works against points right now. TODO: Lines and polygons\n\nIf you retrieve a bunch of results from a bounding box query (common with R-Tree geo DBs), but you want to filter the rectangular result set by circular radius:\n\n
\n  \n    // get the center of the original bounding box\n    var center = gju.rectangleCentroid({ \n      \"type\": \"Polygon\",\n      \"coordinates\": [[[-122.677, 45.523],[-122.675, 45.524]]]\n    }),\n    // draw a circular polygon from the center of\n    // the bounding box with a 100 meter radius\n    circle = g.drawCircle(100, center);\n    \n    for (var point in listOfPointsReturnedFromBoundingBoxQuery) {\n      if (gju.pointInPolygon(point, circle)) {\n        // ... do stuff with points inside the circle\n      } \n    }\n  \n
\n\nh2. Distance between two points\n\nUses the Haversine distance formula to calculate the distance between two points on the Earth's curved surface (as the crow flies, no hills!). Returns the distance in meters.\n\n
\n      \n    gju.pointDistance({type: 'Point', coordinates:[-122.67738461494446, 45.52319466622903]}, \n                      {type: 'Point', coordinates:[-122.67652630805969, 45.52319466622903]})\n    => 66.86677669313518\n  \n
\n\nh2. and much much more!\n\n* \"example usage with GeoCouch\":https://github.com/maxogden/vmxch/blob/master/lists/radius.js\n\nh4. License\n\nThe MIT License\n\nCopyright (c) 2010 Max Ogden\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.","geojson-utils":"(function() {\n var gju = this.gju = {};\n \n // Export the geojson object for **CommonJS**\n if (typeof module !== 'undefined' && module.exports) {\n module.exports = gju;\n }\n \n // adapted from http://www.kevlindev.com/gui/math/intersection/Intersection.js\n gju.lineStringsIntersect = function(l1, l2) {\n var intersects = [];\n for (var i = 0; i <= l1.coordinates.length - 2; ++i) {\n for (var j = 0; j <= l2.coordinates.length - 2; ++j) { \n var a1 = {x: l1.coordinates[i][1], y: l1.coordinates[i][0]},\n a2 = {x: l1.coordinates[i+1][1], y: l1.coordinates[i+1][0]},\n b1 = {x: l2.coordinates[j][1], y: l2.coordinates[j][0]},\n b2 = {x: l2.coordinates[j+1][1], y: l2.coordinates[j+1][0]},\n ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x),\n ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x),\n u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y);\n if ( u_b != 0 ) {\n var ua = ua_t / u_b,\n ub = ub_t / u_b;\n if ( 0 <= ua && ua <= 1 && 0 <= ub && ub <= 1 ) {\n intersects.push({ \n 'type': 'Point',\n 'coordinates': [a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)]\n });\n }\n }\n }\n }\n if (intersects.length == 0) intersects = false;\n return intersects;\n }\n\n // adapted from http://jsfromhell.com/math/is-point-in-poly\n gju.pointInPolygon = function(point, polygon) {\n var x = point.coordinates[1],\n y = point.coordinates[0],\n poly = polygon.coordinates[0]; //TODO: support polygons with holes\n for (var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i) {\n var px = poly[i][1], py = poly[i][0],\n jx = poly[j][1], jy = poly[j][0];\n if (((py <= y && y < jy) || (jy <= y && y < py)) && (x < (jx - px) * (y - py) / (jy - py) + px)) {\n c = [point];\n }\n }\n return c;\n }\n \n gju.numberToRadius = function(number) {\n return number * Math.PI / 180;\n }\n\n gju.numberToDegree = function(number) {\n return number * 180 / Math.PI;\n }\n \n // written with help from @tautologe \n gju.drawCircle = function(radiusInMeters, centerPoint) {\n var center = [centerPoint.coordinates[1], centerPoint.coordinates[0]],\n dist = (radiusInMeters / 1000) / 6371, // convert meters to radiant\n radCenter = [gju.numberToRadius(center[0]), gju.numberToRadius(center[1])],\n steps = 15, // 15 sided circle\n poly = [[center[0], center[1]]];\n for (var i = 0; i < steps + 1; i++) {\n \tvar brng = 2 * Math.PI * i / steps; \n \tvar lat = Math.asin(Math.sin(radCenter[0]) * Math.cos(dist) + \n Math.cos(radCenter[0]) * Math.sin(dist) * Math.cos(brng));\n \tvar lng = radCenter[1] + Math.atan2(Math.sin(brng) * Math.sin(dist) *\n Math.cos(radCenter[0]), \n Math.cos(dist) - Math.sin(radCenter[0]) *\n Math.sin(lat));\n poly[i] = [];\n poly[i][1] = gju.numberToDegree(lat);\n poly[i][0] = gju.numberToDegree(lng);\n }\n return { \"type\": \"Polygon\",\n \"coordinates\": [poly] };\n }\n\n gju.rectangleCentroid = function(rectangle) {\n var bbox = rectangle.coordinates[0];\n var xmin = bbox[0][0], ymin = bbox[0][1], xmax = bbox[1][0], ymax = bbox[1][1];\n var xwidth = xmax - xmin;\n var ywidth = ymax - ymin;\n return { 'type': 'Point',\n \t 'coordinates': [xmin + xwidth/2, ymin + ywidth/2] };\n }\n \n // from http://www.movable-type.co.uk/scripts/latlong.html\n gju.pointDistance = function(pt1, pt2) {\n var lon1 = pt1.coordinates[0], lat1 = pt1.coordinates[1],\n lon2 = pt2.coordinates[0], lat2 = pt2.coordinates[1],\n dLat = gju.numberToRadius(lat2 - lat1),\n dLon = gju.numberToRadius(lon2 - lon1),\n a = Math.sin(dLat/2) * Math.sin(dLat/2) +\n Math.cos(gju.numberToRadius(lat1)) * Math.cos(gju.numberToRadius(lat2)) * \n Math.sin(dLon/2) * Math.sin(dLon/2),\n c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n return (6371 * c) * 1000; // returns meters\n }\n\n})();"}},"views":{"all":{"map":"/**\n * A simple map function mocking _all, but allows usage with lists etc.\n * \n */\nfunction(doc) {\n emit(doc.id, doc);\n}"}},"lists":{"knn-clustering":"/**\n * A clustering algorithm that clusters object based on their proximity, producing k distinct clusters.\n */\nfunction(head, req) {\n\tstart({\"code\": 501,\"headers\":{\"Content-Type\" : \"text/plain\"}});\n\t//TODO: implement kNN clustering list \n}","proximity-clustering":"/**\n * A clustering algorithm that clusters object based on their proximity, using a threshold value.\n */\nfunction(head, req) {\t\n\n var g = require('vendor/clustering/ProximityCluster'),\n row,\n threshold =100;\n\n start({\"headers\":{\"Content-Type\" : \"application/json\"}});\n if ('callback' in req.query) send(req.query['callback'] + \"(\");\n \n if('threshold' in req.query){ threshold = req.query.threshold;}\n var pc = new g.PointCluster(parseInt(threshold)); \n \n while (row = getRow()) {\n pc.addToClosestCluster(row.value);\n }\n\n send(JSON.stringify({\"rows\":pc.getClusters()}));\n\n if ('callback' in req.query) send(\")\");\n};","kml":"/**\n * A list function that transforms a spatial view result set into a KML feed.\n * \n * @author Benjamin Erb\n */\nfunction(head, req) {\n var row, out, sep = '\\n';\n \n start({\"headers\":{\"Content-Type\" : \"application/vnd.google-earth.kml+xml\"}});\n send('\\n');\n send('\\n');\n send('\\n');\n send('GeoCouch Result - KML Feed\\n');\n while (row = getRow()) {\n \tif(row.geometry){\n send('\\t');\n send(''+row.id+'');\n send(''+row.geometry.coordinates[0]+','+row.geometry.coordinates[1]+',0');\n send('\\n');\n \t}\n }\n send('\\n');\n send('\\n');\n};","radius":"/**\n * This will take the centroid of the bbox parameter and a supplied radius \n * parameter in meters and filter the rectangularly shaped bounding box \n * result set by circular radius.\n * \n * @author Max Ogden\n */\nfunction(head, req) {\n var gju = require('vendor/geojson-js-utils/geojson-utils'),\n row,\n out,\n radius = req.query.radius,\n bbox = JSON.parse(\"[\" + req.query.bbox + \"]\"),\n center = gju.rectangleCentroid({ \n \"type\": \"Polygon\",\n \"coordinates\": [[[bbox[0], bbox[1]], [bbox[2], bbox[3]]]]\n }),\n callback = req.query.callback,\n circle = gju.drawCircle(radius, center),\n startedOutput = false;\n \n if (req.headers.Accept.indexOf('application/json') != -1)\n start({\"headers\":{\"Content-Type\" : \"application/json\"}});\n else\n start({\"headers\":{\"Content-Type\" : \"text/plain\"}});\n\n if ('callback' in req.query) send(req.query['callback'] + \"(\");\n send('{\"type\": \"FeatureCollection\", \"features\":[');\n while (row = getRow()) {\n if (gju.pointInPolygon(row.geometry, circle)) {\n if (startedOutput) send(\",\\n\");\n out = '{\"type\": \"Feature\", \"geometry\": ' + JSON.stringify(row.geometry) +\n ', \"properties\": ' + JSON.stringify(row.value) + '}';\n send(out);\n startedOutput = true;\n }\n }\n send(\"\\n]};\");\n if ('callback' in req.query) send(\")\");\n};","geojson":"/**\n * This function outputs a GeoJSON FeatureCollection (compatible with\n * OpenLayers). JSONP requests are supported as well.\n *\n * @author Volker Mische\n */\nfunction(head, req) {\n var row, out, sep = '\\n';\n\n // Send the same Content-Type as CouchDB would\n if (req.headers.Accept.indexOf('application/json')!=-1) {\n start({\"headers\":{\"Content-Type\" : \"application/json\"}});\n }\n else {\n start({\"headers\":{\"Content-Type\" : \"text/plain\"}});\n }\n\n if ('callback' in req.query) {\n send(req.query['callback'] + \"(\");\n }\n\n send('{\"type\": \"FeatureCollection\", \"features\":[');\n while (row = getRow()) {\n out = JSON.stringify({type: \"Feature\", geometry: row.geometry,\n properties: row.value});\n send(sep + out);\n sep = ',\\n';\n }\n send(\"]}\");\n\n if ('callback' in req.query) {\n send(\")\");\n }\n};"},"spatial":{"geomsOnly":"/**\n * A simple spatial view that emits only the GeoJSON object without\n * further values.\n */\nfunction(doc){\n if(doc.geometry){\n emit(doc.geometry, null);\n }\n}","geomsFull":"/**\n * A simple spatial view that emits the GeoJSON plus the complete document.\n */\nfunction(doc){\n if(doc.geometry){\n emit(doc.geometry, doc);\n }\n}","geoms":"/**\n * A simple spatial view that emits GeoJSON plus the original document id.\n */\nfunction(doc){\n if(doc.geometry){\n emit(doc.geometry, doc._id);\n }\n}"},"couchapp":{"signatures":{},"objects":{},"manifest":["lists/","lists/radius.js","lists/kml.js","lists/geojson.js","lists/proximity-clustering.js","lists/knn-clustering.js","views/","views/all/","views/all/map.js","spatial/","spatial/geoms.js","spatial/geomsOnly.js","spatial/geomsFull.js","vendor/","vendor/geojson-js-utils/","vendor/geojson-js-utils/README.textile","vendor/geojson-js-utils/geojson-utils.js","vendor/clustering/","vendor/clustering/ProximityCluster.js","vendor/clustering/KNNCluster.js"]}}