Skip to main content Skills Marktplatz Entdecken und erkunden Sie KI-Skills, die von der Community erstellt wurden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Prompt kopierenPrompt-Details anzeigen Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
npx skills add https://github.com/xjtulyc/awesome-rosetta-skills --skill urban-remote-sensingDer Befehl bleibt in einer Zeile. Scrollen Sie horizontal, um ihn vor dem Kopieren vollständig zu prüfen.
Sie bevorzugen eine lokale Kopie? Laden Sie die Dateien herunter, die SkillsMP derzeit vorliegen.
ZIP herunterladen Herunterladen... Mehr aus diesem Repository Astronomical data analysis with astropy and astroquery — FITS I/O, WCS transforms, catalog cross-matching, aperture photometry, and CMB power spectra.
Seismological data analysis with ObsPy — FDSN waveform download, response removal, phase picking, moment tensor inversion, and seismicity mapping.
Download and analyze oceanographic data from Copernicus Marine Service and Argo floats using copernicusmarine, gsw, and xarray.
Verwandte Berufe SOC
Basierend auf der SOC-Berufsklassifikation
name urban-remote-sensing description Use this Skill for urban remote sensing: impervious surface mapping, NDVI urban heat island, building footprint extraction, and land use change detection.
tags ["urban-science","remote-sensing","satellite","urban-heat-island","land-use"] version 1.0.0 authors [{"name":"Rosetta Skills Contributors","github":"@xjtulyc"}] license MIT platforms ["claude-code","codex","gemini-cli","cursor"] dependencies {"python":["numpy>=1.24","scipy>=1.11","scikit-learn>=1.3","matplotlib>=3.7","rasterio>=1.3","geopandas>=0.14"]} last_updated 2026-03-17 status stable
Urban Remote Sensing Analysis
One-line summary : Analyze urban land cover from satellite imagery: impervious surface mapping, NDVI-based urban heat island, LULC classification, and spectral change detection using scikit-learn and rasterio.
When to Use This Skill
When mapping impervious surfaces and urban expansion from multispectral imagery
When analyzing urban heat island effects using LST and NDVI correlation
When classifying urban land use/land cover with random forest or SVM
When detecting urban change between two time periods
When computing urban spatial metrics (patch density, fragmentation, edge density)
When analyzing building shadows and 3D urban form from satellite data
Trigger keywords : urban remote sensing, impervious surface, urban heat island, NDVI urban, LULC classification, land use change, satellite urban, building extraction, urban sprawl, rasterio, spectral unmixing, change detection, Landsat urban
Background & Key Concepts
Urban Spectral Signatures
Surface type Red NIR SWIR NDVI NDBI Dense vegetation Low High Low 0.6–0.9 −0.5 to −0.3 Urban/impervious Medium Low High −0.1 to 0.1 0.1–0.4 Water Low Very low Low −0.3 to 0 −0.5 to −0.2 Bare soil Medium Medium High 0.0–0.2 −0.1 to 0.1
Urban Heat Island (UHI)
Surface UHI = LST(urban) − LST(suburban/rural). Correlated with:
Low NDVI (less vegetation cooling)
High NDBI (more impervious surface)
Building density and anthropogenic heat
LST estimated from Landsat Band 10 (TIRS):
$$
T_s = \frac{B_{10} / \varepsilon^{1/4}}{1 + (\lambda \cdot B_{10} / \rho) \cdot \ln(\varepsilon)}
$$
Environment Setup
Install Dependencies pip install numpy>=1.24 scipy>=1.11 scikit-learn>=1.3 matplotlib>=3.7 \
rasterio>=1.3 geopandas>=0.14
pip install earthengine-api>=0.1.380 geemap>=0.29
Verify Installation import numpy as np
import rasterio
from rasterio.transform import from_bounds
print (f"rasterio: {rasterio.__version__} " )
from rasterio.io import MemoryFile
import numpy as np
data = np.random.randint(0 , 4000 , (4 , 100 , 100 ), dtype=np.uint16)
print (f"Test raster: shape={data.shape} " )
Core Workflow
Step 1: Urban Spectral Index Computation import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
np.random.seed(42 )
SIZE = 200
y_grid, x_grid = np.mgrid[0 :SIZE, 0 :SIZE]
cx, cy = SIZE/2 , SIZE/2
dist_from_center = np.sqrt((x_grid - cx)**2 + (y_grid - cy)**2 )
lc_map = np.zeros((SIZE, SIZE), dtype=int )
lc_map[dist_from_center < 30 ] = 0
lc_map[(dist_from_center >= 30 ) & (dist_from_center < 60 )] = 1
lc_map[(dist_from_center >= 60 ) & (dist_from_center < 80 )] = 2
lc_map[(dist_from_center >= 80 ) & (dist_from_center < 100 )] = 4
lc_map[dist_from_center >= 100 ] = 6
for px, py in [(50 , 80 ), (150 , 70 ), (80 , 140 )]:
mask = (np.sqrt((x_grid-px)**2 + (y_grid-py)**2 ) < 15 )
lc_map[mask & (dist_from_center < 100 )] = 3
lc_map[170 :190 , 10 :50 ] = 5
lc_map[100 :130 , 160 :190 ] = 5
signatures = {
0 : {'B4' : 1800 , 'B8' : 1500 , 'B11' : 2200 , 'B10' : 320 },
1 : {'B4' : 1500 , 'B8' : 2800 , 'B11' : 1800 , 'B10' : 295 },
2 : {'B4' : 2000 , 'B8' : 1200 , 'B11' : 2500 , 'B10' : 310 },
3 : {'B4' : 800 , 'B8' : 5500 , 'B11' : 900 , 'B10' : 275 },
4 : {'B4' : 1200 , 'B8' : 3500 , 'B11' : 1500 , 'B10' : 285 },
5 : {'B4' : 600 , 'B8' : 400 , 'B11' : 300 , 'B10' : 270 },
6 : {'B4' : 900 , 'B8' : 4500 , 'B11' : 1200 , 'B10' : 278 },
}
sig_names = ['Downtown' , 'Residential' , 'Industrial' , 'Urban park' , 'Suburban' , 'Water' , 'Agriculture' ]
def make_band (lc, sig_key, noise=150 ):
band = np.zeros_like(lc, dtype=float )
for cls, sig in signatures.items():
band[lc == cls] = sig[sig_key]
band += np.random.randn(*band.shape) * noise
return np.clip(band, 0 , 10000 )
B4 = make_band(lc_map, 'B4' )
B8 = make_band(lc_map, 'B8' )
B11 = make_band(lc_map, 'B11' )
LST_sim = make_band(lc_map, 'B10' )
NDVI = (B8 - B4) / (B8 + B4 + 1e-6 )
NDBI = (B11 - B8) / (B11 + B8 + 1e-6 )
NDWI = (B4 - B11) / (B4 + B11 + 1e-6 )
impervious_mask = (NDBI > 0.05 ) & (NDVI < 0.2 )
print (f"Impervious surface fraction: {impervious_mask.mean()*100 :.1 f} %" )
fig, axes = plt.subplots(2 , 3 , figsize=(16 , 10 ))
lc_colors = ['#8B0000' ,'#FFA500' ,'#808080' ,'#228B22' ,'#90EE90' ,'#0000FF' ,'#90EE90' ]
cmap_lc = ListedColormap(lc_colors)
im0 = axes[0 ][0 ].imshow(lc_map, cmap=cmap_lc, vmin=0 , vmax=6 )
axes[0 ][0 ].set_title("Land Cover Map" )
plt.colorbar(im0, ax=axes[0 ][0 ], ticks=range (7 ))
axes[0 ][0 ].set_xticks([]); axes[0 ][0 ].set_yticks([])
im1 = axes[0 ][1 ].imshow(NDVI, cmap='RdYlGn' , vmin=-0.5 , vmax=0.9 )
plt.colorbar(im1, ax=axes[0 ][1 ], label='NDVI' )
axes[0 ][1 ].set_title("NDVI (Vegetation)" ); axes[0 ][1 ].set_xticks([]); axes[0 ][1 ].set_yticks([])
im2 = axes[0 ][2 ].imshow(NDBI, cmap='RdBu_r' , vmin=-0.5 , vmax=0.5 )
plt.colorbar(im2, ax=axes[0 ][2 ], label='NDBI' )
axes[0 ][2 ].set_title("NDBI (Built-up Index)" ); axes[0 ][2 ].set_xticks([]); axes[0 ][2 ].set_yticks([])
im3 = axes[1 ][0 ].imshow(LST_sim, cmap='hot' , vmin=265 , vmax=335 )
plt.colorbar(im3, ax=axes[1 ][0 ], label='LST (K)' )
axes[1 ][0 ].set_title("Land Surface Temperature" ); axes[1 ][0 ].set_xticks([]); axes[1 ][0 ].set_yticks([])
im4 = axes[1 ][1 ].imshow(impervious_mask.astype(float ), cmap='Reds' , vmin=0 , vmax=1 )
plt.colorbar(im4, ax=axes[1 ][1 ], label='Impervious (1=yes)' )
axes[1 ][1 ].set_title(f"Impervious Surface ({impervious_mask.mean()*100 :.1 f} %)" )
axes[1 ][1 ].set_xticks([]); axes[1 ][1 ].set_yticks([])
ndvi_flat = NDVI.flatten()
lst_flat = LST_sim.flatten()
im5 = axes[1 ][2 ].scatter(ndvi_flat[::20 ], lst_flat[::20 ], c=lc_map.flatten()[::20 ],
cmap=cmap_lc, vmin=0 , vmax=6 , s=5 , alpha=0.5 )
from scipy.stats import pearsonr
r, p = pearsonr(ndvi_flat, lst_flat)
axes[1 ][2 ].set_xlabel("NDVI" ); axes[1 ][2 ].set_ylabel("LST (K)" )
axes[1 ][2 ].set_title(f"UHI: NDVI vs. LST\n(r={r:.3 f} , p={p:.2 e} )" )
axes[1 ][2 ].grid(True , alpha=0.3 )
plt.suptitle("Urban Remote Sensing Analysis — Spectral Indices and UHI" )
plt.tight_layout()
plt.savefig("urban_spectral_analysis.png" , dpi=150 )
plt.show()
Step 2: Urban LULC Classification import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, accuracy_score
features = np.column_stack([
B4.flatten(), B8.flatten(), B11.flatten(),
NDVI.flatten(), NDBI.flatten(), NDWI.flatten(),
LST_sim.flatten(),
])
labels = lc_map.flatten()
X_train, X_test, y_train, y_test = train_test_split(
features, labels, test_size=0.25 , random_state=42 , stratify=labels
)
rf = RandomForestClassifier(n_estimators=100 , random_state=42 , n_jobs=-1 )
rf.fit(X_train, y_train)
y_pred = rf.predict(X_test)
y_pred_prob = rf.predict_proba(X_test)
acc = accuracy_score(y_test, y_pred)
print (f"Overall accuracy: {acc*100 :.2 f} %" )
print (classification_report(y_test, y_pred, target_names=sig_names, zero_division=0 ))
fi = dict (zip (['B4' ,'B8' ,'B11' ,'NDVI' ,'NDBI' ,'NDWI' ,'LST' ], rf.feature_importances_))
print ("\nFeature importances:" )
for feat, imp in sorted (fi.items(), key=lambda x: -x[1 ]):
print (f" {feat} : {imp:.4 f} " )
classified_map = rf.predict(features).reshape(SIZE, SIZE)
fig, axes = plt.subplots(1 , 3 , figsize=(16 , 5 ))
cmap_lc = ListedColormap(['#8B0000' ,'#FFA500' ,'#808080' ,'#228B22' ,'#90EE90' ,'#0000FF' ,'#90EE90' ])
axes[0 ].imshow(lc_map, cmap=cmap_lc, vmin=0 , vmax=6 )
axes[0 ].set_title("True Land Cover" ); axes[0 ].set_xticks([]); axes[0 ].set_yticks([])
axes[1 ].imshow(classified_map, cmap=cmap_lc, vmin=0 , vmax=6 )
axes[1 ].set_title(f"RF Classification (OA={acc*100 :.1 f} %)" ); axes[1 ].set_xticks([]); axes[1 ].set_yticks([])
from sklearn.metrics import confusion_matrix
cm = confusion_matrix(y_test, y_pred, normalize='true' )
im = axes[2 ].imshow(cm, cmap='Blues' , vmin=0 , vmax=1 )
plt.colorbar(im, ax=axes[2 ])
axes[2 ].set_xticks(range (7 )); axes[2 ].set_xticklabels(sig_names, rotation=40 , ha='right' , fontsize=7 )
axes[2 ].set_yticks(range (7 )); axes[2 ].set_yticklabels(sig_names, fontsize=7 )
axes[2 ].set_title("Confusion Matrix (normalized)" ); axes[2 ].set_xlabel("Predicted" ); axes[2 ].set_ylabel("True" )
plt.tight_layout()
plt.savefig("lulc_classification.png" , dpi=150 )
plt.show()
Step 3: Urban Heat Island Profile Analysis import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import pearsonr
from scipy.ndimage import gaussian_filter
LST_C = LST_sim - 273.15
y_grid, x_grid = np.mgrid[0 :SIZE, 0 :SIZE]
cx, cy = SIZE/2 , SIZE/2
dist_pix = np.sqrt((x_grid - cx)**2 + (y_grid - cy)**2 )
ring_radii = np.arange(0 , dist_pix.max (), 5 )
ring_lst = []
ring_ndvi = []
ring_dist = []
for r0, r1 in zip (ring_radii[:-1 ], ring_radii[1 :]):
mask = (dist_pix >= r0) & (dist_pix < r1)
if mask.sum () > 0 :
ring_lst.append(LST_C[mask].mean())
ring_ndvi.append(NDVI[mask].mean())
ring_dist.append((r0+r1)/2 )
ring_dist = np.array(ring_dist)
ring_lst = np.array(ring_lst)
ring_ndvi = np.array(ring_ndvi)
dist_km = ring_dist * 10 / 1000
uhi_intensity = ring_lst[:3 ].mean() - ring_lst[-5 :].mean()
print (f"Urban Heat Island intensity: {uhi_intensity:.2 f} °C" )
r_corr, p_corr = pearsonr(NDVI.flatten(), LST_C.flatten())
print (f"NDVI–LST Pearson r = {r_corr:.4 f} , p = {p_corr:.2 e} " )
fig, axes = plt.subplots(1 , 3 , figsize=(15 , 5 ))
axes[0 ].plot(dist_km, ring_lst, 'r-o' , linewidth=2 , markersize=5 )
axes[0 ].set_xlabel("Distance from center (km)" ); axes[0 ].set_ylabel("Mean LST (°C)" )
axes[0 ].set_title("Urban Heat Island Profile\n(Distance from city center)" )
axes[0 ].grid(True , alpha=0.3 )
axes[0 ].annotate(f"UHI intensity={uhi_intensity:.1 f} °C" ,
xy=(0 , ring_lst[0 ]), xytext=(2 , ring_lst[0 ]+1 ),
arrowprops=dict (arrowstyle='->' , color='red' ), color='red' )
axes[1 ].plot(dist_km, ring_ndvi, 'g-o' , linewidth=2 , markersize=5 )
axes[1 ].set_xlabel("Distance from center (km)" ); axes[1 ].set_ylabel("Mean NDVI" )
axes[1 ].set_title("Vegetation Gradient\n(Distance from city center)" )
axes[1 ].grid(True , alpha=0.3 )
sc = axes[2 ].scatter(NDVI.flatten()[::5 ], LST_C.flatten()[::5 ],
c=lc_map.flatten()[::5 ],
cmap=ListedColormap(['#8B0000' ,'#FFA500' ,'#808080' ,'#228B22' ,'#90EE90' ,'#0000FF' ,'#90EE90' ]),
vmin=0 , vmax=6 , s=2 , alpha=0.3 )
z = np.polyfit(NDVI.flatten(), LST_C.flatten(), 1 )
x_fit = np.linspace(NDVI.min (), NDVI.max (), 100 )
axes[2 ].plot(x_fit, np.polyval(z, x_fit), 'k-' , linewidth=2.5 , label=f"y={z[0 ]:.1 f} x+{z[1 ]:.1 f} " )
axes[2 ].set_xlabel("NDVI" ); axes[2 ].set_ylabel("LST (°C)" )
axes[2 ].set_title(f"UHI: NDVI–LST Relationship\nr={r_corr:.3 f} " )
axes[2 ].legend(); axes[2 ].grid(True , alpha=0.3 )
plt.tight_layout()
plt.savefig("uhi_analysis.png" , dpi=150 )
plt.show()
Advanced Usage
Urban Expansion Change Detection import numpy as np
import matplotlib.pyplot as plt
np.random.seed(99 )
ndbi_t1 = NDBI.copy()
ndbi_t2 = NDBI.copy()
suburban_mask = (dist_from_center >= 75 ) & (dist_from_center < 95 )
ndbi_t2[suburban_mask] += np.random.uniform(0.1 , 0.3 , suburban_mask.sum ())
ndbi_t2 = np.clip(ndbi_t2, -1 , 1 )
ndbi_change = ndbi_t2 - ndbi_t1
new_urban = (ndbi_change > 0.15 ) & (ndbi_t1 < 0.1 )
new_urban_pct = new_urban.mean() * 100
print (f"New urban pixels detected: {new_urban_pct:.2 f} % of scene" )
fig, axes = plt.subplots(1 , 3 , figsize=(14 , 4 ))
axes[0 ].imshow(ndbi_t1, cmap='RdBu_r' , vmin=-0.5 , vmax=0.5 ); axes[0 ].set_title("NDBI T1" ); axes[0 ].axis('off' )
axes[1 ].imshow(ndbi_t2, cmap='RdBu_r' , vmin=-0.5 , vmax=0.5 ); axes[1 ].set_title("NDBI T2" ); axes[1 ].axis('off' )
axes[2 ].imshow(new_urban.astype(float ), cmap='hot' , vmin=0 , vmax=1 )
axes[2 ].set_title(f"Urban Expansion ({new_urban_pct:.1 f} % new)" ); axes[2 ].axis('off' )
plt.tight_layout(); plt.savefig("urban_expansion.png" , dpi=150 ); plt.show()
Troubleshooting
Rasterio fails to read satellite data
import rasterio
with rasterio.open ("landsat_band4.tif" ) as src:
B4_real = src.read(1 ).astype(float )
transform = src.transform
crs = src.crs
print (f"CRS: {crs} , shape: {B4_real.shape} " )
Classification accuracy too low (<80%)
Add more training samples per class
Include texture features (GLCM)
Use higher spectral resolution or more bands
Try SVM with RBF kernel for small sample sizes
NDVI undefined (0/0 division) NDVI = np.where((B8 + B4) > 0 , (B8 - B4) / (B8 + B4), 0 )
Version Compatibility Package Tested versions Notes rasterio 1.3 CRS handling changed in 1.3 geopandas 0.14 Requires shapely 2.0 scikit-learn 1.3, 1.4 RandomForest API stable
External Resources
Official Documentation
Key Papers
Weng, Q. (2012). Remote sensing of impervious surfaces in the urban areas . ISPRS Journal.
Voogt, J.A. & Oke, T.R. (2003). Thermal remote sensing of urban climates . Remote Sensing of Environment.
Examples
Example 1: Urban Greenness Index by Neighborhood import numpy as np
import pandas as pd
zones_analysis = []
for cls_id, cls_name in enumerate (sig_names):
mask = lc_map == cls_id
if mask.sum () > 0 :
zones_analysis.append({
'class' : cls_name,
'area_pix' : mask.sum (),
'mean_ndvi' : NDVI[mask].mean(),
'mean_lst_C' : LST_C[mask].mean(),
'pct_impervious' : impervious_mask[mask].mean() * 100 ,
})
df_zones = pd.DataFrame(zones_analysis)
print ("Urban zone characteristics:" )
print (df_zones.round (3 ).to_string(index=False ))
Example 2: Sky View Factor (Urban Canyon) import numpy as np
import matplotlib.pyplot as plt
svf_approx = np.exp(-0.1 * (NDBI + 0.5 ) * 10 )
svf_approx = np.clip(svf_approx, 0 , 1 )
fig, ax = plt.subplots(figsize=(7 , 6 ))
im = ax.imshow(svf_approx, cmap='Blues' , vmin=0 , vmax=1 )
plt.colorbar(im, ax=ax, label='Sky View Factor (0=blocked, 1=open)' )
ax.set_title("Sky View Factor (Urban Canyon Proxy)" ); ax.axis('off' )
plt.tight_layout(); plt.savefig("sky_view_factor.png" , dpi=150 ); plt.show()
print (f"Mean SVF downtown: {svf_approx[lc_map==0 ].mean():.3 f} " )
print (f"Mean SVF rural: {svf_approx[lc_map==6 ].mean():.3 f} " )
Last updated: 2026-03-17 | Maintainer: @xjtulyc
Issues: GitHub Issues