mapdata 2.11.0__tar.gz → 2.13.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapdata
3
- Version: 2.11.0
3
+ Version: 2.13.0
4
4
  Summary: An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database
5
5
  Home-page: https://osdn.net/project/mapdata/
6
6
  Author: Dreas Nielsen
@@ -24,8 +24,8 @@
24
24
  #
25
25
  # ==================================================================
26
26
 
27
- version = "2.11.0"
28
- vdate = "2023-09-16"
27
+ version = "2.13.0"
28
+ vdate = "2023-12-30"
29
29
 
30
30
  copyright = "2023"
31
31
 
@@ -459,6 +459,23 @@ class CsvFile(object):
459
459
  return self
460
460
 
461
461
 
462
+ def sort_columns(columns):
463
+ # Sorts a list of one, two, or three sublists, where each sublist is a column. Rows are sorted by the first column.
464
+ # The returned value is also column-wise, but sorted by rows.
465
+ if len(columns) == 1:
466
+ return sorted(columns)
467
+ nrows = len(columns[0])
468
+ if len(columns) == 2:
469
+ rowdata = [[columns[0][i], columns[1][i]] for i in range(nrows)]
470
+ rowdata.sort()
471
+ return [[rowdata[i][0] for i in range(nrows)], [rowdata[i][1] for i in range(nrows)]]
472
+ else:
473
+ # len(columns) should be 3, though this is not checked
474
+ rowdata = [[columns[0][i], columns[1][i], columns[2][i]] for i in range(nrows)]
475
+ rowdata.sort()
476
+ 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)]]
477
+
478
+
462
479
  def treeview_sort_column(tv, col, reverse):
463
480
  # Sort columns in Tkinter Treeview. From https://stackoverflow.com/questions/1966929/tk-treeview-column-sort#1967793
464
481
  colvals = [(tv.set(k, col), k) for k in tv.get_children()]
@@ -1435,8 +1452,10 @@ class MapUI(object):
1435
1452
  clone.ylog_var.set(plot_obj.ylog_var.get())
1436
1453
  clone.x_sel["values"] = copy.copy(plot_obj.x_sel["values"])
1437
1454
  clone.y_sel["values"] = copy.copy(plot_obj.y_sel["values"])
1455
+ clone.groupby_sel["values"] = copy.copy(plot_obj.groupby_sel["values"])
1438
1456
  clone.x_sel["state"] = plot_obj.x_sel["state"]
1439
1457
  clone.y_sel["state"] = plot_obj.y_sel["state"]
1458
+ clone.groupby_sel["state"] = plot_obj.groupby_sel["state"]
1440
1459
  clone.xlog_ck["state"] = plot_obj.xlog_ck["state"]
1441
1460
  clone.ylog_ck["state"] = plot_obj.ylog_ck["state"]
1442
1461
  clone.data_btn["state"] = plot_obj.data_btn["state"]
@@ -2778,7 +2797,7 @@ class PlotDialog(object):
2778
2797
  self.plot_title = None
2779
2798
  # For histogram
2780
2799
  self.bins = 10
2781
- # For groups at Jenks breaks on Q-Q plots
2800
+ # For display of groups at Jenks breaks on Q-Q plots
2782
2801
  self.qq_groups = False
2783
2802
 
2784
2803
  def set_autoupdate():
@@ -2805,9 +2824,10 @@ class PlotDialog(object):
2805
2824
  self.type_var = tk.StringVar(ctrl_frame, "")
2806
2825
  type_lbl = ttk.Label(ctrl_frame, text="Plot type:")
2807
2826
  type_lbl.grid(row=0, column=0, sticky=tk.E, padx=(6,3), pady=(3,3))
