ESJL commited on
Commit
db4d14c
·
verified ·
1 Parent(s): a5468e8

Create terrenos_2026.py

Browse files
Files changed (1) hide show
  1. terrenos_2026.py +196 -0
terrenos_2026.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glob
2
+ import os
3
+
4
+ import geopandas as gpd
5
+ import gradio as gr
6
+ import numpy as np
7
+ import pandas as pd
8
+ import xgboost as xgb
9
+ from pyproj import Transformer
10
+ from shapely import wkb
11
+ from shapely.geometry import Point
12
+
13
+ # Configuracao de recursos
14
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
15
+ PARQUET_PATH = os.path.join(BASE_DIR, "base_referencia_final.parquet")
16
+
17
+ # Modelo final do notebook TERRITORIAL_2026_SHAP_VTOTAL_Final_Ouro.ipynb
18
+ MODEL_PATTERN = os.path.join(BASE_DIR, "TERRITORIAL_2026_SHAP_VTOTAL_20260407*.json")
19
+ model_candidates = sorted(glob.glob(MODEL_PATTERN))
20
+ if not model_candidates:
21
+ raise FileNotFoundError(
22
+ "Nao foi encontrado modelo com padrao "
23
+ "'TERRITORIAL_2026_SHAP_VTOTAL_20260407*.json'."
24
+ )
25
+ MODEL_PATH = model_candidates[-1]
26
+
27
+ # Features exigidas pelo modelo final salvo no JSON
28
+ FEATURE_NAMES = [
29
+ "FONTE",
30
+ "AREA",
31
+ "TESTADA",
32
+ "IAPOND",
33
+ "taxa_ocupacao",
34
+ "avg_bldg_footprint",
35
+ "dist_to_main_road",
36
+ "dist_to_park",
37
+ "RH_x_Ano_Dado",
38
+ ]
39
+
40
+ # Variaveis espaciais derivadas por proximidade (nao informadas pelo usuario)
41
+ CONTEXT_FEATURES = [
42
+ "taxa_ocupacao",
43
+ "avg_bldg_footprint",
44
+ "dist_to_main_road",
45
+ "dist_to_park",
46
+ ]
47
+
48
+ print("Carregando modelo e base de referencia...")
49
+ model = xgb.Booster()
50
+ model.load_model(MODEL_PATH)
51
+
52
+ model_features = model.feature_names
53
+ if model_features is not None and model_features != FEATURE_NAMES:
54
+ raise ValueError(
55
+ "Features do JSON diferem das configuradas no app.\n"
56
+ f"Modelo: {model_features}\n"
57
+ f"App: {FEATURE_NAMES}"
58
+ )
59
+
60
+ try:
61
+ # Tentativa direta como GeoParquet
62
+ gdf_ref = gpd.read_parquet(PARQUET_PATH)
63
+ except Exception:
64
+ # Fallback para parquet comum com geometria em WKB
65
+ df_temp = pd.read_parquet(PARQUET_PATH)
66
+ if "geometry" not in df_temp.columns:
67
+ raise ValueError("A base de referencia nao possui coluna 'geometry'.")
68
+
69
+ df_temp["geometry"] = df_temp["geometry"].apply(
70
+ lambda x: wkb.loads(bytes(x)) if isinstance(x, (bytes, bytearray, memoryview)) else x
71
+ )
72
+ gdf_ref = gpd.GeoDataFrame(df_temp, geometry="geometry", crs="EPSG:31982")
73
+
74
+ if gdf_ref.crs is None:
75
+ gdf_ref = gdf_ref.set_crs("EPSG:31982", allow_override=True)
76
+
77
+ missing_context = [c for c in CONTEXT_FEATURES if c not in gdf_ref.columns]
78
+ if missing_context:
79
+ raise ValueError(
80
+ "A base de referencia nao possui as colunas espaciais exigidas: "
81
+ + ", ".join(missing_context)
82
+ )
83
+
84
+ df_ref = pd.DataFrame(gdf_ref[CONTEXT_FEATURES].copy()).astype(float)
85
+
86
+ # Converte coordenadas de entrada (WGS84) para o CRS da base de referencia
87
+ transformer = Transformer.from_crs("EPSG:4326", gdf_ref.crs, always_xy=True)
88
+
89
+
90
+ def predict_terreno(
91
+ area_val,
92
+ testada_val,
93
+ rh_val,
94
+ iapond_val,
95
+ lat_val,
96
+ lon_val,
97
+ ano_val,
98
+ fonte_str,
99
+ ):
100
+ try:
101
+ area_val = float(area_val)
102
+ testada_val = float(testada_val)
103
+ rh_val = float(rh_val)
104
+ iapond_val = float(iapond_val)
105
+ lat_val = float(lat_val)
106
+ lon_val = float(lon_val)
107
+ ano_int = int(ano_val)
108
+
109
+ if area_val <= 0:
110
+ raise ValueError("Area total deve ser maior que zero.")
111
+ if testada_val <= 0:
112
+ raise ValueError("Testada principal deve ser maior que zero.")
113
+ if rh_val < 0:
114
+ raise ValueError("RH nao pode ser negativo.")
115
+
116
+ # Engenharia de variaveis conforme treino no notebook
117
+ rh_x_ano = rh_val * ano_int
118
+
119
+ # Busca do vizinho mais proximo por distancia euclidiana (nos bastidores)
120
+ pt_x, pt_y = transformer.transform(lon_val, lat_val)
121
+ distancias = gdf_ref.geometry.distance(Point(pt_x, pt_y))
122
+ idx_vizinho = distancias.idxmin()
123
+ urb_vars = df_ref.loc[idx_vizinho]
124
+
125
+ features = {
126
+ "FONTE": 1.0 if str(fonte_str).strip().lower() == "oferta" else 0.0,
127
+ "AREA": float(np.log1p(area_val)),
128
+ "TESTADA": float(np.log1p(testada_val)),
129
+ "IAPOND": iapond_val,
130
+ "taxa_ocupacao": float(urb_vars["taxa_ocupacao"]),
131
+ "avg_bldg_footprint": float(urb_vars["avg_bldg_footprint"]),
132
+ "dist_to_main_road": float(np.log1p(max(urb_vars["dist_to_main_road"], 0.0))),
133
+ "dist_to_park": float(urb_vars["dist_to_park"]),
134
+ "RH_x_Ano_Dado": float(rh_x_ano),
135
+ }
136
+
137
+ X_input = pd.DataFrame([features], columns=FEATURE_NAMES)
138
+ dmatrix = xgb.DMatrix(X_input, feature_names=FEATURE_NAMES)
139
+
140
+ # Target do treino foi log1p(VTOTAL)
141
+ pred_log_vtotal = float(model.predict(dmatrix)[0])
142
+ vtotal = max(float(np.expm1(pred_log_vtotal)), 0.0)
143
+ vunit = vtotal / area_val
144
+
145
+ return f"R$ {vunit:,.2f} / m2", f"R$ {vtotal:,.2f}"
146
+
147
+ except Exception as e:
148
+ return f"Erro: {str(e)}", "Revise os valores informados."
149
+
150
+
151
+ with gr.Blocks(title="Territorial XGBoost 2026") as demo:
152
+ gr.Markdown("# Territorial XGBoost 2026")
153
+ gr.Markdown(
154
+ "Modelo XGBoost com Spatial K-Fold, otimizado com Optuna.
155
+ "Variaveis espaciais de contexto são calculadas automaticamente por proximidade do ponto informado (lat/lon)."
156
+ )
157
+
158
+ with gr.Row():
159
+ with gr.Column():
160
+ with gr.Group():
161
+ gr.Markdown("### Dimensões, Potencial Construtivo e Região Homogênea")
162
+ area = gr.Number(label="Area Total (m2)", value=300)
163
+ testada = gr.Number(label="Testada Principal (m)", value=10)
164
+ iapond = gr.Number(label="IA Ponderado", value=1.3)
165
+ rh = gr.Number(label="RH (Indice de Valorizacao)", value=50)
166
+
167
+ with gr.Column():
168
+ with gr.Group():
169
+ gr.Markdown("### Localizacao, Tempo e Fonte")
170
+ lat = gr.Number(label="Latitude", value=-30.0346)
171
+ lon = gr.Number(label="Longitude", value=-51.2177)
172
+ ano = gr.Dropdown(
173
+ choices=[str(y) for y in range(2016, 2027)],
174
+ label="Ano do Dado",
175
+ value="2026",
176
+ )
177
+ fonte = gr.Radio(
178
+ ["Venda", "Oferta"],
179
+ label="Tipo de Dado (Fonte)",
180
+ value="Venda",
181
+ )
182
+
183
+ gr.Markdown("---")
184
+ btn = gr.Button("CALCULAR VALOR ESTIMADO", variant="primary")
185
+ vunit_out = gr.Textbox(label="Valor Unitario Estimado (R$/m2)")
186
+ vtotal_out = gr.Textbox(label="Valor Total Estimado (R$)")
187
+
188
+ btn.click(
189
+ predict_terreno,
190
+ inputs=[area, testada, rh, iapond, lat, lon, ano, fonte],
191
+ outputs=[vunit_out, vtotal_out],
192
+ )
193
+
194
+
195
+ if __name__ == "__main__":
196
+ demo.launch()