worldpop
Version:
Compute the population within a polygon from GeoJSON population data
93 lines (84 loc) • 2.86 kB
JavaScript
var xtend = require('xtend')
var turfarea = require('turf-area')
var through = require('through2')
var tiledData = require('./lib/tiled-data')
var clip = require('./lib/clip')
var fix = require('./lib/fix')
var debug = require('debug')('polypop:main')
var debugTotal = require('debug')('polypop:totalTime')
var DEFAULT_PROGRESS_FREQ = 100
module.exports = worldpop
/**
* Computes the total population within the given polygon.
*
* @param opts - Options:
* max_zoom, min_zoom
*
* {(string|ReadableStream<Feature>)} source - A GeoJSON Feature
* stream or Tilelive uri for the tiled population data, where each feature
* represents an area of constant population density.
*
* {string} layer - If `source` is a tile source, the layer in which to find
* the population features.
*
* {function} density - A function that accepts a feature from
* `source` and returns the population density for that feature.
*
* {Feature<Polygon>} polygon - The polygon whose interior
* population we want.
*
* {function} progress - A progress callback, called periodically
* with the current state of {totalPopulation, totalArea, polygonArea}. (You can
* estimate % complete with totalArea/polygonArea.)
*
* {Number} progressFrequency - Frequency (in # of features) of
* progress callback.
* @param cb - completion callback, called with {totalPopulation, totalArea,
* polygonArea}.
* @return - a GeoJSON feature stream of constant-population polygons, clipped
* to the poly of interest
*/
function worldpop (opts, cb) {
opts.min_zoom = opts.min_zoom || 8
opts.max_zoom = opts.max_zoom || 12
var poly = opts.polygon
var source = typeof opts.source.pipe === 'function' ? opts.source :
tiledData(opts.source, opts.layer, poly, opts)
var progressFrequency = opts.progressFrequency || DEFAULT_PROGRESS_FREQ
var result = {
count: 0,
totalPopulation: 0,
totalArea: 0,
polygonArea: turfarea(poly)
}
debugTotal('start', Date.now())
return source
.pipe(fix())
.pipe(clip(poly))
.pipe(through.obj(function write (feat, _, next) {
next(null, popped(opts.density, poly, feat))
}))
.on('data', function (feat) {
result.totalPopulation += feat.properties.population
result.totalArea += feat.properties.area
result.count++
if (opts.progress && result.count % progressFrequency === 0) {
var snap = xtend({}, result)
setTimeout(function () { opts.progress(snap) }, 0)
}
})
.on('end', function () {
debugTotal('end', Date.now())
debugTotal(result)
cb(null, result)
})
.on('error', cb)
}
function popped (densityFn, poly, feat) {
var density = densityFn(feat)
feat.properties.area = turfarea(feat)
feat.properties.population = feat.properties.area * density
debug(feat.properties)
return feat
}