2808
- self.type_sel = ttk.Combobox(ctrl_frame, state="readonly", textvariable=self.type_var, width=20,
2809
- values=["Box plot", "Breaks groups", "Breaks optimum", "Category counts", "Categorical KD plot", "Categorical stripchart", "Empirical CDF", "Histogram", "Kernel density (KD) plot", "Line plot", "Normal Q-Q plot", "Scatter plot", "Violin plot", "Y range plot"])
2827
+ self.type_sel = ttk.Combobox(ctrl_frame, state="readonly", textvariable=self.type_var, width=20, height=15,
2828
+ 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"])
2810
2829
  self.type_sel.grid(row=0, column=1, columnspan=2, sticky=tk.W, padx=(3,6), pady=(3,3))
2830
+ self.type_sel["state"] = "normal"
2811
2831
  self.type_sel.bind("<<ComboboxSelected>>", self.set_xy)
2812
2832
 
2813
2833
  self.sel_only_var = tk.StringVar(ctrl_frame, "0")
@@ -2823,14 +2843,14 @@ class PlotDialog(object):
2823
2843
  self.x_var = tk.StringVar(ctrl_frame, "")
2824
2844
  x_lbl = ttk.Label(ctrl_frame, text="X column:")
2825
2845
  x_lbl.grid(row=0, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2826
- self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=20)
2846
+ self.x_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.x_var, width=24)
2827
2847
  self.x_sel.grid(row=0, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2828
2848
  self.x_sel.bind("<<ComboboxSelected>>", self.x_changed)
2829
2849
 
2830
2850
  self.y_var = tk.StringVar(ctrl_frame, "")
2831
2851
  y_lbl = ttk.Label(ctrl_frame, text="Y column:")
2832
2852
  y_lbl.grid(row=1, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2833
- self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=20)
2853
+ self.y_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.y_var, width=24)
2834
2854
  self.y_sel.grid(row=1, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2835
2855
  self.y_sel.bind("<<ComboboxSelected>>", self.y_changed)
2836
2856
 
@@ -2844,6 +2864,13 @@ class PlotDialog(object):
2844
2864
  onvalue="1", offvalue="0")
2845
2865
  self.ylog_ck.grid(row=1, column=5, sticky=tk.W, padx=(6,6), pady=(3,3))
2846
2866
 
2867
+ self.groupby_var = tk.StringVar(ctrl_frame, "")
2868
+ groupby_lbl = ttk.Label(ctrl_frame, text="Group by:")
2869
+ groupby_lbl.grid(row=2, column=3, sticky=tk.E, padx=(6,3), pady=(3,3))
2870
+ self.groupby_sel = ttk.Combobox(ctrl_frame, state="disabled", textvariable=self.groupby_var, width=24)
2871
+ self.groupby_sel.grid(row=2, column=4, sticky=tk.W, padx=(3,6), pady=(3,3))
2872
+ self.groupby_sel.bind("<<ComboboxSelected>>", self.groupby_changed)
2873
+
2847
2874
  # Plot
2848
2875
  self.content_frame = tk.Frame(self.dlg, borderwidth=3, relief=tk.RIDGE)
2849
2876
  self.content_frame.grid(row=2, column=0, sticky=tk.NSEW)
@@ -2940,14 +2967,18 @@ class PlotDialog(object):
2940
2967
 
2941
2968
  def y_changed(self, *args):
2942
2969
  plot_type = self.type_var.get()
2943
- if plot_type in ("Category counts", "Histogram", "Empirical CDF"):
2970
+ if plot_type in ("Category counts", "Histogram", "Empirical CDF", "Min-max plot"):
2944
2971
  self.ylog_ck["state"] = "disabled"
2945
2972
  else:
2946
2973
  self.ylog_ck["state"] = "normal"
2947
2974
  self.q_redraw(args)
2948
2975
 
2976
+ def groupby_changed(self, *args):
2977
+ self.q_redraw(args)
2978
+
2949
2979
  def set_xy(self, *args):
2950
2980
  # Enable X and Y value selection, and set Combobox values based on plot type and column types.
2981
+ # Also sets the groupby Combobox values if applicable.
2951
2982
  self.plotfig.clear()
2952
2983
  self.plot_title = None
2953
2984
  self.dlg.bind("<Alt-b>")
