mesa-react / backend /app /core /map_jitter.py
Guilherme Silberfarb Costa
alteracoes nos mapas
440f380
Raw
History Blame Contribute Delete
2.75 kB
from __future__ import annotations
import math
import numpy as np
import pandas as pd
JITTER_MERCADO_PASSO_METROS = 5.0
JITTER_MERCADO_MAX_RAIO_METROS = 28.0
def aplicar_jitter_dados_mercado(
df_mapa: pd.DataFrame,
lat_col: str,
lon_col: str,
lat_plot_col: str = "__mesa_lat_plot__",
lon_plot_col: str = "__mesa_lon_plot__",
*,
precisao: int = 7,
passo_metros: float = JITTER_MERCADO_PASSO_METROS,
max_raio_metros: float = JITTER_MERCADO_MAX_RAIO_METROS,
) -> pd.DataFrame:
"""
Aplica jitter visual determinístico apenas em pontos de mercado sobrepostos.
As coordenadas originais permanecem nas colunas de entrada; as coordenadas
deslocadas são gravadas em lat_plot_col/lon_plot_col.
"""
df_plot = df_mapa.copy()
df_plot[lat_plot_col] = pd.to_numeric(df_plot[lat_col], errors="coerce").astype(float)
df_plot[lon_plot_col] = pd.to_numeric(df_plot[lon_col], errors="coerce").astype(float)
if len(df_plot) <= 1:
return df_plot
chave_lat = df_plot[lat_col].round(precisao)
chave_lon = df_plot[lon_col].round(precisao)
grupos = df_plot.groupby([chave_lat, chave_lon], sort=False)
metros_por_grau_lat = 111_320.0
lat_plot_pos = int(df_plot.columns.get_loc(lat_plot_col))
lon_plot_pos = int(df_plot.columns.get_loc(lon_plot_col))
for _, idx_labels in grupos.indices.items():
posicoes = np.asarray(idx_labels, dtype=int)
if posicoes.size <= 1:
continue
base_lat = float(df_plot.iat[int(posicoes[0]), lat_plot_pos])
base_lon = float(df_plot.iat[int(posicoes[0]), lon_plot_pos])
if not np.isfinite(base_lat) or not np.isfinite(base_lon):
continue
seed_val = int((abs(base_lat) * 1_000_000.0) + (abs(base_lon) * 1_000_000.0) * 3.0) % 360
angulo_base = math.radians(seed_val)
cos_lat = max(abs(math.cos(math.radians(base_lat))), 1e-6)
metros_por_grau_lon = metros_por_grau_lat * cos_lat
for pos, pos_idx in enumerate(posicoes):
if pos == 0:
continue
pos_ring = pos - 1
ring = 1
while pos_ring >= (6 * ring):
pos_ring -= 6 * ring
ring += 1
slots_ring = max(6 * ring, 1)
angulo = angulo_base + (2.0 * math.pi * (pos_ring / slots_ring))
raio_m = min(ring * passo_metros, max_raio_metros)
delta_lat = (raio_m * math.sin(angulo)) / metros_por_grau_lat
delta_lon = (raio_m * math.cos(angulo)) / metros_por_grau_lon
df_plot.iat[int(pos_idx), lat_plot_pos] = base_lat + delta_lat
df_plot.iat[int(pos_idx), lon_plot_pos] = base_lon + delta_lon
return df_plot