UNPKG

catchment-area

Version:

A utility for computing the catchment area of given source and destination points. A source may correspond to a health facility while the destinations (catchment areas) are the settlements served by the facility

166 lines (151 loc) 6.45 kB
const expect = require('chai').expect const geokdbush = require('geokdbush') const { Util, CatchmentAreaGenerator } = require('../index.js') const configObject = { sources: [0, 1], destinations: [2, 3, 4, 5, 6], coordinates: [ [8.521614074707031, 12.041335177559377], [8.546504974365234, 11.986935141127049], [8.402481079101562, 11.965776606631184], [8.450889587402344, 12.078938641761814], [8.558778762817383, 11.999024987663574], [8.549938201904297, 12.007420395469195], [8.554229736328125, 11.956876093184725] ], profile: 'car', drivetimes: [10] } describe('Util', function () { describe('#computeMaxDistance()', function () { it('should compute correct distance', function () { // distance in kilometers travelled in 60 minutes when travelling at 80km/hr let distance = Util.computeMaxDistance(60, 80) expect(distance).to.be.a('number') expect(distance).to.equal(80) }) }) describe('#makeCatchmentArea()', function () { it('should create a valid catchment area object', function () { let time = 10 // minutes let distance = Util.computeMaxDistance(10, 80) let source = [8.521614074707031, 12.041335177559377] let destinations = [ [8.558778762817383, 11.999024987663574], [8.549938201904297, 12.007420395469195] ] let catchmentArea = Util.makeCatchmentArea(time, distance, source, destinations) expect(catchmentArea).to.be.an('object') expect(catchmentArea).to.deep.include({drivetime: time}) expect(catchmentArea).to.deep.include({source: source}) expect(catchmentArea).to.deep.include({distance: distance}) expect(catchmentArea).to.deep.include({destinations: destinations}) expect(catchmentArea).to.have.property('source').that.include.members(source) }) }) }) describe('CatchmentAreaGenerator', function () { describe('#getters', function () { it('should initialize expected attributes', function () { let generator = new CatchmentAreaGenerator(configObject) expect(generator).to.have.property('sources') expect(generator).to.have.property('destinations') expect(generator).to.have.property('drivetimes') expect(generator).to.have.property('speed') }) it('should have correct values for expected attributes', function () { let sources = [ [8.521614074707031, 12.041335177559377], [8.546504974365234, 11.986935141127049] ] let destinations = [ [8.402481079101562, 11.965776606631184], [8.450889587402344, 12.078938641761814], [8.558778762817383, 11.999024987663574], [8.549938201904297, 12.007420395469195], [8.554229736328125, 11.956876093184725] ] let generator = new CatchmentAreaGenerator(configObject) expect(generator.sources).to.deep.equal(sources) expect(generator.destinations).to.deep.equal(destinations) expect(generator.drivetimes).to.deep.equal([10]) expect(generator.speed).to.equal(80) }) }) describe('#getCatchedPoints', function () { it('should return all coordinates in an array of catchment areas', function () { let catchmentAreas = [ Util.makeCatchmentArea( 5, 10, [8.521614074707031, 12.041335177559377], [ [8.402481079101562, 11.965776606631184], [8.450889587402344, 12.078938641761814] ] ), Util.makeCatchmentArea( 10, 25, [8.546504974365234, 11.986935141127049], [ [8.558778762817383, 11.999024987663574] ] ) ] let expectedCoordinates = [ [8.402481079101562, 11.965776606631184], [8.450889587402344, 12.078938641761814], [8.558778762817383, 11.999024987663574] ] let coordinates = (new CatchmentAreaGenerator(configObject)).getCatchedPoints(catchmentAreas) expect(coordinates).to.deep.equal(expectedCoordinates) }) }) describe('#getUncatchedPoints', function () { it('should return all coordinates that does not belong to a catchment area', function () { let generator = new CatchmentAreaGenerator(configObject) let allPoints = generator.destinations let catchmentAreas = generator.run() let catchedPoints = generator.getCatchedPoints(catchmentAreas) let uncatchedPoints = generator.getUncatchedPoints(allPoints, catchedPoints) // these points are farther than 13.5km from the two sources in configObject let expectedUncatchedPoints = [ [ 8.402481079101562, 11.965776606631184 ], [ 8.450889587402344, 12.078938641761814 ] ] expect(uncatchedPoints).to.have.lengthOf(2) expect(uncatchedPoints).to.deep.equal(expectedUncatchedPoints) }) }) describe('#generate', function () { it('should generate a catchment area for a given source', function () { const distance = 80 * (configObject.drivetimes[0] / 60) const source = configObject.coordinates[configObject.sources[0]] const destinations = configObject.destinations.map(destination => configObject.coordinates[destination]) const clusterSize = Math.floor(configObject.destinations.length / configObject.sources.length) const generator = new CatchmentAreaGenerator(configObject, 80) const destinationsInCatchment = generator.generate(distance, source, destinations, clusterSize) expect(destinationsInCatchment).to.be.an('array').that.is.not.empty destinationsInCatchment.map(destination => { let [srcLon, srcLat] = source let [destLon, destLat] = destination let actualDistance = geokdbush.distance(srcLon, srcLat, destLon, destLat) expect(actualDistance).to.be.at.most(distance) }) }) }) describe('#run', function () { it('should generate catchment areas for all available sources', function () { const travelSpeed = 100 // kilometers per hour const drivetime = configObject.drivetimes[0] // drive time in minutes const distance = travelSpeed * (drivetime / 60) const generator = new CatchmentAreaGenerator(configObject, travelSpeed) const catchmentAreas = generator.run() expect(catchmentAreas).to.have.lengthOf(2) catchmentAreas.map(catchmentArea => { return expect(catchmentArea.distance).to.equal(distance) }) }) }) })