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

1 lines 8.92 kB
{"version":3,"sources":["lib/util.ts","lib/gen.ts","lib/index.ts","index.ts"],"names":[],"mappings":"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","file":"index.map","sourceRoot":"src","sourcesContent":["import * as typing from './typing'\n\nexport function computeMaxDistance (\n travelTime: typing.Minutes,\n travelSpeed: typing.KilometersPerHour\n ): typing.Kilometers {\n // convert minutes to hours and compute distance\n return travelSpeed * (travelTime / 60)\n}\n\n/**\n * Creates a new catchment area object from the given parameters\n *\n * @param {Minutes} drivetime\n * @param {Kilometers} distance\n * @param {Coord} source\n * @param {Array<Coord>} destinations\n * @return {CatchmentArea}\n **/\nexport function makeCatchmentArea(\n drivetime: typing.Minutes,\n distance: typing.Kilometers,\n source: typing.Coord,\n destinations: Array<typing.Coord>\n ): typing.CatchmentArea {\n return {\n source,\n destinations,\n drivetime,\n distance\n }\n}\n","import kdbush from 'kdbush'\nimport * as geokdbush from 'geokdbush'\nimport * as R from 'ramda'\nimport * as typing from './typing'\nimport * as Util from './util'\n\nexport class CatchmentAreaGenerator {\n config: typing.Config;\n travelSpeed: typing.KilometersPerHour;\n\n constructor (configObject: typing.Config, travelSpeed: typing.KilometersPerHour = 80) {\n this.config = configObject;\n this.travelSpeed = travelSpeed\n }\n\n /**\n * Get coordinates of source points\n *\n * @return {Array<Coord>}\n **/\n get sources (): Array<typing.Coord> {\n return this.config.sources.map(source => this.config.coordinates[source]);\n }\n\n /**\n * Get coordinates of destination points\n *\n * @return {Array<Coord>}\n **/\n get destinations (): Array<typing.Coord> {\n return this.config.destinations.map(destination => this.config.coordinates[destination]);\n }\n\n /**\n * Get drive times for which catchment areas are to be computed\n *\n * @return {Array<Minutes>} a list of drive times in minutes\n **/\n get drivetimes (): Array<typing.Minutes> {\n return this.config.drivetimes;\n }\n\n /**\n * Get estimated travel speed\n *\n * @return {KilometersPerHour} a list of drive times in minutes\n **/\n get speed (): typing.KilometersPerHour {\n return this.travelSpeed\n }\n\n /**\n * Get all points for which the catchment area have been computed\n *\n * @param {Array<CatchmentArea>} catchmentAreas\n * @return {Array<Coord>}\n **/\n getCatchedPoints (catchmentAreas: Array<typing.CatchmentArea>): Array<typing.Coord> {\n return catchmentAreas.reduce(\n (catchedPoints: Array<typing.Coord>, catchmentArea: typing.CatchmentArea) => catchedPoints.concat(catchmentArea.destinations),\n []\n );\n }\n\n /**\n * Given two arrays of points return an array of points belonging to the first array\n * but not in the second array i.e A - (A /\\ B)\n *\n * @param {Array<Coord>} allPoints\n * @param {Array<Coord>} catchedPoints\n * @return {Array<Coord>}\n **/\n getUncatchedPoints (allPoints: Array<typing.Coord>, catchedPoints: Array<typing.Coord>): Array<typing.Coord> {\n return R.differenceWith((allPointsPoint: typing.Coord, catchedPointsPoint: typing.Coord) => {\n const [long1, lat1] = allPointsPoint;\n const [long2, lat2] = catchedPointsPoint;\n\n return long1 == long2 && lat1 == lat2;\n }, allPoints, catchedPoints);\n }\n\n /**\n * Compute destinations that belong to a given source's catchment area\n *\n * @param {Kilometers} distance\n * @param {Coord} source\n * @param {Array<Coord>} destinations\n * @param {number} clusterSize\n * @return {Array<Coord>}\n **/\n generate (\n distance: typing.Kilometers,\n source: typing.Coord,\n destinations: Array<typing.Coord>,\n clusterSize: number): Array<typing.Coord> {\n // If there are fewer possible destinations than cluster size just assign them to the source.\n // This ignores the max distance so some destinations may be farther away than expected but avoids\n // leaving any destination uncatched.\n if (destinations.length <= clusterSize) {\n return destinations;\n }\n\n const [long, lat] = source;\n const tree = kdbush(destinations);\n const nearest: Array<typing.Coord> = geokdbush.around(tree, long, lat, clusterSize, distance);\n\n return nearest;\n }\n\n /**\n * Run the CatchmentAreaGenerator\n *\n * @param {String} travelMode the mode of transport, possible values are defined in `TRAVEL_MODE` enum\n * @param {String} roadType the type of road travelled, possible values are defined in `ROAD_TYPE` enum\n * @param {String} transitMode used when travelMode is `TRAVEL_MODE.TRANSIT`, possible values are defined in `TRANSIT_MODE` enum\n * @return {Array<Object>} an array of objects with each object having the keys `source` and `destinations`,\n * `source` is the coordinate of the source while destinations is a list of coordinates of points\n * in the `source`'s catchment area\n **/\n run () {\n let sources: Array<typing.Coord> = this.sources;\n let destinations: Array<typing.Coord> = this.destinations;\n let drivetimes: Array<typing.Minutes> = this.drivetimes;\n\n if (!sources.length) {\n throw new Error('The catchment area generator config object must contain at least one source!');\n }\n\n let catchmentAreas: Array<typing.CatchmentArea> = [];\n let clusterSize: number = Math.floor(destinations.length / sources.length);\n\n for (let drivetime of drivetimes) {\n let maxDistance: typing.Kilometers = Util.computeMaxDistance(drivetime, this.speed);\n\n for (let source of sources) {\n let catchedPoints: Array<typing.Coord> = this.getCatchedPoints(catchmentAreas);\n let availableDestinations: Array<typing.Coord> = this.getUncatchedPoints(destinations, catchedPoints);\n\n let catchmentAreaForSource: Array<typing.Coord> = this.generate(maxDistance, source, availableDestinations, clusterSize);\n\n catchmentAreas.push(Util.makeCatchmentArea(drivetime, maxDistance, source, catchmentAreaForSource));\n }\n }\n\n return catchmentAreas;\n }\n}\n","import * as LibUtil from './util'\nexport { CatchmentAreaGenerator } from './gen'\n\nexport const Util = LibUtil","import { CatchmentAreaGenerator as LibGen } from './lib'\nexport { Util } from './lib'\n\nexport const CatchmentAreaGenerator = LibGen\nexport default CatchmentAreaGenerator"]}