GEOCODE / app_5.py
ESJL's picture
Rename app.py to app_5.py
54d36b7 verified
import gradio as gr
import pandas as pd
import geopandas as gpd
# ==============================
# Carregamento único dos eixos
# ==============================
gdf_eixos = gpd.read_file("EixosLogradouros.shp", engine="fiona")
gdf_eixos = gdf_eixos.to_crs("EPSG:4326")
eixos_por_cdlog = {cdlog: grupo.copy() for cdlog, grupo in gdf_eixos.groupby("CDLOG")}
# ==============================
# Funções auxiliares
# ==============================
def formatar_float_visualizacao(df, casas=4):
df_visual = df.copy()
float_cols = df_visual.select_dtypes(include=["float"]).columns
df_visual[float_cols] = df_visual[float_cols].round(casas)
return df_visual
def carregar_abas(arquivo_excel):
if arquivo_excel is None:
return gr.update(choices=[]), None
xls = pd.ExcelFile(arquivo_excel.name)
abas = xls.sheet_names
return gr.update(choices=abas, value=abas[0]), abas[0]
def listar_colunas(arquivo_excel, aba):
if arquivo_excel is None or aba is None:
return gr.update(choices=[]), gr.update(choices=[])
df = pd.read_excel(arquivo_excel.name, sheet_name=aba)
colunas = df.columns.tolist()
colunas_upper = {col.upper(): col for col in colunas}
coluna_cdlog_pre = colunas_upper.get("CTM", colunas_upper.get("CDLOG", None))
coluna_num_pre = next(
(colunas_upper[nome.upper()] for nome in ["Nº GEO", "NUM_GEO", "NUM"]
if nome.upper() in colunas_upper),
None
)
return (
gr.update(choices=colunas, value=coluna_cdlog_pre),
gr.update(choices=colunas, value=coluna_num_pre),
)
def exibir_tabela(arquivo_excel, aba, coluna_num):
if arquivo_excel is None or aba is None:
return None, ""
df = pd.read_excel(arquivo_excel.name, sheet_name=aba)
df["_idx"] = range(len(df))
if coluna_num in df.columns:
df[coluna_num] = (
pd.to_numeric(df[coluna_num], errors="coerce")
.fillna(0)
.astype(int)
)
texto = f"O DataFrame possui {df.shape[0]} linhas e {df.shape[1]} colunas."
return formatar_float_visualizacao(df), texto
# ==============================
# INTERPOLAÇÃO
# ==============================
def interpolar(df, coluna_cdlog, coluna_num):
df = df.copy()
df[coluna_num] = pd.to_numeric(df[coluna_num], errors="coerce").fillna(0).astype(int)
lons, lats, falhas = [], [], []
for _, row in df.iterrows():
idx = row["_idx"]
cdlog = row[coluna_cdlog]
numero = row[coluna_num]
segmentos = eixos_por_cdlog.get(cdlog)
if segmentos is None:
lons.append(None)
lats.append(None)
falhas.append({
"_idx": idx,
"CDLOG": cdlog,
"Numero Atual": numero,
"Motivo": "CDLOG não encontrado",
"Lado": "",
"Intervalo": "",
"Sugestões": []
})
continue
lado = "Par" if numero % 2 == 0 else "Ímpar"
ini_col, fim_col = (
("NRPARINI", "NRPARFIN")
if lado == "Par"
else ("NRIMPINI", "NRIMPFIN")
)
segmentos = segmentos.copy()
segmentos[ini_col] = pd.to_numeric(segmentos[ini_col], errors="coerce")
segmentos[fim_col] = pd.to_numeric(segmentos[fim_col], errors="coerce")
segmentos = segmentos.dropna(subset=[ini_col, fim_col])
cond = (segmentos[ini_col] <= numero) & (segmentos[fim_col] >= numero)
seg_validos = segmentos[cond]
if seg_validos.empty:
if not segmentos.empty:
diffs = (segmentos[ini_col] - numero).abs()
min_index = diffs.idxmin()
linha_proxima = segmentos.loc[min_index]
ini = linha_proxima[ini_col]
fim = linha_proxima[fim_col]
if pd.notna(ini) and pd.notna(fim):
numeros_validos = list(range(int(ini), int(fim) + 1, 2))
numeros_validos.sort(key=lambda x: abs(x - numero))
falhas.append({
"_idx": idx,
"CDLOG": cdlog,
"Numero Atual": numero,
"Motivo": "Numeração fora do intervalo",
"Lado": lado,
"Intervalo": f"{int(ini)} até {int(fim)}",
"Sugestões": numeros_validos[:10]
})
lons.append(None)
lats.append(None)
continue
linha = seg_validos.iloc[0]
geom = linha.geometry
ini = linha[ini_col]
fim = linha[fim_col]
if fim == ini:
lons.append(None)
lats.append(None)
continue
frac = (numero - ini) / (fim - ini)
frac = max(0, min(1, frac))
ponto = geom.interpolate(geom.length * frac)
lons.append(ponto.x)
lats.append(ponto.y)
df["lon"] = lons
df["lat"] = lats
output_path = "dados_interpolados.xlsx"
df.to_excel(output_path, index=False)
colunas_fixas = ["_idx", "CDLOG", "Numero Atual", "Motivo", "Lado", "Intervalo", "Sugestões"]
df_falhas = pd.DataFrame(falhas, columns=colunas_fixas)
return formatar_float_visualizacao(df), output_path, df_falhas
# ==============================
# CORREÇÃO ASSISTIDA
# ==============================
def preparar_sugestoes(df_falhas, evt: gr.SelectData):
if df_falhas is None or evt is None or not evt.index:
return None, "", "", gr.update(choices=[], value=None)
linha = df_falhas.iloc[evt.index[0]]
idx = linha["_idx"]
lado = linha.get("Lado", "")
intervalo = linha.get("Intervalo", "")
sugestoes = linha.get("Sugestões", [])
if isinstance(sugestoes, str):
sugestoes = [
int(x.strip()) for x in sugestoes.split(",")
if x.strip().isdigit()
]
return idx, lado, intervalo, gr.update(choices=sugestoes, value=None)
# 🔥 ALTERAÇÃO PRINCIPAL AQUI
def aplicar_sugestao(df_original, idx, numero_escolhido, coluna_num):
if numero_escolhido is None:
return df_original, formatar_float_visualizacao(df_original)
df_original = df_original.copy()
df_original.loc[df_original["_idx"] == idx, coluna_num] = int(numero_escolhido)
return df_original, formatar_float_visualizacao(df_original)
# ==============================
# INTERFACE
# ==============================
with gr.Blocks() as app:
gr.Markdown("## DAI - Geolocalização com Correção Assistida")
estado_tabela = gr.State()
arquivo = gr.File(label="Arquivo Excel", file_types=[".xlsx"])
dropdown_abas = gr.Dropdown(label="Aba")
linhas_info = gr.Textbox(label="Linhas e Colunas", interactive=False)
dropdown_cdlog = gr.Dropdown(label="Coluna CDLOG")
dropdown_num = gr.Dropdown(label="Coluna Número")
tabela = gr.Dataframe(label="Tabela Original", interactive=False)
btn_interpolar = gr.Button("Interpolar")
tabela_resultado = gr.Dataframe(label="Com Coordenadas", interactive=False)
arquivo_saida = gr.File(label="Arquivo Gerado")
falhas = gr.Dataframe(label="Falhas", interactive=True)
gr.Markdown("### Correção Assistida")
idx_selecionado = gr.Number(visible=False)
info_lado = gr.Textbox(label="Lado", interactive=False)
info_intervalo = gr.Textbox(label="Intervalo Válido", interactive=False)
radio_sugestoes = gr.Radio(label="Escolha o Número Correto")
btn_aplicar_sugestao = gr.Button("Aplicar Sugestão")
# Eventos
arquivo.change(fn=carregar_abas, inputs=arquivo, outputs=[dropdown_abas, dropdown_abas])
dropdown_abas.change(
fn=exibir_tabela,
inputs=[arquivo, dropdown_abas, dropdown_num],
outputs=[tabela, linhas_info],
).then(
fn=lambda df: df,
inputs=tabela,
outputs=estado_tabela
).then(
fn=listar_colunas,
inputs=[arquivo, dropdown_abas],
outputs=[dropdown_cdlog, dropdown_num]
)
btn_interpolar.click(
fn=interpolar,
inputs=[tabela, dropdown_cdlog, dropdown_num],
outputs=[tabela_resultado, arquivo_saida, falhas]
)
falhas.select(
fn=preparar_sugestoes,
inputs=[falhas],
outputs=[idx_selecionado, info_lado, info_intervalo, radio_sugestoes]
)
btn_aplicar_sugestao.click(
fn=aplicar_sugestao,
inputs=[estado_tabela, idx_selecionado, radio_sugestoes, dropdown_num],
outputs=[estado_tabela, tabela] # ← atualiza também a tabela original
).then(
fn=interpolar,
inputs=[estado_tabela, dropdown_cdlog, dropdown_num],
outputs=[tabela_resultado, arquivo_saida, falhas]
)
app.launch(server_name="0.0.0.0", server_port=7860)