├── .gitignore ├── LICENSE ├── README.md ├── geojson-utils.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Max Ogden 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GeoJSON Utilities for JavaScript 2 | ================================ 3 | 4 | Here you will find some functions to help you manipulate and work with GeoJSON 5 | objects. 6 | 7 | Some algorithms adapted from [bjwbell/canvas-geolib](https://github.com/bjwbell/canvas-geolib) 8 | 9 | ## How to use! 10 | 11 | Load up in a browser OR `npm install geojson-utils` 12 | 13 | ```javascript 14 | var gju = require('geojson-utils'); 15 | ``` 16 | 17 | You now have an object named `gju` that contains all of the helper functions. 18 | 19 | Remember, GeoJSON coordinates are ordered [x,y] or [longitude,latitude] to comply with the Open 20 | Geospatial Consortium's recommendation! 21 | 22 | ## Line intersections 23 | 24 | ```javascript 25 | gju.linesIntersect({ "type": "LineString", "coordinates": [[0, 2], [5, 2]] }, 26 | { "type": "LineString", "coordinates": [[3, 0], [3, 4], [4,4], [4,0]] }) 27 | // [{"type":"Point","coordinates":[2,3]},{"type":"Point","coordinates":[2,4]}] 28 | 29 | gju.linesIntersect({ "type": "LineString", "coordinates": [[0, 2], [5, 2]] }, 30 | { "type": "LineString", "coordinates": [[0, 0], [5, 0]] }) 31 | // false 32 | ``` 33 | 34 | ## Point in polygon 35 | 36 | ```javascript 37 | gju.pointInPolygon({"type":"Point","coordinates":[3,3]}, 38 | {"type":"Polygon", "coordinates":[[[0,0],[6,0],[6,6],[0,6]]]}) 39 | // [{"type":"Point","coordinates":[3,3]}] 40 | gju.pointInPolygon({"type":"Point","coordinates":[-1,-1]}, 41 | {"type":"Polygon", "coordinates":[[[0,0],[6,0],[6,6],[0,6]]]}) 42 | // false 43 | ``` 44 | 45 | ## Radius filtering 46 | 47 | If you retrieve a bunch of results from a bounding box query (common with R-tree 48 | geo DBs), but you want to filter the rectangular result set by circular radius: 49 | 50 | ```javascript 51 | // get the center of the original bounding box 52 | var center = gju.rectangleCentroid({ 53 | "type": "Polygon", 54 | "coordinates": [[[-122.677, 45.523], [-122.675, 45.524]]] 55 | }), 56 | // radius (in meters) 57 | radius = 100; 58 | 59 | for (var i in geometryObjectsWithinBBox) { 60 | if (gju.geometryWithinRadius(geometryObjectsWithinBBox[i], center, radius)) { 61 | // ... do stuff with objects inside the circle 62 | } 63 | } 64 | ``` 65 | 66 | ## Distance between two points 67 | 68 | Uses the Haversine distance formula to calculate the distance between two points 69 | on the Earth's curved surface (as the crow flies, no hills!). Returns the 70 | distance in meters. 71 | 72 | ```javascript 73 | gju.pointDistance({type: 'Point', coordinates:[-122.67738461494446, 45.52319466622903]}, 74 | {type: 'Point', coordinates:[-122.67652630805969, 45.52319466622903]}) 75 | // 66.86677669313518 76 | ``` 77 | 78 | ## and much much more! 79 | 80 | * "example usage with [GeoCouch](https://github.com/maxogden/vmxch/blob/master/lists/radius.js)" 81 | 82 | #### License 83 | 84 | The MIT License 85 | 86 | Copyright (c) 2010 Max Ogden 87 | 88 | Permission is hereby granted, free of charge, to any person obtaining a copy 89 | of this software and associated documentation files (the "Software"), to deal 90 | in the Software without restriction, including without limitation the rights 91 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 92 | copies of the Software, and to permit persons to whom the Software is 93 | furnished to do so, subject to the following conditions: 94 | 95 | The above copyright notice and this permission notice shall be included in 96 | all copies or substantial portions of the Software. 97 | 98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 99 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 100 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 101 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 102 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 103 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 104 | THE SOFTWARE. 105 | -------------------------------------------------------------------------------- /geojson-utils.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var gju = this.gju = {}; 3 | 4 | // Export the geojson object for **CommonJS** 5 | if (typeof module !== 'undefined' && module.exports) { 6 | module.exports = gju; 7 | } 8 | 9 | // adapted from http://www.kevlindev.com/gui/math/intersection/Intersection.js 10 | gju.lineStringsIntersect = function (l1, l2) { 11 | var intersects = []; 12 | for (var i = 0; i <= l1.coordinates.length - 2; ++i) { 13 | for (var j = 0; j <= l2.coordinates.length - 2; ++j) { 14 | var a1 = { 15 | x: l1.coordinates[i][1], 16 | y: l1.coordinates[i][0] 17 | }, 18 | a2 = { 19 | x: l1.coordinates[i + 1][1], 20 | y: l1.coordinates[i + 1][0] 21 | }, 22 | b1 = { 23 | x: l2.coordinates[j][1], 24 | y: l2.coordinates[j][0] 25 | }, 26 | b2 = { 27 | x: l2.coordinates[j + 1][1], 28 | y: l2.coordinates[j + 1][0] 29 | }, 30 | ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x), 31 | ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x), 32 | u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); 33 | if (u_b != 0) { 34 | var ua = ua_t / u_b, 35 | ub = ub_t / u_b; 36 | if (0 <= ua && ua <= 1 && 0 <= ub && ub <= 1) { 37 | intersects.push({ 38 | 'type': 'Point', 39 | 'coordinates': [a1.x + ua * (a2.x - a1.x), a1.y + ua * (a2.y - a1.y)] 40 | }); 41 | } 42 | } 43 | } 44 | } 45 | if (intersects.length == 0) intersects = false; 46 | return intersects; 47 | } 48 | 49 | // Bounding Box 50 | 51 | function boundingBoxAroundPolyCoords (coords) { 52 | var xAll = [], yAll = [] 53 | 54 | for (var i = 0; i < coords[0].length; i++) { 55 | xAll.push(coords[0][i][1]) 56 | yAll.push(coords[0][i][0]) 57 | } 58 | 59 | xAll = xAll.sort(function (a,b) { return a - b }) 60 | yAll = yAll.sort(function (a,b) { return a - b }) 61 | 62 | return [ [xAll[0], yAll[0]], [xAll[xAll.length - 1], yAll[yAll.length - 1]] ] 63 | } 64 | 65 | gju.pointInBoundingBox = function (point, bounds) { 66 | return !(point.coordinates[1] < bounds[0][0] || point.coordinates[1] > bounds[1][0] || point.coordinates[0] < bounds[0][1] || point.coordinates[0] > bounds[1][1]) 67 | } 68 | 69 | // Point in Polygon 70 | // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html#Listing the Vertices 71 | 72 | function pnpoly (x,y,coords) { 73 | var vert = [ [0,0] ] 74 | 75 | for (var i = 0; i < coords.length; i++) { 76 | for (var j = 0; j < coords[i].length; j++) { 77 | vert.push(coords[i][j]) 78 | } 79 | vert.push(coords[i][0]) 80 | vert.push([0,0]) 81 | } 82 | 83 | var inside = false 84 | for (var i = 0, j = vert.length - 1; i < vert.length; j = i++) { 85 | if (((vert[i][0] > y) != (vert[j][0] > y)) && (x < (vert[j][1] - vert[i][1]) * (y - vert[i][0]) / (vert[j][0] - vert[i][0]) + vert[i][1])) inside = !inside 86 | } 87 | 88 | return inside 89 | } 90 | 91 | gju.pointInPolygon = function (p, poly) { 92 | var coords = (poly.type == "Polygon") ? [ poly.coordinates ] : poly.coordinates 93 | 94 | var insideBox = false 95 | for (var i = 0; i < coords.length; i++) { 96 | if (gju.pointInBoundingBox(p, boundingBoxAroundPolyCoords(coords[i]))) insideBox = true 97 | } 98 | if (!insideBox) return false 99 | 100 | var insidePoly = false 101 | for (var i = 0; i < coords.length; i++) { 102 | if (pnpoly(p.coordinates[1], p.coordinates[0], coords[i])) insidePoly = true 103 | } 104 | 105 | return insidePoly 106 | } 107 | 108 | // support multi (but not donut) polygons 109 | gju.pointInMultiPolygon = function (p, poly) { 110 | var coords_array = (poly.type == "MultiPolygon") ? [ poly.coordinates ] : poly.coordinates 111 | 112 | var insideBox = false 113 | var insidePoly = false 114 | for (var i = 0; i < coords_array.length; i++){ 115 | var coords = coords_array[i]; 116 | for (var j = 0; j < coords.length; j++) { 117 | if (!insideBox){ 118 | if (gju.pointInBoundingBox(p, boundingBoxAroundPolyCoords(coords[j]))) { 119 | insideBox = true 120 | } 121 | } 122 | } 123 | if (!insideBox) return false 124 | for (var j = 0; j < coords.length; j++) { 125 | if (!insidePoly){ 126 | if (pnpoly(p.coordinates[1], p.coordinates[0], coords[j])) { 127 | insidePoly = true 128 | } 129 | } 130 | } 131 | } 132 | 133 | return insidePoly 134 | } 135 | 136 | gju.numberToRadius = function (number) { 137 | return number * Math.PI / 180; 138 | } 139 | 140 | gju.numberToDegree = function (number) { 141 | return number * 180 / Math.PI; 142 | } 143 | 144 | // written with help from @tautologe 145 | gju.drawCircle = function (radiusInMeters, centerPoint, steps) { 146 | var center = [centerPoint.coordinates[1], centerPoint.coordinates[0]], 147 | dist = (radiusInMeters / 1000) / 6371, 148 | // convert meters to radiant 149 | radCenter = [gju.numberToRadius(center[0]), gju.numberToRadius(center[1])], 150 | steps = steps || 15, 151 | // 15 sided circle 152 | poly = [[center[0], center[1]]]; 153 | for (var i = 0; i < steps; i++) { 154 | var brng = 2 * Math.PI * i / steps; 155 | var lat = Math.asin(Math.sin(radCenter[0]) * Math.cos(dist) 156 | + Math.cos(radCenter[0]) * Math.sin(dist) * Math.cos(brng)); 157 | var lng = radCenter[1] + Math.atan2(Math.sin(brng) * Math.sin(dist) * Math.cos(radCenter[0]), 158 | Math.cos(dist) - Math.sin(radCenter[0]) * Math.sin(lat)); 159 | poly[i] = []; 160 | poly[i][1] = gju.numberToDegree(lat); 161 | poly[i][0] = gju.numberToDegree(lng); 162 | } 163 | return { 164 | "type": "Polygon", 165 | "coordinates": [poly] 166 | }; 167 | } 168 | 169 | // assumes rectangle starts at lower left point 170 | gju.rectangleCentroid = function (rectangle) { 171 | var bbox = rectangle.coordinates[0]; 172 | var xmin = bbox[0][0], 173 | ymin = bbox[0][1], 174 | xmax = bbox[2][0], 175 | ymax = bbox[2][1]; 176 | var xwidth = xmax - xmin; 177 | var ywidth = ymax - ymin; 178 | return { 179 | 'type': 'Point', 180 | 'coordinates': [xmin + xwidth / 2, ymin + ywidth / 2] 181 | }; 182 | } 183 | 184 | // from http://www.movable-type.co.uk/scripts/latlong.html 185 | gju.pointDistance = function (pt1, pt2) { 186 | var lon1 = pt1.coordinates[0], 187 | lat1 = pt1.coordinates[1], 188 | lon2 = pt2.coordinates[0], 189 | lat2 = pt2.coordinates[1], 190 | dLat = gju.numberToRadius(lat2 - lat1), 191 | dLon = gju.numberToRadius(lon2 - lon1), 192 | a = Math.pow(Math.sin(dLat / 2), 2) + Math.cos(gju.numberToRadius(lat1)) 193 | * Math.cos(gju.numberToRadius(lat2)) * Math.pow(Math.sin(dLon / 2), 2), 194 | c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 195 | return (6371 * c) * 1000; // returns meters 196 | }, 197 | 198 | // checks if geometry lies entirely within a circle 199 | // works with Point, LineString, Polygon 200 | gju.geometryWithinRadius = function (geometry, center, radius) { 201 | if (geometry.type == 'Point') { 202 | return gju.pointDistance(geometry, center) <= radius; 203 | } else if (geometry.type == 'LineString' || geometry.type == 'Polygon') { 204 | var point = {}; 205 | var coordinates; 206 | if (geometry.type == 'Polygon') { 207 | // it's enough to check the exterior ring of the Polygon 208 | coordinates = geometry.coordinates[0]; 209 | } else { 210 | coordinates = geometry.coordinates; 211 | } 212 | for (var i in coordinates) { 213 | point.coordinates = coordinates[i]; 214 | if (gju.pointDistance(point, center) > radius) { 215 | return false; 216 | } 217 | } 218 | } 219 | return true; 220 | } 221 | 222 | // adapted from http://paulbourke.net/geometry/polyarea/javascript.txt 223 | gju.area = function (polygon) { 224 | var area = 0; 225 | // TODO: polygon holes at coordinates[1] 226 | var points = polygon.coordinates[0]; 227 | var j = points.length - 1; 228 | var p1, p2; 229 | 230 | for (var i = 0; i < points.length; j = i++) { 231 | var p1 = { 232 | x: points[i][1], 233 | y: points[i][0] 234 | }; 235 | var p2 = { 236 | x: points[j][1], 237 | y: points[j][0] 238 | }; 239 | area += p1.x * p2.y; 240 | area -= p1.y * p2.x; 241 | } 242 | 243 | area /= 2; 244 | return area; 245 | }, 246 | 247 | // adapted from http://paulbourke.net/geometry/polyarea/javascript.txt 248 | gju.centroid = function (polygon) { 249 | var f, x = 0, 250 | y = 0; 251 | // TODO: polygon holes at coordinates[1] 252 | var points = polygon.coordinates[0]; 253 | var j = points.length - 1; 254 | var p1, p2; 255 | 256 | for (var i = 0; i < points.length; j = i++) { 257 | var p1 = { 258 | x: points[i][1], 259 | y: points[i][0] 260 | }; 261 | var p2 = { 262 | x: points[j][1], 263 | y: points[j][0] 264 | }; 265 | f = p1.x * p2.y - p2.x * p1.y; 266 | x += (p1.x + p2.x) * f; 267 | y += (p1.y + p2.y) * f; 268 | } 269 | 270 | f = gju.area(polygon) * 6; 271 | return { 272 | 'type': 'Point', 273 | 'coordinates': [y / f, x / f] 274 | }; 275 | }, 276 | 277 | gju.simplify = function (source, kink) { /* source[] array of geojson points */ 278 | /* kink in metres, kinks above this depth kept */ 279 | /* kink depth is the height of the triangle abc where a-b and b-c are two consecutive line segments */ 280 | kink = kink || 20; 281 | source = source.map(function (o) { 282 | return { 283 | lng: o.coordinates[0], 284 | lat: o.coordinates[1] 285 | } 286 | }); 287 | 288 | var n_source, n_stack, n_dest, start, end, i, sig; 289 | var dev_sqr, max_dev_sqr, band_sqr; 290 | var x12, y12, d12, x13, y13, d13, x23, y23, d23; 291 | var F = (Math.PI / 180.0) * 0.5; 292 | var index = new Array(); /* aray of indexes of source points to include in the reduced line */ 293 | var sig_start = new Array(); /* indices of start & end of working section */ 294 | var sig_end = new Array(); 295 | 296 | /* check for simple cases */ 297 | 298 | if (source.length < 3) return (source); /* one or two points */ 299 | 300 | /* more complex case. initialize stack */ 301 | 302 | n_source = source.length; 303 | band_sqr = kink * 360.0 / (2.0 * Math.PI * 6378137.0); /* Now in degrees */ 304 | band_sqr *= band_sqr; 305 | n_dest = 0; 306 | sig_start[0] = 0; 307 | sig_end[0] = n_source - 1; 308 | n_stack = 1; 309 | 310 | /* while the stack is not empty ... */ 311 | while (n_stack > 0) { 312 | 313 | /* ... pop the top-most entries off the stacks */ 314 | 315 | start = sig_start[n_stack - 1]; 316 | end = sig_end[n_stack - 1]; 317 | n_stack--; 318 | 319 | if ((end - start) > 1) { /* any intermediate points ? */ 320 | 321 | /* ... yes, so find most deviant intermediate point to 322 | either side of line joining start & end points */ 323 | 324 | x12 = (source[end].lng() - source[start].lng()); 325 | y12 = (source[end].lat() - source[start].lat()); 326 | if (Math.abs(x12) > 180.0) x12 = 360.0 - Math.abs(x12); 327 | x12 *= Math.cos(F * (source[end].lat() + source[start].lat())); /* use avg lat to reduce lng */ 328 | d12 = (x12 * x12) + (y12 * y12); 329 | 330 | for (i = start + 1, sig = start, max_dev_sqr = -1.0; i < end; i++) { 331 | 332 | x13 = source[i].lng() - source[start].lng(); 333 | y13 = source[i].lat() - source[start].lat(); 334 | if (Math.abs(x13) > 180.0) x13 = 360.0 - Math.abs(x13); 335 | x13 *= Math.cos(F * (source[i].lat() + source[start].lat())); 336 | d13 = (x13 * x13) + (y13 * y13); 337 | 338 | x23 = source[i].lng() - source[end].lng(); 339 | y23 = source[i].lat() - source[end].lat(); 340 | if (Math.abs(x23) > 180.0) x23 = 360.0 - Math.abs(x23); 341 | x23 *= Math.cos(F * (source[i].lat() + source[end].lat())); 342 | d23 = (x23 * x23) + (y23 * y23); 343 | 344 | if (d13 >= (d12 + d23)) dev_sqr = d23; 345 | else if (d23 >= (d12 + d13)) dev_sqr = d13; 346 | else dev_sqr = (x13 * y12 - y13 * x12) * (x13 * y12 - y13 * x12) / d12; // solve triangle 347 | if (dev_sqr > max_dev_sqr) { 348 | sig = i; 349 | max_dev_sqr = dev_sqr; 350 | } 351 | } 352 | 353 | if (max_dev_sqr < band_sqr) { /* is there a sig. intermediate point ? */ 354 | /* ... no, so transfer current start point */ 355 | index[n_dest] = start; 356 | n_dest++; 357 | } else { /* ... yes, so push two sub-sections on stack for further processing */ 358 | n_stack++; 359 | sig_start[n_stack - 1] = sig; 360 | sig_end[n_stack - 1] = end; 361 | n_stack++; 362 | sig_start[n_stack - 1] = start; 363 | sig_end[n_stack - 1] = sig; 364 | } 365 | } else { /* ... no intermediate points, so transfer current start point */ 366 | index[n_dest] = start; 367 | n_dest++; 368 | } 369 | } 370 | 371 | /* transfer last point */ 372 | index[n_dest] = n_source - 1; 373 | n_dest++; 374 | 375 | /* make return array */ 376 | var r = new Array(); 377 | for (var i = 0; i < n_dest; i++) 378 | r.push(source[index[i]]); 379 | 380 | return r.map(function (o) { 381 | return { 382 | type: "Point", 383 | coordinates: [o.lng, o.lat] 384 | } 385 | }); 386 | } 387 | 388 | // http://www.movable-type.co.uk/scripts/latlong.html#destPoint 389 | gju.destinationPoint = function (pt, brng, dist) { 390 | dist = dist/6371; // convert dist to angular distance in radians 391 | brng = gju.numberToRadius(brng); 392 | 393 | var lon1 = gju.numberToRadius(pt.coordinates[0]); 394 | var lat1 = gju.numberToRadius(pt.coordinates[1]); 395 | 396 | var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) + 397 | Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) ); 398 | var lon2 = lon1 + Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1), 399 | Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2)); 400 | lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180º 401 | 402 | return { 403 | 'type': 'Point', 404 | 'coordinates': [gju.numberToDegree(lon2), gju.numberToDegree(lat2)] 405 | }; 406 | }; 407 | 408 | })(); 409 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "geojson-utils", 3 | "version": "1.1.0", 4 | "description": "GeoJSON Utilities for JavaScript", 5 | "main": "geojson-utils.js", 6 | "scripts": { 7 | "test": "node test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/maxogden/geojson-js-utils.git" 12 | }, 13 | "keywords": [ 14 | "geojson", 15 | "geocouch" 16 | ], 17 | "author": "Max Ogden", 18 | "license": "MIT", 19 | "readmeFilename": "README.textile" 20 | } 21 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var gju = require('./') 2 | 3 | var diagonalUp = { "type": "LineString","coordinates": [ 4 | [0, 0], [10, 10] 5 | ]} 6 | var diagonalDown = { "type": "LineString","coordinates": [ 7 | [10, 0], [0, 10] 8 | ]} 9 | var farAway = { "type": "LineString","coordinates": [ 10 | [100, 100], [110, 110] 11 | ]} 12 | 13 | if (!gju.lineStringsIntersect(diagonalUp, diagonalDown)) throw new Error() 14 | if (gju.lineStringsIntersect(diagonalUp, farAway)) throw new Error() 15 | 16 | var box = { 17 | "type": "Polygon", 18 | "coordinates": [ 19 | [ [0, 0], [10, 0], [10, 10], [0, 10] ] 20 | ] 21 | } 22 | 23 | var inBox = {"type": "Point", "coordinates": [5, 5]} 24 | var outBox = {"type": "Point", "coordinates": [15, 15]} 25 | 26 | if (!gju.pointInPolygon(inBox, box)) throw new Error() 27 | if (gju.pointInPolygon(outBox, box)) throw new Error() 28 | 29 | if (gju.drawCircle(10, {"type": "Point", "coordinates": [0, 0]}).coordinates[0].length !== 15) throw new Error() 30 | if (gju.drawCircle(10, {"type": "Point", "coordinates": [0, 0]}, 50).coordinates[0].length !== 50) throw new Error() 31 | 32 | var centroid = gju.rectangleCentroid(box) 33 | if (centroid.coordinates[0] !== 5) throw new Error() 34 | if (centroid.coordinates[1] !== 5) throw new Error() 35 | 36 | var fairyLand = {"type": "Point", "coordinates": [-122.260000705719, 37.80919060818706]} 37 | var navalBase = {"type": "Point", "coordinates": [-122.32083320617676, 37.78774223089045]} 38 | if (Math.floor(gju.pointDistance(fairyLand, navalBase)) !== 5852) throw new Error() 39 | 40 | 41 | var point = {"type": "Point", "coordinates": [ 705, 261 ]}; 42 | var poly = {"type": "Polygon", "coordinates": [ [702.5,344.50000000000006], [801.890625, 43 | 245.109375], [749.7351485148515,234.28465346534657] ]}; 44 | 45 | if (gju.pointInPolygon(point,poly)) throw new Error(); 46 | 47 | point = {"type": "Point", "coordinates": [0.5, 0.5]}; 48 | 49 | poly = {"type": "Polygon", "coordinates": [[[0, 2], [2, 2], [2,0]]]}; 50 | 51 | if (gju.pointInPolygon(point,poly)) throw new Error(); 52 | 53 | var singlepoint = {"type": "Point", "coordinates": [-1, -1]}; 54 | var multipoly = {"type": "MultiPolygon", 55 | "coordinates": [ 56 | [ [ [0,0],[0,10],[10,10],[10,0],[0,0] ] ] , 57 | [ [ [10,10],[10,20],[20,20],[20,10],[10,10] ] ] 58 | ] 59 | }; 60 | 61 | if (!gju.pointInMultiPolygon(point,multipoly)) throw new Error(); 62 | if (gju.pointInMultiPolygon(singlepoint,multipoly)) throw new Error(); 63 | 64 | console.log('all passed') --------------------------------------------------------------------------------