diffusion-cartogram 0.2.0__tar.gz → 0.2.1__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/diffusion_cartogram.egg-info → diffusion_cartogram-0.2.1}/PKG-INFO +23 -4
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/README.md +2 -2
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/__init__.py +7 -3
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/core.py +166 -1
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/core_2d.py +160 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1/diffusion_cartogram.egg-info}/PKG-INFO +23 -4
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/pyproject.toml +2 -2
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/LICENSE +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/README_pypi.md +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/visualization.py +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/visualization_2d.py +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/SOURCES.txt +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/dependency_links.txt +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/requires.txt +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/top_level.txt +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/setup.cfg +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_core.py +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_core_2d.py +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_interpolation.py +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_io.py +0 -0
- {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_vizualization.py +0 -0
{diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info → diffusion_cartogram-0.2.1}/PKG-INFO
RENAMED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: diffusion-cartogram
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation
|
|
5
5
|
Author-email: Jonah Spector <spector.jo@northeastern.edu>
|
|
6
|
-
License
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Jonah Spector
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
7
27
|
Project-URL: Homepage, https://github.com/jspector792/diffusion-cartogram
|
|
8
28
|
Project-URL: Repository, https://github.com/jspector792/diffusion-cartogram
|
|
9
29
|
Project-URL: Issues, https://github.com/jspector792/diffusion-cartogram/issues
|
|
@@ -36,7 +56,6 @@ Requires-Dist: geopandas>=0.12; extra == "all"
|
|
|
36
56
|
Requires-Dist: rasterio>=1.3; extra == "all"
|
|
37
57
|
Requires-Dist: shapely>=2.0; extra == "all"
|
|
38
58
|
Requires-Dist: pymeshlab>=2023.12; extra == "all"
|
|
39
|
-
Dynamic: license-file
|
|
40
59
|
|
|
41
60
|
# diffusion-cartogram
|
|
42
61
|
[](https://badge.fury.io/py/diffusion-cartogram)
|
|
@@ -253,8 +253,8 @@ If you use this package in academic work, please cite the appropriate original p
|
|
|
253
253
|
And optionally, this implementation:
|
|
254
254
|
```bibtex
|
|
255
255
|
@software{vderm2026,
|
|
256
|
-
title={diffusion-cartogram: A Python
|
|
257
|
-
author={Jonah Spector},
|
|
256
|
+
title={diffusion-cartogram: A Python package for diffusion deformations in 2 and 3 dimensions},
|
|
257
|
+
author={Jonah Spector and Albert-László Barabási},
|
|
258
258
|
year={2026},
|
|
259
259
|
url={https://github.com/jspector792/diffusion-cartogram}
|
|
260
260
|
}
|
|
@@ -29,13 +29,14 @@ cartogram deformation from GeoJSON / Shapefile inputs.
|
|
|
29
29
|
>>> vd.plot_map_2d(deformed, title='Population Cartogram')
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
|
-
__version__ = '0.2.
|
|
32
|
+
__version__ = '0.2.1'
|
|
33
33
|
|
|
34
34
|
# Core VDERM classes and algorithms
|
|
35
35
|
from .core import (
|
|
36
36
|
VDERMGrid,
|
|
37
37
|
run_VDERM,
|
|
38
38
|
run_VDERM_with_tracking,
|
|
39
|
+
animate_surface_posthoc,
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
# I/O functions
|
|
@@ -85,7 +86,8 @@ __all__ = [
|
|
|
85
86
|
'VDERMGrid',
|
|
86
87
|
'run_VDERM',
|
|
87
88
|
'run_VDERM_with_tracking',
|
|
88
|
-
|
|
89
|
+
'animate_surface_posthoc',
|
|
90
|
+
|
|
89
91
|
# I/O
|
|
90
92
|
'write_xyz',
|
|
91
93
|
'read_xyz',
|
|
@@ -126,6 +128,7 @@ if _has_visualization:
|
|
|
126
128
|
from .core_2d import (
|
|
127
129
|
VDERMGrid2D,
|
|
128
130
|
run_VDERM_2d_with_tracking,
|
|
131
|
+
animate_map_posthoc,
|
|
129
132
|
)
|
|
130
133
|
|
|
131
134
|
# 2-D grid utilities
|
|
@@ -172,7 +175,8 @@ __all__ += [
|
|
|
172
175
|
# 2-D classes and algorithms
|
|
173
176
|
'VDERMGrid2D',
|
|
174
177
|
'run_VDERM_2d_with_tracking',
|
|
175
|
-
|
|
178
|
+
'animate_map_posthoc',
|
|
179
|
+
# 2-D grid utilities
|
|
176
180
|
'compute_grid_dimensions_2d',
|
|
177
181
|
'make_initial_grid_2d',
|
|
178
182
|
'print_grid_info_2d',
|
|
@@ -1018,7 +1018,172 @@ def run_VDERM_with_tracking(grid, surface_points,
|
|
|
1018
1018
|
|
|
1019
1019
|
return grid
|
|
1020
1020
|
|
|
1021
|
-
|
|
1021
|
+
|
|
1022
|
+
def _interpolate_field_to_points(points, grid_params, field):
|
|
1023
|
+
"""Interpolate a scalar field defined on the (L, M, N) grid to arbitrary points."""
|
|
1024
|
+
L, M, N = grid_params['shape']
|
|
1025
|
+
h = grid_params['h']
|
|
1026
|
+
min_bounds = grid_params['min_bounds']
|
|
1027
|
+
|
|
1028
|
+
x = min_bounds[0] + np.arange(L) * h
|
|
1029
|
+
y = min_bounds[1] + np.arange(M) * h
|
|
1030
|
+
z = min_bounds[2] + np.arange(N) * h
|
|
1031
|
+
|
|
1032
|
+
interp = RegularGridInterpolator((x, y, z), field, bounds_error=False, fill_value=0)
|
|
1033
|
+
return interp(points)
|
|
1034
|
+
|
|
1035
|
+
|
|
1036
|
+
def animate_surface_posthoc(grid, surface_points, n_frames=30,
|
|
1037
|
+
output_folder='vderm_posthoc_exports',
|
|
1038
|
+
initial_densities=None, tau=0.3,
|
|
1039
|
+
surface_folder='vderm_surface'):
|
|
1040
|
+
"""
|
|
1041
|
+
Generate an approximate surface animation from a completed VDERM run
|
|
1042
|
+
without re-running the (expensive) vector-field interpolation at
|
|
1043
|
+
every intermediate step.
|
|
1044
|
+
|
|
1045
|
+
Only the initial and final surfaces are ever interpolated from the
|
|
1046
|
+
grid's displacement field. Intermediate frames are produced by
|
|
1047
|
+
linearly interpolating point positions (and, if provided, densities)
|
|
1048
|
+
between those two surfaces along an eased timescale that starts fast
|
|
1049
|
+
and decelerates (``1 - exp(-t/tau)``), which visually approximates
|
|
1050
|
+
the true VDERM motion (fast initial advection, slowing as the
|
|
1051
|
+
density field equalizes) without needing to know it exactly.
|
|
1052
|
+
|
|
1053
|
+
This is a display tool, not a physically accurate reconstruction of
|
|
1054
|
+
intermediate states — see the warning printed when this function
|
|
1055
|
+
runs, and use ``run_VDERM_with_tracking`` if intermediate accuracy
|
|
1056
|
+
matters.
|
|
1057
|
+
|
|
1058
|
+
Parameters
|
|
1059
|
+
----------
|
|
1060
|
+
grid : VDERMGrid
|
|
1061
|
+
A grid that has already been deformed (e.g. via run_VDERM or
|
|
1062
|
+
run_VDERM_with_tracking). Its current positions are treated as
|
|
1063
|
+
the final state.
|
|
1064
|
+
surface_points : ndarray, shape (n_points, 3)
|
|
1065
|
+
Original (undeformed) surface point cloud.
|
|
1066
|
+
n_frames : int, default=30
|
|
1067
|
+
Number of frames to export, including the initial and final
|
|
1068
|
+
frames. Must be >= 2.
|
|
1069
|
+
output_folder : str, default='vderm_posthoc_exports'
|
|
1070
|
+
Base directory for exports.
|
|
1071
|
+
initial_densities : callable or ndarray, optional
|
|
1072
|
+
Density field assigned to the grid before deformation, in the
|
|
1073
|
+
same form accepted by ``VDERMGrid.set_density``: either a
|
|
1074
|
+
callable ``density_func(x, y, z) -> density`` or an array of
|
|
1075
|
+
shape (L, M, N). If given, per-point densities are eased from
|
|
1076
|
+
this initial field to the grid's current (final) density field
|
|
1077
|
+
and each frame is colored accordingly. If omitted, every frame
|
|
1078
|
+
is colored using only the final density field.
|
|
1079
|
+
tau : float, default=0.3
|
|
1080
|
+
Time constant of the easing curve ``1 - exp(-t/tau))``, evaluated
|
|
1081
|
+
over a normalized t in [0, 1] and renormalized so the curve runs
|
|
1082
|
+
exactly from 0 to 1. Smaller values front-load more of the motion
|
|
1083
|
+
into the earliest frames.
|
|
1084
|
+
surface_folder : str, default='vderm_surface'
|
|
1085
|
+
Subfolder (under output_folder) that exports are written to.
|
|
1086
|
+
Matches the default subfolder expected by
|
|
1087
|
+
``animate_surface_deformation``, so the output of this function
|
|
1088
|
+
can be fed directly into it.
|
|
1089
|
+
|
|
1090
|
+
Returns
|
|
1091
|
+
-------
|
|
1092
|
+
frame_paths : list of str
|
|
1093
|
+
Paths to the exported .xyz files, in frame order.
|
|
1094
|
+
|
|
1095
|
+
Notes
|
|
1096
|
+
-----
|
|
1097
|
+
Exported files use the same 7-column (x y z n_x n_y n_z rho) format
|
|
1098
|
+
as run_VDERM_with_tracking's surface exports, and the same
|
|
1099
|
+
'surface_iteration_NNNN.xyz' / 'surface_final_iteration_NNNN.xyz'
|
|
1100
|
+
naming convention, so they can be visualized with
|
|
1101
|
+
animate_surface_deformation() without any extra arguments. The
|
|
1102
|
+
normal-vector columns are not real per-frame velocities (none are
|
|
1103
|
+
computed in this approximation) — they hold the net displacement
|
|
1104
|
+
direction of each point, provided only so downstream tools that
|
|
1105
|
+
expect a normals column keep working.
|
|
1106
|
+
|
|
1107
|
+
Examples
|
|
1108
|
+
--------
|
|
1109
|
+
>>> final_grid = vd.run_VDERM(grid, n_max=500)
|
|
1110
|
+
>>> vd.animate_surface_posthoc(final_grid, points, n_frames=40,
|
|
1111
|
+
... output_folder='my_deformation_posthoc',
|
|
1112
|
+
... initial_densities=head_density)
|
|
1113
|
+
>>> vd.animate_surface_deformation('my_deformation_posthoc')
|
|
1114
|
+
"""
|
|
1115
|
+
print("=" * 70)
|
|
1116
|
+
print("POST-HOC ANIMATION NOTICE")
|
|
1117
|
+
print("=" * 70)
|
|
1118
|
+
print("This post-hoc animation tool is meant for display purposes only and")
|
|
1119
|
+
print("may not be faithful to the real deformation during intermediate")
|
|
1120
|
+
print("steps. If you are interested in viewing the process of the real")
|
|
1121
|
+
print("deformation, use the run_VDERM_with_tracking function, which is")
|
|
1122
|
+
print("slower, but is guaranteed to be accurate at each frame.")
|
|
1123
|
+
print("=" * 70)
|
|
1124
|
+
|
|
1125
|
+
if n_frames < 2:
|
|
1126
|
+
raise ValueError("n_frames must be >= 2 (need at least an initial and final frame)")
|
|
1127
|
+
if tau <= 0:
|
|
1128
|
+
raise ValueError(f"tau must be > 0, got {tau}")
|
|
1129
|
+
|
|
1130
|
+
out_dir = os.path.join(output_folder, surface_folder)
|
|
1131
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
1132
|
+
|
|
1133
|
+
params = {'shape': (grid.L, grid.M, grid.N), 'h': grid.h, 'min_bounds': grid.min_bounds}
|
|
1134
|
+
displacement_field = grid.get_displacement_field()
|
|
1135
|
+
|
|
1136
|
+
initial_surface = surface_points
|
|
1137
|
+
final_surface = interpolate_to_surface(surface_points, params, displacement_field)
|
|
1138
|
+
net_displacement = final_surface - initial_surface
|
|
1139
|
+
|
|
1140
|
+
# Eased timescale: fast at first, decelerating; renormalized to hit exactly 0 and 1
|
|
1141
|
+
t = np.linspace(0.0, 1.0, n_frames)
|
|
1142
|
+
ease = (1.0 - np.exp(-t / tau))
|
|
1143
|
+
ease = ease / ease[-1]
|
|
1144
|
+
|
|
1145
|
+
final_surface_densities = interpolate_densities(surface_points, grid)
|
|
1146
|
+
|
|
1147
|
+
if initial_densities is not None:
|
|
1148
|
+
if callable(initial_densities):
|
|
1149
|
+
xs = grid.min_bounds[0] + np.arange(grid.L) * grid.h
|
|
1150
|
+
ys = grid.min_bounds[1] + np.arange(grid.M) * grid.h
|
|
1151
|
+
zs = grid.min_bounds[2] + np.arange(grid.N) * grid.h
|
|
1152
|
+
initial_rho = np.zeros_like(grid.rho)
|
|
1153
|
+
for i, x in enumerate(xs):
|
|
1154
|
+
for j, y in enumerate(ys):
|
|
1155
|
+
for k, z in enumerate(zs):
|
|
1156
|
+
initial_rho[i, j, k] = initial_densities(x, y, z)
|
|
1157
|
+
else:
|
|
1158
|
+
initial_rho = np.asarray(initial_densities, dtype=float)
|
|
1159
|
+
if initial_rho.shape != grid.rho.shape:
|
|
1160
|
+
raise ValueError(
|
|
1161
|
+
f"initial_densities array shape {initial_rho.shape} must match "
|
|
1162
|
+
f"grid shape {grid.rho.shape}"
|
|
1163
|
+
)
|
|
1164
|
+
initial_surface_densities = _interpolate_field_to_points(surface_points, params, initial_rho)
|
|
1165
|
+
else:
|
|
1166
|
+
initial_surface_densities = final_surface_densities
|
|
1167
|
+
|
|
1168
|
+
frame_paths = []
|
|
1169
|
+
for frame_idx, alpha in enumerate(ease):
|
|
1170
|
+
positions_i = initial_surface + alpha * net_displacement
|
|
1171
|
+
densities_i = initial_surface_densities + alpha * (final_surface_densities - initial_surface_densities)
|
|
1172
|
+
|
|
1173
|
+
if frame_idx == n_frames - 1:
|
|
1174
|
+
filename = f'surface_final_iteration_{frame_idx:04d}.xyz'
|
|
1175
|
+
else:
|
|
1176
|
+
filename = f'surface_iteration_{frame_idx:04d}.xyz'
|
|
1177
|
+
|
|
1178
|
+
filepath = os.path.join(out_dir, filename)
|
|
1179
|
+
write_xyz(filepath, positions_i, normals=net_displacement, densities=densities_i)
|
|
1180
|
+
frame_paths.append(filepath)
|
|
1181
|
+
|
|
1182
|
+
print(f"\n{n_frames} post-hoc animation frames saved to: {out_dir}/")
|
|
1183
|
+
|
|
1184
|
+
return frame_paths
|
|
1185
|
+
|
|
1186
|
+
|
|
1022
1187
|
def interpolate_densities(surface_points, grid):
|
|
1023
1188
|
"""
|
|
1024
1189
|
Interpolate density values from grid to surface points.
|
|
@@ -829,3 +829,163 @@ def run_VDERM_2d_with_tracking(
|
|
|
829
829
|
print(f" Map point states (x y rho): {map_folder}/")
|
|
830
830
|
|
|
831
831
|
return grid
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def _interpolate_field_to_points_2d(points, grid_params, field):
|
|
835
|
+
"""Interpolate a scalar field defined on the (L, M) grid to arbitrary points."""
|
|
836
|
+
L, M = grid_params['shape']
|
|
837
|
+
h = grid_params['h']
|
|
838
|
+
mb = grid_params['min_bounds']
|
|
839
|
+
|
|
840
|
+
x = mb[0] + np.arange(L) * h
|
|
841
|
+
y = mb[1] + np.arange(M) * h
|
|
842
|
+
|
|
843
|
+
interp = RegularGridInterpolator((x, y), field, bounds_error=False, fill_value=0.0)
|
|
844
|
+
return interp(points)
|
|
845
|
+
|
|
846
|
+
|
|
847
|
+
def animate_map_posthoc(grid, map_points, n_frames=30,
|
|
848
|
+
output_folder='vderm_2d_posthoc_exports',
|
|
849
|
+
initial_densities=None, tau=0.3,
|
|
850
|
+
map_folder='vderm_map'):
|
|
851
|
+
"""
|
|
852
|
+
Generate an approximate 2-D map animation from a completed VDERM run
|
|
853
|
+
without re-running the (expensive) vector-field interpolation at
|
|
854
|
+
every intermediate step.
|
|
855
|
+
|
|
856
|
+
Mirrors animate_surface_posthoc() from core.py — only the initial and
|
|
857
|
+
final map states are ever interpolated from the grid's displacement
|
|
858
|
+
field. Intermediate frames are produced by linearly interpolating
|
|
859
|
+
point positions (and, if provided, densities) between those two
|
|
860
|
+
states along an eased timescale that starts fast and decelerates
|
|
861
|
+
(``1 - exp(-t/tau)``), approximating the true VDERM motion (fast
|
|
862
|
+
initial advection, slowing as the density field equalizes).
|
|
863
|
+
|
|
864
|
+
This is a display tool, not a physically accurate reconstruction of
|
|
865
|
+
intermediate states — see the warning printed when this function
|
|
866
|
+
runs, and use ``run_VDERM_2d_with_tracking`` if intermediate accuracy
|
|
867
|
+
matters.
|
|
868
|
+
|
|
869
|
+
Parameters
|
|
870
|
+
----------
|
|
871
|
+
grid : VDERMGrid2D
|
|
872
|
+
A grid that has already been deformed (e.g. via run_VDERM or
|
|
873
|
+
run_VDERM_2d_with_tracking). Its current positions are treated
|
|
874
|
+
as the final state.
|
|
875
|
+
map_points : ndarray, shape (n_points, 2)
|
|
876
|
+
Original (undeformed) map point set.
|
|
877
|
+
n_frames : int, default=30
|
|
878
|
+
Number of frames to export, including the initial and final
|
|
879
|
+
frames. Must be >= 2.
|
|
880
|
+
output_folder : str, default='vderm_2d_posthoc_exports'
|
|
881
|
+
Base directory for exports.
|
|
882
|
+
initial_densities : callable or ndarray, optional
|
|
883
|
+
Density field assigned to the grid before deformation, in the
|
|
884
|
+
same form accepted by ``VDERMGrid2D.set_density``: either a
|
|
885
|
+
callable ``density_func(x, y) -> density`` or an array of shape
|
|
886
|
+
(L, M). If given, per-point densities are eased from this
|
|
887
|
+
initial field to the grid's current (final) density field and
|
|
888
|
+
each frame is colored accordingly. If omitted, frames are
|
|
889
|
+
exported without a density column.
|
|
890
|
+
tau : float, default=0.3
|
|
891
|
+
Time constant of the easing curve ``1 - exp(-t/tau)``, evaluated
|
|
892
|
+
over a normalized t in [0, 1] and renormalized so the curve runs
|
|
893
|
+
exactly from 0 to 1. Smaller values front-load more of the motion
|
|
894
|
+
into the earliest frames.
|
|
895
|
+
map_folder : str, default='vderm_map'
|
|
896
|
+
Subfolder (under output_folder) that exports are written to.
|
|
897
|
+
Matches the default subfolder expected by
|
|
898
|
+
``animate_map_deformation_2d``, so the output of this function
|
|
899
|
+
can be fed directly into it.
|
|
900
|
+
|
|
901
|
+
Returns
|
|
902
|
+
-------
|
|
903
|
+
frame_paths : list of str
|
|
904
|
+
Paths to the exported .csv files, in frame order.
|
|
905
|
+
|
|
906
|
+
Notes
|
|
907
|
+
-----
|
|
908
|
+
Exported files use the same CSV format ('x y' or 'x y rho') and the
|
|
909
|
+
same 'map_iteration_NNNN.csv' / 'map_final_iteration_NNNN.csv' naming
|
|
910
|
+
convention as run_VDERM_2d_with_tracking's map exports, so they can be
|
|
911
|
+
visualized with animate_map_deformation_2d() without any extra
|
|
912
|
+
arguments.
|
|
913
|
+
|
|
914
|
+
Examples
|
|
915
|
+
--------
|
|
916
|
+
>>> final_grid = vd.run_VDERM(grid, n_max=500)
|
|
917
|
+
>>> vd.animate_map_posthoc(final_grid, pts, n_frames=40,
|
|
918
|
+
... output_folder='my_cartogram_posthoc',
|
|
919
|
+
... initial_densities=grid.rho.copy())
|
|
920
|
+
>>> vd.animate_map_deformation_2d('my_cartogram_posthoc')
|
|
921
|
+
"""
|
|
922
|
+
print("=" * 70)
|
|
923
|
+
print("POST-HOC ANIMATION NOTICE")
|
|
924
|
+
print("=" * 70)
|
|
925
|
+
print("This post-hoc animation tool is meant for display purposes only and")
|
|
926
|
+
print("may not be faithful to the real deformation during intermediate")
|
|
927
|
+
print("steps. If you are interested in viewing the process of the real")
|
|
928
|
+
print("deformation, use the run_VDERM_2d_with_tracking function, which is")
|
|
929
|
+
print("slower, but is guaranteed to be accurate at each frame.")
|
|
930
|
+
print("=" * 70)
|
|
931
|
+
|
|
932
|
+
if n_frames < 2:
|
|
933
|
+
raise ValueError("n_frames must be >= 2 (need at least an initial and final frame)")
|
|
934
|
+
if tau <= 0:
|
|
935
|
+
raise ValueError(f"tau must be > 0, got {tau}")
|
|
936
|
+
|
|
937
|
+
out_dir = os.path.join(output_folder, map_folder)
|
|
938
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
939
|
+
|
|
940
|
+
params = {'shape': (grid.L, grid.M), 'h': grid.h, 'min_bounds': grid.min_bounds}
|
|
941
|
+
displacement_field = grid.get_displacement_field()
|
|
942
|
+
|
|
943
|
+
initial_map = map_points
|
|
944
|
+
final_map = interpolate_to_map_2d(map_points, params, displacement_field)
|
|
945
|
+
|
|
946
|
+
# Eased timescale: fast at first, decelerating; renormalized to hit exactly 0 and 1
|
|
947
|
+
t = np.linspace(0.0, 1.0, n_frames)
|
|
948
|
+
ease = (1.0 - np.exp(-t / tau))
|
|
949
|
+
ease = ease / ease[-1]
|
|
950
|
+
|
|
951
|
+
have_densities = initial_densities is not None
|
|
952
|
+
if have_densities:
|
|
953
|
+
final_map_densities = interpolate_densities_2d(map_points, grid)
|
|
954
|
+
|
|
955
|
+
if callable(initial_densities):
|
|
956
|
+
xs = grid.min_bounds[0] + np.arange(grid.L) * grid.h
|
|
957
|
+
ys = grid.min_bounds[1] + np.arange(grid.M) * grid.h
|
|
958
|
+
initial_rho = np.zeros_like(grid.rho)
|
|
959
|
+
for i, x in enumerate(xs):
|
|
960
|
+
for j, y in enumerate(ys):
|
|
961
|
+
initial_rho[i, j] = initial_densities(x, y)
|
|
962
|
+
else:
|
|
963
|
+
initial_rho = np.asarray(initial_densities, dtype=float)
|
|
964
|
+
if initial_rho.shape != grid.rho.shape:
|
|
965
|
+
raise ValueError(
|
|
966
|
+
f"initial_densities array shape {initial_rho.shape} must match "
|
|
967
|
+
f"grid shape {grid.rho.shape}"
|
|
968
|
+
)
|
|
969
|
+
initial_map_densities = _interpolate_field_to_points_2d(map_points, params, initial_rho)
|
|
970
|
+
|
|
971
|
+
frame_paths = []
|
|
972
|
+
for frame_idx, alpha in enumerate(ease):
|
|
973
|
+
positions_i = initial_map + alpha * (final_map - initial_map)
|
|
974
|
+
|
|
975
|
+
if have_densities:
|
|
976
|
+
densities_i = initial_map_densities + alpha * (final_map_densities - initial_map_densities)
|
|
977
|
+
else:
|
|
978
|
+
densities_i = None
|
|
979
|
+
|
|
980
|
+
if frame_idx == n_frames - 1:
|
|
981
|
+
filename = f'map_final_iteration_{frame_idx:04d}.csv'
|
|
982
|
+
else:
|
|
983
|
+
filename = f'map_iteration_{frame_idx:04d}.csv'
|
|
984
|
+
|
|
985
|
+
filepath = os.path.join(out_dir, filename)
|
|
986
|
+
write_csv_2d(filepath, positions_i, densities_i)
|
|
987
|
+
frame_paths.append(filepath)
|
|
988
|
+
|
|
989
|
+
print(f"\n{n_frames} post-hoc animation frames saved to: {out_dir}/")
|
|
990
|
+
|
|
991
|
+
return frame_paths
|
{diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1/diffusion_cartogram.egg-info}/PKG-INFO
RENAMED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
2
|
Name: diffusion-cartogram
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation
|
|
5
5
|
Author-email: Jonah Spector <spector.jo@northeastern.edu>
|
|
6
|
-
License
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Jonah Spector
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
7
27
|
Project-URL: Homepage, https://github.com/jspector792/diffusion-cartogram
|
|
8
28
|
Project-URL: Repository, https://github.com/jspector792/diffusion-cartogram
|
|
9
29
|
Project-URL: Issues, https://github.com/jspector792/diffusion-cartogram/issues
|
|
@@ -36,7 +56,6 @@ Requires-Dist: geopandas>=0.12; extra == "all"
|
|
|
36
56
|
Requires-Dist: rasterio>=1.3; extra == "all"
|
|
37
57
|
Requires-Dist: shapely>=2.0; extra == "all"
|
|
38
58
|
Requires-Dist: pymeshlab>=2023.12; extra == "all"
|
|
39
|
-
Dynamic: license-file
|
|
40
59
|
|
|
41
60
|
# diffusion-cartogram
|
|
42
61
|
[](https://badge.fury.io/py/diffusion-cartogram)
|
|
@@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "diffusion-cartogram"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.1"
|
|
8
8
|
description = "Volumetric Density-Equalizing Reference Map — 3D shape deformation and 2D cartogram generation"
|
|
9
9
|
readme = "README_pypi.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
|
-
license = "
|
|
11
|
+
license = {file = "LICENSE"}
|
|
12
12
|
authors = [
|
|
13
13
|
{name = "Jonah Spector", email = "spector.jo@northeastern.edu"}
|
|
14
14
|
]
|
|
File without changes
|
|
File without changes
|
{diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/visualization.py
RENAMED
|
File without changes
|
{diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/visualization_2d.py
RENAMED
|
File without changes
|
{diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/requires.txt
RENAMED
|
File without changes
|
{diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|