@@ -2960,8 +2991,11 @@ class PlotDialog(object):
2960
2991
  self.plot_data_labels = None
2961
2992
  self.data_btn["state"] = "disabled"
2962
2993
  self.plot_data_btn["state"] = "disabled"
2994
+ # Category columns. Does not include date columns for most uses, but may include dates for some.
2963
2995
  categ_columns = [c[0] for c in self.column_specs if c[1] in ("string", "boolean")]
2964
2996
  categ_columns.sort()
2997
+ categ_columns2 = [c[0] for c in self.column_specs if c[1] in ("string", "boolean", "date")]
2998
+ categ_columns2.sort()
2965
2999
  # quant_columns includes date and timestamp columns
2966
3000
  quant_columns = [c[0] for c in self.column_specs if c[1] in ("int", "float", "date", "timestamp", "timestamptz")]
2967
3001
  quant_columns.sort()
@@ -2971,17 +3005,21 @@ class PlotDialog(object):
2971
3005
  date_columns.sort()
2972
3006
  self.x_var.set('')
2973
3007
  self.y_var.set('')
3008
+ self.groupby_var.set('')
2974
3009
  self.xlog_ck["state"] = "normal"
2975
3010
  self.ylog_ck["state"] = "normal"
2976
3011
  self.x_sel["state"] = "readonly"
2977
3012
  self.y_sel["state"] = "readonly"
3013
+ self.groupby_sel["state"] = "readonly"
2978
3014
  plot_type = self.type_var.get()
2979
3015
  if plot_type == "Category counts":
3016
+ self.x_sel["state"] = "normal"
2980
3017
  self.x_sel["values"] = categ_columns
2981
3018
  self.xlog_ck["state"] = "disabled"
2982
3019
  self.y_sel["state"] = "disabled"
2983
3020
  self.ylog_ck["state"] = "disabled"
2984
3021
  elif plot_type in ("Histogram", "Empirical CDF", "Kernel density (KD) plot", "Normal Q-Q plot", "Breaks groups", "Breaks optimum"):
3022
+ self.x_sel["state"] = "normal"
2985
3023
  self.x_sel["values"] = numeric_columns
2986
3024
  self.y_sel["state"] = "disabled"
2987
3025
  self.ylog_ck["state"] = "disabled"
@@ -2990,12 +3028,26 @@ class PlotDialog(object):
2990
3028
  if plot_type == "Normal Q-Q plot":
2991
3029
  self.dlg.bind("<Alt-g>", self.show_groups)
2992
3030
  elif plot_type in ("Box plot", "Categorical KD plot", "Categorical stripchart", "Violin plot"):
2993
- self.x_sel["values"] = list(set(categ_columns) | set(date_columns))
3031
+ xcols = list(set(categ_columns) | set(date_columns))
3032
+ xcols.sort()
3033
+ self.x_sel["state"] = "normal"
3034
+ self.x_sel["values"] = xcols
2994
3035
  self.xlog_ck["state"] = "disabled"
2995
3036
  self.y_sel["values"] = numeric_columns
3037
+ elif plot_type == "Min-max plot":
3038
+ self.x_sel["state"] = "normal"
3039
+ self.x_sel["values"] = quant_columns
3040
+ self.y_sel["state"] = "normal"
3041
+ self.y_sel["values"] = categ_columns2
3042
+ self.ylog_ck["state"] = "disabled"
2996
3043
  else:
3044
+ self.x_sel["state"] = "normal"
2997
3045
  self.x_sel["values"] = quant_columns
3046
+ self.y_sel["state"] = "normal"
2998
3047
  self.y_sel["values"] = quant_columns
3048
+ if plot_type in ("Scatter plot", "Line plot"):
3049
+ self.groupby_sel["state"] = "normal"
3050
+ self.groupby_sel["values"] = [''] + categ_columns2
2999
3051
 
3000
3052
  def q_redraw(self, get_data=True, *args):
3001
3053
  # Conditionally (re)draw the plot.
