wbgapi360 0.3.0__py3-none-any.whl → 0.3.1__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.
wbgapi360/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.3.0'
32
- __version_tuple__ = version_tuple = (0, 3, 0)
31
+ __version__ = version = '0.3.1'
32
+ __version_tuple__ = version_tuple = (0, 3, 1)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -46,8 +46,38 @@ REGION_BBOX = {
46
46
  }
47
47
 
48
48
  class Visualizer:
49
+ # Reference figsize for default font sizes (FT standard)
50
+ REF_FIGSIZE = (10, 6)
51
+ REF_FONTS = {
52
+ 'title': 18,
53
+ 'subtitle': 12,
54
+ 'axis_label': 10,
55
+ 'tick': 9,
56
+ 'source': 8,
57
+ 'legend': 9,
58
+ 'annotation': 9
59
+ }
60
+
49
61
  def __init__(self):
50
- pass
62
+ self._current_figsize = self.REF_FIGSIZE
63
+
64
+ def _get_scaled_fonts(self, figsize=None):
65
+ """Calculate font sizes scaled proportionally to figsize."""
66
+ fs = figsize or self._current_figsize
67
+ # Scale factor based on width (primary driver of text readability)
68
+ scale = fs[0] / self.REF_FIGSIZE[0]
69
+ # Clamp scale between 0.6 and 1.2 to avoid extreme sizes
70
+ scale = max(0.6, min(1.2, scale))
71
+
72
+ return {
73
+ 'title': int(self.REF_FONTS['title'] * scale),
74
+ 'subtitle': int(self.REF_FONTS['subtitle'] * scale),
75
+ 'axis_label': int(self.REF_FONTS['axis_label'] * scale),
76
+ 'tick': int(self.REF_FONTS['tick'] * scale),
77
+ 'source': int(self.REF_FONTS['source'] * scale),
78
+ 'legend': int(self.REF_FONTS['legend'] * scale),
79
+ 'annotation': int(self.REF_FONTS['annotation'] * scale)
80
+ }
51
81
 
52
82
  def _apply_theme_context(self):
53
83
  """Configura el estilo FT explícitamente."""
@@ -103,19 +133,26 @@ class Visualizer:
103
133
  ax.xaxis.set_major_formatter(ticker.FuncFormatter(ft_formatter))
104
134
 
105
135
  def _finalize_chart(self, fig, ax, title, subtitle, save_path, source="World Bank Data360", is_regional_map=False):
106
- # Main title (Serif, Dark, 18-22pt)
136
+ # Get scaled font sizes based on current figsize
137
+ fonts = self._get_scaled_fonts()
138
+
139
+ # Main title (Serif, Dark, scaled)
107
140
  ax.text(x=0, y=1.15, s=title, transform=ax.transAxes,
108
- fontsize=20, weight='semibold', color=FT_TITLE_COLOR, ha='left',
141
+ fontsize=fonts['title'], weight='semibold', color=FT_TITLE_COLOR, ha='left',
109
142
  fontfamily='serif')
110
143
 
111
- # Subtitle (Sans, Medium grey, 13-15pt)
144
+ # Subtitle (Sans, Medium grey, scaled)
112
145
  if subtitle:
113
146
  ax.text(x=0, y=1.06, s=subtitle, transform=ax.transAxes,
114
- fontsize=14, color=FT_SUBTITLE_COLOR, ha='left')
147
+ fontsize=fonts['subtitle'], color=FT_SUBTITLE_COLOR, ha='left')
115
148
 
116
149
  # Source note
117
150
  plt.figtext(0.05, 0.01, f"Source: {source}",
118
- fontsize=9, color=FT_SOURCE_COLOR, ha='left', va='bottom')
151
+ fontsize=fonts['source'], color=FT_SOURCE_COLOR, ha='left', va='bottom')
152
+
153
+ # Scale tick labels
154
+ ax.tick_params(axis='both', labelsize=fonts['tick'])
155
+
119
156
 
120
157
  # Adjust layout based on map type
121
158
  if is_regional_map:
@@ -220,7 +257,9 @@ class Visualizer:
220
257
  var_name='INDICATOR', value_name='OBS_VALUE')
221
258
  df = df.rename(columns={t_col: 'TIME_PERIOD'})
222
259
 
223
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
260
+ actual_figsize = figsize or DEFAULT_FIGSIZE
261
+ self._current_figsize = actual_figsize
262
+ fig, ax = plt.subplots(figsize=actual_figsize)
224
263
 
225
264
  hue_col = 'REF_AREA'
226
265
  if 'REF_AREA' in df.columns and df['REF_AREA'].nunique() == 1 and 'INDICATOR' in df.columns:
