|
1 | 1 | import os |
| 2 | + |
| 3 | +import dask.array as da |
| 4 | +import datashader as ds |
| 5 | +import noise |
| 6 | +import numpy as np |
| 7 | +import pandas as pd |
2 | 8 | import xarray as xr |
3 | 9 |
|
4 | 10 |
|
5 | | -__all__ = ["available", "get_data"] |
| 11 | +__all__ = [ |
| 12 | + 'available', |
| 13 | + 'get_data', |
| 14 | + 'make_terrain', |
| 15 | +] |
6 | 16 |
|
7 | 17 | _module_path = os.path.dirname(os.path.abspath(__file__)) |
8 | 18 | _available_datasets = [p for p in next(os.walk(_module_path))[1] |
@@ -34,3 +44,77 @@ def get_data(dataset): |
34 | 44 | msg += f'Available folders are {available_datasets}.' |
35 | 45 | raise ValueError(msg) |
36 | 46 | return data |
| 47 | + |
| 48 | + |
| 49 | +def make_terrain( |
| 50 | + shape=(1024, 1024), |
| 51 | + scale=100.0, |
| 52 | + octaves=6, |
| 53 | + persistence=0.5, |
| 54 | + lacunarity=2.0, |
| 55 | + chunks=(512, 512) |
| 56 | +): |
| 57 | + """ |
| 58 | + Generate a pseudo-random terrain data dask array. |
| 59 | +
|
| 60 | + Parameters |
| 61 | + ---------- |
| 62 | + shape : int or tuple of int, default=(1024, 1024) |
| 63 | + Output array shape. |
| 64 | + scale : float, default=100.0 |
| 65 | + Noise factor scale. |
| 66 | + octaves : int, default=6 |
| 67 | + Number of waves when generating the noise. |
| 68 | + persistence : float, default=0.5 |
| 69 | + Amplitude of each successive octave relative. |
| 70 | + lacunarity : float, default=2.0 |
| 71 | + Frequency of each successive octave relative. |
| 72 | + chunks : int or tuple of int, default=(512, 512) |
| 73 | + Number of samples on each block. |
| 74 | +
|
| 75 | + Returns |
| 76 | + ------- |
| 77 | + terrain : xarray.DataArray |
| 78 | + 2D array of generated terrain values. |
| 79 | + """ |
| 80 | + def _func(arr, block_id=None): |
| 81 | + block_ystart = block_id[0] * arr.shape[0] |
| 82 | + block_xstart = block_id[1] * arr.shape[1] |
| 83 | + out = np.zeros(arr.shape) |
| 84 | + for i in range(out.shape[0]): |
| 85 | + for j in range(out.shape[1]): |
| 86 | + out[i][j] = noise.pnoise2( |
| 87 | + (block_ystart + i)/scale, |
| 88 | + (block_xstart + j)/scale, |
| 89 | + octaves=octaves, |
| 90 | + persistence=persistence, |
| 91 | + lacunarity=lacunarity, |
| 92 | + repeatx=1024, |
| 93 | + repeaty=1024, |
| 94 | + base=42, |
| 95 | + ) |
| 96 | + return out |
| 97 | + |
| 98 | + data = ( |
| 99 | + da.zeros(shape=shape, chunks=chunks, dtype=np.float32) |
| 100 | + .map_blocks(_func, dtype=np.float32) |
| 101 | + ) |
| 102 | + |
| 103 | + cvs = ds.Canvas( |
| 104 | + x_range=(0, 500), |
| 105 | + y_range=(0, 500), |
| 106 | + plot_width=shape[1], |
| 107 | + plot_height=shape[0], |
| 108 | + ) |
| 109 | + |
| 110 | + hack_agg = cvs.points(pd.DataFrame({'x': [], 'y': []}), 'x', 'y') |
| 111 | + |
| 112 | + agg = xr.DataArray( |
| 113 | + data, |
| 114 | + name='terrain', |
| 115 | + coords=hack_agg.coords, |
| 116 | + dims=hack_agg.dims, |
| 117 | + attrs={'res': 1}, |
| 118 | + ) |
| 119 | + |
| 120 | + return agg |
0 commit comments