File size: 4,801 Bytes
e7e46ae
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import numpy as np, pandas as pd, geopandas as gpd, rasterio
from rasterio.features import geometry_mask
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
import xgboost as xgb
import matplotlib; matplotlib.use('Agg'); import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, Normalize
D='dados'; OUT='figuras_saida'
BANDS=['DEM','Declividade','Orientacao','Tmax','Tmin','Ppt','UmidSolo','VelVento','UmidRel','NDVI','IAF','CoberturaTerra','DistEstradas','DistResidencias']
CMAP=LinearSegmentedColormap.from_list('risco',['#1a9850','#fee08b','#d73027'])

# ---- treina modelos ----
d=pd.read_csv(f'{D}/amostras_features.csv')
cont=[b for b in BANDS if b!='CoberturaTerra']
lc=pd.get_dummies(d['CoberturaTerra'].astype(int), prefix='LC')
Xtr=pd.concat([d[cont], lc], axis=1); ytr=d['classe'].astype(int).values
cols=list(Xtr.columns)
modelos={'Random Forest':RandomForestClassifier(300,random_state=42,n_jobs=-1),
         'Gradient Boosting':GradientBoostingClassifier(random_state=42),
         'XGBoost':xgb.XGBClassifier(n_estimators=300,max_depth=4,learning_rate=0.1,subsample=0.9,eval_metric='logloss',random_state=42)}
for m in modelos.values(): m.fit(Xtr,ytr)

# ---- stack raster ----
s=rasterio.open(f'{D}/stack_andaluzia.tif'); arr=s.read().astype('float64')  # (14,H,W)
H,W=s.shape
gA=gpd.read_file(f'{D}/andaluzia.geojson').to_crs(4326)
mask_inside=geometry_mask([geom for geom in gA.geometry], out_shape=(H,W), transform=s.transform, invert=True)
flat={BANDS[i]:arr[i].ravel() for i in range(14)}
df=pd.DataFrame(flat)
# one-hot da cobertura alinhado às colunas de treino
import numpy as _np
lcr=pd.get_dummies(pd.Series(_np.nan_to_num(df['CoberturaTerra'].values,nan=0).round().astype(int)), prefix='LC')
Xpix=pd.concat([df[cont], lcr], axis=1).reindex(columns=cols, fill_value=0).fillna(0)
finite=np.isfinite(arr).all(axis=0).ravel()
valid=mask_inside.ravel() & finite
risk_maps={}
from alocacao_core import suavizar_risco
SUAVIZA=5   # filtro de mediana 3x3 para remover pixels isolados ('sal e pimenta')
for nome,m in modelos.items():
    p=np.full(H*W, np.nan)
    p[valid]=m.predict_proba(Xpix.values[valid])[:,1]
    risk_maps[nome]=suavizar_risco(p.reshape(H,W), size=SUAVIZA)
    # salva GeoTIFF (suavizado)
    prof=s.profile; prof.update(count=1,dtype='float32',nodata=np.nan)
    with rasterio.open(f'{D}/risco_{nome.replace(" ","_")}.tif','w',**prof) as dst:
        dst.write(risk_maps[nome].astype('float32'),1)

# ---- figura comparativa (CNN + 3 modelos) ----
cnn=rasterio.open(f'{D}/mapa_risco_andaluzia.tif'); cnn_d=cnn.read(1).astype('float64'); cnn_d=np.where(cnn_d>0,cnn_d,np.nan)
cnn_d=suavizar_risco(cnn_d, size=SUAVIZA)
def ext(src): 
    ymin=min(src.bounds.top,src.bounds.bottom); ymax=max(src.bounds.top,src.bounds.bottom)
    return [src.bounds.left,src.bounds.right,ymin,ymax]
fig,ax=plt.subplots(2,2,figsize=(11,9.5)); ax=ax.ravel()
# CNN (south-up -> origin lower)
ax[0].imshow(cnn_d,extent=ext(cnn),origin='lower',cmap=CMAP,vmin=0,vmax=1)
ax[0].set_title('(a) CNN (deep learning)')
for i,(nome,rm) in enumerate(risk_maps.items(),start=1):
    im=ax[i].imshow(rm,extent=ext(s),origin='upper',cmap=CMAP,vmin=0,vmax=1)
    ax[i].set_title(f'({chr(97+i)}) {nome}')
# focos de incêndio reais de 2008 (classe = fogo)
focos=pd.read_csv(f'{D}/focos_incendio_2008.csv')
import os as _os
prov=gpd.read_file(f'{D}/provincias_andaluzia.geojson') if _os.path.exists(f'{D}/provincias_andaluzia.geojson') else None
for a in ax:
    a.scatter(focos['lon'],focos['lat'],s=4,c='black',alpha=0.45,linewidths=0,zorder=5)
    if prov is not None:
        prov.boundary.plot(ax=a,color='black',linewidth=0.8,linestyle='--',alpha=0.6)
    gA.boundary.plot(ax=a,color='black',linewidth=0.6); a.set_xlabel('Longitude (°)'); a.set_ylabel('Latitude (°)')
from matplotlib.lines import Line2D
ax[0].legend(handles=[Line2D([0],[0],marker='o',color='w',markerfacecolor='black',markersize=4,label='2008 fire occurrences')],
             loc='lower left',fontsize=7,framealpha=0.85)
cb=fig.colorbar(plt.cm.ScalarMappable(norm=Normalize(0,1),cmap=CMAP),ax=ax,fraction=0.025,pad=0.02); cb.set_label('Fire risk')
fig.suptitle('Risk maps by model (with 2008 fire occurrences)',fontsize=14)
fig.savefig(f'{OUT}/fig_mapas_modelos.png',dpi=190,bbox_inches='tight'); print('fig_mapas_modelos.png salvo')

# concordância espacial entre mapas (correlação dos pixels válidos)
import itertools
v=valid & np.isfinite(cnn_d.ravel()[:len(valid)]) if cnn_d.size==len(valid) else valid
print('correlação espacial (pixels válidos) entre modelos:')
keys=list(risk_maps); 
for a,b in itertools.combinations(keys,2):
    x=risk_maps[a].ravel()[valid]; y=risk_maps[b].ravel()[valid]
    print(f'  {a} x {b}: r={np.corrcoef(x,y)[0,1]:.3f}')