@@ -311,26 +350,24 @@ class Visualizer:
311
350
 
312
351
  return self._finalize_chart(fig, ax, title, subtitle, save_path=save_path)
313
352
 
314
-
315
353
  def plot_bar(self, df, title="", subtitle="", save_path=None, figsize=None):
316
354
  self._apply_theme_context()
317
355
  df, val_label = self._prepare_data(df, auto_rename=True)
318
- fig, ax = plt.subplots(figsize=figsize or (8, 6))
356
+ actual_figsize = figsize or (8, 6)
357
+ self._current_figsize = actual_figsize # Store for font scaling
358
+ fig, ax = plt.subplots(figsize=actual_figsize)
319
359
 
320
360
  df_sorted = df.sort_values('OBS_VALUE', ascending=False)
321
-
322
- # Color logic: One dominant category? Usually bars are same category compared.
323
- # Use Main Blue for all, or Palette if 'hue' exists?
324
- # "Para múltiples categorías... paleta desaturada"
325
- # We'll use color cycling if there is a 'category' column distinguishable?
326
- # Usually barplot is 1 measure across countries. All same color (Blue or Petrol).
361
+ fonts = self._get_scaled_fonts()
327
362
 
328
363
  sns.barplot(
329
364
  data=df_sorted, x='OBS_VALUE', y='REF_AREA',
330
365
  color=FT_BLUE, ax=ax, edgecolor=None
331
- # Note: Using simple color=FT_BLUE for sobriety unless strictly multi-category variable
332
366
  )
333
367
 
368
+ # Scale y-axis labels (country names)
369
+ ax.tick_params(axis='y', labelsize=fonts['axis_label'])
370
+
334
371
  ax.xaxis.grid(True, color=FT_GRID_COLOR)
335
372
  ax.yaxis.grid(False)
336
373
  ax.set_xlabel("")
@@ -338,10 +375,13 @@ class Visualizer:
338
375
  self._format_axis(ax, 'x')
339
376
  self._finalize_chart(fig, ax, title, subtitle, save_path)
340
377
 
378
+
341
379
  def plot_column(self, df, title="", subtitle="", save_path=None, figsize=None):
342
380
  self._apply_theme_context()
343
381
  df, val_label = self._prepare_data(df, auto_rename=True)
344
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
382
+ actual_figsize = figsize or DEFAULT_FIGSIZE
383
+ self._current_figsize = actual_figsize
384
+ fig, ax = plt.subplots(figsize=actual_figsize)
345
385
 
346
386
  sns.barplot(
347
387
  data=df, x='REF_AREA', y='OBS_VALUE',
@@ -358,7 +398,9 @@ class Visualizer:
358
398
  def plot_scatter(self, df, title="", subtitle="", save_path=None, figsize=None):
359
399
  self._apply_theme_context()
360
400
  df, val_label = self._prepare_data(df)
361
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
401
+ actual_figsize = figsize or DEFAULT_FIGSIZE
402
+ self._current_figsize = actual_figsize
403
+ fig, ax = plt.subplots(figsize=actual_figsize)
362
404
 
363
405
  numeric_cols = df.select_dtypes(include=['number']).columns.tolist()
364
406
  cols = [c for c in numeric_cols if c != 'TIME_PERIOD']
@@ -448,7 +490,9 @@ class Visualizer:
448
490
  df_wide = df.pivot_table(index='TIME_PERIOD', columns='REF_AREA', values='OBS_VALUE', aggfunc='sum')
449
491
  df_wide = df_wide.fillna(0)
450
492
 
451
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
493
+ actual_figsize = figsize or DEFAULT_FIGSIZE
494
+ self._current_figsize = actual_figsize
495
+ fig, ax = plt.subplots(figsize=actual_figsize)
452
496
 
453
497
  # Clean Palette for stack
454
498
  # FT uses muted colors for composition.
@@ -532,7 +576,9 @@ class Visualizer:
532
576
 
533
577
  world_data = world.merge(df_map, left_on=iso_world_col, right_on=merge_col, how='left')
534
578
 
535
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE_MAP)
579
+ actual_figsize = figsize or DEFAULT_FIGSIZE_MAP
580
+ self._current_figsize = actual_figsize
581
+ fig, ax = plt.subplots(figsize=actual_figsize)
536
582
 
537
583
  # Base world map (light neutral background)
538
584
  world.plot(ax=ax, color='#F6F6F6', edgecolor='#E0E0E0', linewidth=0.3)
@@ -632,7 +678,9 @@ class Visualizer:
632
678
  iso_world_col = 'ISO_A3' if 'ISO_A3' in world.columns else 'iso_a3'
