aabpl 0.1.0__py3-none-any.whl

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 (42) hide show
  1. aabpl/__init__.py +24 -0
  2. aabpl/doc/__init__.py +0 -0
  3. aabpl/doc/docstrings.py +12 -0
  4. aabpl/illustrations/__init__.py +4 -0
  5. aabpl/illustrations/distribution_plot.py +109 -0
  6. aabpl/illustrations/illustrate_cell_pattern.py +75 -0
  7. aabpl/illustrations/illustrate_nested_grid.py +90 -0
  8. aabpl/illustrations/illustrate_optimal_grid_spacing.py +294 -0
  9. aabpl/illustrations/illustrate_point_to_cell_region_assignment.py +368 -0
  10. aabpl/illustrations/illustrate_point_to_disk.py +148 -0
  11. aabpl/illustrations/plot_pt_vars.py +59 -0
  12. aabpl/illustrations/plot_utils.py +489 -0
  13. aabpl/main.py +317 -0
  14. aabpl/radius_search/__init__.py +0 -0
  15. aabpl/radius_search/grid_class.py +272 -0
  16. aabpl/radius_search/nested_search.py +557 -0
  17. aabpl/radius_search/offset_region_classes.py +752 -0
  18. aabpl/radius_search/offset_regions.py +904 -0
  19. aabpl/radius_search/optimal_grid_spacing.py +258 -0
  20. aabpl/radius_search/pts_radius_search.py +194 -0
  21. aabpl/radius_search/pts_to_cells.py +131 -0
  22. aabpl/radius_search/pts_to_offset_regions.py +591 -0
  23. aabpl/radius_search/radius_search_class.py +405 -0
  24. aabpl/radius_search/two_dimensional_weak_ordering_class.py +503 -0
  25. aabpl/random_distribution.py +218 -0
  26. aabpl/testing/__init__.py +0 -0
  27. aabpl/testing/test_performance.py +237 -0
  28. aabpl/utils/__init__.py +0 -0
  29. aabpl/utils/distances_to_cell.py +550 -0
  30. aabpl/utils/general.py +229 -0
  31. aabpl/utils/intersections.py +186 -0
  32. aabpl/utils/rotations.py +138 -0
  33. aabpl/valid_area.py +38 -0
  34. aabpl-0.1.0.dist-info/LICENSE +21 -0
  35. aabpl-0.1.0.dist-info/METADATA +171 -0
  36. aabpl-0.1.0.dist-info/RECORD +42 -0
  37. aabpl-0.1.0.dist-info/WHEEL +5 -0
  38. aabpl-0.1.0.dist-info/top_level.txt +1 -0
  39. primelocations/__init__.py +21 -0
  40. primelocations/main.py +273 -0
  41. primelocations/random_distribution.py +219 -0
  42. primelocations/valid_area.py +38 -0
