mapdata 2.12.0__tar.gz → 2.14.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.
- {mapdata-2.12.0/mapdata.egg-info → mapdata-2.14.0}/PKG-INFO +1 -1
- {mapdata-2.12.0 → mapdata-2.14.0}/mapdata/mapdata.py +220 -27
- {mapdata-2.12.0 → mapdata-2.14.0/mapdata.egg-info}/PKG-INFO +1 -1
- {mapdata-2.12.0 → mapdata-2.14.0}/setup.py +1 -1
- {mapdata-2.12.0 → mapdata-2.14.0}/LICENSE.txt +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/MANIFEST.in +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/README.md +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/mapdata/mapdata_2.py +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/mapdata/mapdata_cli.py +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/mapdata.egg-info/SOURCES.txt +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/mapdata.egg-info/dependency_links.txt +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/mapdata.egg-info/top_level.txt +0 -0
- {mapdata-2.12.0 → mapdata-2.14.0}/setup.cfg +0 -0
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
#
|
|
25
25
|
# ==================================================================
|
|
26
26
|
|
|
27
|
-
version = "2.
|
|
28
|
-
vdate = "2023-12-
|
|
27
|
+
version = "2.14.0"
|
|
28
|
+
vdate = "2023-12-31"
|
|
29
29
|
|
|
30
30
|
copyright = "2023"
|
|
31
31
|
|
|
@@ -459,15 +459,23 @@ class CsvFile(object):
|
|
|
459
459
|
return self
|
|
460
460
|
|
|
461
461
|
|
|
462
|
-
def sort_columns(columns):
|
|
463
|
-
# Sorts a list of one or
|
|
462
|
+
def sort_columns(columns, sortby=0):
|
|
463
|
+
# Sorts a list of one, two, or three sublists, where each sublist is a column.
|
|
464
|
+
# Rows are sorted by the 'sortby' column, which is zero-based.
|
|
464
465
|
# The returned value is also column-wise, but sorted by rows.
|
|
465
466
|
if len(columns) == 1:
|
|
466
467
|
return sorted(columns)
|
|
467
468
|
nrows = len(columns[0])
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
469
|
+
if len(columns) == 2:
|
|
470
|
+
rowdata = [[columns[0][i], columns[1][i]] for i in range(nrows)]
|
|
471
|
+
rowdata.sort(key = lambda c: c[sortby])
|
|
472
|
+
return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)]]
|
|
473
|
+
else:
|
|
474
|
+
# len(columns) should be 3, though this is not checked
|
|
475
|
+
rowdata = [[columns[0][i], columns[1][i], columns[2][i]] for i in range(nrows)]
|
|
476
|
+
rowdata.sort(key = lambda c: c[sortby])
|
|
477
|
+
return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)], [rowdata[i][2] for i in range(nrows)]]
|
|
478
|
+
|
|
471
479
|
|
|
472
480
|
def treeview_sort_column(tv, col, reverse):
|
|
473
481
|
# Sort columns in Tkinter Treeview. From https://stackoverflow.com/questions/1966929/tk-treeview-column-sort#1967793
|
|
@@ -906,6 +914,7 @@ class MapUI(object):
|
|
|
906
914
|
self.basemap_var = tk.StringVar(self.win, bm_name)
|
|
907
915
|
self.map_option_menu = ttk.Combobox(ctrlframe, state="readonly", textvariable=self.basemap_var,
|
|
908
916
|
values=self.available_tile_servers(), width=18)
|
|
917
|
+
self.map_option_menu["state"] = "normal"
|
|
909
918
|
self.map_option_menu.bind('<<ComboboxSelected>>', self.change_basemap)
|
|
910
919
|
self.map_option_menu.grid(row=0, column=1, padx=(5, 30), pady=(2, 5), sticky=tk.W)
|
|
911
920
|
# Multi-select option
|
|
@@ -1409,8 +1418,9 @@ class MapUI(object):
|
|
|
1409
1418
|
# Plotting support. Return all data for the specified columns as a list of column-oriented lists.
|
|
1410
1419
|
res = []
|
|
1411
1420
|
for c in column_list:
|
|
1412
|
-
|
|
1413
|
-
|
|
1421
|
+
if c in self.headers:
|
|
1422
|
+
i = self.headers.index(c)
|
|
1423
|
+
res.append([row[i] for row in self.rows])
|
|
1414
1424
|
return res
|
|
1415
1425
|
def get_sel_data(self, column_list):
|
|
1416
1426
|
# Plotting support. Return data from selected rows for the specified columns, as a list of lists.
|
|
@@ -1445,8 +1455,10 @@ class MapUI(object):
|
|
|
1445
1455
|
clone.ylog_var.set(plot_obj.ylog_var.get())
|
|
1446
1456
|
clone.x_sel["values"] = copy.copy(plot_obj.x_sel["values"])
|
|
1447
1457
|
clone.y_sel["values"] = copy.copy(plot_obj.y_sel["values"])
|
|
1458
|
+
clone.groupby_sel["values"] = copy.copy(plot_obj.groupby_sel["values"])
|
|
1448
1459
|
clone.x_sel["state"] = plot_obj.x_sel["state"]
|
|
1449
1460
|
clone.y_sel["state"] = plot_obj.y_sel["state"]
|
|
1461
|
+
clone.groupby_sel["state"] = plot_obj.groupby_sel["state"]
|
|
1450
1462
|
clone.xlog_ck["state"] = plot_obj.xlog_ck["state"]
|
|
1451
1463
|
clone.ylog_ck["state"] = plot_obj.ylog_ck["state"]
|
|
1452
1464
|
clone.data_btn["state"] = plot_obj.data_btn["state"]
|
|
@@ -1460,6 +1472,9 @@ class MapUI(object):
|
|
|
1460
1472
|
clone.dlg.bind("<Alt-y>", clone.set_ylabel)
|
|
1461
1473
|
if clone.type_var.get() == "Histogram":
|
|
1462
1474
|
clone.dlg.bind("<Alt-b>", clone.set_bins)
|
|
1475
|
+
clone.alpha = plot_obj.alpha
|
|
1476
|
+
if clone.type_var.get() in ("Scatter plot", "Categorical stripchart", "Categorical KD plot"):
|
|
1477
|
+
clone.dlg.bind("<Alt-a>", clone.set_alpha)
|
|
1463
1478
|
clone.dataset = copy.copy(plot_obj.dataset)
|
|
1464
1479
|
clone.plot_data = copy.copy(plot_obj.plot_data)
|
|
1465
1480
|
clone.data_labels = copy.copy(plot_obj.data_labels)
|
|
@@ -2786,10 +2801,16 @@ class PlotDialog(object):
|
|
|
2786
2801
|
self.dlg.columnconfigure(0, weight=1)
|
|
2787
2802
|
self.auto_update = True
|
|
2788
2803
|
self.plot_title = None
|
|
2804
|
+
# For transparency on some plots
|
|
2805
|
+
self.alpha = 0.45
|
|
2789
2806
|
# For histogram
|
|
2790
2807
|
self.bins = 10
|
|
2791
|
-
# For groups at Jenks breaks on Q-Q plots
|
|
2808
|
+
# For display of groups at Jenks breaks on Q-Q plots
|
|
2792
2809
|
self.qq_groups = False
|
|
2810
|
+
# For display of lines at X and Y Jenks breaks on scatter plots
|
|
2811
|
+
self.scatter_breaks = False
|
|
2812
|
+
self.scatter_x_breaks = None
|
|
2813
|
+
self.scatter_y_breaks = None
|
|
2793
2814
|
|
|
2794
2815
|
def set_autoupdate():
|
|
2795
2816
|
if self.autoupdate_var.get() == "1":
|
|
@@ -2818,6 +2839,7 @@ class PlotDialog(object):
|
|
|
2818
2839
|
self.type_sel = ttk.Combobox(ctrl_frame, state="readonly", textvariable=self.type_var, width=20, height=15,
|
|
2819
2840
|
values=["Box plot", "Breaks groups", "Breaks optimum", "Category counts", "Categorical KD plot", "Categorical stripchart", "Empirical CDF", "Histogram", "Kernel density (KD) plot", "Line plot", "Min-max plot", "Normal Q-Q plot", "Scatter plot", "Violin plot", "Y range plot"])
|
|
2820
2841
|
self.type_sel.grid(row=0, column=1, columnspan=2, sticky=tk.W, padx=(3,6), pady=(3,3))
|
|
2842
|
+
self.type_sel["state"] = "normal"
|
|
2821
2843
|
self.type_sel.bind("<<ComboboxSelected>>", self.set_xy)
|
|
2822
2844
|
|
|
2823
2845
|
self.sel_only_var = tk.StringVar(ctrl_frame, "0")
|
|
@@ -2833,14 +2855,14 @@ class PlotDialog(object):
|
|
|
2833
2855
|
self.x_var = tk.StringVar(ctrl_frame, "")
|
|
2834
2856
|
x_lbl = ttk.Label(ctrl_frame, text="X column:")
|
|
2835
2857
|
x_lbl.grid(row=0, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
|
|
2836
|
-
self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=
|
|
2858
|
+
self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=24)
|
|
2837
2859
|
self.x_sel.grid(row=0, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
|
|
2838
2860
|
self.x_sel.bind("<<ComboboxSelected>>", self.x_changed)
|
|
2839
2861
|
|
|
2840
2862
|
self.y_var = tk.StringVar(ctrl_frame, "")
|
|
2841
2863
|
y_lbl = ttk.Label(ctrl_frame, text="Y column:")
|
|
2842
2864
|
y_lbl.grid(row=1, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
|
|
2843
|
-
self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=
|
|
2865
|
+
self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=24)
|
|
2844
2866
|
self.y_sel.grid(row=1, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
|
|
2845
2867
|
self.y_sel.bind("<<ComboboxSelected>>", self.y_changed)
|
|
2846
2868
|
|
|
@@ -2854,6 +2876,13 @@ class PlotDialog(object):
|
|
|
2854
2876
|
onvalue="1", offvalue="0")
|
|
2855
2877
|
self.ylog_ck.grid(row=1, column=5, sticky=tk.W, padx=(6,6), pady=(3,3))
|
|
2856
2878
|
|
|
2879
|
+
self.groupby_var = tk.StringVar(ctrl_frame, "")
|
|
2880
|
+
groupby_lbl = ttk.Label(ctrl_frame, text="Group by:")
|
|
2881
|
+
groupby_lbl.grid(row=2, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
|
|
2882
|
+
self.groupby_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.groupby_var, width=24)
|
|
2883
|
+
self.groupby_sel.grid(row=2, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
|
|
2884
|
+
self.groupby_sel.bind("<<ComboboxSelected>>", self.groupby_changed)
|
|
2885
|
+
|
|
2857
2886
|
# Plot
|
|
2858
2887
|
self.content_frame = tk.Frame(self.dlg, borderwidth=3, relief=tk.RIDGE)
|
|
2859
2888
|
self.content_frame.grid(row=2, column=0, sticky=tk.NSEW)
|
|
@@ -2913,7 +2942,7 @@ class PlotDialog(object):
|
|
|
2913
2942
|
for j in range(variables):
|
|
2914
2943
|
row.append(self.dataset[j][i])
|
|
2915
2944
|
rowwise_data.append(row)
|
|
2916
|
-
tframe, tdata = treeview_table(dlg.content_frame, rowwise_data, self.data_labels)
|
|
2945
|
+
tframe, tdata = treeview_table(dlg.content_frame, rowwise_data, self.data_labels[0:variables])
|
|
2917
2946
|
tframe.grid(row=0, column=0, sticky=tk.NSEW)
|
|
2918
2947
|
dlg.show()
|
|
2919
2948
|
|
|
@@ -2956,10 +2985,15 @@ class PlotDialog(object):
|
|
|
2956
2985
|
self.ylog_ck["state"] = "normal"
|
|
2957
2986
|
self.q_redraw(args)
|
|
2958
2987
|
|
|
2988
|
+
def groupby_changed(self, *args):
|
|
2989
|
+
self.q_redraw(args)
|
|
2990
|
+
|
|
2959
2991
|
def set_xy(self, *args):
|
|
2960
2992
|
# Enable X and Y value selection, and set Combobox values based on plot type and column types.
|
|
2993
|
+
# Also sets the groupby Combobox values if applicable.
|
|
2961
2994
|
self.plotfig.clear()
|
|
2962
2995
|
self.plot_title = None
|
|
2996
|
+
self.dlg.bind("<Alt-a>")
|
|
2963
2997
|
self.dlg.bind("<Alt-b>")
|
|
2964
2998
|
self.dlg.bind("<Alt-g>")
|
|
2965
2999
|
self.plot_axes = self.plotfig.add_subplot(111)
|
|
@@ -2978,24 +3012,28 @@ class PlotDialog(object):
|
|
|
2978
3012
|
# quant_columns includes date and timestamp columns
|
|
2979
3013
|
quant_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float", "date", "timestamp", "timestamptz")]
|
|
2980
3014
|
quant_columns.sort()
|
|
2981
|
-
numeric_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float")]
|
|
2982
|
-
numeric_columns.sort()
|
|
3015
|
+
self.numeric_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float")]
|
|
3016
|
+
self.numeric_columns.sort()
|
|
2983
3017
|
date_columns = [c[0] for c in self.column_specs if c[1] in ("date", "timestamp", "timestamptz")]
|
|
2984
3018
|
date_columns.sort()
|
|
2985
3019
|
self.x_var.set('')
|
|
2986
3020
|
self.y_var.set('')
|
|
3021
|
+
self.groupby_var.set('')
|
|
2987
3022
|
self.xlog_ck["state"] = "normal"
|
|
2988
3023
|
self.ylog_ck["state"] = "normal"
|
|
2989
3024
|
self.x_sel["state"] = "readonly"
|
|
2990
3025
|
self.y_sel["state"] = "readonly"
|
|
3026
|
+
self.groupby_sel["state"] = "readonly"
|
|
2991
3027
|
plot_type = self.type_var.get()
|
|
2992
3028
|
if plot_type == "Category counts":
|
|
3029
|
+
self.x_sel["state"] = "normal"
|
|
2993
3030
|
self.x_sel["values"] = categ_columns
|
|
2994
3031
|
self.xlog_ck["state"] = "disabled"
|
|
2995
3032
|
self.y_sel["state"] = "disabled"
|
|
2996
3033
|
self.ylog_ck["state"] = "disabled"
|
|
2997
3034
|
elif plot_type in ("Histogram", "Empirical CDF", "Kernel density (KD) plot", "Normal Q-Q plot", "Breaks groups", "Breaks optimum"):
|
|
2998
|
-
self.x_sel["
|
|
3035
|
+
self.x_sel["state"] = "normal"
|
|
3036
|
+
self.x_sel["values"] = self.numeric_columns
|
|
2999
3037
|
self.y_sel["state"] = "disabled"
|
|
3000
3038
|
self.ylog_ck["state"] = "disabled"
|
|
3001
3039
|
if plot_type == "Histogram":
|
|
@@ -3005,16 +3043,25 @@ class PlotDialog(object):
|
|
|
3005
3043
|
elif plot_type in ("Box plot", "Categorical KD plot", "Categorical stripchart", "Violin plot"):
|
|
3006
3044
|
xcols = list(set(categ_columns) | set(date_columns))
|
|
3007
3045
|
xcols.sort()
|
|
3046
|
+
self.x_sel["state"] = "normal"
|
|
3008
3047
|
self.x_sel["values"] = xcols
|
|
3009
3048
|
self.xlog_ck["state"] = "disabled"
|
|
3010
|
-
self.y_sel["
|
|
3049
|
+
self.y_sel["state"] = "normal"
|
|
3050
|
+
self.y_sel["values"] = self.numeric_columns
|
|
3011
3051
|
elif plot_type == "Min-max plot":
|
|
3052
|
+
self.x_sel["state"] = "normal"
|
|
3012
3053
|
self.x_sel["values"] = quant_columns
|
|
3054
|
+
self.y_sel["state"] = "normal"
|
|
3013
3055
|
self.y_sel["values"] = categ_columns2
|
|
3014
3056
|
self.ylog_ck["state"] = "disabled"
|
|
3015
3057
|
else:
|
|
3058
|
+
self.x_sel["state"] = "normal"
|
|
3016
3059
|
self.x_sel["values"] = quant_columns
|
|
3060
|
+
self.y_sel["state"] = "normal"
|
|
3017
3061
|
self.y_sel["values"] = quant_columns
|
|
3062
|
+
if plot_type in ("Scatter plot", "Line plot"):
|
|
3063
|
+
self.groupby_sel["state"] = "normal"
|
|
3064
|
+
self.groupby_sel["values"] = [''] + categ_columns2 + ['* Breaks in X', '* Breaks in Y']
|
|
3018
3065
|
|
|
3019
3066
|
def q_redraw(self, get_data=True, *args):
|
|
3020
3067
|
# Conditionally (re)draw the plot.
|
|
@@ -3041,6 +3088,8 @@ class PlotDialog(object):
|
|
|
3041
3088
|
column_list = [self.x_var.get()]
|
|
3042
3089
|
if self.y_var.get() != '':
|
|
3043
3090
|
column_list.append(self.y_var.get())
|
|
3091
|
+
if self.groupby_var.get() != '':
|
|
3092
|
+
column_list.append(self.groupby_var.get())
|
|
3044
3093
|
# Get either only the selected data or all data.
|
|
3045
3094
|
if self.sel_only_var.get() == "1":
|
|
3046
3095
|
dataset = self.parent.get_sel_data(column_list)
|
|
@@ -3082,6 +3131,9 @@ class PlotDialog(object):
|
|
|
3082
3131
|
# Set data labels
|
|
3083
3132
|
if self.y_var.get() != '':
|
|
3084
3133
|
self.data_labels = [self.x_var.get(), self.y_var.get()]
|
|
3134
|
+
grpvar = self.groupby_var.get()
|
|
3135
|
+
if grpvar not in ('', '* Breaks in X', '* Breaks in Y') :
|
|
3136
|
+
self.data_labels.append(grpvar)
|
|
3085
3137
|
else:
|
|
3086
3138
|
self.data_labels = [self.x_var.get()]
|
|
3087
3139
|
# Log-transform data if specified.
|
|
@@ -3158,7 +3210,7 @@ class PlotDialog(object):
|
|
|
3158
3210
|
self.plot_data_labels = [self.data_labels[0], "Cumulative frequency"]
|
|
3159
3211
|
elif plot_type == "Kernel density (KD) plot":
|
|
3160
3212
|
self.plot_data = self.dataset
|
|
3161
|
-
|
|
3213
|
+
self.plot_data_labels = [self.x_var.get()]
|
|
3162
3214
|
elif plot_type == "Min-max plot":
|
|
3163
3215
|
# Min and max X for each Y
|
|
3164
3216
|
y_vals = list(set(self.dataset[1]))
|
|
@@ -3209,12 +3261,49 @@ class PlotDialog(object):
|
|
|
3209
3261
|
self.plot_data_labels = [self.data_labels[0], self.data_labels[1] + " min", self.data_labels[1] + " max"]
|
|
3210
3262
|
elif plot_type == "Line plot":
|
|
3211
3263
|
# Sort by X
|
|
3212
|
-
|
|
3213
|
-
ds.
|
|
3214
|
-
|
|
3215
|
-
|
|
3264
|
+
#if self.groupby_var.get() == '':
|
|
3265
|
+
# ds = list(zip(self.dataset[0], self.dataset[1]))
|
|
3266
|
+
#else:
|
|
3267
|
+
# ds = list(zip(self.dataset[0], self.dataset[1], self.dataset[2]))
|
|
3268
|
+
#ds.sort()
|
|
3269
|
+
#ds2 = list(zip(*ds))
|
|
3270
|
+
ds2 = sort_columns(self.dataset)
|
|
3271
|
+
if self.groupby_var.get() == '':
|
|
3272
|
+
self.plot_data = [list(ds2[0]), list(ds2[1])]
|
|
3273
|
+
else:
|
|
3274
|
+
self.plot_data = [list(ds2[0]), list(ds2[1]), list(ds2[2])]
|
|
3216
3275
|
self.plot_data_labels = self.data_labels
|
|
3217
|
-
elif plot_type
|
|
3276
|
+
elif plot_type == "Scatter plot":
|
|
3277
|
+
if self.groupby_var.get() == "* Breaks in X":
|
|
3278
|
+
if self.x_var.get() in self.numeric_columns:
|
|
3279
|
+
ds = sort_columns(self.dataset)
|
|
3280
|
+
oj = optimum_jenks(ds[0], 8)
|
|
3281
|
+
jnb = jenkspy.JenksNaturalBreaks(oj)
|
|
3282
|
+
jnb.fit(ds[0])
|
|
3283
|
+
self.plot_data = [ds[0], ds[1], ["Group "+str(lbl+1) for lbl in jnb.labels_]]
|
|
3284
|
+
self.plot_data_labels = self.data_labels + ['Breaks in X']
|
|
3285
|
+
else:
|
|
3286
|
+
# Can't compute breaks for X
|
|
3287
|
+
self.groupby_var.set('')
|
|
3288
|
+
self.plot_data = self.dataset
|
|
3289
|
+
self.plot_data_labels = self.data_labels
|
|
3290
|
+
elif self.groupby_var.get() == "* Breaks in Y":
|
|
3291
|
+
if self.y_var.get() in self.numeric_columns:
|
|
3292
|
+
ds = sort_columns(self.dataset, sortby=1)
|
|
3293
|
+
oj = optimum_jenks(ds[1], 8)
|
|
3294
|
+
jnb = jenkspy.JenksNaturalBreaks(oj)
|
|
3295
|
+
jnb.fit(ds[1])
|
|
3296
|
+
self.plot_data = [ds[0], ds[1], ["Group "+str(lbl+1) for lbl in jnb.labels_]]
|
|
3297
|
+
self.plot_data_labels = self.data_labels + ['Breaks in Y']
|
|
3298
|
+
else:
|
|
3299
|
+
# Can't compute breaks for Y
|
|
3300
|
+
self.groupby_var.set('')
|
|
3301
|
+
self.plot_data = self.dataset
|
|
3302
|
+
self.plot_data_labels = self.data_labels
|
|
3303
|
+
else:
|
|
3304
|
+
self.plot_data = self.dataset
|
|
3305
|
+
self.plot_data_labels = self.data_labels
|
|
3306
|
+
elif plot_type in ("Histogram", "Y range plot"):
|
|
3218
3307
|
# No special preparation
|
|
3219
3308
|
self.plot_data = self.dataset
|
|
3220
3309
|
self.plot_data_labels = self.data_labels
|
|
@@ -3232,11 +3321,52 @@ class PlotDialog(object):
|
|
|
3232
3321
|
self.plot_axes.set_xlabel(self.x_var.get())
|
|
3233
3322
|
self.plot_axes.set_ylabel("Counts")
|
|
3234
3323
|
elif plot_type == "Scatter plot":
|
|
3235
|
-
self.
|
|
3324
|
+
self.dlg.bind("<Alt-a>", self.set_alpha)
|
|
3325
|
+
self.dlg.bind("<Alt-b>", self.set_scatter_breaks)
|
|
3326
|
+
if self.scatter_breaks:
|
|
3327
|
+
if self.x_var.get() in self.numeric_columns:
|
|
3328
|
+
x_vals = copy.copy(self.plot_data[0])
|
|
3329
|
+
x_vals.sort()
|
|
3330
|
+
oj = optimum_jenks(x_vals, 8)
|
|
3331
|
+
jnb = jenkspy.JenksNaturalBreaks(oj)
|
|
3332
|
+
jnb.fit(x_vals)
|
|
3333
|
+
self.scatter_x_breaks = jnb.inner_breaks_
|
|
3334
|
+
for b in self.scatter_x_breaks:
|
|
3335
|
+
self.plot_axes.axvline(b, color='0.8', alpha=0.5)
|
|
3336
|
+
if self.y_var.get() in self.numeric_columns:
|
|
3337
|
+
y_vals = copy.copy(self.plot_data[1])
|
|
3338
|
+
y_vals.sort()
|
|
3339
|
+
oj = optimum_jenks(y_vals, 8)
|
|
3340
|
+
jnb = jenkspy.JenksNaturalBreaks(oj)
|
|
3341
|
+
jnb.fit(y_vals)
|
|
3342
|
+
self.scatter_y_breaks = jnb.inner_breaks_
|
|
3343
|
+
for b in self.scatter_y_breaks:
|
|
3344
|
+
self.plot_axes.axhline(b, color='0.8', alpha=0.5)
|
|
3345
|
+
if self.groupby_var.get() == '':
|
|
3346
|
+
self.plot_axes.scatter(self.plot_data[0], self.plot_data[1], alpha=self.alpha)
|
|
3347
|
+
else:
|
|
3348
|
+
groups = list(set(self.plot_data[2]))
|
|
3349
|
+
groups.sort()
|
|
3350
|
+
datarows = len(self.plot_data[0])
|
|
3351
|
+
for g in groups:
|
|
3352
|
+
pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
|
|
3353
|
+
pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
|
|
3354
|
+
self.plot_axes.plot(pdx, pdy, marker='o', alpha=self.alpha, linestyle='', label=g)
|
|
3355
|
+
self.plot_axes.legend()
|
|
3236
3356
|
self.plot_axes.set_xlabel(self.plot_data_labels[0])
|
|
3237
3357
|
self.plot_axes.set_ylabel(self.plot_data_labels[1])
|
|
3238
3358
|
elif plot_type == "Line plot":
|
|
3239
|
-
self.
|
|
3359
|
+
if self.groupby_var.get() == '':
|
|
3360
|
+
self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
|
|
3361
|
+
else:
|
|
3362
|
+
groups = list(set(self.plot_data[2]))
|
|
3363
|
+
groups.sort()
|
|
3364
|
+
datarows = len(self.plot_data[0])
|
|
3365
|
+
for g in groups:
|
|
3366
|
+
pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
|
|
3367
|
+
pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
|
|
3368
|
+
self.plot_axes.plot(pdx, pdy, label=g)
|
|
3369
|
+
self.plot_axes.legend()
|
|
3240
3370
|
self.plot_axes.set_xlabel(self.plot_data_labels[0])
|
|
3241
3371
|
self.plot_axes.set_ylabel(self.plot_data_labels[1])
|
|
3242
3372
|
elif plot_type == "Breaks groups":
|
|
@@ -3299,14 +3429,16 @@ class PlotDialog(object):
|
|
|
3299
3429
|
self.plot_axes.set_xlabel(self.x_var.get())
|
|
3300
3430
|
self.plot_axes.set_ylabel(self.data_labels[1])
|
|
3301
3431
|
elif plot_type == "Categorical KD plot":
|
|
3432
|
+
self.dlg.bind("<Alt-a>", self.set_alpha)
|
|
3302
3433
|
sns.kdeplot({self.x_var.get(): self.dataset[0], self.y_var.get(): self.dataset[1]},
|
|
3303
|
-
x=self.y_var.get(), hue=self.x_var.get(), multiple="layer", fill=True, alpha=
|
|
3434
|
+
x=self.y_var.get(), hue=self.x_var.get(), multiple="layer", fill=True, alpha=self.alpha, ax=self.plot_axes,
|
|
3304
3435
|
warn_singular=False)
|
|
3305
3436
|
self.plot_axes.set_xlabel(self.y_var.get())
|
|
3306
3437
|
self.plot_axes.set_ylabel("Density")
|
|
3307
3438
|
elif plot_type == "Categorical stripchart":
|
|
3439
|
+
self.dlg.bind("<Alt-a>", self.set_alpha)
|
|
3308
3440
|
sns.stripplot({self.x_var.get(): self.dataset[0], self.y_var.get(): self.dataset[1]},
|
|
3309
|
-
x=self.x_var.get(), y=self.y_var.get(), alpha=
|
|
3441
|
+
x=self.x_var.get(), y=self.y_var.get(), alpha=self.alpha, ax=self.plot_axes)
|
|
3310
3442
|
self.plot_axes.set_xlabel(self.x_var.get())
|
|
3311
3443
|
self.plot_axes.set_ylabel(self.data_labels[1])
|
|
3312
3444
|
elif plot_type == "Violin plot":
|
|
@@ -3345,6 +3477,20 @@ class PlotDialog(object):
|
|
|
3345
3477
|
self.bins = num_bins
|
|
3346
3478
|
if self.type_var.get() == "Histogram":
|
|
3347
3479
|
self.q_redraw()
|
|
3480
|
+
def set_alpha(self, *args):
|
|
3481
|
+
dlg = OneFloatDialog(self.dlg, "Transparency", "Enter the transparency (alpha) value", min_value=0.0, max_value=1.0, initial=self.alpha)
|
|
3482
|
+
new_alpha = dlg.show()
|
|
3483
|
+
if new_alpha is not None:
|
|
3484
|
+
self.alpha = new_alpha
|
|
3485
|
+
if self.type_var.get() in ("Scatter plot", "Categorical stripchart", "Categorical KD plot"):
|
|
3486
|
+
self.q_redraw()
|
|
3487
|
+
def set_scatter_breaks(self, *args):
|
|
3488
|
+
# Toggle display of vertical and horizontal lines at natural breaks values on a scatter plot.
|
|
3489
|
+
# plot_data[0] and plot_data[1] must be x and y, respectively.
|
|
3490
|
+
self.scatter_breaks = not self.scatter_breaks
|
|
3491
|
+
if self.type_var.get() == "Scatter plot":
|
|
3492
|
+
self.q_redraw()
|
|
3493
|
+
|
|
3348
3494
|
def show_groups(self, *args):
|
|
3349
3495
|
if self.type_var.get() == "Normal Q-Q plot":
|
|
3350
3496
|
self.qq_groups = not self.qq_groups
|
|
@@ -3562,6 +3708,53 @@ class OneIntDialog(object):
|
|
|
3562
3708
|
return self.entry_var.get()
|
|
3563
3709
|
|
|
3564
3710
|
|
|
3711
|
+
class OneFloatDialog(object):
|
|
3712
|
+
def __init__(self, parent, title, prompt, min_value, max_value, initial):
|
|
3713
|
+
self.dlg = tk.Toplevel(parent)
|
|
3714
|
+
self.dlg.title(title)
|
|
3715
|
+
prompt_frame = tk.Frame(self.dlg)
|
|
3716
|
+
prompt_frame.grid(row=0, column=0, sticky=tk.NSEW, pady=(3,3))
|
|
3717
|
+
prompt_frame.columnconfigure(0, weight=1)
|
|
3718
|
+
msg_lbl = ttk.Label(prompt_frame, text=prompt)
|
|
3719
|
+
msg_lbl.grid(row=0, column=0, sticky=tk.W, padx=(6,6), pady=(6,6))
|
|
3720
|
+
self.entry_var = tk.DoubleVar(self.dlg, initial)
|
|
3721
|
+
self.val_entry = ttk.Spinbox(prompt_frame, textvariable=self.entry_var, from_=min_value, to=max_value)
|
|
3722
|
+
self.val_entry.grid(row=1, column=0, sticky=tk.EW, padx=(6,6), pady=(3,3))
|
|
3723
|
+
btn_frame = tk.Frame(self.dlg, borderwidth=3, relief=tk.RIDGE)
|
|
3724
|
+
btn_frame.columnconfigure(0, weight=1)
|
|
3725
|
+
btn_frame.grid(row=2, column=0, sticky=tk.EW, pady=(3,3))
|
|
3726
|
+
btn_frame.columnconfigure(0, weight=1)
|
|
3727
|
+
self.val_entry.focus()
|
|
3728
|
+
# Buttons
|
|
3729
|
+
self.canceled = False
|
|
3730
|
+
self.ok_btn = ttk.Button(btn_frame, text="OK", command=self.do_select, underline=0)
|
|
3731
|
+
self.ok_btn.grid(row=0, column=0, sticky=tk.E, padx=(6,3))
|
|
3732
|
+
self.dlg.bind('<Alt-o>', self.do_select)
|
|
3733
|
+
cancel_btn = ttk.Button(btn_frame, text="Cancel", command=self.do_cancel, underline=0)
|
|
3734
|
+
cancel_btn.grid(row=0, column=1, sticky=tk.E, padx=(3,6))
|
|
3735
|
+
self.dlg.bind("<Escape>", self.do_cancel)
|
|
3736
|
+
self.dlg.bind("<Alt-c>", self.do_cancel)
|
|
3737
|
+
self.dlg.bind("<Return>", self.do_select)
|
|
3738
|
+
self.dlg.bind("<Alt-o>", self.do_select)
|
|
3739
|
+
def do_cancel(self, *args):
|
|
3740
|
+
self.canceled = True
|
|
3741
|
+
self.dlg.destroy()
|
|
3742
|
+
def do_select(self, *args):
|
|
3743
|
+
self.canceled = False
|
|
3744
|
+
self.dlg.destroy()
|
|
3745
|
+
def show(self):
|
|
3746
|
+
self.dlg.grab_set()
|
|
3747
|
+
center_window(self.dlg)
|
|
3748
|
+
raise_window(self.dlg)
|
|
3749
|
+
self.dlg.resizable(True, False)
|
|
3750
|
+
self.dlg.attributes('-topmost', 'true')
|
|
3751
|
+
self.dlg.wait_window(self.dlg)
|
|
3752
|
+
if self.canceled:
|
|
3753
|
+
return None
|
|
3754
|
+
else:
|
|
3755
|
+
return self.entry_var.get()
|
|
3756
|
+
|
|
3757
|
+
|
|
3565
3758
|
class GetEditorDialog(object):
|
|
3566
3759
|
def __init__(self, parent, current_editor):
|
|
3567
3760
|
self.dlg = tk.Toplevel(parent)
|
|
@@ -5,7 +5,7 @@ with io.open('README.md', encoding='utf-8') as f:
|
|
|
5
5
|
long_description = f.read()
|
|
6
6
|
|
|
7
7
|
setuptools.setup(name='mapdata',
|
|
8
|
-
version='2.
|
|
8
|
+
version='2.14.0',
|
|
9
9
|
description="An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database",
|
|
10
10
|
author='Dreas Nielsen',
|
|
11
11
|
author_email='dreas.nielsen@gmail.com',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|