633
679
  world_data = world.merge(df_map, left_on=iso_world_col, right_on=merge_col, how='inner')
634
680
 
635
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE_MAP)
681
+ actual_figsize = figsize or DEFAULT_FIGSIZE_MAP
682
+ self._current_figsize = actual_figsize
683
+ fig, ax = plt.subplots(figsize=actual_figsize)
636
684
  world.plot(ax=ax, color='#F6F6F6', edgecolor='#E0E0E0', linewidth=0.3)
637
685
 
638
686
  if not world_data.empty:
@@ -700,7 +748,9 @@ class Visualizer:
700
748
  iso_world_col = 'ISO_A3' if 'ISO_A3' in world.columns else 'iso_a3'
701
749
  world_data = world.merge(df_map, left_on=iso_world_col, right_on=merge_col, how='left')
702
750
 
703
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE_MAP)
751
+ actual_figsize = figsize or DEFAULT_FIGSIZE_MAP
752
+ self._current_figsize = actual_figsize
753
+ fig, ax = plt.subplots(figsize=actual_figsize)
704
754
  world.plot(ax=ax, color='#F6F6F6', edgecolor='#E0E0E0', linewidth=0.3)
705
755
 
706
756
  if not world_data.dropna(subset=[value_col]).empty:
@@ -789,7 +839,9 @@ class Visualizer:
789
839
  iso_world_col = 'ISO_A3' if 'ISO_A3' in world.columns else 'iso_a3'
790
840
  world_data = world.merge(df_map, left_on=iso_world_col, right_on=merge_col, how='left')
791
841
 
792
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE_MAP)
842
+ actual_figsize = figsize or DEFAULT_FIGSIZE_MAP
843
+ self._current_figsize = actual_figsize
844
+ fig, ax = plt.subplots(figsize=actual_figsize)
793
845
  world.plot(ax=ax, color='#F6F6F6', edgecolor='#E0E0E0', linewidth=0.3)
794
846
 
795
847
  if not world_data.dropna(subset=[value_col]).empty:
@@ -894,7 +946,9 @@ class Visualizer:
894
946
  # Handle duplicates
895
947
  df_pivot = df.pivot_table(index='TIME_PERIOD', columns='REF_AREA', values='OBS_VALUE', aggfunc='mean')
896
948
 
897
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
949
+ actual_figsize = figsize or DEFAULT_FIGSIZE
950
+ self._current_figsize = actual_figsize
951
+ fig, ax = plt.subplots(figsize=actual_figsize)
898
952
 
899
953
  df_pivot.plot(kind='bar', stacked=True, ax=ax, width=0.8, color=FT_PALETTE, edgecolor='white', linewidth=0.5)
900
954
 
@@ -923,7 +977,9 @@ class Visualizer:
923
977
  except ValueError:
924
978
  df_pivot = df.pivot_table(index='TIME_PERIOD', columns='REF_AREA', values='OBS_VALUE', aggfunc='mean')
925
979
 
926
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
980
+ actual_figsize = figsize or DEFAULT_FIGSIZE
981
+ self._current_figsize = actual_figsize
982
+ fig, ax = plt.subplots(figsize=actual_figsize)
927
983
 
928
984
  df_pivot.plot(kind='area', stacked=True, ax=ax, alpha=0.9, color=FT_PALETTE, linewidth=0)
929
985
 
@@ -956,7 +1012,9 @@ class Visualizer:
956
1012
  top_entities = df[df['TIME_PERIOD'] == latest_year].nsmallest(top_n, 'Rank')['REF_AREA'].unique()
957
1013
  df_filtered = df[df['REF_AREA'].isin(top_entities)]
958
1014
 
959
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
1015
+ actual_figsize = figsize or DEFAULT_FIGSIZE
1016
+ self._current_figsize = actual_figsize
1017
+ fig, ax = plt.subplots(figsize=actual_figsize)
960
1018
 
961
1019
  # Invert Y axis for Rank 1 at top
962
1020
  ax.invert_yaxis()
@@ -990,7 +1048,9 @@ class Visualizer:
990
1048
  else:
991
1049
  return
992
1050
 
993
- fig, ax = plt.subplots(figsize=figsize or (6, 6))
1051
+ actual_figsize = figsize or (6, 6)
1052
+ self._current_figsize = actual_figsize
1053
+ fig, ax = plt.subplots(figsize=actual_figsize)
994
1054
 
995
1055
  colors = [FT_BLUE, FT_GREEN, FT_MUSTARD, FT_RED, FT_PURPLE, FT_NEUTRAL_DARK, FT_NEUTRAL_LIGHT]
