diffusion-cartogram 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diffusion_cartogram-0.2.0/LICENSE +21 -0
- diffusion_cartogram-0.2.0/PKG-INFO +287 -0
- diffusion_cartogram-0.2.0/README.md +270 -0
- diffusion_cartogram-0.2.0/README_pypi.md +247 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram/__init__.py +202 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram/core.py +1329 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram/core_2d.py +831 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram/visualization.py +1490 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram/visualization_2d.py +534 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info/PKG-INFO +287 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info/SOURCES.txt +19 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info/dependency_links.txt +1 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info/requires.txt +19 -0
- diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info/top_level.txt +1 -0
- diffusion_cartogram-0.2.0/pyproject.toml +59 -0
- diffusion_cartogram-0.2.0/setup.cfg +4 -0
- diffusion_cartogram-0.2.0/tests/test_core.py +374 -0
- diffusion_cartogram-0.2.0/tests/test_core_2d.py +372 -0
- diffusion_cartogram-0.2.0/tests/test_interpolation.py +70 -0
- diffusion_cartogram-0.2.0/tests/test_io.py +160 -0
- diffusion_cartogram-0.2.0/tests/test_vizualization.py +214 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jonah Spector
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: diffusion-cartogram
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation
|
|
5
|
+
Author-email: Jonah Spector <spector.jo@northeastern.edu>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jspector792/diffusion-cartogram
|
|
8
|
+
Project-URL: Repository, https://github.com/jspector792/diffusion-cartogram
|
|
9
|
+
Project-URL: Issues, https://github.com/jspector792/diffusion-cartogram/issues
|
|
10
|
+
Keywords: cartogram,deformation,3d,2d,visualization,density-equalizing,vderm,geojson,shapefile
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy>=1.20
|
|
24
|
+
Requires-Dist: scipy>=1.7
|
|
25
|
+
Requires-Dist: matplotlib>=3.3
|
|
26
|
+
Requires-Dist: pandas>=1.3
|
|
27
|
+
Requires-Dist: tqdm>=4.60
|
|
28
|
+
Provides-Extra: 2d
|
|
29
|
+
Requires-Dist: geopandas>=0.12; extra == "2d"
|
|
30
|
+
Requires-Dist: rasterio>=1.3; extra == "2d"
|
|
31
|
+
Requires-Dist: shapely>=2.0; extra == "2d"
|
|
32
|
+
Provides-Extra: 3d
|
|
33
|
+
Requires-Dist: pymeshlab>=2023.12; extra == "3d"
|
|
34
|
+
Provides-Extra: all
|
|
35
|
+
Requires-Dist: geopandas>=0.12; extra == "all"
|
|
36
|
+
Requires-Dist: rasterio>=1.3; extra == "all"
|
|
37
|
+
Requires-Dist: shapely>=2.0; extra == "all"
|
|
38
|
+
Requires-Dist: pymeshlab>=2023.12; extra == "all"
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# diffusion-cartogram
|
|
42
|
+
[](https://badge.fury.io/py/diffusion-cartogram)
|
|
43
|
+
[](https://github.com/jspector792/diffusion-cartogram/releases)
|
|
44
|
+
[](https://www.python.org/downloads/)
|
|
45
|
+
[](https://opensource.org/licenses/MIT)
|
|
46
|
+
|
|
47
|
+
Density-Equalizing Reference Map — a Python implementation of the VDERM algorithm for **3D shape deformation** and (new in v2.0) **2D cartogram generation** from GeoJSON / Shapefile inputs.
|
|
48
|
+
|
|
49
|
+
## Overview
|
|
50
|
+
|
|
51
|
+
diffusion-cartogram implements the Volumetric Density-Equalizing Reference Map (VDERM) method by [Choi & Rycroft (2020)](https://link.springer.com/article/10.1007/s10915-021-01411-4). VDERM is a 3D generalization of the diffusion-based cartogram method, enabling volume-preserving deformations of 3D objects based on prescribed density distributions.
|
|
52
|
+
|
|
53
|
+
v2.0 extends this to **2D** using the same diffusion-advection process — no FFTs, just the VDERM physics with one spatial dimension removed — making 2D cartograms straightforward to produce from standard geographic files.
|
|
54
|
+
|
|
55
|
+
### Applications
|
|
56
|
+
|
|
57
|
+
- 3D data visualization and cartograms
|
|
58
|
+
- 2D geographic cartograms from GeoJSON / Shapefiles
|
|
59
|
+
- Adaptive mesh refinement
|
|
60
|
+
- Shape modeling and morphing
|
|
61
|
+
|
|
62
|
+
### Key Features
|
|
63
|
+
|
|
64
|
+
- **3D**: Fast regular grid interpolation, STL / VTK / XYZ export, optional PyMeshLab mesh support
|
|
65
|
+
- **2D (new)**: GeoJSON and Shapefile input, GeoTIFF built-in densities, 2D scatter / heatmap visualization
|
|
66
|
+
- Comprehensive matplotlib animations for both 2D and 3D pipelines
|
|
67
|
+
- Progress tracking with intermediate state exports
|
|
68
|
+
- Automatic grid sizing with customizable padding
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
### Base / lite (no optional dependencies)
|
|
73
|
+
```bash
|
|
74
|
+
pip install diffusion-cartogram
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### With 2-D geographic I/O (GeoJSON, Shapefile, GeoTIFF)
|
|
78
|
+
```bash
|
|
79
|
+
pip install diffusion-cartogram[2D]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### With 3-D mesh support (STL, OBJ, Poisson reconstruction)
|
|
83
|
+
```bash
|
|
84
|
+
pip install diffusion-cartogram[3D]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Full installation
|
|
88
|
+
```bash
|
|
89
|
+
pip install diffusion-cartogram[all]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Development
|
|
93
|
+
```bash
|
|
94
|
+
git clone https://github.com/jspector792/diffusion-cartogram.git
|
|
95
|
+
cd diffusion-cartogram
|
|
96
|
+
pip install -e .[all]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Quick Start — 3D
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import diffusion_cartogram as vd
|
|
103
|
+
import numpy as np
|
|
104
|
+
|
|
105
|
+
# 1. Load a mesh and sample a surface point cloud
|
|
106
|
+
surface_points, normals = vd.create_pcd('mesh.stl', n_pts=25000)
|
|
107
|
+
|
|
108
|
+
# 2. Create computational grid (automatically sized)
|
|
109
|
+
params = vd.make_initial_grid(surface_points, max_points=32768)
|
|
110
|
+
|
|
111
|
+
# 3. Initialize VDERM grid and set density
|
|
112
|
+
grid = vd.VDERMGrid(params['shape'], params['h'], params['min_bounds'])
|
|
113
|
+
|
|
114
|
+
def my_density(x, y, z):
|
|
115
|
+
r = np.sqrt((x - 1.5)**2 + (y - 1.5)**2 + (z - 1.5)**2)
|
|
116
|
+
return 1.0 + 3.0 * np.exp(-5 * r**2)
|
|
117
|
+
|
|
118
|
+
grid.set_density(my_density)
|
|
119
|
+
|
|
120
|
+
# 4. Run deformation
|
|
121
|
+
result = vd.run_VDERM(grid, n_max=100, max_eps=0.02)
|
|
122
|
+
|
|
123
|
+
# 5. Apply deformation to surface and export
|
|
124
|
+
final_surface = vd.interpolate_to_surface(
|
|
125
|
+
surface_points, params, result.get_displacement_field()
|
|
126
|
+
)
|
|
127
|
+
vd.export_mesh_file('deformed_mesh.stl', final_surface)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Quick Start — 2D Cartogram
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import diffusion_cartogram as vd
|
|
134
|
+
|
|
135
|
+
# 1. Load geographic boundary (GeoJSON or Shapefile)
|
|
136
|
+
pts, crs = vd.read_geojson('countries.geojson') # or read_shapefile()
|
|
137
|
+
|
|
138
|
+
# 2. Create 2D grid sized to the data
|
|
139
|
+
params = vd.make_initial_grid_2d(pts, max_points=16384)
|
|
140
|
+
grid = vd.VDERMGrid2D(params['shape'], params['h'], params['min_bounds'])
|
|
141
|
+
|
|
142
|
+
# 3. Set density from a GeoTIFF (e.g. population raster)
|
|
143
|
+
vd.density_from_geotiff(grid, 'population.tif')
|
|
144
|
+
# or define analytically: grid.set_density(lambda x, y: ...)
|
|
145
|
+
|
|
146
|
+
# 4. Run deformation — run_VDERM works for both 2D and 3D grids
|
|
147
|
+
result = vd.run_VDERM(grid, n_max=200, max_eps=0.02)
|
|
148
|
+
|
|
149
|
+
# 5. Apply deformation to map points
|
|
150
|
+
deformed = vd.interpolate_to_map_2d(
|
|
151
|
+
pts, params, result.get_displacement_field()
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# 6. Visualize
|
|
155
|
+
dens = vd.interpolate_densities_2d(pts, result)
|
|
156
|
+
vd.plot_map_before_after(pts, deformed, densities=dens,
|
|
157
|
+
title='Population Cartogram')
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Examples
|
|
161
|
+
|
|
162
|
+
Detailed Jupyter notebook examples are in the `examples/` directory:
|
|
163
|
+
|
|
164
|
+
- **01_quickStart.ipynb**: Basic 3-D workflow and concepts
|
|
165
|
+
- **02_boundaryConditions.ipynb**: Boundary condition effects
|
|
166
|
+
- **03_densityFields.ipynb**: Different density functions
|
|
167
|
+
- **04_tracking.ipynb**: Animations and intermediate exports
|
|
168
|
+
- **05_diffusion-cartogram_lite.ipynb**: 3-D point-cloud workflow without mesh dependencies
|
|
169
|
+
- **06_2D_quickStart.ipynb**: 2-D quick start — 2×2 grid from scratch (base install)
|
|
170
|
+
- **07_worldCartogram.ipynb**: World population cartogram from GeoJSON / Shapefile / GeoTIFF (`pip install diffusion-cartogram[2D]`)
|
|
171
|
+
- **08_2D_lite.ipynb**: 2-D lite mode — XY CSV files only (base install)
|
|
172
|
+
|
|
173
|
+
## File Formats
|
|
174
|
+
|
|
175
|
+
### 3D — XYZ (space-delimited text)
|
|
176
|
+
```
|
|
177
|
+
# 3 cols: positions only
|
|
178
|
+
x y z
|
|
179
|
+
# 4 cols: positions + density
|
|
180
|
+
x y z rho
|
|
181
|
+
# 6 cols: positions + normals / velocities
|
|
182
|
+
x y z n_x n_y n_z
|
|
183
|
+
# 7 cols: complete
|
|
184
|
+
x y z n_x n_y n_z rho
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### 2D — CSV (space-delimited text)
|
|
188
|
+
```
|
|
189
|
+
# 2 cols: positions only
|
|
190
|
+
x y
|
|
191
|
+
# 3 cols: positions + density
|
|
192
|
+
x y rho
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Geographic inputs: **GeoJSON**, **Shapefile** (via geopandas), **GeoTIFF** density rasters (via rasterio).
|
|
196
|
+
|
|
197
|
+
## Tips
|
|
198
|
+
|
|
199
|
+
### Grid Resolution
|
|
200
|
+
- **Quick test / 2D**: 4 000–16 000 points (64²–128²)
|
|
201
|
+
- **Standard 3D**: 30 000–50 000 points (30–35³)
|
|
202
|
+
- **High quality 3D**: 100 000–250 000 points (45–60³)
|
|
203
|
+
|
|
204
|
+
### Density Field Design
|
|
205
|
+
- Keep densities positive: ρ > 0
|
|
206
|
+
- Avoid sharp discontinuities near object boundaries
|
|
207
|
+
- Embed large gradients in a uniform density sea rather than against a wall
|
|
208
|
+
|
|
209
|
+
### Numerical Stability
|
|
210
|
+
If epsilon becomes very large or negative, reduce the timestep:
|
|
211
|
+
```python
|
|
212
|
+
vd.run_VDERM(grid, dt=0.001)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Known issues
|
|
216
|
+
|
|
217
|
+
### macOS + conda: OpenMP conflict with pymeshlab (3D only)
|
|
218
|
+
|
|
219
|
+
Users running the 3D reconstruction features (`export_mesh_file`, `export_mesh_vtk`)
|
|
220
|
+
on macOS in a conda environment may encounter a hard crash:
|
|
221
|
+
```
|
|
222
|
+
OMP: Error #15: Initializing libomp.dylib, but found libomp.dylib already initialized.
|
|
223
|
+
```
|
|
224
|
+
This is caused by a conflict between the OpenMP runtime bundled inside pymeshlab's
|
|
225
|
+
wheel and the one loaded by conda-forge's numpy/scipy. It is not specific to
|
|
226
|
+
diffusion-cartogram — it will occur with any package that uses both pymeshlab and
|
|
227
|
+
conda-forge numpy on macOS.
|
|
228
|
+
|
|
229
|
+
**Fix:** Replace pymeshlab's bundled `libomp.dylib` with a symlink to conda's. Both
|
|
230
|
+
are LLVM OpenMP with the same ABI, so this is safe. The file to replace is at
|
|
231
|
+
`$CONDA_PREFIX/lib/python*/site-packages/pymeshlab/Frameworks/libomp.dylib`.
|
|
232
|
+
See [this discussion](https://stackoverflow.com/questions/53014306/error-15-initializing-libiomp5-dylib-but-found-libiomp5-dylib-already-initial) for
|
|
233
|
+
approaches and context. Note that the fix is environment-level and will need to be
|
|
234
|
+
reapplied if pymeshlab is reinstalled or upgraded.
|
|
235
|
+
|
|
236
|
+
## Dependencies
|
|
237
|
+
|
|
238
|
+
| Dependency | Required for |
|
|
239
|
+
|---|---|
|
|
240
|
+
| numpy, scipy, matplotlib, tqdm | Always required |
|
|
241
|
+
| geopandas, rasterio, shapely | 2-D geographic I/O (`pip install diffusion-cartogram[2D]`) |
|
|
242
|
+
| pymeshlab | 3-D mesh I/O and reconstruction (`pip install diffusion-cartogram[3D]`) |
|
|
243
|
+
|
|
244
|
+
## Citation
|
|
245
|
+
|
|
246
|
+
If you use this package in academic work, please cite the appropriate original paper(s):
|
|
247
|
+
```bibtex
|
|
248
|
+
@article{choi2021volumetric,
|
|
249
|
+
title={Volumetric density-equalizing reference map with applications},
|
|
250
|
+
author={Choi, Gary Pui-Tung and Rycroft, Chris H},
|
|
251
|
+
journal={Journal of Scientific Computing},
|
|
252
|
+
volume={86},
|
|
253
|
+
number={3},
|
|
254
|
+
pages={1--26},
|
|
255
|
+
year={2021},
|
|
256
|
+
publisher={Springer}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
@article{gastner2004,
|
|
260
|
+
title = {Diffusion-based method for producing density-equalizing maps},
|
|
261
|
+
author = {Gastner, Michael T. and Newman, M. E. J.},
|
|
262
|
+
journal = {Proceedings of the National Academy of Sciences},
|
|
263
|
+
volume = {101},
|
|
264
|
+
number = {20},
|
|
265
|
+
pages = {7499--7504},
|
|
266
|
+
year = {2004},
|
|
267
|
+
doi = {10.1073/pnas.0400280101}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
And optionally, this implementation:
|
|
271
|
+
```bibtex
|
|
272
|
+
@software{vderm2026,
|
|
273
|
+
title={diffusion-cartogram: A Python implementation of Volumetric Density-Equalizing Reference Map},
|
|
274
|
+
author={Jonah Spector},
|
|
275
|
+
year={2026},
|
|
276
|
+
url={https://github.com/jspector792/diffusion-cartogram}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
283
|
+
|
|
284
|
+
## Acknowledgments
|
|
285
|
+
|
|
286
|
+
- Original VDERM algorithm by Gary P.T. Choi and Chris H. Rycroft
|
|
287
|
+
- Based on the diffusion cartogram method by Gastner & Newman (2004)
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# diffusion-cartogram
|
|
2
|
+
[](https://badge.fury.io/py/diffusion-cartogram)
|
|
3
|
+
[](https://github.com/jspector792/diffusion-cartogram/releases)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Volumetric Density-Equalizing Reference Map — a Python implementation of the VDERM algorithm for **3D shape deformation** and (new in v2.0) **2D cartogram generation** from GeoJSON / Shapefile inputs.
|
|
8
|
+
|
|
9
|
+
<table>
|
|
10
|
+
<tr>
|
|
11
|
+
<td align="center"><b>3-D shape deformation (cube)</b></td>
|
|
12
|
+
<td align="center"><b>2-D cartogram (2×2 grid)</b></td>
|
|
13
|
+
</tr>
|
|
14
|
+
<tr>
|
|
15
|
+
<td><img src="examples/cube.gif"/></td>
|
|
16
|
+
<td><img src="examples/square.gif"/></td>
|
|
17
|
+
</tr>
|
|
18
|
+
</table>
|
|
19
|
+
|
|
20
|
+
## Overview
|
|
21
|
+
|
|
22
|
+
diffusion-cartogram implements the Volumetric Density-Equalizing Reference Map (VDERM) method by [Choi & Rycroft (2020)](https://link.springer.com/article/10.1007/s10915-021-01411-4). VDERM is a 3D generalization of the diffusion-based cartogram method, enabling volume-preserving deformations of 3D objects based on prescribed density distributions.
|
|
23
|
+
|
|
24
|
+
v2.0 extends this to **2D** using the same diffusion-advection process — just one spatial dimension removed — making 2D cartograms straightforward to produce from standard geographic files (GeoJSON, Shapefile, GeoTIFF).
|
|
25
|
+
|
|
26
|
+
### Applications
|
|
27
|
+
|
|
28
|
+
- 3D data visualization and cartograms
|
|
29
|
+
- **2D geographic cartograms** from GeoJSON / Shapefiles (new in v2.0)
|
|
30
|
+
- Adaptive mesh refinement
|
|
31
|
+
- Shape modeling and morphing
|
|
32
|
+
|
|
33
|
+
### Key Features
|
|
34
|
+
|
|
35
|
+
- **3D**: Fast regular grid interpolation, XYZ / STL / VTK export, optional PyMeshLab mesh support
|
|
36
|
+
- **2D (new)**: GeoJSON and Shapefile input, GeoTIFF built-in densities, 2D heatmap / scatter visualization
|
|
37
|
+
- Comprehensive matplotlib animations for both pipelines
|
|
38
|
+
- Progress tracking with intermediate state exports
|
|
39
|
+
- Automatic grid sizing with customizable padding
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
### Base / lite (no optional dependencies)
|
|
44
|
+
```bash
|
|
45
|
+
pip install diffusion-cartogram
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### With 2-D geographic I/O (GeoJSON, Shapefile, GeoTIFF)
|
|
49
|
+
```bash
|
|
50
|
+
pip install diffusion-cartogram[2D]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### With 3-D mesh support (STL, OBJ, Poisson reconstruction)
|
|
54
|
+
```bash
|
|
55
|
+
pip install diffusion-cartogram[3D]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Full installation
|
|
59
|
+
```bash
|
|
60
|
+
pip install diffusion-cartogram[all]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Development
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/jspector792/diffusion-cartogram.git
|
|
66
|
+
cd diffusion-cartogram
|
|
67
|
+
pip install -e .[all]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick Start — 3D
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import diffusion_cartogram as vd
|
|
74
|
+
import numpy as np
|
|
75
|
+
|
|
76
|
+
surface_points, normals = vd.create_pcd('mesh.stl', n_pts=25000)
|
|
77
|
+
params = vd.make_initial_grid(surface_points, max_points=32768)
|
|
78
|
+
grid = vd.VDERMGrid(params['shape'], params['h'], params['min_bounds'])
|
|
79
|
+
|
|
80
|
+
def my_density(x, y, z):
|
|
81
|
+
r = np.sqrt((x - 1.5)**2 + (y - 1.5)**2 + (z - 1.5)**2)
|
|
82
|
+
return 1.0 + 3.0 * np.exp(-5 * r**2)
|
|
83
|
+
|
|
84
|
+
grid.set_density(my_density)
|
|
85
|
+
result = vd.run_VDERM(grid, n_max=100, max_eps=0.02)
|
|
86
|
+
|
|
87
|
+
final_surface = vd.interpolate_to_surface(
|
|
88
|
+
surface_points, params, result.get_displacement_field()
|
|
89
|
+
)
|
|
90
|
+
vd.export_mesh_file('deformed_mesh.stl', final_surface)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Quick Start — 2D Cartogram
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import diffusion_cartogram as vd
|
|
97
|
+
|
|
98
|
+
# Load geographic boundary (GeoJSON or Shapefile)
|
|
99
|
+
pts, crs = vd.read_geojson('countries.geojson')
|
|
100
|
+
|
|
101
|
+
# Create grid and set density from a GeoTIFF
|
|
102
|
+
params = vd.make_initial_grid_2d(pts, max_points=16384)
|
|
103
|
+
grid = vd.VDERMGrid2D(params['shape'], params['h'], params['min_bounds'])
|
|
104
|
+
vd.density_from_geotiff(grid, 'population.tif')
|
|
105
|
+
|
|
106
|
+
# run_VDERM works for both 2D and 3D grids
|
|
107
|
+
result = vd.run_VDERM(grid, n_max=200, max_eps=0.02)
|
|
108
|
+
|
|
109
|
+
deformed = vd.interpolate_to_map_2d(
|
|
110
|
+
pts, params, result.get_displacement_field()
|
|
111
|
+
)
|
|
112
|
+
dens = vd.interpolate_densities_2d(pts, result)
|
|
113
|
+
vd.plot_map_before_after(pts, deformed, densities=dens,
|
|
114
|
+
title='Population Cartogram')
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Examples
|
|
118
|
+
|
|
119
|
+
Detailed Jupyter notebook examples are available in the `examples/` directory:
|
|
120
|
+
|
|
121
|
+
- **01_quickStart.ipynb**: Basic 3-D workflow and concepts
|
|
122
|
+
- **02_boundaryConditions.ipynb**: Understanding and using boundary conditions
|
|
123
|
+
- **03_densityFields.ipynb**: Different density functions and their effects
|
|
124
|
+
- **04_tracking.ipynb**: Creating animations and tracking a 3-D deformation
|
|
125
|
+
- **05_diffusion-cartogram_lite.ipynb**: 3-D point-cloud workflow without mesh dependencies
|
|
126
|
+
- **06_2D_quickStart.ipynb**: 2-D quick start — 2×2 grid from scratch (base install)
|
|
127
|
+
- **07_worldCartogram.ipynb**: World population cartogram from GeoJSON / Shapefile / GeoTIFF (`pip install diffusion-cartogram[2D]`)
|
|
128
|
+
- **08_2D_lite.ipynb**: 2-D lite mode — XY CSV files only (base install)
|
|
129
|
+
|
|
130
|
+
## File Formats
|
|
131
|
+
|
|
132
|
+
### 3D — XYZ (space-delimited text)
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
x y z # positions only
|
|
136
|
+
x y z rho # + density
|
|
137
|
+
x y z nx ny nz # + normals / velocities
|
|
138
|
+
x y z nx ny nz rho # complete
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`read_xyz()` and `write_xyz()` detect and handle all variants automatically.
|
|
142
|
+
|
|
143
|
+
### 2D — CSV (space-delimited text)
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
x y # positions only
|
|
147
|
+
x y rho # + density
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`read_csv_2d()` and `write_csv_2d()` handle both variants.
|
|
151
|
+
Geographic inputs: **GeoJSON** and **Shapefile** (via geopandas), **GeoTIFF** density rasters (via rasterio).
|
|
152
|
+
|
|
153
|
+
## Tips and Best Practices
|
|
154
|
+
|
|
155
|
+
### Choosing Grid Resolution
|
|
156
|
+
|
|
157
|
+
**3D:**
|
|
158
|
+
- Quick test: 15,000–30,000 points (20–30³)
|
|
159
|
+
- Standard: 30,000–50,000 points (30–35³)
|
|
160
|
+
- High quality: 100,000–250,000 points (45–60³)
|
|
161
|
+
|
|
162
|
+
**2D:**
|
|
163
|
+
- Quick test / small regions: 1,000–4,000 points (32²–64²)
|
|
164
|
+
- Standard cartogram: 4,000–16,000 points (64²–128²)
|
|
165
|
+
- High quality: 16,000–65,000 points (128²–256²)
|
|
166
|
+
|
|
167
|
+
Higher resolution gives smoother results but increases computation time.
|
|
168
|
+
|
|
169
|
+
### Density Field Design
|
|
170
|
+
|
|
171
|
+
For smooth, predictable deformations:
|
|
172
|
+
- Keep densities positive: ρ > 0
|
|
173
|
+
- Keep sharp discontinuities 2-3 grid cells away from surface of the object
|
|
174
|
+
- When possible, keep large density gradients embedded in a uniform density sea, rather than against a fixed boundary
|
|
175
|
+
|
|
176
|
+
### Boundary Conditions
|
|
177
|
+
|
|
178
|
+
The algorithm uses no-flux boundary conditions via ghost nodes:
|
|
179
|
+
- Density doesn't leak through boundaries
|
|
180
|
+
- Boundaries can still move slightly (typically << 1 grid cell)
|
|
181
|
+
- Use padding to minimize boundary effects unless a fixed boundary is needed
|
|
182
|
+
|
|
183
|
+
### Numerical Stability
|
|
184
|
+
|
|
185
|
+
If you encounter instability (epsilon becoming very large or negative):
|
|
186
|
+
|
|
187
|
+
1. Try smaller timestep: `vd.run_VDERM(grid, dt=0.001)`
|
|
188
|
+
2. Check your density field for extreme gradients
|
|
189
|
+
3. Increase grid resolution
|
|
190
|
+
|
|
191
|
+
For most cases, automatic timestep selection works well.
|
|
192
|
+
|
|
193
|
+
## Known issues
|
|
194
|
+
|
|
195
|
+
### macOS + conda: OpenMP conflict with pymeshlab (3D only)
|
|
196
|
+
|
|
197
|
+
Users running the 3D reconstruction features (`export_mesh_file`, `export_mesh_vtk`)
|
|
198
|
+
on macOS in a conda environment may encounter a hard crash:
|
|
199
|
+
```
|
|
200
|
+
OMP: Error #15: Initializing libomp.dylib, but found libomp.dylib already initialized.
|
|
201
|
+
```
|
|
202
|
+
This is caused by a conflict between the OpenMP runtime bundled inside pymeshlab's
|
|
203
|
+
wheel and the one loaded by conda-forge's numpy/scipy. It is not specific to
|
|
204
|
+
diffusion-cartogram — it will occur with any package that uses both pymeshlab and
|
|
205
|
+
conda-forge numpy on macOS.
|
|
206
|
+
|
|
207
|
+
**Fix:** Replace pymeshlab's bundled `libomp.dylib` with a symlink to conda's. Both
|
|
208
|
+
are LLVM OpenMP with the same ABI, so this is safe. The file to replace is at
|
|
209
|
+
`$CONDA_PREFIX/lib/python*/site-packages/pymeshlab/Frameworks/libomp.dylib`.
|
|
210
|
+
See [this discussion](https://stackoverflow.com/questions/53014306/error-15-initializing-libiomp5-dylib-but-found-libiomp5-dylib-already-initial) for
|
|
211
|
+
approaches and context. Note that the fix is environment-level and will need to be
|
|
212
|
+
reapplied if pymeshlab is reinstalled or upgraded.
|
|
213
|
+
|
|
214
|
+
## Dependencies
|
|
215
|
+
|
|
216
|
+
### Required
|
|
217
|
+
- numpy >= 1.20
|
|
218
|
+
- scipy >= 1.7
|
|
219
|
+
- matplotlib >= 3.3
|
|
220
|
+
- pandas >= 1.3
|
|
221
|
+
- tqdm >= 4.60
|
|
222
|
+
|
|
223
|
+
### Optional
|
|
224
|
+
- pymeshlab >= 2023.12 — 3-D mesh I/O and Poisson reconstruction (`pip install diffusion-cartogram[3D]`)
|
|
225
|
+
- geopandas >= 0.12, rasterio >= 1.3, shapely >= 2.0 — 2-D geographic I/O (`pip install diffusion-cartogram[2D]`)
|
|
226
|
+
|
|
227
|
+
## Citation
|
|
228
|
+
|
|
229
|
+
If you use this package in academic work, please cite the appropriate original paper(s):
|
|
230
|
+
```bibtex
|
|
231
|
+
@article{choi2021volumetric,
|
|
232
|
+
title={Volumetric density-equalizing reference map with applications},
|
|
233
|
+
author={Choi, Gary Pui-Tung and Rycroft, Chris H},
|
|
234
|
+
journal={Journal of Scientific Computing},
|
|
235
|
+
volume={86},
|
|
236
|
+
number={3},
|
|
237
|
+
pages={1--26},
|
|
238
|
+
year={2021},
|
|
239
|
+
publisher={Springer}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
@article{gastner2004,
|
|
243
|
+
title = {Diffusion-based method for producing density-equalizing maps},
|
|
244
|
+
author = {Gastner, Michael T. and Newman, M. E. J.},
|
|
245
|
+
journal = {Proceedings of the National Academy of Sciences},
|
|
246
|
+
volume = {101},
|
|
247
|
+
number = {20},
|
|
248
|
+
pages = {7499--7504},
|
|
249
|
+
year = {2004},
|
|
250
|
+
doi = {10.1073/pnas.0400280101}
|
|
251
|
+
}
|
|
252
|
+
```
|
|
253
|
+
And optionally, this implementation:
|
|
254
|
+
```bibtex
|
|
255
|
+
@software{vderm2026,
|
|
256
|
+
title={diffusion-cartogram: A Python implementation of Volumetric Density-Equalizing Reference Map},
|
|
257
|
+
author={Jonah Spector},
|
|
258
|
+
year={2026},
|
|
259
|
+
url={https://github.com/jspector792/diffusion-cartogram}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
266
|
+
|
|
267
|
+
## Acknowledgments
|
|
268
|
+
|
|
269
|
+
- Original VDERM algorithm by Gary P.T. Choi and Chris H. Rycroft
|
|
270
|
+
- Based on the diffusion cartogram method by Gastner & Newman (2004)
|