@@ -3004,7 +3056,7 @@ class PlotDialog(object):
3004
3056
  "Normal Q-Q plot", "Breaks groups", "Breaks optimum", "Histogram") \
3005
3057
  and self.x_var.get() != '') \
3006
3058
  or (plot_type in ("Scatter plot", "Line plot", "Box plot", "Categorical KD plot", "Categorical stripchart", \
3007
- "Violin plot", "Y range plot") and self.x_var.get() != '' and self.y_var.get() != '')
3059
+ "Min-max plot", "Violin plot", "Y range plot") and self.x_var.get() != '' and self.y_var.get() != '')
3008
3060
  if can_redraw:
3009
3061
  self.plotfig.clear()
3010
3062
  self.plot_axes = self.plotfig.add_subplot(111)
@@ -3022,6 +3074,9 @@ class PlotDialog(object):
3022
3074
  column_list = [self.x_var.get()]
3023
3075
  if self.y_var.get() != '':
3024
3076
  column_list.append(self.y_var.get())
3077
+ if self.groupby_var.get() != '':
3078
+ column_list.append(self.groupby_var.get())
3079
+ # Get either only the selected data or all data.
3025
3080
  if self.sel_only_var.get() == "1":
3026
3081
  dataset = self.parent.get_sel_data(column_list)
3027
3082
  else:
@@ -3057,9 +3112,13 @@ class PlotDialog(object):
3057
3112
  cast_fn = data_type_cast_fn(y_data_type)
3058
3113
  for i in range(len(clean_data[1])):
3059
3114
  clean_data[1][i] = cast_fn(clean_data[1][i])
3115
+ # Sort the dataset by X values
3116
+ clean_data = sort_columns(clean_data)
3060
3117
  # Set data labels
3061
3118
  if self.y_var.get() != '':
3062
3119
  self.data_labels = [self.x_var.get(), self.y_var.get()]
3120
+ if self.groupby_var.get() != '':
3121
+ self.data_labels.append(self.groupby_var.get())
3063
3122
  else:
3064
3123
  self.data_labels = [self.x_var.get()]
3065
3124
  # Log-transform data if specified.
@@ -3106,6 +3165,7 @@ class PlotDialog(object):
3106
3165
  elif plot_type in ("Box plot", "Categorical KD plot", "Categorical stripchart", "Violin plot"):
3107
3166
  # A list of Y values for each X value
3108
3167
  x_vals = list(set(self.dataset[0]))
3168
+ x_vals.sort()
3109
3169
  ds = list(zip(self.dataset[0], self.dataset[1]))
3110
3170
  plot_data = []
3111
3171
  for x in x_vals:
@@ -3136,6 +3196,23 @@ class PlotDialog(object):
3136
3196
  elif plot_type == "Kernel density (KD) plot":
3137
3197
  self.plot_data = self.dataset
3138
3198
  self.plot_data_labels = [self.x_var.get()]
3199
+ elif plot_type == "Min-max plot":
3200
+ # Min and max X for each Y
3201
+ y_vals = list(set(self.dataset[1]))
3202
+ y_vals.sort()
3203
+ plotdata = dict(zip(y_vals, [[None, None] for _ in y_vals]))
3204
+ for i in range(len(self.dataset[1])):
3205
+ x = self.dataset[0][i]
3206
+ y = self.dataset[1][i]
3207
+ x_vals = plotdata[y]
3208
+ if x_vals[0] is None or x < x_vals[0]:
3209
+ plotdata[y][0] = x
3210
+ if x_vals[1] is None or x > x_vals[1]:
3211
+ plotdata[y][1] = x
3212
+ x1 = [plotdata[y][0] for y in y_vals]
3213
+ x2 = [plotdata[y][1] for y in y_vals]
3214
+ self.plot_data = [y_vals, x1, x2]
3215
+ self.plot_data_labels = [self.data_labels[1], self.data_labels[0] + " min", self.data_labels[0] + " max"]
3139
3216
  elif plot_type == "Normal Q-Q plot":
3140
3217
  x_vals = copy.copy(self.dataset[0])