aabpl/__init__.py ADDED
@@ -0,0 +1,24 @@
1
+ # from importlib.metadata import version
2
+ # TODO replace * with more expliced exports for the final version of the package
3
+ # # from .radius_search import *
4
+ # import utils
5
+ # import testing
6
+ # import illustrations
7
+ # import radius_search
8
+ from . import main
9
+ # from .main import *
10
+ # from .radius_search.optimal_grid_spacing import *
11
+ # from .radius_search.offset_regions import *
12
+ # from ..utils.general import *
13
+ # from ..illustrations.illustrate_nested_grid import *
14
+ # from ..illustrations.illustrate_optimal_grid_spacing import *
15
+ # from ..illustrations.illustrate_point_to_cell_region_assignment import *
16
+ # # from .nested_search import *
17
+ # from .radius_search.radius_search_class import *
18
+ # from ..illustrations.plot_utils import *
19
+ # from .radius_search.two_dimensional_weak_ordering_class import *
20
+ # from .main import *
21
+ # from ..illustrations.plot_utils import *
22
+ # TODO ensure to no exports imports (like np.array)?
23
+
24
+ # __version__ = version('primelocations')
aabpl/doc/__init__.py ADDED
File without changes
@@ -0,0 +1,12 @@
1
+ arguments_to_replace = list({
2
+ '<y_coord_name>':(
3
+ """x (numpy.array):
4
+ Array of x/longtitude coordinates"""
5
+ )
6
+
7
+ }.items())
8
+
9
+ def fixdocstring(func):
10
+ # for k,v in arguments_to_replace:
11
+ # func.__doc__ = func.__doc__.replace(k, v)
12
+ return func
@@ -0,0 +1,4 @@
1
+ # from .plot_utils import *
2
+ # from .illustrate_nested_grid import *
3
+ # from .illustrate_optimal_grid_spacing import *
4
+ # from .illustrate_point_to_cell_region_assignment import *
@@ -0,0 +1,109 @@
1
+ from pandas import DataFrame as _pd_DataFrame
2
+ from numpy import (
3
+ array as _np_array,
4
+ linspace as _np_linspace,
5
+ searchsorted as _np_searchsorted
6
+ )
7
+ from matplotlib.pyplot import (subplots as _plt_subplots)
8
+
9
+ def create_distribution_plot(
10
+ sums_df:_pd_DataFrame,
11
+ cluster_threshold_values:list,
12
+ k_th_percentiles:list,
13
+ radius=None,
14
+ plot_kwargs:dict={},
15
+ ):
16
+ """
17
+ TODO Descripiton
18
+ """
19
+ (n_random_points, ncols) = sums_df.shape
20
+ # specify default plot kwargs and add defaults
21
+ default_kwargs = {
22
+ 's':0.8,
23
+ 'color':'#eaa',
24
+
25
+ 'figsize': (5*ncols,5),
26
+ 'fig':None,
27
+ 'axs':None,
28
+
29
+ 'hlines':{'color':'red', 'linewidth':1},
30
+ 'vlines':{'color':'red', 'linewidth':1},
31
+ }
32
+ kwargs = {}
33
+ for k in plot_kwargs:
34
+ if k in [k for k,v in default_kwargs.items() if type(v)==dict]:
35
+ kwargs[k] = {**default_kwargs.pop(k), **plot_kwargs.pop(k)}
36
+ kwargs.update(default_kwargs)
37
+ kwargs.update(plot_kwargs)
38
+ figsize = kwargs.pop('figsize')
39
+ fig = kwargs.pop('fig')
40
+ axs = kwargs.pop('axs')
41
+
42
+ if fig is None or axs is None:
43
+ fig, axs = _plt_subplots(1,ncols, figsize=figsize)
44
+
45
+ fig.suptitle(
46
+ "Values for indicator" + ("" if ncols==1 else "s") +
47
+ ("" if radius is None else (" within "+ str(radius) +" distance")) +
48
+ " around " + str(n_random_points)+ " randomly points drawn within valid area."
49
+ )
50
+
51
+ xmin, xmax = 0, 100
52
+ xs = _np_linspace(xmin,xmax,n_random_points)
53
+ vals = sums_df.values
54
+ colnames = sums_df.columns
55
+
56
+ for (i, colname, cluster_threshold_value, k_th_percentile) in zip(
57
+ range(ncols), colnames, cluster_threshold_values, k_th_percentiles):
58
+ ys = vals[:,i]
59
+ ys.sort()
60
+ ymin, ymax = ys.min(),ys.max()
61
+ # round percentile value as far as necessary only
62
+ # e.g. threshold value is 10.5328... and next smaller/larger in distribution are 10.51..., 10.6...
63
+ # rounding to threshold value to firrst digit s.t. thath it lies between those value is sufficient (e.g. 10.53)
64
+ idx = _np_searchsorted(ys, cluster_threshold_value)
65
+ next_smaller_val, next_larger_val = ys[max([0,idx-1])], ys[idx]
66
+ sufficient_digits = next((
67
+ i for i in range(100) if (
68
+ (
69
+ (next_smaller_val == next_larger_val or cluster_threshold_values==next_smaller_val) and
70
+ round(next_smaller_val,i)==next_smaller_val
71
+ ) or (
72
+ round(next_larger_val, i) != round(cluster_threshold_value, i) and
73
+ round(next_smaller_val, i) != round(cluster_threshold_value, i)
74
+ )
75
+ )),100)
76
+
77
+ ax_title = (
78
+ "Threshold value for "+str(k_th_percentile)+"th-percentile is "+
79
+ str(round(cluster_threshold_value, sufficient_digits)) + " for "+ colname
80
+ )
81
+
82
+ # SELECT AX (IF MULTIPLE)
83
+ ax = axs.flat[i] if ncols > 1 else axs
84
+
85
+ # SET TITLE
86
+ ax.set_title(ax_title)
87
+
88
+ # SET TICKS
89
+ xtick_steps, ytick_steps = 5, 5
90
+ xticks = _np_array(sorted(
91
+ [x for x in _np_linspace(xmin,xmax,xtick_steps) if abs(x-k_th_percentile) > (xmax-xmin)/(xtick_steps*2)] +
92
+ [k_th_percentile]
93
+ ))
94
+ ax.set_xticks(xticks, labels=xticks)
95
+ yticks = _np_array(sorted([y for y in _np_linspace(ymin,ymax,ytick_steps) if abs(cluster_threshold_value-y)>(ymax-ymin)/(ytick_steps*10)] + [cluster_threshold_value]))
96
+ ax.set_yticks(yticks, labels=[round(t, sufficient_digits) for t in yticks])
97
+
98
+ # ADD CUTOFF LINES
99
+ ax.hlines(y=cluster_threshold_value, xmin=xmin,xmax=xmax, **kwargs.pop('hlines'))
100
+ ax.vlines(x=k_th_percentile, ymin=ymin,ymax=ymax, **kwargs.pop('vlines'))
101
+
102
+ # ADD DISTRIUBTION PLOT
103
+ ax.scatter(x=xs,y=ys, **plot_kwargs)
104
+
105
+ # SET LIMITS
106
+ ax.set_xlim([xmin,xmax])
107
+ ax.set_ylim([ymin,ymax])
108
+ #
109
+ #
@@ -0,0 +1,75 @@
1
+ from numpy import (
2
+ array as _np_array,
3
+ unique as _np_unique,
4
+ linspace, invert, flip, transpose,
5
+ concatenate,
6
+ sign as _np_sign,
7
+ zeros, min, max, equal, where,
8
+ logical_or, logical_and, all, newaxis
9
+ )
10
+ from math import ceil as _math_ceil
11
+ from pandas import DataFrame as _pd_DataFrame
12
+ from matplotlib.pyplot import (subplots as _plt_subplots, figure as _plt_figure)
13
+ from matplotlib.patches import Circle as _plt_circle, Rectangle as _plt_Rectangle
14
+ from matplotlib.figure import Figure as _plt_Figure
15
+ from matplotlib.axes._axes import Axes as _plt_Axes
16
+ from .plot_utils import (
17
+ create_grid_cell_patches, create_grid_cell_patches_by_type, create_grid_cell_rectangles,
18
+ create_trgl1_patch, create_buffered_trgl1_patch, create_buffered_square_patch, create_debuffered_square_patch, create_debuffered_trgl1_patch, dual_circle_union_patch,
19
+ )
20
+ from .illustrate_point_to_cell_region_assignment import (add_grid_cell_rectangles_by_color,add_circle_patches,)
21
+ from ..utils.distances_to_cell import ( get_cells_relevant_for_disk_by_type, get_cell_farthest_vertex_to_point, )
22
+ from ..utils.general import ( flatten_list, )
23
+
24
+
25
+ def plot_cell_pattern(
26
+ contained_cells,
27
+ overlapped_cells,
28
+ all_cells,
29
+ radius:float,
30
+ grid_spacing:float,
31
+ add_idxs:bool=True,
32
+ **plot_kwargs,
33
+ ):
34
+
35
+ """
36
+ Illustrate method
37
+ """
38
+ # specify default plot kwargs and add defaults
39
+ plot_kwargs = {
40
+ 'fig':None,
41
+ 'ax':None,
42
+ 's':0.8,
43
+ 'color':'#eaa',
44
+ 'figsize': (20,30),
45
+ **plot_kwargs
46
+ }
47
+ figsize = plot_kwargs.pop('figsize')
48
+ fig = plot_kwargs.pop('fig')
49
+ ax = plot_kwargs.pop('ax')
50
+ ###### initialize plot ######################
51
+
52
+ if ax is None:
53
+ fig, ax = _plt_subplots(1,1, figsize=figsize)
54
+ ################################################################################################################
55
+ colors = ['#ccc', 'green', 'red']
56
+ for (row, col), color in flatten_list(
57
+ [[((row, col), color) for row, col in cells] for cells,color in zip(
58
+ [all_cells, contained_cells, overlapped_cells],
59
+ colors)]):
60
+ ax.add_patch(_plt_Rectangle(
61
+ xy = (col-0.5, row-0.5),
62
+ width=1, height=1,
63
+ linewidth=.7, facecolor=color, edgecolor=color, alpha=0.3
64
+ ))
65
+ if add_idxs and color == colors[0]:
66
+ ax.annotate(text=str(row)+","+str(col), xy=(col,row),horizontalalignment='center')
67
+
68
+ ratio = radius/grid_spacing
69
+ cell_steps_max = _math_ceil(ratio+1.5)
70
+
71
+ ax.set_xlim((-cell_steps_max,+cell_steps_max))
72
+ ax.set_ylim((-cell_steps_max,+cell_steps_max))
73
+ ax.set_aspect('equal', adjustable='box')
74
+ #
75
+ #
@@ -0,0 +1,90 @@
1
+ from numpy import (
2
+ array,
3
+ unique as _np_unique,
4
+ ones as _np_ones,
5
+ linspace, invert, flip, transpose, concatenate, sign, zeros, min, max,
6
+ equal, where, logical_or, logical_and, all, newaxis
7
+ )
8
+ from matplotlib.pyplot import (get_cmap as _plt_get_cmap, subplots as _plt_subplots)
9
+ from matplotlib.patches import Rectangle as _plt_Rectangle
10
+
11
+ from .plot_utils import (
12
+ create_grid_cell_patches,
13
+ create_grid_cell_patches_by_type,
14
+ )
15
+
16
+
17
+ def illustrate_nested_grid(
18
+ grid:dict,
19
+ pts_lat_lon,
20
+ cell_ids,
21
+ y_min:float=None,
22
+ y_max:float=None,
23
+ x_min:float=None,
24
+ x_max:float=None,
25
+ ):
26
+
27
+ """
28
+ Illustrate method
29
+ """
30
+ # unpack vals
31
+ grid_spacing = grid.spacing
32
+ nested_grid = grid['nested']
33
+
34
+ # filter pts to be inside bounds:
35
+ filter_mask = _np_ones(len(pts_lat_lon),dtype=bool)
36
+ if y_min != None:
37
+ filter_mask = filter_mask * (pts_lat_lon[:,0]>=y_min)
38
+ if y_max != None:
39
+ filter_mask = filter_mask * (pts_lat_lon[:,0]<=y_max)
40
+ if x_min != None:
41
+ filter_mask = filter_mask * (pts_lat_lon[:,1]>=x_min)
42
+ if x_max != None:
43
+ filter_mask = filter_mask * (pts_lat_lon[:,1]<=x_max)
44
+
45
+ pts_lat_lon = pts_lat_lon[filter_mask]
46
+ cell_ids = cell_ids[filter_mask]
47
+ print('N Pts:',len(cell_ids), 'in', len(_np_unique(cell_ids)),'cells.')
48
+
49
+ def unpackNestedGrid(cell_dict:dict, nest_lvl:int=0):
50
+ """
51
+ TODO Check to move it outside as independent functions
52
+ """
53
+ res = []
54
+ if 'quadrants' in cell_dict:
55
+ for quadrant in cell_dict['quadrants']:
56
+ res += unpackNestedGrid(cell_dict=quadrant,nest_lvl=nest_lvl+1)
57
+ res += [(nest_lvl, tuple([*tuple([*cell_dict['bounds']])]))]
58
+ return res
59
+
60
+ flat_nested_grid = []
61
+ for cell_id in _np_unique(cell_ids):
62
+ flat_nested_grid += unpackNestedGrid(nested_grid[cell_id],nest_lvl=0)
63
+
64
+ flat_nested_grid = sorted(flat_nested_grid)
65
+ max_nest_level = flat_nested_grid[-1][0]
66
+ print('max_nest_level',max_nest_level)
67
+ cmp = _plt_get_cmap("YlOrBr",max_nest_level)
68
+ # print(flat_nested_grid)
69
+ nested_patches = [_plt_Rectangle(
70
+ # x y width height
71
+ (x_min, y_min), x_max-x_min, y_max-y_min,
72
+ linewidth=.7, facecolor=(cmp(nest_lvl) if max_nest_level>0 else 'grey'), edgecolor='#444', alpha=1
73
+ ) for nest_lvl, ((y_min, x_min), (y_max, x_max) ) in flat_nested_grid]
74
+
75
+
76
+ fig, ax = _plt_subplots(figsize=(15,10))
77
+
78
+ for patchToAdd in nested_patches:
79
+ ax.add_patch(patchToAdd)
80
+
81
+ ax.scatter(x=pts_lat_lon[:,1], y=pts_lat_lon[:,0], s=0.2,color='black')
82
+ x_steps = grid.x_steps
83
+ y_steps = grid.y_steps
84
+ ax.set_xticks(x_steps)
85
+ ax.set_yticks(y_steps)
86
+ ax.set_xlim(left=x_min, right=x_max)
87
+ ax.set_ylim(bottom=y_min, top=y_max)
88
+ ax.set_aspect('equal', adjustable='box')
89
+ ax.grid()
90
+ #
@@ -0,0 +1,294 @@
1
+ from numpy import (array, unique, linspace, invert, flip, transpose, concatenate, sign, zeros,
2
+ min as _np_min, max as _np_max, equal, where, logical_or, logical_and, all, newaxis)
3
+ from pandas import DataFrame as _pd_DataFrame
4
+ from matplotlib import pyplot as plt
5
+ from matplotlib.animation import (FuncAnimation as _plt_FuncAnimation, PillowWriter as _plt_PillowWriter)
6
+ from matplotlib.figure import Figure as _plt_Figure
7
+ from matplotlib.axes._axes import Axes as _plt_Axes
8
+ # from math import ceil,asin,acos
9
+ from .plot_utils import (
10
+ create_grid_cell_patches,
11
+ create_grid_cell_patches_by_type,
12
+ create_circle_patches
13
+ )
14
+
15
+ ## Animation / GIF for varying grid spacings
16
+
17
+ def set_animation_frames(
18
+ relevantGridSizes:_pd_DataFrame,
19
+ allGridSizes:_pd_DataFrame,
20
+ largest:float=400,
21
+ frames_between_relevant:int = 6
22
+ )->list:
23
+ """
24
+ Returns vector of gridsizes -> each grid_spacing will be an iteration frame
25
+ """
26
+ # define GIF steps
27
+ animation_frames = [largest]
28
+ allGridSizes_indexes = list(allGridSizes.index)
29
+
30
+ for gridsize_0,gridsize_1 in zip(relevantGridSizes.index[:-1], relevantGridSizes.index[1:]):
31
+
32
+ pos_0, pos_1 = allGridSizes_indexes.index(gridsize_0), allGridSizes_indexes.index(gridsize_1)
33
+
34
+ #
35
+ if (pos_1-pos_0) > frames_between_relevant:
36
+ # number of position that should moved along per allGridSizes_indexes frame
37
+ stepsize_factor = (pos_1-pos_0)//frames_between_relevant
38
+ # move an aditional positon for the n<stepsize_adjust frames
39
+ stepsize_adjust = (pos_1-pos_0)%frames_between_relevant
40
+
41
+ animation_frames += [
42
+ allGridSizes_indexes[pos_0+s*stepsize_factor+int(stepsize_adjust<s)]
43
+ for s in range(frames_between_relevant)
44
+ ]
45
+ else:
46
+ #
47
+ animation_frames += allGridSizes_indexes[pos_0+1:pos_1+1]
48
+
49
+ print('Animate GIF with '+str(len(animation_frames))+' frames.')
50
+
51
+ return animation_frames
52
+
53
+
54
+ def add_coverage_plot(
55
+ fig:_plt_Figure,
56
+ allGridSizes:_pd_DataFrame,
57
+ ymax:float=3,
58
+ yticks:list=[.5,1]
59
+ ) -> _plt_Axes:
60
+ """
61
+ adds new axis to fig and create a stackplot on it that shows:
62
+ - percent of area within grid cells that are completly within
63
+ - stacked percent of area within grid cells that are potetially intersected
64
+ -
65
+ Returns: axes
66
+ """
67
+ ax_coverage = fig.add_axes([0.6, 0.63, 0.3, 0.25])
68
+
69
+ ax_coverage.stackplot(
70
+ allGridSizes['grid_spacing'],allGridSizes['share_contain']*100,allGridSizes['share_overlap']*100,
71
+ colors=['green', 'red']
72
+ )
73
+ ax_coverage.plot(allGridSizes['grid_spacing'], allGridSizes['share_overlap']*100, color='#000')
74
+ min_x, max_x = min(allGridSizes['grid_spacing']),max(allGridSizes['grid_spacing'])
75
+ ax_coverage.set_xlim(min_x,max_x)
76
+ ax_coverage.set_ylim(0,100*ymax)
77
+
78
+ ax_coverage.set_yticks([100*x for x in sorted(yticks+[round(ymax)])])
79
+
80
+ # add hline at 100%
81
+ ax_coverage.hlines(y=100,xmin=min_x,xmax=max_x,linewidth=2,color='#000')
82
+
83
+ return ax_coverage
84
+ #
85
+
86
+ def add_cell_counter_plot(
87
+ fig:_plt_Figure,
88
+ allGridSizes:_pd_DataFrame,
89
+ ymax:float=3,
90
+ yticks:list=[.5,1]
91
+ ) -> _plt_Axes:
92
+ """
93
+ adds new axis to fig and create a step plot on that counts number of cells:
94
+ - which are fully contained s.t. the sum of emplyoment will be requested
95
+ - which (might be) intersected s.t. all points within will need to be checked whether they are within radius
96
+ Returns: axes
97
+ """
98
+ ax_counter = fig.add_axes([0.6, 0.43, 0.3, 0.25])
99
+
100
+ ax_counter.plot(
101
+ allGridSizes['grid_spacing'], allGridSizes['contain_count'], color='green'
102
+ )
103
+ ax_counter.plot(
104
+ allGridSizes['grid_spacing'], allGridSizes['overlap_count'], color='red'
105
+ )
106
+ ax_counter.plot(allGridSizes['grid_spacing'], allGridSizes['overlap_count'], color='#000')
107
+ minx, maxx = min(allGridSizes['grid_spacing']),max(allGridSizes['grid_spacing'])
108
+ ax_counter.set_xlim(minx,maxx)
109
+ ax_counter.set_ylim(0,max(allGridSizes['contain_count']+allGridSizes['overlap_count']))
110
+
111
+ ax_counter.set_yticks(yticks)
112
+
113
+ return ax_counter
114
+ #
115
+
116
+ def update_fig_title(fig, grid_spacing:float,it:dict):
117
+ """
118
+ updates fig.suptitle with rounded grid_spacing of current frame
119
+ """
120
+ # display grid_spacing as title
121
+ gridsize_rnd = round(grid_spacing,2)
122
+ fig.suptitle(
123
+ (' ' if gridsize_rnd >= 100 else '') +
124
+ str(gridsize_rnd)[:5+int(gridsize_rnd >= 100)] +
125
+ '0'*max(0,len(str(gridsize_rnd))-int(gridsize_rnd >= 100)-3) +
126
+ '. Frame: '+str(it)
127
+ )
128
+ #
129
+
130
+ def update_main_axis_frame(
131
+ allGridSizes:_pd_DataFrame,
132
+ grid_spacing:float,
133
+ ax_main,
134
+ ax_min:float=0,
135
+ ax_max:float=1,
136
+ radius:float=750,
137
+ ) -> list:
138
+ # clear previous picture
139
+ ax_main.clear()
140
+
141
+ listOfPatches = []
142
+ # create patches for grid cell: green=overlapped, red= (potentially) intersected, grey=outside
143
+ listOfPatches += create_grid_cell_patches(
144
+ grid_spacing=grid_spacing,
145
+ ax_min=ax_min,
146
+ ax_max=ax_max,
147
+ contain_cells_row_col=allGridSizes.loc[grid_spacing, 'contain_ids'],
148
+ overlap_cells_row_col=allGridSizes.loc[grid_spacing, 'overlap_ids'],
149
+ )
150
+
151
+ # create patches for circle around bottom left and top right corner of center grid cell
152
+ listOfPatches += create_circle_patches(
153
+ grid_spacing=grid_spacing,
154
+ radius=radius
155
+ )
156
+
157
+ for patchToAdd in listOfPatches:
158
+ ax_main.add_patch(patchToAdd)
159
+
160
+ points = [p[0] for p in [
161
+ ax_main.plot(0, 0, marker='x', color='black'),
162
+ ]]
163
+
164
+ # set tickmarks and limits
165
+ ax_main.set_yticks([radius-grid_spacing/2, radius, radius+grid_spacing/2])
166
+ ax_main.set_xticks([radius-grid_spacing/2, radius, radius+grid_spacing/2])
167
+ ax_main.set_xlim(ax_min,ax_max)
168
+ ax_main.set_ylim(ax_min,ax_max)
169
+ ax_main.set_aspect('equal', adjustable='box')
170
+
171
+ return *listOfPatches, *points, #, point1,
172
+ #
173
+
174
+ def update_coverage_axis_frame(
175
+ grid_spacing:float,
176
+ max_gridsize:float,
177
+ min_gridsize:float,
178
+ ax_coverage,
179
+ vline,
180
+ ) -> tuple:
181
+ """
182
+
183
+ """
184
+ # update vline
185
+ if vline != None:
186
+ vline.remove()
187
+ vline = ax_coverage.vlines(x=grid_spacing, ymin=0, ymax=3)
188
+ ax_coverage.set_xticks([round(x,0) for x in set([min_gridsize, grid_spacing, max_gridsize])])
189
+ return (vline,)
190
+ #
191
+
192
+ def create_optimal_grid_spacing_gif (
193
+ relevantGridSizes:_pd_DataFrame,
194
+ allGridSizes:_pd_DataFrame,
195
+ largest:float=400,
196
+ smallest:float=80,
197
+ radius:float=750,
198
+ frames_between_relevant:int = 6,
199
+ FuncAnimation_interval:int=500,
200
+ FuncAnimation_repeat:bool=True,
201
+ FuncAnimation_repeat_delay:int=1500,
202
+ FuncAnimation_cache_frame_data:bool=True,
203
+ dpi:int=50,
204
+ PillowWriterFps:int=10,
205
+ output_dir:str='./opt_grid_'
206
+ )->tuple:
207
+
208
+ print('Create GIF')
209
+ # initialise plot and main axis
210
+ fig,ax_main = plt.subplots()
211
+
212
+ # define ax limits for main axis
213
+ min_gridsize = min(relevantGridSizes['grid_spacing'])
214
+ max_gridsize = max(relevantGridSizes['grid_spacing'])
215
+ max_relevant_cells = max(relevantGridSizes['contain_count']+relevantGridSizes['overlap_count'])
216
+ ax_min=-max_gridsize/2
217
+ ax_max= max_gridsize*(max(relevantGridSizes['cell_steps_max'])-.5)
218
+
219
+ # add stackplot to top right corner
220
+ ax_coverage = add_coverage_plot(
221
+ fig=fig, allGridSizes=allGridSizes,
222
+ ymax=1.01*max(relevantGridSizes['share_contain']+relevantGridSizes['share_overlap']),
223
+ yticks=[.5,1])
224
+
225
+ # ax_counter = add_cell_counter_plot(
226
+ # fig=fig,
227
+ # allGridSizes=allGridSizes,
228
+ # ymax=max_relevant_cells,
229
+ # yticks=[int(x*max_relevant_cells/4) for x in range(1,5)]
230
+ # )
231
+
232
+ #
233
+ an_dct={
234
+ 'it':0,
235
+ 'vline':None
236
+ }
237
+
238
+ def animate(grid_spacing):
239
+ """
240
+ function to pass into FuncAnimation to create GIF.
241
+ Will be supplied with list of gridsizes to iterate over.
242
+ """
243
+ an_dct['it']=an_dct['it']+1
244
+
245
+ update_fig_title(fig=fig,grid_spacing=grid_spacing,it=an_dct['it'])
246
+
247
+ # update main axis:
248
+ artistObjects = update_main_axis_frame(
249
+ allGridSizes=allGridSizes,
250
+ grid_spacing=grid_spacing,
251
+ ax_main=ax_main,
252
+ ax_min=ax_min,
253
+ ax_max=ax_max,
254
+ radius=radius,
255
+ )
256
+
257
+ #
258
+ an_dct['vline'], = update_coverage_axis_frame(
259
+ grid_spacing=grid_spacing,
260
+ max_gridsize=max_gridsize,
261
+ min_gridsize=min_gridsize,
262
+ ax_coverage=ax_coverage,
263
+ vline=an_dct['vline']
264
+ )
265
+
266
+ return artistObjects
267
+ #
268
+
269
+ animation_frames = set_animation_frames(
270
+ relevantGridSizes=relevantGridSizes,
271
+ allGridSizes=allGridSizes,
272
+ largest=largest,
273
+ frames_between_relevant=frames_between_relevant
274
+ )
275
+
276
+ ani = _plt_FuncAnimation(
277
+ fig,
278
+ animate,
279
+ interval=FuncAnimation_interval,
280
+ blit=True,
281
+ repeat=FuncAnimation_repeat, repeat_delay=FuncAnimation_repeat_delay,
282
+ cache_frame_data=FuncAnimation_cache_frame_data,
283
+ frames=animation_frames
284
+ )
285
+
286
+ print('Save GIF')
287
+ ani.save(
288
+ output_dir+str(largest)+"_"+str(smallest)+".gif",
289
+ dpi=dpi,
290
+ writer=_plt_PillowWriter(fps=PillowWriterFps)
291
+ )
292
+
293
+ return fig,ax_main,ax_coverage,#ax_counter,
294
+ #