996
1056
 
@@ -1065,7 +1125,9 @@ class Visualizer:
1065
1125
 
1066
1126
  rects = recursive_split(normed, 0, 0, 100, 100)
1067
1127
 
1068
- fig, ax = plt.subplots(figsize=figsize or DEFAULT_FIGSIZE)
1128
+ actual_figsize = figsize or DEFAULT_FIGSIZE
1129
+ self._current_figsize = actual_figsize
1130
+ fig, ax = plt.subplots(figsize=actual_figsize)
1069
1131
  ax.set_xlim(0, 100)
1070
1132
  ax.set_ylim(0, 100)
1071
1133
  ax.set_axis_off()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbgapi360
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Enterprise-grade World Bank Data Client for Humans & AI (MCP)
5
5
  Author-email: Maykol Medrano <mmedrano2@uc.cl>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  wbgapi360/__init__.py,sha256=4ur1kFtjN56mR_xprSejHy2FJb7oleiEJaK4nGSiTvg,1750
2
- wbgapi360/_version.py,sha256=5zTqm8rgXsWYBpB2M3Zw_K1D-aV8wP7NsBLrmMKkrAQ,704
2
+ wbgapi360/_version.py,sha256=gGLpQUQx-ty9SEy9PYw9OgJWWzJLBnCpfJOfzL7SjlI,704
3
3
  wbgapi360/api.py,sha256=kA9kCicFVwwph8wYOsr7uafuRFpfEY2p5SbQy1JVF74,10430
4
4
  wbgapi360/cli.py,sha256=4Y4GXl8rsxfiDUDNCqVSjtC_bgGNOGedfZBN0UWNowg,3034
5
5
  wbgapi360/config.py,sha256=Se0_l6H6C6BHRRNW93GitE9DNzqvVwQIxJW0uHvUQj4,1652
@@ -16,10 +16,10 @@ wbgapi360/metadata/iso_mapping.py,sha256=Cma8snXlYTVm7k0SA7j25hnsfrS7Vwdcyu0NMkI
16
16
  wbgapi360/metadata/resolver.py,sha256=VgdcCk2CUg7RD55UenAGcfhtvup89JUxYgbBEI90T1M,4821
17
17
  wbgapi360/search/engine.py,sha256=TCAwabUfyC0PtxK97FNlS3xWKddLQOWocl57Oo-Gvsg,5827
18
18
  wbgapi360/visual/__init__.py,sha256=FQxrEU8OEBzz9-rLwdGqrLTSPSvcaKVlAm9-xTY4SKM,24
19
- wbgapi360/visual/charts.py,sha256=LbEPI1jEw6QsDo4VCLkDGCNPdEFqX0vuD3JCX89GJKo,46179
20
- wbgapi360-0.3.0.dist-info/licenses/LICENSE,sha256=g-9qOC69nvvAJ8dr4_i3yJsz0iPpL8gswkrLj3-je7o,1071
21
- wbgapi360-0.3.0.dist-info/METADATA,sha256=BWncf7L31EWLyn76IKALX7KhuU2F_8ilveswyobfDGQ,8495
22
- wbgapi360-0.3.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
23
- wbgapi360-0.3.0.dist-info/entry_points.txt,sha256=9nNopsIlKEKw9rNe_6zW-ERL6fWu6eXymmKLBIZzzqI,49
24
- wbgapi360-0.3.0.dist-info/top_level.txt,sha256=q3vycsvqQsLZqAFyCzrPw392QV6wAYbVamr39Z-n2MQ,10
25
- wbgapi360-0.3.0.dist-info/RECORD,,
19
+ wbgapi360/visual/charts.py,sha256=BzlLIOoz0nXrjqoQm44nkNwojrt9gldcob16SCcRFFw,48513
20
+ wbgapi360-0.3.1.dist-info/licenses/LICENSE,sha256=g-9qOC69nvvAJ8dr4_i3yJsz0iPpL8gswkrLj3-je7o,1071
21
+ wbgapi360-0.3.1.dist-info/METADATA,sha256=JBRIiYyhS_sbEZT82KFJWBvZE7ZzRQM1KEu3JCPG5yo,8495
22
+ wbgapi360-0.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
23
+ wbgapi360-0.3.1.dist-info/entry_points.txt,sha256=9nNopsIlKEKw9rNe_6zW-ERL6fWu6eXymmKLBIZzzqI,49
24
+ wbgapi360-0.3.1.dist-info/top_level.txt,sha256=q3vycsvqQsLZqAFyCzrPw392QV6wAYbVamr39Z-n2MQ,10
25
+ wbgapi360-0.3.1.dist-info/RECORD,,