from __future__ import annotations import json from html import escape from pathlib import Path from threading import Lock from typing import Any import folium from branca.element import Element _BASE_DIR = Path(__file__).resolve().parent _BAIRROS_SHP_PATH = _BASE_DIR / "dados" / "Bairros_LC12112_16.shp" _TOOLTIP_FIELDS = ("NOME", "BAIRRO", "NME_BAI", "NOME_BAIRRO") _SIMPLIFY_TOLERANCE = 0.00005 _BAIRROS_CACHE_LOCK = Lock() _BAIRROS_GEOJSON_CACHE: dict[str, Any] | None = None _BAIRROS_GEOJSON_CARREGADO = False def _carregar_bairros_geojson() -> dict[str, Any] | None: global _BAIRROS_GEOJSON_CACHE, _BAIRROS_GEOJSON_CARREGADO with _BAIRROS_CACHE_LOCK: if _BAIRROS_GEOJSON_CARREGADO: return _BAIRROS_GEOJSON_CACHE _BAIRROS_GEOJSON_CARREGADO = True if not _BAIRROS_SHP_PATH.exists(): return None try: import geopandas as gpd except Exception: return None try: gdf = gpd.read_file(_BAIRROS_SHP_PATH, engine="fiona") if gdf is None or gdf.empty: geojson = None else: if gdf.crs is not None: gdf = gdf.to_crs("EPSG:4326") campos = ["geometry"] for campo in _TOOLTIP_FIELDS: if campo in gdf.columns: campos.insert(0, campo) break gdf = gdf.loc[:, campos].copy() if _SIMPLIFY_TOLERANCE > 0: gdf["geometry"] = gdf.geometry.simplify(_SIMPLIFY_TOLERANCE, preserve_topology=True) geojson = json.loads(gdf.to_json(drop_id=True)) except Exception: geojson = None with _BAIRROS_CACHE_LOCK: _BAIRROS_GEOJSON_CACHE = geojson return geojson def add_bairros_layer( mapa: folium.Map, *, show: bool = True, layer_name: str = "Bairros", ) -> bool: geojson = _carregar_bairros_geojson() if not geojson: return False tooltip = None features = geojson.get("features") if isinstance(geojson, dict) else None if isinstance(features, list) and features: props = features[0].get("properties") if isinstance(features[0], dict) else None if isinstance(props, dict): for candidate in _TOOLTIP_FIELDS: if candidate in props: tooltip = folium.GeoJsonTooltip( fields=[candidate], aliases=["Bairro:"], localize=False, sticky=False, labels=True, ) break folium.GeoJson( data=geojson, name=layer_name, show=show, control=True, overlay=True, smooth_factor=0.6, style_function=lambda _: { "color": "#4c6882", "weight": 1.0, "fillColor": "#f39c12", "fillOpacity": 0.04, }, highlight_function=lambda _: { "color": "#e67e22", "weight": 1.6, "fillOpacity": 0.12, }, tooltip=tooltip, ).add_to(mapa) return True def add_indice_marker( camada: folium.map.FeatureGroup, *, lat: float, lon: float, indice: Any, ) -> None: texto = escape(str(indice)) html = ( '
{texto}
' ) folium.Marker( location=[float(lat), float(lon)], icon=folium.DivIcon( html=html, icon_size=(72, 24), icon_anchor=(0, 0), class_name="mesa-indice-label", ), ).add_to(camada) def add_zoom_responsive_circle_markers( mapa: folium.Map, *, min_radius: float = 1.6, max_radius: float = 52.0, reference_zoom: float = 12.0, growth_factor: float = 0.20, ) -> None: if getattr(mapa, "_mesa_zoom_radius_script", False): return map_name = mapa.get_name() script = f""" """ mapa.get_root().html.add_child(Element(script)) setattr(mapa, "_mesa_zoom_radius_script", True) def add_popup_pagination_handlers(mapa: folium.Map) -> None: if getattr(mapa, "_mesa_popup_pager_script", False): return map_name = mapa.get_name() script = f""" """ mapa.get_root().html.add_child(Element(script)) setattr(mapa, "_mesa_popup_pager_script", True)