3141
3218
  x_vals.sort()
@@ -3154,8 +3231,7 @@ class PlotDialog(object):
3154
3231
  # Min and max Y for each X
3155
3232
  x_vals = list(set(self.dataset[0]))
3156
3233
  x_vals.sort()
3157
- y_vals = [[None, None]] * len(x_vals)
3158
- plotdata = dict(zip(x_vals, y_vals))
3234
+ plotdata = dict(zip(x_vals, [[None, None] for i in x_vals]))
3159
3235
  for i in range(len(self.dataset[0])):
3160
3236
  x = self.dataset[0][i]
3161
3237
  y = self.dataset[1][i]
@@ -3170,10 +3246,16 @@ class PlotDialog(object):
3170
3246
  self.plot_data_labels = [self.data_labels[0], self.data_labels[1] + " min", self.data_labels[1] + " max"]
3171
3247
  elif plot_type == "Line plot":
3172
3248
  # Sort by X
3173
- ds = list(zip(self.dataset[0], self.dataset[1]))
3249
+ if self.groupby_var.get() == '':
3250
+ ds = list(zip(self.dataset[0], self.dataset[1]))
3251
+ else:
3252
+ ds = list(zip(self.dataset[0], self.dataset[1], self.dataset[2]))
3174
3253
  ds.sort()
3175
3254
  ds2 = list(zip(*ds))
3176
- self.plot_data = [list(ds2[0]), list(ds2[1])]
3255
+ if self.groupby_var.get() == '':
3256
+ self.plot_data = [list(ds2[0]), list(ds2[1])]
3257
+ else:
3258
+ self.plot_data = [list(ds2[0]), list(ds2[1]), list(ds2[2])]
3177
3259
  self.plot_data_labels = self.data_labels
3178
3260
  elif plot_type in ("Histogram", "Scatter plot", "Y range plot"):
3179
3261
  # No special preparation
@@ -3193,11 +3275,31 @@ class PlotDialog(object):
3193
3275
  self.plot_axes.set_xlabel(self.x_var.get())
3194
3276
  self.plot_axes.set_ylabel("Counts")
3195
3277
  elif plot_type == "Scatter plot":
3196
- self.plot_axes.scatter(self.plot_data[0], self.plot_data[1])
3278
+ if self.groupby_var.get() == '':
3279
+ self.plot_axes.scatter(self.plot_data[0], self.plot_data[1])
3280
+ else:
3281
+ groups = list(set(self.plot_data[2]))
3282
+ groups.sort()
3283
+ datarows = len(self.plot_data[0])
3284
+ for g in groups:
3285
+ pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
3286
+ pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
3287
+ self.plot_axes.plot(pdx, pdy, marker='o', linestyle='', label=g)
3288
+ self.plot_axes.legend()
3197
3289
  self.plot_axes.set_xlabel(self.plot_data_labels[0])
3198
3290
  self.plot_axes.set_ylabel(self.plot_data_labels[1])
3199
3291
  elif plot_type == "Line plot":
3200
- self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
3292
+ if self.groupby_var.get() == '':
3293
+ self.plot_axes.plot(self.plot_data[0], self.plot_data[1])
3294
+ else:
3295
+ groups = list(set(self.plot_data[2]))
3296
+ groups.sort()
3297
+ datarows = len(self.plot_data[0])
3298
+ for g in groups:
3299
+ pdx = [self.plot_data[0][i] for i in range(datarows) if self.plot_data[2][i] == g]
3300
+ pdy = [self.plot_data[1][i] for i in range(datarows) if self.plot_data[2][i] == g]
3301
+ self.plot_axes.plot(pdx, pdy, label=g)
3302
+ self.plot_axes.legend()
3201
3303
  self.plot_axes.set_xlabel(self.plot_data_labels[0])
3202
3304
  self.plot_axes.set_ylabel(self.plot_data_labels[1])
3203
3305
  elif plot_type == "Breaks groups":
@@ -3208,7 +3310,7 @@ class PlotDialog(object):
3208
3310
  ticks.insert(0,0)
