Spaces:
Running
Running
Guilherme Silberfarb Costa commited on
Commit ·
4678b81
1
Parent(s): 7031c42
Speed up logradouro catalog loading
Browse files
backend/app/core/shapefile_runtime.py
CHANGED
|
@@ -30,6 +30,14 @@ def _build_transformer(source_crs: Any, target_crs: str | None):
|
|
| 30 |
return None
|
| 31 |
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
def _iter_features_pyshp(shapefile_path: Path, *, target_crs: str | None) -> list[tuple[dict[str, Any], Any]]:
|
| 34 |
import shapefile
|
| 35 |
from shapely.geometry import shape
|
|
@@ -73,7 +81,7 @@ def _iter_features_pyshp(shapefile_path: Path, *, target_crs: str | None) -> lis
|
|
| 73 |
encoding="utf-8",
|
| 74 |
encodingErrors="replace",
|
| 75 |
)
|
| 76 |
-
field_names = [
|
| 77 |
source_crs = None
|
| 78 |
prj_path = shapefile_path.with_suffix(".prj")
|
| 79 |
if prj_path.exists():
|
|
@@ -214,3 +222,42 @@ def load_vector_geojson(
|
|
| 214 |
)
|
| 215 |
|
| 216 |
return feature_collection
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
return None
|
| 31 |
|
| 32 |
|
| 33 |
+
def _field_name(field: Any) -> str:
|
| 34 |
+
if hasattr(field, "name"):
|
| 35 |
+
return str(getattr(field, "name") or "")
|
| 36 |
+
if isinstance(field, (list, tuple)) and field:
|
| 37 |
+
return str(field[0] or "")
|
| 38 |
+
return str(field or "")
|
| 39 |
+
|
| 40 |
+
|
| 41 |
def _iter_features_pyshp(shapefile_path: Path, *, target_crs: str | None) -> list[tuple[dict[str, Any], Any]]:
|
| 42 |
import shapefile
|
| 43 |
from shapely.geometry import shape
|
|
|
|
| 81 |
encoding="utf-8",
|
| 82 |
encodingErrors="replace",
|
| 83 |
)
|
| 84 |
+
field_names = [_field_name(field) for field in reader.fields[1:]]
|
| 85 |
source_crs = None
|
| 86 |
prj_path = shapefile_path.with_suffix(".prj")
|
| 87 |
if prj_path.exists():
|
|
|
|
| 222 |
)
|
| 223 |
|
| 224 |
return feature_collection
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def load_attribute_records(
|
| 228 |
+
shapefile_path: str | Path,
|
| 229 |
+
*,
|
| 230 |
+
property_fields: tuple[str, ...] | list[str] | None = None,
|
| 231 |
+
) -> list[dict[str, Any]]:
|
| 232 |
+
path = Path(shapefile_path).expanduser().resolve()
|
| 233 |
+
dbf_path = path.with_suffix(".dbf")
|
| 234 |
+
if not dbf_path.exists():
|
| 235 |
+
raise FileNotFoundError(f"DBF nao encontrado: {dbf_path}")
|
| 236 |
+
|
| 237 |
+
wanted_fields = tuple(str(field).strip() for field in (property_fields or ()) if str(field).strip())
|
| 238 |
+
|
| 239 |
+
try:
|
| 240 |
+
import shapefile
|
| 241 |
+
except Exception:
|
| 242 |
+
records: list[dict[str, Any]] = []
|
| 243 |
+
for properties, _ in _load_features(path, target_crs=None):
|
| 244 |
+
if wanted_fields:
|
| 245 |
+
records.append({field: properties.get(field) for field in wanted_fields if field in properties})
|
| 246 |
+
else:
|
| 247 |
+
records.append(dict(properties))
|
| 248 |
+
return records
|
| 249 |
+
|
| 250 |
+
with open(dbf_path, "rb") as dbf_handle:
|
| 251 |
+
reader = shapefile.Reader(dbf=dbf_handle, encoding="utf-8", encodingErrors="replace")
|
| 252 |
+
field_names = [_field_name(field) for field in reader.fields[1:]]
|
| 253 |
+
records: list[dict[str, Any]] = []
|
| 254 |
+
for record in reader.iterRecords():
|
| 255 |
+
values = list(record)
|
| 256 |
+
row = {
|
| 257 |
+
field_name: values[index] if index < len(values) else None
|
| 258 |
+
for index, field_name in enumerate(field_names)
|
| 259 |
+
}
|
| 260 |
+
if wanted_fields:
|
| 261 |
+
row = {field: row.get(field) for field in wanted_fields if field in row}
|
| 262 |
+
records.append(row)
|
| 263 |
+
return records
|
backend/app/services/pesquisa_service.py
CHANGED
|
@@ -20,6 +20,7 @@ from fastapi import HTTPException
|
|
| 20 |
from joblib import load
|
| 21 |
|
| 22 |
from app.core.elaboracao import geocodificacao
|
|
|
|
| 23 |
from app.core.elaboracao.core import _migrar_pacote_v1_para_v2, normalizar_observacao_modelo
|
| 24 |
from app.core.map_layers import (
|
| 25 |
add_bairros_layer,
|
|
@@ -27,6 +28,7 @@ from app.core.map_layers import (
|
|
| 27 |
add_zoom_responsive_circle_markers,
|
| 28 |
)
|
| 29 |
from app.portable_runtime_log import append_runtime_log
|
|
|
|
| 30 |
from app.services import model_repository, trabalhos_tecnicos_service
|
| 31 |
from app.services.serializers import sanitize_value
|
| 32 |
|
|
@@ -2566,6 +2568,44 @@ def _carregar_catalogo_vias() -> list[dict[str, Any]]:
|
|
| 2566 |
if _CATALOGO_VIAS_CACHE is not None:
|
| 2567 |
return list(_CATALOGO_VIAS_CACHE)
|
| 2568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2569 |
gdf = geocodificacao.carregar_eixos()
|
| 2570 |
if gdf is None or gdf.empty:
|
| 2571 |
append_runtime_log("[mesa] pesquisa: base de eixos indisponivel para catalogo de logradouros")
|
|
|
|
| 20 |
from joblib import load
|
| 21 |
|
| 22 |
from app.core.elaboracao import geocodificacao
|
| 23 |
+
from app.core.shapefile_runtime import load_attribute_records
|
| 24 |
from app.core.elaboracao.core import _migrar_pacote_v1_para_v2, normalizar_observacao_modelo
|
| 25 |
from app.core.map_layers import (
|
| 26 |
add_bairros_layer,
|
|
|
|
| 28 |
add_zoom_responsive_circle_markers,
|
| 29 |
)
|
| 30 |
from app.portable_runtime_log import append_runtime_log
|
| 31 |
+
from app.runtime_config import resolve_core_path
|
| 32 |
from app.services import model_repository, trabalhos_tecnicos_service
|
| 33 |
from app.services.serializers import sanitize_value
|
| 34 |
|
|
|
|
| 2568 |
if _CATALOGO_VIAS_CACHE is not None:
|
| 2569 |
return list(_CATALOGO_VIAS_CACHE)
|
| 2570 |
|
| 2571 |
+
registros: list[dict[str, Any]] = []
|
| 2572 |
+
try:
|
| 2573 |
+
registros = load_attribute_records(
|
| 2574 |
+
resolve_core_path("dados", "EixosLogradouros.shp"),
|
| 2575 |
+
property_fields=("CDLOG", "NMIDELOG", "NMIDEPRE", "NMIDEABR"),
|
| 2576 |
+
)
|
| 2577 |
+
append_runtime_log(f"[mesa] pesquisa: catalogo leve de logradouros carregado com {len(registros)} registros")
|
| 2578 |
+
except Exception as exc:
|
| 2579 |
+
append_runtime_log(f"[mesa] pesquisa: falha no catalogo leve de logradouros: {exc}")
|
| 2580 |
+
|
| 2581 |
+
def _catalogo_from_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
| 2582 |
+
vistos: set[int] = set()
|
| 2583 |
+
catalogo_local: list[dict[str, Any]] = []
|
| 2584 |
+
for row in rows:
|
| 2585 |
+
cdlog = _to_int_or_none(row.get("CDLOG"))
|
| 2586 |
+
if cdlog is None or cdlog in vistos:
|
| 2587 |
+
continue
|
| 2588 |
+
vistos.add(cdlog)
|
| 2589 |
+
nome = str(row.get("NMIDELOG") or "").strip()
|
| 2590 |
+
prefixo = str(row.get("NMIDEPRE") or "").strip()
|
| 2591 |
+
abreviado = str(row.get("NMIDEABR") or "").strip()
|
| 2592 |
+
logradouro = " ".join(item for item in [prefixo, nome] if item).strip() or nome or abreviado
|
| 2593 |
+
aliases = _dedupe_strings([logradouro, nome, abreviado])
|
| 2594 |
+
catalogo_local.append(
|
| 2595 |
+
{
|
| 2596 |
+
"cdlog": cdlog,
|
| 2597 |
+
"logradouro": logradouro,
|
| 2598 |
+
"_aliases_norm": [_normalize(item) for item in aliases if _normalize(item)],
|
| 2599 |
+
}
|
| 2600 |
+
)
|
| 2601 |
+
return catalogo_local
|
| 2602 |
+
|
| 2603 |
+
if registros:
|
| 2604 |
+
catalogo = _catalogo_from_rows(registros)
|
| 2605 |
+
_CATALOGO_VIAS_CACHE = list(catalogo)
|
| 2606 |
+
append_runtime_log(f"[mesa] pesquisa: catalogo de logradouros carregado com {len(catalogo)} vias")
|
| 2607 |
+
return list(catalogo)
|
| 2608 |
+
|
| 2609 |
gdf = geocodificacao.carregar_eixos()
|
| 2610 |
if gdf is None or gdf.empty:
|
| 2611 |
append_runtime_log("[mesa] pesquisa: base de eixos indisponivel para catalogo de logradouros")
|