app / modules /dados.py
avalia-se's picture
Update modules/dados.py
204ebac verified
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import gradio as gr
from math import radians, sin, cos, sqrt, atan2
import googlemaps
import locale
from .shared_state import state # Importa o estado compartilhado
# Inicializando o cliente Google Maps com a chave da API
gmaps = googlemaps.Client(key='AIzaSyDoJ6C7NE2CHqFcaHTnhreOfgJeTk4uSH0')
try:
locale.setlocale(locale.LC_ALL, 'pt_BR.UTF-8')
except locale.Error:
# Fallback para uma localidade padrão (ex.: 'C' ou 'en_US.UTF-8')
locale.setlocale(locale.LC_ALL, 'C')
# Load the data
data_path = 'data_2.xlsx'
df = pd.read_excel(data_path, sheet_name='Planilha1')
# Garantir que a coluna "Data" seja do tipo datetime
df['Data'] = pd.to_datetime(df['Data'], errors='coerce')
# Função para calcular a distância haversine
def haversine(lat1, lon1, lat2, lon2):
raio_terra = 6371000 # Raio da Terra em metros
dlat = radians(lat2 - lat1)
dlon = radians(lon2 - lon1)
a = sin(dlat / 2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
return raio_terra * c
# Função unificada para gerar mapa, estatísticas, DataFrame filtrado e arquivo de download
def unified_action(selected_types, selected_bairros, selected_fonte, min_atotal, max_atotal, min_apriv, max_apriv, min_ater, max_ater, min_date, max_date, address=None, radius=None):
filtered_df = df.copy()
# Filtrando os dados com base nos tipos
if "Todos" not in selected_types:
filtered_df = filtered_df[filtered_df['Tipo'].isin(selected_types)]
# Filtrando os dados com base nos bairros
if "Todos" not in selected_bairros:
filtered_df = filtered_df[filtered_df['Bairro'].isin(selected_bairros)]
# Filtrando os dados com base na fonte
if "Todos" not in selected_fonte:
filtered_df = filtered_df[filtered_df['Fonte'].isin(selected_fonte)]
# Filtrando por área total
if min_atotal is not None:
filtered_df = filtered_df[filtered_df['Atotal'] >= min_atotal]
if max_atotal is not None:
filtered_df = filtered_df[filtered_df['Atotal'] <= max_atotal]
# Filtrando por área privativa
if min_apriv is not None:
filtered_df = filtered_df[filtered_df['Apriv'] >= min_apriv]
if max_apriv is not None:
filtered_df = filtered_df[filtered_df['Apriv'] <= max_apriv]
# Filtrando por área de terreno
if min_ater is not None:
filtered_df = filtered_df[filtered_df['Ater'] >= min_ater]
if max_ater is not None:
filtered_df = filtered_df[filtered_df['Ater'] <= max_ater]
# Filtrando por data, se especificada
if min_date is not None:
filtered_df = filtered_df[filtered_df['Data'] >= pd.to_datetime(min_date)]
if max_date is not None:
filtered_df = filtered_df[filtered_df['Data'] <= pd.to_datetime(max_date)]
# Filtrando por endereço e raio, se fornecidos
if address:
try:
result = gmaps.geocode(address)
if result:
coordinates = result[0]['geometry']['location']
center_lat = coordinates['lat']
center_lon = coordinates['lng']
if radius: # Se o raio for especificado
# Calculando distâncias e filtrando
filtered_df['Distance'] = filtered_df.apply(
lambda row: haversine(center_lat, center_lon, row['Latitude'], row['Longitude']),
axis=1
)
filtered_df = filtered_df[filtered_df['Distance'] <= radius]
else: # Se nenhum raio for especificado
# Filtrar pelo nome da rua ou bairro
address_components = result[0]['address_components']
street = None
neighborhood = None
for component in address_components:
if 'route' in component['types']: # Rua
street = component['long_name']
if 'sublocality' in component['types'] or 'locality' in component['types']: # Bairro
neighborhood = component['long_name']
if street:
filtered_df = filtered_df[filtered_df['Localização'].str.contains(street, na=False)]
elif neighborhood:
filtered_df = filtered_df[filtered_df['Bairro'].str.contains(neighborhood, na=False)]
except Exception as e:
print(f"Erro ao filtrar pelo endereço: {e}")
# Criando o mapa
fig = px.scatter_mapbox(
filtered_df,
lat="Latitude",
lon="Longitude",
hover_name="Localização",
hover_data={
"Valor": True,
"Vunit_priv": True,
"Tipo": True,
"url": True,
},
color="Tipo",
size="Valor",
size_max=30,
zoom=10
)
fig.update_layout(
mapbox_style="open-street-map",
autosize=True,
height=600,
margin={"r": 0, "t": 0, "l": 0, "b": 0},
)
# Adicionando círculo ao mapa se endereço e raio forem fornecidos
if address and radius:
try:
raio_terra = 6371000
pontos_lat = []
pontos_lon = []
for angulo in range(360):
ang_rad = radians(angulo)
d_lat = (radius / raio_terra) * cos(ang_rad)
d_lon = (radius / (raio_terra * cos(radians(center_lat)))) * sin(ang_rad)
pontos_lat.append(center_lat + d_lat * (180 / 3.141592653589793))
pontos_lon.append(center_lon + d_lon * (180 / 3.141592653589793))
fig.add_trace(go.Scattermapbox(
lat=pontos_lat,
lon=pontos_lon,
mode='lines',
fill='toself',
fillcolor='rgba(0, 0, 255, 0.2)',
line=dict(color='blue', width=2),
name='Raio'
))
fig.add_trace(go.Scattermapbox(
lat=[center_lat],
lon=[center_lon],
mode='markers',
marker=dict(size=10, color='red'),
name='Centro'
))
except Exception as e:
print(f"Erro ao adicionar o círculo no mapa: {e}")
# Gerando estatísticas
if filtered_df.empty:
stats_text = "Nenhum dado disponível."
else:
stats_text = (f"Quantidade de Dados: {len(filtered_df):n}\n"
f"Média do Valor: {locale.format_string('%.2f', filtered_df['Valor'].mean(), grouping=True)}\n"
f"Máximo do Valor: {locale.format_string('%.2f', filtered_df['Valor'].max(), grouping=True)}\n"
f"Mínimo do Valor: {locale.format_string('%.2f', filtered_df['Valor'].min(), grouping=True)}")
# Salvando DataFrame filtrado em arquivo CSV
file_path = "dados_filtrados.xlsx"
filtered_df.to_excel(file_path)
return fig, stats_text, filtered_df, file_path
# List of unique property types, bairros, and fonte, with "Todos" as default
property_types = ["Todos"] + sorted(df['Tipo'].dropna().unique().tolist())
bairro_list = ["Todos"] + sorted(df['Bairro'].dropna().unique().tolist())
fonte_list = ["Todos"] + sorted(df['Fonte'].dropna().unique().tolist())
# Encontrar a menor e a maior data no DataFrame
min_data = df['Data'].min()
max_data = df['Data'].max()
def dados_tab():
with gr.Tab("Pesquisar Dados"):
toggle_filters = gr.Checkbox(label="Exibir Campos de Pesquisa", value=True)
with gr.Row(visible=True) as filters:
tipo_filter = gr.Dropdown(label="Selecione os Tipos de Imóvel", choices=property_types, value=["Todos"], multiselect=True)
bairro_filter = gr.Dropdown(label="Selecione os Bairros", choices=bairro_list, value=["Todos"], multiselect=True)
fonte_filter = gr.Dropdown(label="Selecione a Fonte", choices=fonte_list, value=["Todos"], multiselect=True)
with gr.Row(visible=True) as area_filters:
min_atotal = gr.Number(label="Área Total Mínima", value=None)
max_atotal = gr.Number(label="Área Total Máxima", value=None)
min_apriv = gr.Number(label="Área Privativa Mínima", value=None)
max_apriv = gr.Number(label="Área Privativa Máxima", value=None)
min_ater = gr.Number(label="Área Terreno Mínima", value=None)
max_ater = gr.Number(label="Área Terreno Máxima", value=None)
with gr.Row(visible=True) as date_filters:
min_date = gr.Textbox(label="Data Inicial (AAAA-MM-DD)", value=min_data.strftime('%Y-%m-%d'))
max_date = gr.Textbox(label="Data Final (AAAA-MM-DD)", value=max_data.strftime('%Y-%m-%d'))
with gr.Row(visible=True) as address_filters:
address_input = gr.Textbox(label="Endereço (Opcional)")
radius_input = gr.Number(label="Raio em metros (Opcional)")
with gr.Row():
search_button = gr.Button("Pesquisar")
clear_button = gr.Button("Limpar")
map_output = gr.Plot()
stats_output = gr.Textbox(lines=4, label="Estatísticas")
filtered_df_output = gr.DataFrame(label="Dados Filtrados")
download_output = gr.File(label="Baixar Dados Filtrados")
def clear_action():
return (["Todos"], ["Todos"], ["Todos"], None, None, None, None, None, None, min_data.strftime('%Y-%m-%d'), max_data.strftime('%Y-%m-%d'), "", None, None, "", pd.DataFrame(), None)
toggle_filters.change(
lambda show: (
gr.update(visible=show),
gr.update(visible=show),
gr.update(visible=show),
gr.update(visible=show)
),
inputs=[toggle_filters],
outputs=[filters, area_filters, address_filters, date_filters]
)
search_button.click(
unified_action,
inputs=[tipo_filter, bairro_filter, fonte_filter, min_atotal, max_atotal, min_apriv, max_apriv,
min_ater, max_ater, min_date, max_date, address_input, radius_input],
outputs=[map_output, stats_output, filtered_df_output, download_output]
)
clear_button.click(
clear_action,
outputs=[tipo_filter, bairro_filter, fonte_filter, min_atotal, max_atotal, min_apriv, max_apriv,
min_ater, max_ater, min_date, max_date, address_input, radius_input,
map_output, stats_output, filtered_df_output, download_output]
)
return locals(), filtered_df_output
### implementar o KNN
### Arrumar a questão de que somente clicando no botão "Limpar" é possível acionar os filtros
### Filtro por data
### Zoon oelo filtro de raio
### Gráfico de linhas