3209
3311
  ticks.append(ticks[-1]+1)
3210
3312
  self.plot_axes.set_xticks(ticks)
3211
- lbls = self.plot_data_labels
3313
+ lbls = copy.copy(self.plot_data_labels)
3212
3314
  lbls.insert(0, "")
3213
3315
  lbls.append("")
3214
3316
  self.plot_axes.set_xticklabels(lbls)
@@ -3227,6 +3329,13 @@ class PlotDialog(object):
3227
3329
  sns.kdeplot({self.x_var.get(): self.dataset[0]}, x=self.x_var.get(), fill=True, ax=self.plot_axes)
3228
3330
  self.plot_axes.set_xlabel(self.x_var.get())
3229
3331
  self.plot_axes.set_ylabel("Density")
3332
+ elif plot_type == "Min-max plot":
3333
+ self.plot_axes.hlines(self.plot_data[0], self.plot_data[1], self.plot_data[2], linewidths=5.0)
3334
+ if self.xlog_ck["state"] != "disabled" and self.xlog_var.get() == "1":
3335
+ self.plot_axes.set_xlabel("Log10 of " + self.x_var.get())
3336
+ else:
3337
+ self.plot_axes.set_xlabel(self.x_var.get())
3338
+ self.plot_axes.set_ylabel(self.plot_data_labels[0])
3230
3339
  elif plot_type == "Normal Q-Q plot":
3231
3340
  if self.qq_groups:
3232
3341
  self.plot_axes.scatter(self.plot_data[2], self.plot_data[1], c=self.plot_data[3], cmap="tab10")
@@ -3240,14 +3349,22 @@ class PlotDialog(object):
3240
3349
  elif plot_type == "Y range plot":
3241
3350
  self.plot_axes.fill_between(self.plot_data[0], self.plot_data[1], self.plot_data[2])
3242
3351
  self.plot_axes.set_xlabel(self.x_var.get())
3243
- self.plot_axes.set_ylabel(self.y_var.get())
3352
+ if self.xlog_ck["state"] != "disabled" and self.xlog_var.get() == "1":
3353
+ self.plot_axes.set_xlabel("Log10 of " + self.x_var.get())
3354
+ else:
3355
+ self.plot_axes.set_xlabel(self.x_var.get())
3356
+ if self.ylog_ck["state"] != "disabled" and self.ylog_var.get() == "1":
3357
+ self.plot_axes.set_ylabel("Log10 of " + self.y_var.get())
3358
+ else:
3359
+ self.plot_axes.set_ylabel(self.y_var.get())
3244
3360
  elif plot_type == "Box plot":
3245
3361
  self.plot_axes.boxplot(self.plot_data, labels=self.plot_data_labels)
3246
3362
  self.plot_axes.set_xlabel(self.x_var.get())
3247
3363
  self.plot_axes.set_ylabel(self.data_labels[1])
3248
3364
  elif plot_type == "Categorical KD plot":
3249
3365
  sns.kdeplot({self.x_var.get(): self.dataset[0], self.y_var.get(): self.dataset[1]},
3250
- x=self.y_var.get(), hue=self.x_var.get(), multiple="layer", fill=True, alpha=0.35, ax=self.plot_axes)
3366
+ x=self.y_var.get(), hue=self.x_var.get(), multiple="layer", fill=True, alpha=0.35, ax=self.plot_axes,
3367
+ warn_singular=False)
3251
3368
  self.plot_axes.set_xlabel(self.y_var.get())
3252
3369
  self.plot_axes.set_ylabel("Density")
3253
3370
  elif plot_type == "Categorical stripchart":
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mapdata
3
- Version: 2.11.0
3
+ Version: 2.13.0
4
4
  Summary: An interactive map and table explorer for geographic coordinates in a spreadsheet, CSV file, or database
5
5
  Home-page: https://osdn.net/project/mapdata/
6
6
  Author: Dreas Nielsen
@@ -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.11.0',
8
+ version='2.13.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