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.
Files changed (21) hide show
  1. {diffusion_cartogram-0.2.0/diffusion_cartogram.egg-info → diffusion_cartogram-0.2.1}/PKG-INFO +23 -4
  2. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/README.md +2 -2
  3. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/__init__.py +7 -3
  4. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/core.py +166 -1
  5. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/core_2d.py +160 -0
  6. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1/diffusion_cartogram.egg-info}/PKG-INFO +23 -4
  7. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/pyproject.toml +2 -2
  8. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/LICENSE +0 -0
  9. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/README_pypi.md +0 -0
  10. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/visualization.py +0 -0
  11. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram/visualization_2d.py +0 -0
  12. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/SOURCES.txt +0 -0
  13. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/dependency_links.txt +0 -0
  14. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/requires.txt +0 -0
  15. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/diffusion_cartogram.egg-info/top_level.txt +0 -0
  16. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/setup.cfg +0 -0
  17. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_core.py +0 -0
  18. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_core_2d.py +0 -0
  19. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_interpolation.py +0 -0
  20. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_io.py +0 -0
  21. {diffusion_cartogram-0.2.0 → diffusion_cartogram-0.2.1}/tests/test_vizualization.py +0 -0
@@ -1,9 +1,29 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: diffusion-cartogram
3
- Version: 0.2.0
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-Expression: MIT
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
  [![PyPI version](https://badge.fury.io/py/diffusion-cartogram.svg)](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 implementation of Volumetric Density-Equalizing Reference Map},
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.0'
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
- # 2-D grid utilities
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
@@ -1,9 +1,29 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.1
2
2
  Name: diffusion-cartogram
3
- Version: 0.2.0
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-Expression: MIT
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
  [![PyPI version](https://badge.fury.io/py/diffusion-cartogram.svg)](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.0"
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 = "MIT"
11
+ license = {file = "LICENSE"}
12
12
  authors = [
13
13
  {name = "Jonah Spector", email = "spector.jo@northeastern.edu"}
14
14
  ]