import io, base64,os,uuid import matplotlib.pyplot as plt import matplotlib.colors as colors import matplotlib.cm as cm import matplotlib.gridspec as gridspec import geopandas as gpd import pandas as pd def show_two_plots(gdf, name1, name2, vmin, vmax, colorscheme, title1, title2, label, split=False): #dynamically change marker size n_points = len(gdf) marker_size = 8500 / n_points marker_size = max(2, min(marker_size, 200)) filter_marker_size = marker_size*0.6 print(marker_size) print('split', split) if gdf.empty: raise ValueError("GeoDataFrame is empty") required_cols = [name1, name2] for col in required_cols: if col not in gdf.columns: raise ValueError(f"Column '{col}' not found in gdf") #Create a shared colormap and normalization cmap = plt.colormaps[colorscheme] norm = colors.Normalize(vmin=vmin, vmax=vmax) # Create subplots fig = plt.figure(figsize=(12, 6)) gs = gridspec.GridSpec(1, 3, width_ratios=[1, 1, 0.05], wspace=0.3) axes = [fig.add_subplot(gs[0]), fig.add_subplot(gs[1]), fig.add_subplot(gs[2]) ] if split == False: # First plot gdf.plot(column=name1, cmap=cmap, norm=norm, ax=axes[0], marker='s', markersize=marker_size) else: # Split data into three GeoDataFrames # below_100 = gdf[gdf[split] <= 100] above_capacity = gdf[gdf[split] > (100-gdf['Pct_Construccion'])] above_100 = gdf[gdf[split] > 100] above_colors = {"capacity":"chocolate", "100":"firebrick"} # Plot values ≤ 100 using colormap gdf.plot(column=name1, cmap=colorscheme, ax=axes[0], vmin=vmin, vmax=vmax, marker='s', markersize=marker_size) # Plot values > capacity in orange above_capacity.plot(color=above_colors['capacity'], ax=axes[0], label='> capacidad debido a la construcción', marker='x', markersize=filter_marker_size) # Plot values > 100 in red above_100.plot(color=above_colors['100'], ax=axes[0], label='> 100% cobertura arbórea', marker='x', markersize=filter_marker_size) # Add legend manually for orange points orange_patch = plt.Line2D([0], [0], marker='o', color='w', label='> capacidad debido a la construcción', markerfacecolor=above_colors['capacity'], markersize=8) # Add legend manually for red points red_patch = plt.Line2D([0], [0], marker='o', color='w', label='> 100% cobertura arbórea', markerfacecolor=above_colors['100'], markersize=8) axes[0].legend(handles=[orange_patch, red_patch]) # axes[0].set_title(title1) axes[0].set_title(title1, pad=-30) axes[0].set_axis_off() # Second plot (original) gdf.plot(column=name2, cmap=cmap, norm=norm, ax=axes[1],marker='s', markersize=marker_size) axes[1].set_title(title2, pad=-30) axes[1].set_axis_off() # Shared colorbar sm = cm.ScalarMappable(cmap=cmap, norm=norm) sm._A = [] # Dummy array for the colormap cbar = fig.colorbar(sm, cax=axes[2], orientation='vertical', fraction=0.03, pad=0.02) cbar.set_label(label) return fig def show_one_plot(gdf, name, vmin, vmax, colorscheme, title, label): #dynamically change marker size n_points = len(gdf) marker_size = 8500 / n_points marker_size = max(2, min(marker_size, 200)) # Second set of plots cmap = plt.colormaps[colorscheme] norm = colors.Normalize(vmin, vmax) # Create subplots fig = plt.figure(figsize=(12, 6)) gs = gridspec.GridSpec(1, 2, width_ratios=[1, 0.025], wspace=0.3) axes = [fig.add_subplot(gs[0]), fig.add_subplot(gs[1])] gdf.plot(column=name, cmap=cmap, norm=norm, ax=axes[0], marker='s', markersize=marker_size) axes[0].set_title(title, pad=-30) axes[0].set_axis_off() # Add a colorbar sm = cm.ScalarMappable(cmap=cmap, norm=norm) sm._A = [] # Dummy data for the colorbar cbar = fig.colorbar(sm, cax=axes[1], orientation='vertical', fraction=0.03, pad=0.02) cbar.set_label(label) return fig def show_two_plots_base64( gdf, name1, name2, vmin, vmax, colorscheme, title1, title2, label, split=False ): """Genera dos mapas (antes/después) de 1024×1024 px y los devuelve en base64.""" cmap = plt.colormaps[colorscheme] norm = colors.Normalize(vmin=vmin, vmax=vmax) # Tamaño fijo fig_size_inch = (5.12, 5.12) dpi_val = 200 # Tamaño de marcador dinámico n_points = len(gdf) marker_size = 8500 / n_points marker_size = max(2, min(marker_size, 200)) filter_marker_size = marker_size * 0.6 def fig_to_base64(fig): buf = io.BytesIO() fig.savefig(buf, format="png", bbox_inches="tight", dpi=dpi_val) buf.seek(0) return base64.b64encode(buf.read()).decode("utf-8") def make_fig(column, title, apply_split=False): # Fijamos tamaño físico y DPI fig, ax = plt.subplots(figsize=fig_size_inch, dpi=dpi_val) # Mapa base gdf.plot( column=column, cmap=cmap, norm=norm, ax=ax, marker='s', markersize=marker_size ) # Aplicar split sólo al mapa “después” if apply_split and split: above_capacity = gdf[gdf[split] > (100 - gdf["Pct_Construccion"])] above_100 = gdf[gdf[split] > 100] above_colors = {"capacity": "chocolate", "100": "firebrick"} above_capacity.plot( color=above_colors["capacity"], ax=ax, marker='x', markersize=filter_marker_size ) above_100.plot( color=above_colors["100"], ax=ax, marker='x', markersize=filter_marker_size ) # Leyenda orange_patch = plt.Line2D( [0], [0], marker='o', color='w', label='> capacidad (construcción)', markerfacecolor=above_colors['capacity'], markersize=8 ) red_patch = plt.Line2D( [0], [0], marker='o', color='w', label='> 100% cobertura arbórea', markerfacecolor=above_colors['100'], markersize=8 ) ax.legend(handles=[orange_patch, red_patch]) ax.set_title(title, pad=-25) ax.set_axis_off() sm = cm.ScalarMappable(cmap=cmap, norm=norm) sm._A = [] fig.colorbar( sm, ax=ax, orientation='vertical', fraction=0.03, pad=0.02 ).set_label(label) return fig # “Después” (aplica split) fig_after = make_fig(name1, title1, apply_split=True) # “Antes” (sin split) fig_before = make_fig(name2, title2, apply_split=False) return fig_to_base64(fig_after), fig_to_base64(fig_before) def show_two_plots_base64_clean( gdf, name1, name2, vmin, vmax, colorscheme, title1, title2, label, split=False ): """ Genera dos mapas (antes/después) de 1024×1024 px. Cada mapa incluye su colorbar, pero no título ni leyenda. Devuelve dict con imágenes base64, títulos y leyenda HTML aparte. """ cmap = plt.colormaps[colorscheme] norm = colors.Normalize(vmin=vmin, vmax=vmax) fig_size_inch = (5.12, 5.12) dpi_val = 200 n_points = len(gdf) marker_size = 8500 / n_points marker_size = max(2, min(marker_size, 200)) filter_marker_size = marker_size * 0.6 def fig_to_base64(fig): buf = io.BytesIO() fig.savefig(buf, format="png", bbox_inches="tight", dpi=dpi_val) buf.seek(0) return base64.b64encode(buf.read()).decode("utf-8") def make_fig(column, apply_split=False): fig, ax = plt.subplots(figsize=fig_size_inch, dpi=dpi_val) # Base plot gdf.plot( column=column, cmap=cmap, norm=norm, ax=ax, marker='s', markersize=marker_size ) # Split solo en el "después" if apply_split and split: above_capacity = gdf[gdf[split] > (100 - gdf["Pct_Construccion"])] above_100 = gdf[gdf[split] > 100] above_colors = {"capacity": "chocolate", "100": "firebrick"} above_capacity.plot( color=above_colors["capacity"], ax=ax, marker='x', markersize=filter_marker_size ) above_100.plot( color=above_colors["100"], ax=ax, marker='x', markersize=filter_marker_size ) # Quitar ejes y agregar colorbar ax.set_axis_off() sm = cm.ScalarMappable(cmap=cmap, norm=norm) sm._A = [] fig.colorbar( sm, ax=ax, orientation='vertical', fraction=0.03, pad=0.02 ).set_label(label) plt.tight_layout() return fig # Generar ambas figuras fig_after = make_fig(name1, apply_split=True) fig_before = make_fig(name2, apply_split=False) # Convertir ambas a base64 img_after = fig_to_base64(fig_after) img_before = fig_to_base64(fig_before) # Leyenda HTML (solo si split activo) legend_html = "" if split: legend_html = """