diff --git a/.gitignore b/.gitignore index c3e7e0b..75e8543 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,11 @@ build/Release # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git node_modules -.DS_Store \ No newline at end of file +.DS_Store + +# Output of tests +test/fixtures/out + +# Extra tests +test/test-extra.js +test/fixtures-extra diff --git a/bench/bench-run.js b/bench/bench-run.js new file mode 100644 index 0000000..7fca99d --- /dev/null +++ b/bench/bench-run.js @@ -0,0 +1,22 @@ +'use strict'; + +var Benchmark = require('benchmark'); + +module.exports = function (config) { + var suite = new Benchmark.Suite(); + + for (var name in config) { + suite.add(name, config[name]); + } + + suite + .on('cycle', function (event) { + console.log(String(event.target)); + }) + .on('complete', function () { + var fastest = this.filter('fastest')[0]; + var slowest = this.filter('slowest')[0]; + console.log(fastest.name + ' is ' + (Math.round(10 * fastest.hz / slowest.hz) / 10) + 'x faster'); + }) + .run(); +}; diff --git a/bench/bench-turf-buffer-lineString-large.js b/bench/bench-turf-buffer-lineString-large.js new file mode 100644 index 0000000..b6aa137 --- /dev/null +++ b/bench/bench-turf-buffer-lineString-large.js @@ -0,0 +1,28 @@ +'use strict'; + +var runBench = require('./bench-run.js'); + +var buffer_geodesicconvolution = require('../'); +var buffer_master = require('turf-buffer'); + +var fs = require('fs'); + +var lineString = JSON.parse(fs.readFileSync(__dirname+'/fixtures/LineString-brazil.geojson')); + +runBench({ + 'turf-buffer-lineString-master': function () { + buffer_master(lineString, 1, 'kilometers'); + }, + 'turf-buffer-lineString-geodesicconvolution': function () { + buffer_geodesicconvolution(lineString, 1, 'kilometers'); + } +}); + +runBench({ + 'turf-buffer-lineString-master': function () { + buffer_master(lineString, 100, 'kilometers'); + }, + 'turf-buffer-lineString-geodesicconvolution': function () { + buffer_geodesicconvolution(lineString, 100, 'kilometers'); + } +}); diff --git a/bench/bench-turf-buffer-lineString.js b/bench/bench-turf-buffer-lineString.js new file mode 100644 index 0000000..78a1b82 --- /dev/null +++ b/bench/bench-turf-buffer-lineString.js @@ -0,0 +1,19 @@ +'use strict'; + +var runBench = require('./bench-run.js'); + +var buffer_geodesicconvolution = require('../'); +var buffer_master = require('turf-buffer'); + +var fs = require('fs'); + +var lineString = JSON.parse(fs.readFileSync(__dirname+'/fixtures/LineString.geojson')); + +runBench({ + 'turf-buffer-lineString-master': function () { + buffer_master(lineString, 1, 'kilometers'); + }, + 'turf-buffer-lineString-geodesicconvolution': function () { + buffer_geodesicconvolution(lineString, 1, 'kilometers'); + } +}); diff --git a/bench/bench-turf-buffer-point.js b/bench/bench-turf-buffer-point.js new file mode 100644 index 0000000..6dfc81e --- /dev/null +++ b/bench/bench-turf-buffer-point.js @@ -0,0 +1,19 @@ +'use strict'; + +var runBench = require('./bench-run.js'); + +var buffer_geodesicconvolution = require('../'); +var buffer_master = require('turf-buffer'); + +var fs = require('fs'); + +var point = JSON.parse(fs.readFileSync(__dirname+'/fixtures/Point.geojson')); + +runBench({ + 'turf-buffer-point-master': function () { + buffer_master(point, 1, 'kilometers'); + }, + 'turf-buffer-point-geodesicconvolution': function () { + buffer_geodesicconvolution(point, 1, 'kilometers'); + } +}); diff --git a/bench/bench-turf-buffer-polygon-large.js b/bench/bench-turf-buffer-polygon-large.js new file mode 100644 index 0000000..afbef74 --- /dev/null +++ b/bench/bench-turf-buffer-polygon-large.js @@ -0,0 +1,28 @@ +'use strict'; + +var runBench = require('./bench-run.js'); + +var buffer_geodesicconvolution = require('../'); +var buffer_master = require('turf-buffer'); + +var fs = require('fs'); + +var polygon = JSON.parse(fs.readFileSync(__dirname+'/fixtures/Polygon-brazil.geojson')); + +runBench({ + 'turf-buffer-polygon-master': function () { + buffer_master(polygon, 1, 'kilometers'); + }, + 'turf-buffer-polygon-geodesicconvolution': function () { + buffer_geodesicconvolution(polygon, 1, 'kilometers'); + } +}); + +runBench({ + 'turf-buffer-polygon-master': function () { + buffer_master(polygon, 100, 'kilometers'); + }, + 'turf-buffer-polygon-geodesicconvolution': function () { + buffer_geodesicconvolution(polygon, 100, 'kilometers'); + } +}); diff --git a/bench/bench-turf-buffer-polygon.js b/bench/bench-turf-buffer-polygon.js new file mode 100644 index 0000000..f7971fe --- /dev/null +++ b/bench/bench-turf-buffer-polygon.js @@ -0,0 +1,19 @@ +'use strict'; + +var runBench = require('./bench-run.js'); + +var buffer_geodesicconvolution = require('../'); +var buffer_master = require('turf-buffer'); + +var fs = require('fs'); + +var polygon = JSON.parse(fs.readFileSync(__dirname+'/fixtures/Polygon.geojson')); + +runBench({ + 'turf-buffer-polygon-master': function () { + buffer_master(polygon, 1, 'kilometers'); + }, + 'turf-buffer-polygon-geodesicconvolution': function () { + buffer_geodesicconvolution(polygon, 1, 'kilometers'); + } +}); diff --git a/bench/fixtures/LineString-brazil.geojson b/bench/fixtures/LineString-brazil.geojson new file mode 100644 index 0000000..5bcddc4 --- /dev/null +++ b/bench/fixtures/LineString-brazil.geojson @@ -0,0 +1 @@ +{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-57.62513342958296,-30.21629485445426],[-56.29089962423908,-28.852760512000895],[-55.16228634298457,-27.881915378533463],[-54.490725267135524,-27.47475676850579],[-53.64873531758789,-26.92347258881609],[-53.628348965048744,-26.124865004177472],[-54.13004960795439,-25.547639255477254],[-54.625290696823576,-25.739255466415514],[-54.42894609233059,-25.162184747012166],[-54.29347632507745,-24.570799655863965],[-54.29295956075452,-24.02101409271073],[-54.65283423523513,-23.83957813893396],[-55.02790178080955,-24.00127369557523],[-55.40074723979542,-23.956935316668805],[-55.517639329639636,-23.571997572526637],[-55.610682745981144,-22.655619398694846],[-55.79795813660691,-22.356929620047822],[-56.47331743022939,-22.086300144135283],[-56.8815095689029,-22.28215382252148],[-57.937155727761294,-22.090175876557172],[-57.8706739976178,-20.73268767668195],[-58.166392381408045,-20.176700941653678],[-57.85380164247451,-19.96999521248619],[-57.949997321185826,-19.40000416430682],[-57.67600887717431,-18.96183969490403],[-57.49837114117099,-18.174187513911292],[-57.734558274961,-17.55246835700777],[-58.28080400250225,-17.271710300366017],[-58.38805843772404,-16.877109063385276],[-58.24121985536668,-16.299573256091293],[-60.158389655179036,-16.258283786690086],[-60.54296566429515,-15.093910414289596],[-60.251148851142936,-15.07721892665932],[-60.26432634137737,-14.645979099183641],[-60.45919816755003,-14.354007256734555],[-60.503304002511136,-13.775954685117659],[-61.08412126325565,-13.479383640194598],[-61.71320431176078,-13.489202162330052],[-62.127080857986385,-13.198780612849724],[-62.803060268796386,-13.000653171442686],[-63.19649878605057,-12.627032565972435],[-64.3163529120316,-12.461978041232193],[-65.40228146021303,-11.566270440317155],[-65.32189876978302,-10.895872084194679],[-65.44483700220539,-10.511451104375432],[-65.33843522811642,-9.761987806846392],[-66.6469083319628,-9.931331475466862],[-67.17380123561074,-10.306812432499612],[-68.04819230820539,-10.712059014532485],[-68.27125362819326,-11.01452117273682],[-68.78615759954948,-11.03638030359628],[-69.52967810736496,-10.951734307502194],[-70.0937522040469,-11.123971856331012],[-70.54868567572841,-11.009146823778465],[-70.48189388699117,-9.490118096558845],[-71.30241227892154,-10.079436130415374],[-72.18489071316985,-10.053597914269432],[-72.56303300646564,-9.520193780152717],[-73.22671342639016,-9.462212823121234],[-73.01538265653255,-9.032833347208062],[-73.57105933296707,-8.424446709835834],[-73.98723548042966,-7.523829847853065],[-73.7234014553635,-7.340998630404414],[-73.72448666044164,-6.91859547285064],[-73.1200274319236,-6.629930922068239],[-73.21971126981461,-6.089188734566078],[-72.9645072089412,-5.741251315944893],[-72.89192765978726,-5.274561455916981],[-71.74840572781655,-4.593982842633011],[-70.92884334988358,-4.401591485210368],[-70.7947688463023,-4.251264743673303],[-69.89363521999663,-4.298186944194327],[-69.44410193548961,-1.556287123219818],[-69.42048580593223,-1.122618503426409],[-69.5770653957766,-0.549991957200163],[-70.02065589057005,-0.185156345219539],[-70.01556576198931,0.541414292804205],[-69.45239600287246,0.706158758950693],[-69.25243404811906,0.602650865070075],[-69.21863766140018,0.985676581217433],[-69.80459672715773,1.089081122233466],[-69.81697323269162,1.714805202639624],[-67.86856502955884,1.692455145673392],[-67.5378100246747,2.03716278727633],[-67.2599975246736,1.719998684084956],[-67.0650481838525,1.130112209473225],[-66.87632585312258,1.253360500489336],[-66.32576514348496,0.724452215982012],[-65.54826738143757,0.78925446207603],[-65.35471330428837,1.0952822941085],[-64.61101192895987,1.328730576987042],[-64.19930579289051,1.49285492594602],[-64.08308549666609,1.91636912679408],[-63.368788011311665,2.200899562993129],[-63.42286739770512,2.411067613124175],[-64.2699991522658,2.497005520025567],[-64.40882788761792,3.126786200366624],[-64.3684944322141,3.797210394705246],[-64.81606401229402,4.056445217297423],[-64.62865943058755,4.14848094320925],[-63.88834286157416,4.020530096854571],[-63.093197597899106,3.770571193858785],[-62.804533047116706,4.006965033377952],[-62.08542965355913,4.162123521334308],[-60.96689327660154,4.536467596856639],[-60.601179165271944,4.91809804933213],[-60.73357418480372,5.200277207861901],[-60.21368343773133,5.244486395687602],[-59.980958624904886,5.014061184098139],[-60.11100236676738,4.574966538914083],[-59.767405768458715,4.423502915866607],[-59.53803992373123,3.958802598481938],[-59.815413174057866,3.606498521332085],[-59.97452490908456,2.755232652188056],[-59.71854570172675,2.24963043864436],[-59.64604366722126,1.786893825686789],[-59.03086157900265,1.317697658692722],[-58.540012986878295,1.268088283692521],[-58.429477098205965,1.463941962078721],[-58.11344987652502,1.507195135907025],[-57.66097103537737,1.682584947105639],[-57.335822923396904,1.948537705895759],[-56.78270423036083,1.863710842288654],[-56.539385748914555,1.899522609866921],[-55.995698004771754,1.817667141116601],[-55.905600145070885,2.02199575439866],[-56.0733418442903,2.220794989425499],[-55.973322109589375,2.510363877773017],[-55.569755011606,2.421506252447131],[-55.09758744975514,2.523748073736613],[-54.524754197799716,2.311848863123785],[-54.08806250671725,2.105556545414629],[-53.77852067728892,2.376702785650082],[-53.554839240113544,2.334896551925951],[-53.41846513529531,2.053389187015981],[-52.939657151894956,2.124857692875636],[-52.55642473001842,2.504705308437053],[-52.249337531123956,3.241094468596245],[-51.65779741067889,4.156232408053029],[-51.31714636901086,4.203490505383954],[-51.069771287629656,3.650397650564031],[-50.508875291533656,1.901563828942457],[-49.97407589374506,1.736483465986069],[-49.94710079608871,1.046189683431223],[-50.699251268096916,0.222984117021682],[-50.38821082213214,-0.078444512536819],[-48.62056677915632,-0.235489190271821],[-48.58449662941659,-1.237805271005001],[-47.824956427590635,-0.5816179337628],[-46.566583624851226,-0.941027520352776],[-44.905703090990414,-1.551739597178134],[-44.417619187993665,-2.137750339367976],[-44.58158850765578,-2.691308282078524],[-43.418791266440195,-2.383110039889793],[-41.47265682632825,-2.912018324397116],[-39.97866533055404,-2.873054294449041],[-38.50038347019657,-3.700652357603396],[-37.2232521225352,-4.820945733258917],[-36.45293738457639,-5.109403578312154],[-35.59779578301047,-5.149504489770649],[-35.23538896334756,-5.464937432480247],[-34.89602983248683,-6.738193047719711],[-34.729993455533034,-7.343220716992967],[-35.12821204277422,-8.996401462442286],[-35.636966518687714,-9.649281508017815],[-37.046518724097,-11.040721123908803],[-37.68361161960736,-12.171194756725823],[-38.42387651218844,-13.038118584854288],[-38.67388709161652,-13.057652276260619],[-38.953275722802545,-13.793369642800023],[-38.88229814304965,-15.667053724838768],[-39.16109249526431,-17.208406670808472],[-39.2673392400564,-17.867746270420483],[-39.58352149103423,-18.262295830968938],[-39.76082333022764,-19.59911345792741],[-40.77474077001034,-20.904511814052423],[-40.94475623225061,-21.93731698983781],[-41.754164191238225,-22.370675551037458],[-41.98828426773656,-22.970070489190896],[-43.07470374202475,-22.96769337330547],[-44.64781185563781,-23.351959323827842],[-45.35213578955992,-23.796841729428582],[-46.47209326840554,-24.088968601174543],[-47.64897233742066,-24.885199069927722],[-48.4954581365777,-25.877024834905654],[-48.64100480812774,-26.623697605090932],[-48.474735887228654,-27.17591196056189],[-48.661520351747626,-28.18613453543572],[-48.8884574041574,-28.674115085567884],[-49.587329474472675,-29.224469089476337],[-50.696874152211485,-30.98446502047296],[-51.576226162306156,-31.77769825615321],[-52.256081305538046,-32.24536996839467],[-52.712099982297694,-33.19657805759118],[-53.373661668498244,-33.768377780900764],[-53.6505439927181,-33.20200408298183],[-53.209588995971544,-32.727666110974724],[-53.78795162618219,-32.047242526987624],[-54.57245154480512,-31.494511407193748],[-55.601510179249345,-30.853878676071393],[-55.97324459494094,-30.883075860316303],[-56.976025763564735,-30.109686374636127],[-57.62513342958296,-30.21629485445426]]}} diff --git a/bench/fixtures/LineString.geojson b/bench/fixtures/LineString.geojson new file mode 100644 index 0000000..991f184 --- /dev/null +++ b/bench/fixtures/LineString.geojson @@ -0,0 +1,41 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -58.45962524414062, + -34.5286213832762 + ], + [ + -58.50151062011719, + -34.54841811625336 + ], + [ + -58.531723022460945, + -34.61625687562895 + ], + [ + -58.530349731445305, + -34.65523908026755 + ], + [ + -58.45550537109375, + -34.70831578223845 + ], + [ + -58.41636657714844, + -34.65806316573297 + ], + [ + -58.363494873046875, + -34.651285198954135 + ], + [ + -58.34564208984375, + -34.62925297943919 + ] + ] + } +} \ No newline at end of file diff --git a/bench/fixtures/MultiLineString.geojson b/bench/fixtures/MultiLineString.geojson new file mode 100644 index 0000000..60d5690 --- /dev/null +++ b/bench/fixtures/MultiLineString.geojson @@ -0,0 +1,37 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiLineString", + "coordinates": [ + [ + [ + 3.70513916015625, + 51.03945105265764 + ], + [ + 3.71063232421875, + 51.06545683812254 + ], + [ + 3.7278421020507812, + 51.04279701362964 + ], + [ + 3.746038208007812, + 51.0584442677861 + ] + ], + [ + [ + 3.7441062927246094, + 51.04387630433032 + ], + [ + 3.6912345886230464, + 51.059631085129986 + ] + ] + ] + } + } \ No newline at end of file diff --git a/bench/fixtures/MultiPoint.geojson b/bench/fixtures/MultiPoint.geojson new file mode 100644 index 0000000..f0b69d0 --- /dev/null +++ b/bench/fixtures/MultiPoint.geojson @@ -0,0 +1,21 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [ + -89.43283081054688, + 43.07089421067248 + ], + [ + -89.37103271484375, + 43.08456131144392 + ], + [ + -89.329833984375, + 43.107249487936684 + ] + ] + } +} \ No newline at end of file diff --git a/bench/fixtures/MultiPolygon.geojson b/bench/fixtures/MultiPolygon.geojson new file mode 100644 index 0000000..09eaab2 --- /dev/null +++ b/bench/fixtures/MultiPolygon.geojson @@ -0,0 +1,113 @@ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 3.7075424194335938, + 51.03966692839972 + ], + [ + 3.733978271484375, + 51.02995152419996 + ], + [ + 3.7751770019531254, + 51.045279344649146 + ], + [ + 3.76556396484375, + 51.08907598780727 + ], + [ + 3.7140655517578125, + 51.09144802136697 + ], + [ + 3.742904663085937, + 51.06168097070514 + ], + [ + 3.68316650390625, + 51.061465197550305 + ], + [ + 3.7075424194335938, + 51.03966692839972 + ] + ], + [ + [ + 3.7199020385742188, + 51.04938029577453 + ], + [ + 3.730201721191406, + 51.05585474252802 + ], + [ + 3.7408447265625, + 51.04333666212316 + ], + [ + 3.720245361328125, + 51.04204149517769 + ], + [ + 3.7199020385742188, + 51.04938029577453 + ] + ] + ], + [ + [ + [ + 3.8119125366210938, + 51.03966692839972 + ], + [ + 3.8376617431640625, + 51.05089108097961 + ], + [ + 3.7902832031250004, + 51.10201287469917 + ], + [ + 3.773117065429687, + 51.0662119746483 + ], + [ + 3.8119125366210938, + 51.03966692839972 + ] + ], + [ + [ + 3.8002395629882812, + 51.06427017012091 + ], + [ + 3.8005828857421875, + 51.068585180672635 + ], + [ + 3.8174057006835933, + 51.06405440903407 + ], + [ + 3.8129425048828125, + 51.05002778118271 + ], + [ + 3.8002395629882812, + 51.06427017012091 + ] + ] + ] + ] + } + } diff --git a/geojson/Point.geojson b/bench/fixtures/Point.geojson similarity index 100% rename from geojson/Point.geojson rename to bench/fixtures/Point.geojson diff --git a/bench/fixtures/Polygon-brazil.geojson b/bench/fixtures/Polygon-brazil.geojson new file mode 100644 index 0000000..2936e48 --- /dev/null +++ b/bench/fixtures/Polygon-brazil.geojson @@ -0,0 +1 @@ +{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[-57.62513342958296,-30.21629485445426],[-56.29089962423908,-28.852760512000895],[-55.16228634298457,-27.881915378533463],[-54.490725267135524,-27.47475676850579],[-53.64873531758789,-26.92347258881609],[-53.628348965048744,-26.124865004177472],[-54.13004960795439,-25.547639255477254],[-54.625290696823576,-25.739255466415514],[-54.42894609233059,-25.162184747012166],[-54.29347632507745,-24.570799655863965],[-54.29295956075452,-24.02101409271073],[-54.65283423523513,-23.83957813893396],[-55.02790178080955,-24.00127369557523],[-55.40074723979542,-23.956935316668805],[-55.517639329639636,-23.571997572526637],[-55.610682745981144,-22.655619398694846],[-55.79795813660691,-22.356929620047822],[-56.47331743022939,-22.086300144135283],[-56.8815095689029,-22.28215382252148],[-57.937155727761294,-22.090175876557172],[-57.8706739976178,-20.73268767668195],[-58.166392381408045,-20.176700941653678],[-57.85380164247451,-19.96999521248619],[-57.949997321185826,-19.40000416430682],[-57.67600887717431,-18.96183969490403],[-57.49837114117099,-18.174187513911292],[-57.734558274961,-17.55246835700777],[-58.28080400250225,-17.271710300366017],[-58.38805843772404,-16.877109063385276],[-58.24121985536668,-16.299573256091293],[-60.158389655179036,-16.258283786690086],[-60.54296566429515,-15.093910414289596],[-60.251148851142936,-15.07721892665932],[-60.26432634137737,-14.645979099183641],[-60.45919816755003,-14.354007256734555],[-60.503304002511136,-13.775954685117659],[-61.08412126325565,-13.479383640194598],[-61.71320431176078,-13.489202162330052],[-62.127080857986385,-13.198780612849724],[-62.803060268796386,-13.000653171442686],[-63.19649878605057,-12.627032565972435],[-64.3163529120316,-12.461978041232193],[-65.40228146021303,-11.566270440317155],[-65.32189876978302,-10.895872084194679],[-65.44483700220539,-10.511451104375432],[-65.33843522811642,-9.761987806846392],[-66.6469083319628,-9.931331475466862],[-67.17380123561074,-10.306812432499612],[-68.04819230820539,-10.712059014532485],[-68.27125362819326,-11.01452117273682],[-68.78615759954948,-11.03638030359628],[-69.52967810736496,-10.951734307502194],[-70.0937522040469,-11.123971856331012],[-70.54868567572841,-11.009146823778465],[-70.48189388699117,-9.490118096558845],[-71.30241227892154,-10.079436130415374],[-72.18489071316985,-10.053597914269432],[-72.56303300646564,-9.520193780152717],[-73.22671342639016,-9.462212823121234],[-73.01538265653255,-9.032833347208062],[-73.57105933296707,-8.424446709835834],[-73.98723548042966,-7.523829847853065],[-73.7234014553635,-7.340998630404414],[-73.72448666044164,-6.91859547285064],[-73.1200274319236,-6.629930922068239],[-73.21971126981461,-6.089188734566078],[-72.9645072089412,-5.741251315944893],[-72.89192765978726,-5.274561455916981],[-71.74840572781655,-4.593982842633011],[-70.92884334988358,-4.401591485210368],[-70.7947688463023,-4.251264743673303],[-69.89363521999663,-4.298186944194327],[-69.44410193548961,-1.556287123219818],[-69.42048580593223,-1.122618503426409],[-69.5770653957766,-0.549991957200163],[-70.02065589057005,-0.185156345219539],[-70.01556576198931,0.541414292804205],[-69.45239600287246,0.706158758950693],[-69.25243404811906,0.602650865070075],[-69.21863766140018,0.985676581217433],[-69.80459672715773,1.089081122233466],[-69.81697323269162,1.714805202639624],[-67.86856502955884,1.692455145673392],[-67.5378100246747,2.03716278727633],[-67.2599975246736,1.719998684084956],[-67.0650481838525,1.130112209473225],[-66.87632585312258,1.253360500489336],[-66.32576514348496,0.724452215982012],[-65.54826738143757,0.78925446207603],[-65.35471330428837,1.0952822941085],[-64.61101192895987,1.328730576987042],[-64.19930579289051,1.49285492594602],[-64.08308549666609,1.91636912679408],[-63.368788011311665,2.200899562993129],[-63.42286739770512,2.411067613124175],[-64.2699991522658,2.497005520025567],[-64.40882788761792,3.126786200366624],[-64.3684944322141,3.797210394705246],[-64.81606401229402,4.056445217297423],[-64.62865943058755,4.14848094320925],[-63.88834286157416,4.020530096854571],[-63.093197597899106,3.770571193858785],[-62.804533047116706,4.006965033377952],[-62.08542965355913,4.162123521334308],[-60.96689327660154,4.536467596856639],[-60.601179165271944,4.91809804933213],[-60.73357418480372,5.200277207861901],[-60.21368343773133,5.244486395687602],[-59.980958624904886,5.014061184098139],[-60.11100236676738,4.574966538914083],[-59.767405768458715,4.423502915866607],[-59.53803992373123,3.958802598481938],[-59.815413174057866,3.606498521332085],[-59.97452490908456,2.755232652188056],[-59.71854570172675,2.24963043864436],[-59.64604366722126,1.786893825686789],[-59.03086157900265,1.317697658692722],[-58.540012986878295,1.268088283692521],[-58.429477098205965,1.463941962078721],[-58.11344987652502,1.507195135907025],[-57.66097103537737,1.682584947105639],[-57.335822923396904,1.948537705895759],[-56.78270423036083,1.863710842288654],[-56.539385748914555,1.899522609866921],[-55.995698004771754,1.817667141116601],[-55.905600145070885,2.02199575439866],[-56.0733418442903,2.220794989425499],[-55.973322109589375,2.510363877773017],[-55.569755011606,2.421506252447131],[-55.09758744975514,2.523748073736613],[-54.524754197799716,2.311848863123785],[-54.08806250671725,2.105556545414629],[-53.77852067728892,2.376702785650082],[-53.554839240113544,2.334896551925951],[-53.41846513529531,2.053389187015981],[-52.939657151894956,2.124857692875636],[-52.55642473001842,2.504705308437053],[-52.249337531123956,3.241094468596245],[-51.65779741067889,4.156232408053029],[-51.31714636901086,4.203490505383954],[-51.069771287629656,3.650397650564031],[-50.508875291533656,1.901563828942457],[-49.97407589374506,1.736483465986069],[-49.94710079608871,1.046189683431223],[-50.699251268096916,0.222984117021682],[-50.38821082213214,-0.078444512536819],[-48.62056677915632,-0.235489190271821],[-48.58449662941659,-1.237805271005001],[-47.824956427590635,-0.5816179337628],[-46.566583624851226,-0.941027520352776],[-44.905703090990414,-1.551739597178134],[-44.417619187993665,-2.137750339367976],[-44.58158850765578,-2.691308282078524],[-43.418791266440195,-2.383110039889793],[-41.47265682632825,-2.912018324397116],[-39.97866533055404,-2.873054294449041],[-38.50038347019657,-3.700652357603396],[-37.2232521225352,-4.820945733258917],[-36.45293738457639,-5.109403578312154],[-35.59779578301047,-5.149504489770649],[-35.23538896334756,-5.464937432480247],[-34.89602983248683,-6.738193047719711],[-34.729993455533034,-7.343220716992967],[-35.12821204277422,-8.996401462442286],[-35.636966518687714,-9.649281508017815],[-37.046518724097,-11.040721123908803],[-37.68361161960736,-12.171194756725823],[-38.42387651218844,-13.038118584854288],[-38.67388709161652,-13.057652276260619],[-38.953275722802545,-13.793369642800023],[-38.88229814304965,-15.667053724838768],[-39.16109249526431,-17.208406670808472],[-39.2673392400564,-17.867746270420483],[-39.58352149103423,-18.262295830968938],[-39.76082333022764,-19.59911345792741],[-40.77474077001034,-20.904511814052423],[-40.94475623225061,-21.93731698983781],[-41.754164191238225,-22.370675551037458],[-41.98828426773656,-22.970070489190896],[-43.07470374202475,-22.96769337330547],[-44.64781185563781,-23.351959323827842],[-45.35213578955992,-23.796841729428582],[-46.47209326840554,-24.088968601174543],[-47.64897233742066,-24.885199069927722],[-48.4954581365777,-25.877024834905654],[-48.64100480812774,-26.623697605090932],[-48.474735887228654,-27.17591196056189],[-48.661520351747626,-28.18613453543572],[-48.8884574041574,-28.674115085567884],[-49.587329474472675,-29.224469089476337],[-50.696874152211485,-30.98446502047296],[-51.576226162306156,-31.77769825615321],[-52.256081305538046,-32.24536996839467],[-52.712099982297694,-33.19657805759118],[-53.373661668498244,-33.768377780900764],[-53.6505439927181,-33.20200408298183],[-53.209588995971544,-32.727666110974724],[-53.78795162618219,-32.047242526987624],[-54.57245154480512,-31.494511407193748],[-55.601510179249345,-30.853878676071393],[-55.97324459494094,-30.883075860316303],[-56.976025763564735,-30.109686374636127],[-57.62513342958296,-30.21629485445426]]]}} diff --git a/geojson/Polygon.geojson b/bench/fixtures/Polygon.geojson similarity index 100% rename from geojson/Polygon.geojson rename to bench/fixtures/Polygon.geojson diff --git a/geojson/FeatureCollection.geojson b/geojson/FeatureCollection.geojson deleted file mode 100644 index 966478d..0000000 --- a/geojson/FeatureCollection.geojson +++ /dev/null @@ -1,141 +0,0 @@ - { - "type": "FeatureCollection", - "features": [ - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.833, 39.284]}, - "properties": { - "name": "Location B", - "category": "House", - "elevation": 25 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.6, 39.984]}, - "properties": { - "name": "Location A", - "category": "Store", - "elevation": 23 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.221, 39.125]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 29 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.358, 39.987]}, - "properties": { - "name": "Location A", - "category": "Store", - "elevation": 12 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.9221, 39.27]}, - "properties": { - "name": "Location B", - "category": "House", - "elevation": 11 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.534, 39.123]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 49 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.21, 39.12]}, - "properties": { - "name": "Location A", - "category": "Store", - "elevation": 50 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.22, 39.33]}, - "properties": { - "name": "Location B", - "category": "House", - "elevation": 90 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.44, 39.55]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 22 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.77, 39.66]}, - "properties": { - "name": "Location A", - "category": "Store", - "elevation": 99 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.44, 39.11]}, - "properties": { - "name": "Location B", - "category": "House", - "elevation": 55 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.05, 39.92]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 41 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.88, 39.98]}, - "properties": { - "name": "Location A", - "category": "Store", - "elevation": 52 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [-75.55, 39.55]}, - "properties": { - "name": "Location B", - "category": "House", - "elevation": 143 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.33, 39.44]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 76 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.56, 39.24]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 18 - } - }, - { "type": "Feature", - "geometry": {"type": "Point", "coordinates": [ -75.56, 39.36]}, - "properties": { - "name": "Location C", - "category": "Office", - "elevation": 52 - } - } - ] - } \ No newline at end of file diff --git a/geojson/LineString.geojson b/geojson/LineString.geojson deleted file mode 100644 index 152c448..0000000 --- a/geojson/LineString.geojson +++ /dev/null @@ -1 +0,0 @@ -{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-80.08724212646484,32.77428536643231],[-80.08718319576123,32.774407687035286],[-80.08700937968847,32.774766344815355],[-80.08672514230369,32.77534888372924],[-80.08633494766407,32.77614284773365],[-80.08584325982665,32.7771357807853],[-80.08525454284863,32.77831522684092],[-80.0845732607871,32.77966872985719],[-80.08380387769924,32.78118383379086],[-80.08295085764209,32.782848082598626],[-80.06932431738281,32.80743595279523],[-80.06781585015088,32.809918368715984],[-80.06628177869239,32.81238800094813],[-80.06472656706445,32.81483239344839],[-80.06315467932423,32.81723909017347],[-80.06157057952882,32.81959563508009],[-80.05997873173536,32.82188957212499],[-80.05838360000097,32.82410844526483],[-80.05678964838282,32.82623979845637],[-80.05520134093798,32.82827117565631],[-80.03937919482422,32.840455481030595],[-80.03820681391552,32.84031557495512],[-80.03709940542186,32.839912668840455],[-80.03607559555957,32.839228128951426],[-80.03513665748144,32.83827031609085],[-80.0342780125122,32.83705398890388],[-80.03349508197655,32.83559390603568],[-80.03278328719922,32.833904826131416],[-80.03213804950488,32.83200150783622],[-80.03155479021825,32.829898709795266],[-80.02792001184082,32.797823240639616],[-80.02772335677393,32.794477859971806],[-80.02752915733595,32.79112486194529],[-80.02733283485155,32.7877790052052],[-80.02712981064552,32.78445504839672],[-80.02691550604247,32.781167750165],[-80.02668534236719,32.77793186915518],[-80.02643474094434,32.77476216401244],[-80.02615912309864,32.771673393381924],[-80.02585391015478,32.768680315908796],[-80.01923379016112,32.74629318460675],[-80.0182162993667,32.7455995993445],[-80.01710969069532,32.745193569626686],[-80.01590938547167,32.74508985409846],[-80.01462611447754,32.74530625723315],[-80.0133079284668,32.7458529732646],[-80.01196016835938,32.7467148599869],[-80.0105844626587,32.747875435203014],[-80.00918243986817,32.74931821671588],[-80.00775572849122,32.75102672232845],[-79.99089820861818,32.78264416628983],[-79.9893022574707,32.786255749728674],[-79.98770278828124,32.789918788705535],[-79.98610142955323,32.793616801023354],[-79.98449980979004,32.79733330448507],[-79.98289955749513,32.801051816893654],[-79.98130230117187,32.804755856052026],[-79.97970966932374,32.808428939763154],[-79.9781232904541,32.81205458582997],[-79.97654479306641,32.81561631205542],[-79.96016725158691,32.84586268264848],[-79.95881035524904,32.84737175940747],[-79.95748251093748,32.84860264776342],[-79.95618534715575,32.849538865519264],[-79.95492049240723,32.850163930477954],[-79.95368957519531,32.850461360442424],[-79.95251206269141,32.85043662672373],[-79.95140467835009,32.850115427468296],[-79.9503649350996,32.84951162073215],[-79.94939034586817,32.84863906457134],[-79.94225872705078,32.8252668887998],[-79.94187199444922,32.822250267913596],[-79.94151808393359,32.81914505233115],[-79.94119450843213,32.815965100108514],[-79.94089878087306,32.812724269301725],[-79.94062841418457,32.8094364179668],[-79.94038092129493,32.8061154041598],[-79.94015381513232,32.80277508593673],[-79.93994460862501,32.79942932135364],[-79.93975081470117,32.79609196846656],[-79.93792500952149,32.76389966267125],[-79.93772217500099,32.76174417850698],[-79.93750242113087,32.75977726076716],[-79.93726326083936,32.75801276750783],[-79.93700220705468,32.75646455678503],[-79.9367167727051,32.755146486654795],[-79.93640447071876,32.754072415173134],[-79.93606645257714,32.7532530165506],[-79.93574776400976,32.75265405687018],[-79.93546119593701,32.752255715114025],[-79.93394115820313,32.758609055294215],[-79.93390701563526,32.75993476040094],[-79.93388282349024,32.76134718872719],[-79.93386687637793,32.76283757914186],[-79.9338574689082,32.76439717051379],[-79.93385289569092,32.766017201711854],[-79.93385145133594,32.767688911604935],[-79.93385143045312,32.769403539061884],[-79.93385112765235,32.771152322951565],[-79.93384883754345,32.77292650214285],[-79.93320474243163,32.791612879648774],[-79.93304558419482,32.79300843423089],[-79.93286226857812,32.79431548940991],[-79.9326530901914,32.79552528405464],[-79.93241634364453,32.79662905703398],[-79.93215032354738,32.797618047216794],[-79.93185332450977,32.79848349347194],[-79.93152364114161,32.79921663466829],[-79.93115956805272,32.7998087096747],[-79.93076445230713,32.80025548858628],[-79.92563361053467,32.79800331988575],[-79.92511511165772,32.79732586216012],[-79.92459094416503,32.79659927901422],[-79.92406180217284,32.79583041516321],[-79.92352837979736,32.7950261153223],[-79.9229913711548,32.794193224206666],[-79.92245147036132,32.79333858653149],[-79.9219093715332,32.79246904701197],[-79.92136576878663,32.791591450363285],[-79.9208213562378,32.79071264130061],[-79.91497788848876,32.782923310831485],[-79.91447789937743,32.78256384058549],[-79.91398612397462,32.78229213922294],[-79.91350325639647,32.78211505145899],[-79.91302999075928,32.78203942200884],[-79.9125670211792,32.78207209558768],[-79.91211504177247,32.78221991691069],[-79.91167474665528,32.782489730693044],[-79.91124682994385,32.78288838164996],[-79.9108319857544,32.78342271449659],[-79.91051217022705,32.79825185788735],[-79.91083374809764,32.80021842735615],[-79.91118676088965,32.80224954504018],[-79.91156481953027,32.804336287035326],[-79.91196153494678,32.80646972943742],[-79.9123705180664,32.80864094834233],[-79.9127853798164,32.81084101984593],[-79.91319973112402,32.813061020044074],[-79.91360718291651,32.81529202503262],[-79.9140013461211,32.81752511090743],[-79.91563281976319,32.83967415743999],[-79.91536917223829,32.84123614942711],[-79.91500917817969,32.842684211546704],[-79.91454644851464,32.8440094198946],[-79.91397459417041,32.84520285056667],[-79.91328722607422,32.84625557965878],[-79.9124779551533,32.84715868326679],[-79.91154039233496,32.84790323748656],[-79.91046814854639,32.84848031841395],[-79.90925483471483,32.84888100214482],[-79.88681002807616,32.84043101772218],[-79.88412558002344,32.83863843745276],[-79.88136658816114,32.836718132514406],[-79.87854052117969,32.83468042668278],[-79.87565484776954,32.832535643733465],[-79.8727170366211,32.830294107442114],[-79.8697345564248,32.82796614158433],[-79.8667148758711,32.82556206993572],[-79.86366546365039,32.823092216271924],[-79.86059378845312,32.820566904368555],[-79.82747205029295,32.79208082944673],[-79.82471577723045,32.789695273166785],[-79.82203433416699,32.78738846773032],[-79.81943518979296,32.785170736912946],[-79.81692581279883,32.78305240449029],[-79.81451367187499,32.78104379423797],[-79.8122062357119,32.77915522993161],[-79.81001097299999,32.77739703534683],[-79.80793535242968,32.775779534259236],[-79.80598684269138,32.774313050444455],[-79.79534838500975,32.769070906870525],[-79.79543174302734,32.76941324126892],[-79.79566084053221,32.76986395696048],[-79.7960265045117,32.77041767918655],[-79.79651956195312,32.7710690331885],[-79.79713083984373,32.771812644207664],[-79.7978511651709,32.77264313748538],[-79.79867136492186,32.773555138263006],[-79.79958226608397,32.774543271781894],[-79.80057469564451,32.77560216328338],[-79.81424881347655,32.79038281567679],[-79.81550408882812,32.791871571794594],[-79.81672164341308,32.79336121403251],[-79.81789230421874,32.794846367631884],[-79.81900689823242,32.79632165783408],[-79.8200562524414,32.797781709880425],[-79.82103119383301,32.79922114901228],[-79.82192254939454,32.800634600470985],[-79.82272114611327,32.8020166894979],[-79.82341781097657,32.803362041334346],[-79.82317655694578,32.81511763936037],[-79.82264117182325,32.81603438083504],[-79.82205102188087,32.81693771442673],[-79.82140863476954,32.81782681524612],[-79.82071653814013,32.81870085840387],[-79.81997725964357,32.81955901901067],[-79.81919332693067,32.82040047217716],[-79.81836726765233,32.821224393014035],[-79.81750160945947,32.82202995663195],[-79.81659888000293,32.82281633814159],[-79.80494506072998,32.82996459725244],[-79.80379463287794,32.83045645209492],[-79.80263999322362,32.83091840126783],[-79.80148366941798,32.831349619881834],[-79.80032818911182,32.8317492830476],[-79.79917607995607,32.83211656587579],[-79.79802986960156,32.8324506434771],[-79.79689208569923,32.83275069096218],[-79.7957652558999,32.833015883441696],[-79.7946519078545,32.833245396026335],[-79.78379761834717,32.83333571959247],[-79.7828884123711,32.833179884680774],[-79.78198295070703,32.83300497173636],[-79.78108284014503,32.832809895070106],[-79.78018968747509,32.83259356899291],[-79.77930509948729,32.832354907815684],[-79.77843068297167,32.832092825849315],[-79.77756804471827,32.83180623740469],[-79.77671879151708,32.83149405679271],[-79.77588453015821,32.831155198324254],[-79.76815665875245,32.82535650956062],[-79.76762762912402,32.8246128330644],[-79.7671344796084,32.82382836475334],[-79.7666788169956,32.82300201893834],[-79.76626224807569,32.822132709930294],[-79.76588637963867,32.821219352040096],[-79.76555281847462,32.82026085957865],[-79.76526317137353,32.819256146856844],[-79.7650190451255,32.81820412818556],[-79.76482204652052,32.817103717875725],[-79.76629620263671,32.8014305177732],[-79.76689192710352,32.7995741723892],[-79.76759057844433,32.79762646866419],[-79.76838602601123,32.795594594234984],[-79.76927213915624,32.79348573673839],[-79.77024278723144,32.79130708381121],[-79.77129183958887,32.78906582309027],[-79.77241316558056,32.786769142212364],[-79.77360063455859,32.7844242288143],[-79.77484811587502,32.7820382705329],[-79.790777859375,32.75513943126411],[-79.7922672982119,32.752821570053676],[-79.79373705096387,32.750556103238424],[-79.79518098698291,32.748350218455144],[-79.79659297562108,32.746211103340656],[-79.79796688623048,32.74414594553178],[-79.79929658816309,32.742161932665304],[-79.80057595077098,32.740266252378035],[-79.80179884340623,32.73846609230681],[-79.80295913542089,32.736768640088414]]},"properties":{}} \ No newline at end of file diff --git a/index.js b/index.js index 2529176..192bb52 100644 --- a/index.js +++ b/index.js @@ -1,63 +1,296 @@ -// http://stackoverflow.com/questions/839899/how-do-i-calculate-a-point-on-a-circles-circumference -// radians = degrees * (pi/180) -// https://github.com/bjornharrtell/jsts/blob/master/examples/buffer.html +var simplepolygon = require('simplepolygon'); +var destination = require('turf-destination'); +var bearing = require('turf-bearing'); +var helpers = require('turf-helpers'); +var union = require('turf-union'); +var difference = require('turf-difference'); +var simplify = require('turf-simplify'); -var featurecollection = require('turf-featurecollection') -var polygon = require('turf-polygon') -var combine = require('turf-combine') -var jsts = require('jsts') +module.exports = buffer; -module.exports = function(feature, radius, units, done){ - var buffered; +function buffer(feature, radius, units, resolution){ + if (!resolution) resolution = 32; // Same value as JSTS + if (radius < 0) throw new Error("The buffer radius must be positive"); + if (radius == 0) return feature; + if (feature.type === 'FeatureCollection') { + var buffers = []; + feature.features.forEach(function(ft) { + var featureBuffer = buffer(ft, radius, units, resolution); + if (featureBuffer.type === 'Feature') { + buffers.push(featureBuffer); + } else { // featureBuffer.type === 'FeatureCollection' + buffers.push.apply(buffers,featureBuffer.features); + } + }); + return helpers.featureCollection(buffers) + } + if (feature.geometry === null) return feature; + if (['LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'].indexOf(feature.geometry.type) > -1) { // turf-simplify() currently handles points and multipoints incorrectly + feature = simplify(feature, helpers.distanceToDegrees(radius/20, units)); // radius/20 seems like the optimal balance between speed and detail + } + if(feature.geometry.type === 'Point') { + return pointBuffer(feature, radius, units, resolution); + } else if(feature.geometry.type === 'MultiPoint') { + var buffers = []; + feature.geometry.coordinates.forEach(function(coords) { + buffers.push(pointBuffer(helpers.point(coords), radius, units, resolution)); + }); + return helpers.featureCollection(buffers) + } else if(feature.geometry.type === 'LineString') { + return lineBuffer(feature, radius, units, resolution); + } else if(feature.geometry.type === 'MultiLineString') { + var buffers = []; + feature.geometry.coordinates.forEach(function(coords) { + buffers.push(lineBuffer(helpers.lineString(coords), radius, units, resolution)); + }); + return helpers.featureCollection(buffers) + } else if(feature.geometry.type === 'Polygon') { + return polygonBuffer(feature, radius, units, resolution); + } else if(feature.geometry.type === 'MultiPolygon') { + var buffers = []; + feature.geometry.coordinates.forEach(function(coords) { + buffers.push(polygonBuffer(helpers.polygon(coords), radius, units, resolution)); + }); + return helpers.featureCollection(buffers) + } +} + +function pointBuffer(pt, radius, units, resolution) { + var pointOffset = [[]]; + var resMultiple = 360/resolution; + for(var i = 0; i < resolution; i++) { + var spoke = destination(pt, radius, i*resMultiple, units); + pointOffset[0].push(spoke.geometry.coordinates); + } + if(!(equalArrays(pointOffset[0][0],pointOffset[0][pointOffset[0].length-1]))) { + pointOffset[0].push(pointOffset[0][0]); + } + return helpers.polygon(pointOffset) +} + +function lineBuffer(line, radius, units, resolution) { + var lineOffset = []; + + line.geometry.coordinates = removeDuplicates(line.geometry.coordinates); + + if (!(equalArrays(line.geometry.coordinates[0],line.geometry.coordinates[line.geometry.coordinates.length-1]))) { + + // situation at first point + var firstLinePoint = helpers.point(line.geometry.coordinates[0]); + var secondLinePoint = helpers.point(line.geometry.coordinates[1]); + var firstLineBearing = bearing(firstLinePoint, secondLinePoint); + var firstBufferPoint = destination(firstLinePoint, radius, firstLineBearing + 90, units); + + // situation at last point + var lastLinePoint = helpers.point(line.geometry.coordinates[line.geometry.coordinates.length-1]); + var secondlastLinePoint = helpers.point(line.geometry.coordinates[line.geometry.coordinates.length-2]); + var lastLineBearing = bearing(lastLinePoint, secondlastLinePoint); + + lineOffset.push([]); + lineOffset[0].push.apply(lineOffset[0],[firstBufferPoint.geometry.coordinates]); // Add first buffer point in order to close ring + lineOffset[0].push.apply(lineOffset[0],lineOffsetOneSide(line, radius, units, resolution, false, true).geometry.coordinates); + lineOffset[0].push.apply(lineOffset[0],arc(lastLinePoint, radius, lastLineBearing - 90, lastLineBearing + 90, units, resolution, true).geometry.coordinates); + lineOffset[0].push.apply(lineOffset[0],lineOffsetOneSide(line, radius, units, resolution, true, true).geometry.coordinates); + lineOffset[0].push.apply(lineOffset[0],arc(firstLinePoint, radius, firstLineBearing - 90, firstLineBearing + 90, units, resolution, true).geometry.coordinates); - done = done || function () {}; + return offsetToBuffer(helpers.polygon(lineOffset)); - switch(units){ - case 'miles': - radius = radius / 69.047 - break - case 'kilometers': - radius = radius / 111.12 - break - case 'degrees': - break + } else { + + lineOffset.push(ringOffsetOneSide(line, radius, units, resolution, false, true).geometry.coordinates); + lineOffset.push(ringOffsetOneSide(line, radius, units, resolution, true, true).geometry.coordinates); + + return offsetToBuffer(helpers.polygon(lineOffset)); } +} - if(feature.type === 'FeatureCollection'){ - var multi = combine(feature); - multi.properties = {} +function polygonBuffer(poly, radius, units, resolution) { + var polygonOffset = []; - buffered = bufferOp(multi, radius); + poly = rewind(poly); - done(null, buffered); - return buffered; + poly.geometry.coordinates[0] = removeDuplicates(poly.geometry.coordinates[0]); + for (var i = 1; i < poly.geometry.coordinates.length; i++) { + poly.geometry.coordinates[i] = removeDuplicates(poly.geometry.coordinates[i]); } - else{ - buffered = bufferOp(feature, radius); - - done(null, buffered); - return buffered; + + polygonOffset.push(ringOffsetOneSide(helpers.lineString(poly.geometry.coordinates[0]), radius, units, resolution, false, true).geometry.coordinates); + for (var i = 1; i < poly.geometry.coordinates.length; i++) { + polygonOffset.push(ringOffsetOneSide(helpers.lineString(poly.geometry.coordinates[i]), radius, units, resolution, false, true).geometry.coordinates); + } + + return offsetToBuffer(helpers.polygon(polygonOffset)); +} + +function lineOffsetOneSide(line, radius, units, resolution, reverse, right) { + if (reverse === undefined) var reverse = false; + if (right === undefined) var right = true; + if (reverse) line.geometry.coordinates = line.geometry.coordinates.reverse(); + var coords = line.geometry.coordinates; + var lineOffset = []; + if (coords.length == 2) return helpers.lineString(lineOffset) + + for (var i = 1; i < coords.length-1; i++) { + var previousLinePoint = helpers.point(coords[i-1]); + var currentLinePoint = helpers.point(coords[i]); + var nextLinePoint = helpers.point(coords[i+1]); + var previousLineBearing = bearing(currentLinePoint, previousLinePoint); + var nextLineBearing = bearing(currentLinePoint, nextLinePoint); + lineOffset.push.apply(lineOffset, arc(currentLinePoint, radius, previousLineBearing - Math.pow(-1, right + 1) * 90, nextLineBearing + Math.pow(-1, right + 1) * 90, units, resolution, right, true).geometry.coordinates); } + + return helpers.lineString(lineOffset) } -var bufferOp = function(feature, radius){ - var reader = new jsts.io.GeoJSONReader() - var geom = reader.read(JSON.stringify(feature.geometry)) - var buffered = geom.buffer(radius); - var parser = new jsts.io.GeoJSONParser() - buffered = parser.write(buffered) +function ringOffsetOneSide(ring, radius, units, resolution, reverse, right) { + if (reverse === undefined) var reverse = false; + if (right === undefined) var right = true; + if (reverse) ring.geometry.coordinates = ring.geometry.coordinates.reverse(); + var coords = ring.geometry.coordinates; // ring is a lineString + var ringOffset = []; + + // situation at current point = point 0 + var previousRingPoint = helpers.point(coords[coords.length-2]); + var currentRingPoint = helpers.point(coords[0]); + var nextRingPoint = helpers.point(coords[1]); + var nextRingBearing = bearing(currentRingPoint, nextRingPoint); + var currentBufferPoint = destination(currentRingPoint, radius, nextRingBearing + 90, units); + var previousRingBearing = bearing(currentRingPoint, previousRingPoint); + + ringOffset.push.apply(ringOffset, [currentBufferPoint.geometry.coordinates]); // Add first buffer point in order to close ring + ringOffset.push.apply(ringOffset, lineOffsetOneSide(ring, radius, units, resolution, false, right).geometry.coordinates); + ringOffset.push.apply(ringOffset, arc(currentRingPoint, radius, previousRingBearing - Math.pow(-1, right + 1) * 90, nextRingBearing + Math.pow(-1, right + 1) * 90, units, resolution, right, true).geometry.coordinates); + + return helpers.lineString(ringOffset) +} - if(buffered.type === 'MultiPolygon'){ - buffered = { - type: 'Feature', - geometry: buffered, - properties: {} +function arc(pt, radius, bearing1, bearing2, units, resolution, right, shortcut) { + if (right === undefined) var right = true; + if (shortcut === undefined) var shortcut = false; + var arc = []; + var resMultiple = 360/resolution; + var angle = (Math.pow(-1, right + 1) * (bearing1 - bearing2)).modulo(360); + var numSteps = Math.floor(angle/resMultiple); + var step = numSteps; // Counting steps first is easier than checking angle (angle involves checking 'right', 'modulo(360)', lefthandedness of bearings + var bearing = bearing1; + // Add spoke for bearing1 + var spoke = destination(pt, radius, bearing1, units); + arc.push(spoke.geometry.coordinates); + // Add spokes for all bearings between bearing1 to bearing2 + // But don't add spokes if the angle is reflex and the shortcut preference is set. In that case, just add bearing1 and bearing2. This prevents double, zigzag-overlapping arcs, and potentially non-unique vertices, when a lineOffsetOneSide is run on both sides. + if (!(angle > 180 && shortcut)) { + while (step) { + bearing = bearing + Math.pow(-1, !right + 1) * resMultiple; + spoke = destination(pt, radius, bearing, units); + arc.push(spoke.geometry.coordinates); + step--; } - buffered = featurecollection([buffered]) + } else { + arc.push(pt.geometry.coordinates); } - else{ - buffered = featurecollection([polygon(buffered.coordinates)]) + // Add spoke for bearing 2, but only if this spoke has not been added yet. Do this by checking the destination point, since slightly different bearings can create equal destination points. + var spokeBearing2 = destination(pt, radius, bearing2, units); + if (!equalArrays(spokeBearing2.geometry.coordinates,spoke.geometry.coordinates)) { + arc.push(spokeBearing2.geometry.coordinates); } + return helpers.lineString(arc) +} + +function filterNetWinding(fc, filterFn) { + var output = {type: 'FeatureCollection', features: []}; + var i = fc.features.length; + while (i--) { + if (filterFn(fc.features[i].properties.netWinding)) { + output.features.push({type: "Feature", geometry: fc.features[i].geometry, properties: {}}); + } + } + return output; +} + +function unionFeatureCollection(fc) { + // Note: union takes a polygon, but return a polygon or multipolygon (which it can not take in). In case of buffes, however, it will always return a polygon + if (fc.features.length == 0) return {type: "Feature", geometry: null, properties: {}}; + var incrementalUnion = fc.features[0]; + if (fc.features.length == 1) return incrementalUnion; + for (var i = 1; i < fc.features.length; i++) { + incrementalUnion = union(incrementalUnion, fc.features[i]); + } + return incrementalUnion +} + +function offsetToBuffer(polygonOffset) { + // You can inspect the polygonOffset here + // console.log(JSON.stringify(polygonOffset)); + var sp = simplepolygon(polygonOffset); + // You can inspect the simplepolygon output here + // console.log(JSON.stringify(sp)); + var unionWithWindingOne = unionFeatureCollection(filterNetWinding(sp, function (netWinding){return netWinding == 1})); + var unionWithWindingZero = unionFeatureCollection(filterNetWinding(sp, function (netWinding){return netWinding == 0})); + // This last one might have winding -1, so we might have to rewind it if the difference algorithm requires so + + if (unionWithWindingOne.geometry == null) return {type: "Feature", geometry: null, properties: {}}; + if (unionWithWindingZero.geometry == null) return unionWithWindingOne; + return difference(unionWithWindingOne, unionWithWindingZero); +} - return buffered; -} \ No newline at end of file +function winding(poly){ + // compute winding of first ring + var coords = poly.geometry.coordinates[0]; + var leftVtxIndex = 0; + for (var i = 0; i < coords.length-1; i++) { if (coords[i][0] < coords[leftVtxIndex][0]) leftVtxIndex = i; } + var prevVtx = coords[(leftVtxIndex-1).modulo(coords.length-1)]; + var leftVtx = coords[leftVtxIndex]; + var nxtVtx = coords[(leftVtxIndex+1).modulo(coords.length-1)]; + var atan1 = Math.atan((prevVtx[1]-leftVtx[1])/(prevVtx[0]-leftVtx[0])); + var atan2 = Math.atan((nxtVtx[1]-leftVtx[1])/(nxtVtx[0]-leftVtx[0])); + return (atan1 > atan2) ? 1 : -1; +} + +function rewind(poly){ + // outer ring to winding +1, inner rings to winding -1 + if (winding(helpers.polygon([poly.geometry.coordinates[0]])) == -1) poly.geometry.coordinates[0] = poly.geometry.coordinates[0].reverse(); + for (var i = 1; i < poly.geometry.coordinates.length; i++) { + if (winding(helpers.polygon([poly.geometry.coordinates[i]])) == 1) poly.geometry.coordinates[i] = poly.geometry.coordinates[i].reverse(); + } + return poly +} + +function removeDuplicates(arr) { + for (var i = arr.length-1; i > 0; i--) { + if (equalArrays(arr[i],arr[i-1])) { + arr.splice(i,1); + } + } + return arr; +} + + +// Function to compare Arrays of numbers. From http://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript +function equalArrays(array1, array2) { + // if the other array is a falsy value, return + if (!array1 || !array2) + return false; + + // compare lengths - can save a lot of time + if (array1.length != array2.length) + return false; + + for (var i = 0, l=array1.length; i < l; i++) { + // Check if we have nested arrays + if (array1[i] instanceof Array && array2[i] instanceof Array) { + // recurse into the nested arrays + if (!equalArrays(array1[i],array2[i])) + return false; + } + else if (array1[i] != array2[i]) { + // Warning - two different object instances will never be equal: {x:20} != {x:20} + return false; + } + } + return true; +} + +// Fix Javascript modulo for negative number. From http://stackoverflow.com/questions/4467539/javascript-modulo-not-behaving +Number.prototype.modulo = function(n) { + return ((this%n)+n)%n; +} diff --git a/package.json b/package.json index 914cc21..529d979 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "turf-buffer", - "version": "0.0.2", + "version": "1.0.0", "description": "turf buffer module", "main": "index.js", "scripts": { - "test": "tape test.js" + "test": "tape test/test.js" }, "repository": { "type": "git", - "url": "https://github.com/morganherlocker/turf-buffer.git" + "url": "https://github.com/mclaeysb/turf-buffer.git" }, "keywords": [ "buffer", @@ -19,19 +19,24 @@ "geojson", "turf" ], - "author": "morganherlocker", + "author": "Manuel Claeys Bouuaert", "license": "MIT", "bugs": { - "url": "https://github.com/morganherlocker/turf-buffer/issues" + "url": "https://github.com/mclaeysb/turf-buffer/issues" }, - "homepage": "https://github.com/morganherlocker/turf-buffer", + "homepage": "https://github.com/mclaeysb/turf-buffer", "devDependencies": { - "tape": "^2.13.3" + "benchmark": "^2.1.1", + "tape": "^2.13.3", + "turf-buffer": "^3.0.12" }, "dependencies": { - "jsts": "^0.13.4", - "turf-combine": "0.1.0", - "turf-featurecollection": "^0.1.0", - "turf-polygon": "^0.1.1" + "simplepolygon": "^1.0.0", + "turf-destination": "^3.0.0", + "turf-bearing": "^3.0.0", + "turf-helpers": "^3.0.0", + "turf-union": "^3.0.0", + "turf-difference": "^3.0.0", + "turf-simplify": "^3.0.0" } } diff --git a/test.js b/test.js deleted file mode 100644 index 852b42b..0000000 --- a/test.js +++ /dev/null @@ -1,22 +0,0 @@ -var test = require('tape') -var buffer = require('./') -var fs = require('fs') - -test('buffer', function(t){ - var pt = JSON.parse(fs.readFileSync(__dirname+'/geojson/Point.geojson')) - var line = JSON.parse(fs.readFileSync(__dirname+'/geojson/LineString.geojson')) - var polygon = JSON.parse(fs.readFileSync(__dirname+'/geojson/Polygon.geojson')) - var fc = JSON.parse(fs.readFileSync(__dirname+'/geojson/FeatureCollection.geojson')) - - var buffPt = buffer(pt, 10, 'miles') - var buffLine = buffer(pt, 10, 'miles') - var buffPoly = buffer(pt, 10, 'miles') - var buffFC = buffer(fc, 10, 'miles') - - t.ok(buffPt, 'should buffer a point') - t.ok(buffLine, 'should buffer a line') - t.ok(buffPoly, 'should buffer a polygon') - t.ok(buffFC, 'should buffer featurecollection') - - t.end() -}) \ No newline at end of file diff --git a/test/fixtures/LineString.geojson b/test/fixtures/LineString.geojson new file mode 100644 index 0000000..991f184 --- /dev/null +++ b/test/fixtures/LineString.geojson @@ -0,0 +1,41 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -58.45962524414062, + -34.5286213832762 + ], + [ + -58.50151062011719, + -34.54841811625336 + ], + [ + -58.531723022460945, + -34.61625687562895 + ], + [ + -58.530349731445305, + -34.65523908026755 + ], + [ + -58.45550537109375, + -34.70831578223845 + ], + [ + -58.41636657714844, + -34.65806316573297 + ], + [ + -58.363494873046875, + -34.651285198954135 + ], + [ + -58.34564208984375, + -34.62925297943919 + ] + ] + } +} \ No newline at end of file diff --git a/test/fixtures/MultiPoint.geojson b/test/fixtures/MultiPoint.geojson new file mode 100644 index 0000000..f0b69d0 --- /dev/null +++ b/test/fixtures/MultiPoint.geojson @@ -0,0 +1,21 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPoint", + "coordinates": [ + [ + -89.43283081054688, + 43.07089421067248 + ], + [ + -89.37103271484375, + 43.08456131144392 + ], + [ + -89.329833984375, + 43.107249487936684 + ] + ] + } +} \ No newline at end of file diff --git a/test/fixtures/Point.geojson b/test/fixtures/Point.geojson new file mode 100644 index 0000000..8c290bf --- /dev/null +++ b/test/fixtures/Point.geojson @@ -0,0 +1,8 @@ +{ + "type": "Feature", + "geometry": {"type": "Point", "coordinates": [-75.4, 39.4]}, + "properties": { + "name": "Location A", + "category": "Store" + } +} \ No newline at end of file diff --git a/test/fixtures/Polygon.geojson b/test/fixtures/Polygon.geojson new file mode 100644 index 0000000..b187016 --- /dev/null +++ b/test/fixtures/Polygon.geojson @@ -0,0 +1,43 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -79.92141723632812, + 32.953944317478246 + ], + [ + -79.97428894042969, + 32.83690450361482 + ], + [ + -79.97360229492188, + 32.76071688548088 + ], + [ + -79.93034362792969, + 32.76475877693074 + ], + [ + -79.93789672851562, + 32.74108223150125 + ], + [ + -79.80537414550781, + 32.7231762754146 + ], + [ + -79.81773376464844, + 32.923402043498875 + ], + [ + -79.92141723632812, + 32.953944317478246 + ] + ] + ] + } +} diff --git a/test/fixtures/multiLineString.geojson b/test/fixtures/multiLineString.geojson new file mode 100644 index 0000000..60d5690 --- /dev/null +++ b/test/fixtures/multiLineString.geojson @@ -0,0 +1,37 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiLineString", + "coordinates": [ + [ + [ + 3.70513916015625, + 51.03945105265764 + ], + [ + 3.71063232421875, + 51.06545683812254 + ], + [ + 3.7278421020507812, + 51.04279701362964 + ], + [ + 3.746038208007812, + 51.0584442677861 + ] + ], + [ + [ + 3.7441062927246094, + 51.04387630433032 + ], + [ + 3.6912345886230464, + 51.059631085129986 + ] + ] + ] + } + } \ No newline at end of file diff --git a/test/fixtures/multiPolygon.geojson b/test/fixtures/multiPolygon.geojson new file mode 100644 index 0000000..09eaab2 --- /dev/null +++ b/test/fixtures/multiPolygon.geojson @@ -0,0 +1,113 @@ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [ + 3.7075424194335938, + 51.03966692839972 + ], + [ + 3.733978271484375, + 51.02995152419996 + ], + [ + 3.7751770019531254, + 51.045279344649146 + ], + [ + 3.76556396484375, + 51.08907598780727 + ], + [ + 3.7140655517578125, + 51.09144802136697 + ], + [ + 3.742904663085937, + 51.06168097070514 + ], + [ + 3.68316650390625, + 51.061465197550305 + ], + [ + 3.7075424194335938, + 51.03966692839972 + ] + ], + [ + [ + 3.7199020385742188, + 51.04938029577453 + ], + [ + 3.730201721191406, + 51.05585474252802 + ], + [ + 3.7408447265625, + 51.04333666212316 + ], + [ + 3.720245361328125, + 51.04204149517769 + ], + [ + 3.7199020385742188, + 51.04938029577453 + ] + ] + ], + [ + [ + [ + 3.8119125366210938, + 51.03966692839972 + ], + [ + 3.8376617431640625, + 51.05089108097961 + ], + [ + 3.7902832031250004, + 51.10201287469917 + ], + [ + 3.773117065429687, + 51.0662119746483 + ], + [ + 3.8119125366210938, + 51.03966692839972 + ] + ], + [ + [ + 3.8002395629882812, + 51.06427017012091 + ], + [ + 3.8005828857421875, + 51.068585180672635 + ], + [ + 3.8174057006835933, + 51.06405440903407 + ], + [ + 3.8129425048828125, + 51.05002778118271 + ], + [ + 3.8002395629882812, + 51.06427017012091 + ] + ] + ] + ] + } + } diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..85df802 --- /dev/null +++ b/test/test.js @@ -0,0 +1,54 @@ +var test = require('tape'); +var buffer = require('../'); +var fs = require('fs'); +var helpers = require('turf-helpers') + +test('buffer', function(t){ + var point = JSON.parse(fs.readFileSync(__dirname+'/fixtures/Point.geojson')); + var multiPoint = JSON.parse(fs.readFileSync(__dirname+'/fixtures/MultiPoint.geojson')); + var lineString = JSON.parse(fs.readFileSync(__dirname+'/fixtures/LineString.geojson')); + var multiLineString = JSON.parse(fs.readFileSync(__dirname+'/fixtures/MultiLineString.geojson')); + var polygon = JSON.parse(fs.readFileSync(__dirname+'/fixtures/Polygon.geojson')); + var multiPolygon = JSON.parse(fs.readFileSync(__dirname+'/fixtures/MultiPolygon.geojson')); + + // Tests for basic features + var bufferedPoint = buffer(point, 1, 'kilometers', 100); + fs.writeFileSync(__dirname+'/fixtures/out/Point.geojson', JSON.stringify(helpers.featureCollection([bufferedPoint, point]))); + + var bufferedMultiPoint = buffer(multiPoint, 1, 'kilometers'); + bufferedMultiPoint.features.push(multiPoint) + fs.writeFileSync(__dirname+'/fixtures/out/MultiPoint.geojson', JSON.stringify(bufferedMultiPoint)); + + var bufferedLineString = buffer(lineString, 1, 'kilometers'); + fs.writeFileSync(__dirname+'/fixtures/out/LineString.geojson', JSON.stringify(helpers.featureCollection([bufferedLineString, lineString]))); + + var bufferedMultiLineString = buffer(multiLineString, 1, 'kilometers'); + bufferedMultiLineString.features.push(multiLineString) + fs.writeFileSync(__dirname+'/fixtures/out/MultiLineString.geojson', JSON.stringify(bufferedMultiLineString)); + + var bufferedPolygon = buffer(polygon, 1, 'kilometers'); + fs.writeFileSync(__dirname+'/fixtures/out/Polygon.geojson', JSON.stringify(helpers.featureCollection([bufferedPolygon, polygon]))); + + var bufferedMultiPolygon = buffer(multiPolygon, 0.2, 'kilometers'); + bufferedMultiPolygon.features.push(multiPolygon) + fs.writeFileSync(__dirname+'/fixtures/out/MultiPolygon.geojson', JSON.stringify(bufferedMultiPolygon)); + + // Test to buffer a circle LineString with buffer radius = circle radius + var disk = buffer(point, 1, 'kilometers'); + var circle = disk; + circle.geometry.type = "LineString"; + circle.geometry.coordinates = circle.geometry.coordinates[0]; + var bufferedCircle = buffer(circle, 1, 'kilometers'); + fs.writeFileSync(__dirname+'/fixtures/out/Circle.geojson', JSON.stringify(helpers.featureCollection([bufferedCircle, circle]))); + // the simplepolygon output has 32 rings, each with winding > 0 + + t.ok(bufferedPoint, 'should buffer a Point'); + t.ok(bufferedMultiPoint, 'should buffer a MultiPoint'); + t.ok(bufferedLineString, 'should buffer a LineString'); + t.ok(bufferedMultiLineString, 'should buffer a MultiLineString'); + t.ok(bufferedPolygon, 'should buffer a Polygon'); + t.ok(bufferedMultiPolygon, 'should buffer a multiPolygon'); + t.ok(bufferedMultiPolygon, 'should buffer a circle with buffer radius = circle radius, without creating a hole or error in the middle'); + + t.end(); +});