{"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"]}