xrspatial.dasymetric.pycnophylactic#

xrspatial.dasymetric.pycnophylactic(zones: DataArray, values: dict | Series, max_iterations: int = 100, convergence: float = 1e-05, nodata_zone: int | None = None, name: str = 'pycnophylactic') DataArray[source]#

Tobler’s pycnophylactic interpolation preserving zone totals.

Produces a smooth surface by iteratively applying Laplacian smoothing and then rescaling each zone so its pixel sum matches the original value. Unlike disaggregate(), no ancillary weight raster is needed – smoothness alone drives the redistribution.

Parameters:
  • zones (xr.DataArray) – 2-D integer raster identifying zone membership for each pixel.

  • values (dict or pd.Series) – Mapping of zone_id -> value to redistribute.

  • max_iterations (int, default 100) – Maximum number of smoothing-correction iterations.

  • convergence (float, default 1e-5) – Stop when the largest per-pixel change is below this threshold.

  • nodata_zone (int or None, default None) – Zone ID to treat as no-data (output NaN).

  • name (str, default 'pycnophylactic') – Name for the output DataArray.

Returns:

Float raster with the same shape, coordinates, and attributes as zones. Zone totals are preserved (pycnophylactic property).

Return type:

xr.DataArray

Notes

Currently supports numpy and cupy (via CPU fallback) backends. Dask arrays raise NotImplementedError because the algorithm is inherently iterative and requires global zone sums each iteration.

References

Tobler, W. R. (1979). “Smooth Pycnophylactic Interpolation for Geographical Regions”. Journal of the American Statistical Association, 74(367), 519-530.

Examples

>>> import numpy as np, xarray as xr
>>> from xrspatial.dasymetric import pycnophylactic
>>> zones = xr.DataArray(np.array([[1, 1], [2, 2]]), dims=['y', 'x'])
>>> result = pycnophylactic(zones, {1: 100, 2: 50})
>>> float(np.nansum(result.values[:1, :]))  # zone 1 sum
100.0