Por qué combinar ASTER + Sentinel-2 + Landsat
Ningún sensor por sí solo resuelve el mapeo de alteración hidrotermal. Cada uno aporta una pieza distinta del rompecabezas: resolución espacial, resolución espectral SWIR, o profundidad histórica. Este curso te enseña a usar los tres de forma complementaria — y sobre la zona que tú elijas, no sobre una coordenada fija.
| Sensor | Resolución | Bandas clave (óxidos/arcillas) | Revisita | Cobertura temporal | Mejor uso |
|---|---|---|---|---|---|
| Sentinel-2 | 10–20 m | B2, B4, B8, B11, B12 | ~5 días | 2015 — presente | Barrido rápido, alta frecuencia, detalle local |
| ASTER | 15–30–90 m | VNIR B1–B3, SWIR B4–B9, TIR B10–B14 | Programada (no sistemática) | 2000 — presente (VNIR) · SWIR solo hasta 2008 | Mineralogía fina: diferencia alunita, caolinita, sericita |
| Landsat 5/7 | 30 m | TM/ETM+ B1,B3,B4,B5,B7 | 16 días | 1984 — 2012 / 2022 | Contexto histórico, cambios en décadas pasadas |
| Landsat 8/9 | 30 m | OLI SR_B2,SR_B4,SR_B5,SR_B6,SR_B7 + TIR | 16 días (8 combinados) | 2013 — presente | Cobertura regional grande, sin fraccionar en tiles |
| Región espectral | Sentinel-2 | Landsat 8/9 OLI | Landsat 5/7 TM/ETM+ | ASTER |
|---|---|---|---|---|
| Azul | B2 (0.49 μm) | SR_B2 (0.48 μm) | B1 (0.48 μm) | B1 (0.56 μm)* |
| Rojo | B4 (0.66 μm) | SR_B4 (0.65 μm) | B3 (0.66 μm) | B2 (0.66 μm) |
| NIR | B8 (0.84 μm) | SR_B5 (0.86 μm) | B4 (0.83 μm) | B3N (0.81 μm) |
| SWIR 1 | B11 (1.61 μm) | SR_B6 (1.61 μm) | B5 (1.65 μm) | B4 (1.65 μm) |
| SWIR 2 | B12 (2.19 μm) | SR_B7 (2.20 μm) | B7 (2.22 μm) | B5–B9 (2.15–2.43 μm, 5 bandas) |
* ASTER B1 es en realidad verde/rojo (0.52–0.60 μm); se ubica aquí solo como referencia de posición relativa dentro del VNIR.
jarosita · hematita
gossans
illita · esmectita
arcillas Al–OH
calcita · carbonatos
PARAMS que defines una vez.
Tu Área de Interés — para cualquier zona del mundo
Este es el módulo que hace que el curso funcione en cualquier lugar. En vez de un punto fijo grabado en el código, el ROI se construye a partir de un parámetro que tú cambias — el resto del script no se toca.
geometry.FeatureCollection. Es la opción más robusta si ya trabajas con polígonos en QGIS/ArcGIS.Declara todo lo configurable en un único objeto al inicio del script. Cambiar de Potosí a cualquier otro distrito minero del planeta significa editar estas líneas — nada más.
var PARAMS = { // ── ROI: elige 'point' | 'rectangle' | 'polygon' | 'asset' ── roiType : 'point', // se usa solo si roiType = 'point' (ejemplo: reemplaza por tu zona) point : [-65.75, -19.58], bufferM : 5000, // se usa solo si roiType = 'rectangle' rectangle : [-66.00, -19.80, -65.50, -19.40], // se usa solo si roiType = 'asset' (tu FeatureCollection en Assets) assetId : 'users/tu_usuario/mi_area_interes', // ── ventana temporal y calidad de escena ── dateStart : '2023-01-01', dateEnd : '2023-12-31', maxCloud : 20 }; // ── construcción del ROI a partir del PARAMS ── var roi; if (PARAMS.roiType === 'point') { roi = ee.Geometry.Point(PARAMS.point).buffer(PARAMS.bufferM).bounds(); } else if (PARAMS.roiType === 'rectangle') { roi = ee.Geometry.Rectangle(PARAMS.rectangle); } else if (PARAMS.roiType === 'polygon') { roi = geometry; // dibujado con las herramientas del Code Editor } else { roi = ee.FeatureCollection(PARAMS.assetId).geometry(); } Map.centerObject(roi, 10); Map.addLayer(roi, {color: 'yellow'}, 'ROI'); print('Área del ROI (km²):', roi.area().divide(1e6));
Todos los módulos siguientes reutilizan esta misma variable roi y este mismo objeto PARAMS — no vuelvas a escribir coordenadas en ningún otro bloque de código.
bestEffort: true y tileScale en los reduceRegion, y exporta con Export.image.toDrive (que sí procesa en el backend sin ese límite) en vez de intentar visualizar todo interactivamente.
Export.table.toAsset). Así, en tu próxima sesión solo cambias PARAMS.roiType a 'asset' y apuntas al Asset guardado — sin redibujar nada.
Sentinel-2 — óxidos y arcillas en alta frecuencia
10–20 m de resolución y revisita cada ~5 días hacen de Sentinel-2 el mejor punto de partida para cualquier zona: cobertura garantizada desde 2015 y actualización constante, sin depender de programación de escenas.
roi, por la ventana de fechas de PARAMS y por porcentaje de nubes.var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED') .filterBounds(roi) .filterDate(PARAMS.dateStart, PARAMS.dateEnd) .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', PARAMS.maxCloud)) .median() .clip(roi); var ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI'); var rockMask = ndvi.lt(0.25); // excluye vegetación densa
var feOxideS2 = s2.select('B4').divide(s2.select('B2')).rename('Fe_oxido').updateMask(rockMask); var ferrosoS2 = s2.select('B11').divide(s2.select('B8')).rename('Ferroso').updateMask(rockMask); var arcillaS2 = s2.select('B11').divide(s2.select('B12')).rename('Arcilla_OH').updateMask(rockMask); Map.addLayer(arcillaS2, {min:0.9,max:1.4,palette:['000000','ffff00','ff0000']}, 'Arcillas (Sentinel-2)');
| Índice | Fórmula | Qué detecta |
|---|---|---|
| Óxidos de Fe Sabins 1999 |
B4 / B2 | Hematita, goethita, jarosita — gossans y halos de oxidación superficial. |
| Minerales ferrosos | B11 / B8 | Fe²⁺ en silicatos máficos (biotita, clorita, anfíboles alterados). |
| Arcillas Al–OH (general) Crósta & Moore 1989, adaptado |
B11 / B12 | Presencia conjunta de caolinita/illita/sericita/alunita — sin discriminar cuál. |
| NDVI (máscara) | (B8−B4)/(B8+B4) | Excluye vegetación que enmascara la señal mineral de la roca expuesta. |
ASTER — discriminación mineral detallada vía SWIR
Donde Sentinel-2 dice "hay arcilla", ASTER dice cuál. Sus 5 bandas SWIR angostas (B4–B9) permiten separar alunita, caolinita y sericita — minerales que marcan zonas distintas del sistema hidrotermal.
'2008-04-28'.
var aster = ee.ImageCollection('ASTER/AST_L1T_003') .filterBounds(roi) .filterDate('2000-01-01', '2008-04-28') // límite real del SWIR .filter(ee.Filter.lt('CLOUDCOVER', PARAMS.maxCloud)) .median() .clip(roi); print('Escenas ASTER SWIR encontradas en el ROI:', ee.ImageCollection('ASTER/AST_L1T_003').filterBounds(roi).filterDate('2000-01-01','2008-04-28').size()); // ← si esto es 0, tu zona no tiene cobertura ASTER SWIR: ver el aviso del Módulo 6
| Mineral objetivo | Fórmula | Física del índice |
|---|---|---|
| Alunita (AAS) Rowan et al. 2003 — Cuprite |
B7 / B5 | B5 (2.17 μm) absorbe por el enlace Al–OH de alunita; B7 (2.26 μm) refleja. Diagnóstico de alteración argílica avanzada. |
| Caolinita Ninomiya 2002 |
B6 / B5 | Doble absorción Al–OH en 2.17 μm (B5) y 2.21 μm (B6); B5 absorbe más fuerte, cociente > 1 marca caolinita. |
| Sericita / illita Ninomiya 2002 |
B5 / B6 | Absorción de sericita en ~2.20 μm (B5); B6 (~2.21 μm) es zona de reflectancia relativa. |
| Grupos OH (halo distal) Rowan & Mars 2003 |
(B6+B9)/(B8×2) | B6 y B9 son zonas de reflectancia; B8 (~2.33 μm) absorbe en carbonatos e illita. |
| Propilítica / clorita Corbett & Leach 1998 |
B4 / B5 | Clorita y epidota reflejan en B4 (1.65 μm) y absorben en B5 (2.17 μm) — indica la zona más distal del sistema. |
| Óxidos de Fe (VNIR) Sabins 1999 |
B2 / B1 | Goethita y hematita absorben en azul-verde (B1) y reflejan en rojo (B2). Sigue funcionando después de 2008. |
| Sílice (TIR) Ninomiya 2003 |
(B13/B12)+(B13/B14) | Restrahlen feature del cuarzo: alta emisividad en B13 (~10.6 μm) relativa a B12 y B14. Resolución nativa 90 m. |
| Carbonatos (TIR) Ninomiya 2003 |
(B13+B11)/(B12×2) | Calcita y dolomita: alta emisividad en B11 y B13 respecto a B12. Marca halos distales de carbonato. |
var alunitaIndex = aster.select('B07').divide(aster.select('B05')).rename('Alunita'); var caolinitaIndex = aster.select('B06').divide(aster.select('B05')).rename('Caolinita'); var sericitaIndex = aster.select('B05').divide(aster.select('B06')).rename('Sericita'); var oxidosVnirIndex = aster.select('B02').divide(aster.select('B01')).rename('Oxidos_VNIR'); var siliceTirIndex = aster.select('B13').divide(aster.select('B12')) .add(aster.select('B13').divide(aster.select('B14'))) .rename('Silice_TIR'); // requerido por la fusión del Módulo 6 — antes faltaba esta línea
Landsat — décadas de historia y grandes áreas sin fraccionar
Cuando tu ROI cubre miles de km² o necesitas ver cómo cambió una zona en 20-30 años, Landsat es la herramienta correcta: la misma familia de sensores cubre el planeta de forma continua desde 1984.
| Misión | Periodo | Notas |
|---|---|---|
| Landsat 5 (TM) | 1984 – 2012 | Numeración de bandas distinta a OLI: B1 azul, B3 rojo, B4 NIR, B5 SWIR1, B7 SWIR2. |
| Landsat 7 (ETM+) | 1999 – 2022 | Falla del SLC en 2003 introduce franjas sin datos ("gaps") en escenas posteriores. |
| Landsat 8 (OLI/TIRS) | 2013 – presente | Numeración SR_B1…SR_B7 + banda térmica ST_B10. |
| Landsat 9 (OLI-2) | 2021 – presente | Mismo esquema de bandas que Landsat 8; se pueden combinar en una sola colección. |
function aplicarFactoresEscala(img) { var optico = img.select('SR_B.').multiply(0.0000275).add(-0.2); var termico = img.select('ST_B10').multiply(0.00341802).add(149.0); return img.addBands(optico, null, true).addBands(termico, null, true); } var landsat = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2') .merge(ee.ImageCollection('LANDSAT/LC09/C02/T1_L2')) .filterBounds(roi) .filterDate(PARAMS.dateStart, PARAMS.dateEnd) .filter(ee.Filter.lt('CLOUD_COVER', PARAMS.maxCloud)) .map(aplicarFactoresEscala) .median() .clip(roi); var feOxideL8 = landsat.select('SR_B4').divide(landsat.select('SR_B2')).rename('Fe_oxido_L8'); var arcillaL8 = landsat.select('SR_B6').divide(landsat.select('SR_B7')).rename('Arcilla_L8');
'LANDSAT/LT05/C02/T1_L2' (Landsat 5, hasta 2012) o 'LANDSAT/LE07/C02/T1_L2' (Landsat 7). Los mismos índices se recalculan usando SR_B3/SR_B1 (óxidos) y SR_B5/SR_B7 (arcillas) — la física es idéntica, solo cambia el número de banda.
PARAMS.dateStart/dateEnd (ej. 1990 vs. 2023) sobre el mismo roi y resta los índices resultantes. Es la forma más directa de detectar nueva exposición de roca por erosión, movimientos de tierra, o expansión de drenaje ácido de minas (AMD) a lo largo del tiempo.
Fusión multisensor, priorización y exportación
Aquí combinas los índices de los tres sensores en un único mapa de prospectividad, y lo conviertes en targets concretos y capas listas para GIS — sin importar qué zona hayas elegido en el Módulo 2.
Cada índice tiene una escala distinta (unos van de 0.8 a 1.6, otros de 1 a 40). Antes de sumarlos hay que llevarlos a un rango común usando percentiles robustos P5–P95, que ignoran outliers extremos (nubes residuales, píxeles corruptos).
var normalize01 = function(img) { var stats = img.reduceRegion({ reducer: ee.Reducer.percentile([5, 95]), geometry: roi, scale: 30, bestEffort: true }); var minVal = ee.Number(stats.values().get(0)); var maxVal = ee.Number(stats.values().get(1)); var safeMax = ee.Algorithms.If(maxVal.subtract(minVal).gt(1e-10), maxVal, minVal.add(1)); return img.subtract(minVal).divide(ee.Number(safeMax).subtract(minVal)).clamp(0, 1); };
prospectividad pero nunca la agregaba al mapa con Map.addLayer, y el curso nunca daba el código para convertir ese mapa continuo en polígonos de target. El bloque de abajo corrige ambas cosas, y además usa .unmask(0) para que, si tu ROI no tiene cobertura ASTER SWIR, esa capa faltante sume 0 en vez de anular todo el resultado.
// safeNorm(): normaliza [0,1] y además rellena con 0 los píxeles sin datos // (por ejemplo, si tu ROI no tiene cobertura ASTER SWIR) para que un solo // sensor faltante no anule todo el mapa de prospectividad. var safeNorm = function(img) { return normalize01(img).unmask(0); }; var prospectividad = safeNorm(alunitaIndex.max(caolinitaIndex).max(sericitaIndex)).multiply(0.30) .add(safeNorm(arcillaS2).multiply(0.25)) .add(safeNorm(feOxideS2).multiply(0.20)) .add(safeNorm(siliceTirIndex).multiply(0.15)) .add(safeNorm(feOxideL8).multiply(0.10)) .rename('prospectividad'); Map.addLayer(prospectividad, {min: 0, max: 1, palette: ['1a1a2e', '3b0f70', '8c2981', 'de4968', 'fe9f6d', 'fcfdbf']}, 'Prospectividad integrada');
El mapa de prospectividad es continuo (cada píxel vale entre 0 y 1). Para obtener polígonos concretos que puedas llevar a campo, hay que aplicar un umbral, agrupar los píxeles contiguos que lo superan, y calificar cada polígono resultante según su valor medio.
var UMBRAL_TARGET = 0.35; // umbral mínimo para considerar "anomalía" — súbelo/bájalo según tu histograma var AREA_MINIMA_HA = 0.5; // descarta polígonos de 1 píxel (ruido) var targetsRaw = prospectividad.gte(UMBRAL_TARGET).selfMask(); var targets = targetsRaw.reduceToVectors({ reducer: ee.Reducer.countEvery(), geometry: roi, scale: 30, geometryType: 'polygon', eightConnected: true, labelProperty: 'id_target', bestEffort: true }); targets = targets.map(function (f) { var areaHa = f.geometry().area().divide(10000); var stats = prospectividad.reduceRegion({ reducer: ee.Reducer.mean(), geometry: f.geometry(), scale: 30, bestEffort: true }); var media = ee.Number(stats.get('prospectividad')); var prioridad = ee.Algorithms.If(media.gte(0.65), 'P1 - Critica', ee.Algorithms.If(media.gte(0.45), 'P2 - Alta', 'P3 - Moderada')); return f.set({area_ha: areaHa, prospectividad_media: media, prioridad: prioridad}); }).filter(ee.Filter.gt('area_ha', AREA_MINIMA_HA)); print('Targets detectados (tras filtrar por área mínima):', targets.size()); print('Tabla de targets:', targets); var p1 = targets.filter(ee.Filter.eq('prioridad', 'P1 - Critica')); var p2 = targets.filter(ee.Filter.eq('prioridad', 'P2 - Alta')); var p3 = targets.filter(ee.Filter.eq('prioridad', 'P3 - Moderada')); Map.addLayer(p3.style({color: '388bfd', fillColor: '00000000', width: 2}), {}, 'Targets · Prioridad 3'); Map.addLayer(p2.style({color: 'e3b341', fillColor: '00000000', width: 2}), {}, 'Targets · Prioridad 2'); Map.addLayer(p1.style({color: 'f85149', fillColor: '00000000', width: 3}), {}, 'Targets · Prioridad 1');
Este bloque agrega un panel en la esquina del mapa: al hacer clic en cualquier punto, te muestra al instante el valor de la prospectividad y de cada índice individual en ese lugar. También imprime un histograma con la distribución de prospectividad de todo tu ROI.
// Panel + histograma de estadísticas var panel = ui.Panel({ style: {position: 'bottom-left', padding: '10px', width: '280px', backgroundColor: 'rgba(20,20,20,0.85)'} }); panel.add(ui.Label('👆 Haz clic en cualquier punto del mapa', {color: 'white', fontWeight: 'bold'})); Map.add(panel); var capasParaInspeccionar = ee.Image.cat([ prospectividad, alunitaIndex, caolinitaIndex, sericitaIndex, arcillaS2, feOxideS2, siliceTirIndex, feOxideL8 ]); Map.onClick(function (coords) { var punto = ee.Geometry.Point([coords.lon, coords.lat]); var valores = capasParaInspeccionar.reduceRegion({ reducer: ee.Reducer.first(), geometry: punto, scale: 30 }); valores.evaluate(function (resultado) { panel.clear(); panel.add(ui.Label('📍 ' + coords.lon.toFixed(4) + ', ' + coords.lat.toFixed(4), {color: 'white', fontWeight: 'bold'})); for (var nombre in resultado) { var valor = resultado[nombre]; var texto = (valor === null) ? 'sin datos' : valor.toFixed(3); panel.add(ui.Label(nombre + ': ' + texto, {color: '#dddddd', fontSize: '12px'})); } }); }); print(ui.Chart.image.histogram({image: prospectividad, region: roi, scale: 30, maxBuckets: 40}) .setOptions({ title: 'Distribución de prospectividad en el ROI', hAxis: {title: 'Prospectividad (0-1)'}, vAxis: {title: 'N° de píxeles'} }));
El clic no funciona sobre el visor estático de esta página — solo dentro del Code Editor de GEE, donde Map es un mapa interactivo real.
prioridad, prospectividad_media y área en Ha. Importable directo en QGIS/ArcGIS.// 1. Targets como GeoJSON (para QGIS/ArcGIS) Export.table.toDrive({ collection: targets, description: 'Targets_GeoJSON', folder: 'exploracion_mineral', fileNamePrefix: 'targets', fileFormat: 'GeoJSON' }); // 2. Estadísticas de los targets como CSV Export.table.toDrive({ collection: targets, description: 'Targets_CSV', folder: 'exploracion_mineral', fileNamePrefix: 'targets_estadisticas', fileFormat: 'CSV' }); // 3. Stack multisensor completo (todos los índices + prospectividad) Export.image.toDrive({ image: ee.Image.cat([ prospectividad, alunitaIndex, caolinitaIndex, sericitaIndex, oxidosVnirIndex, siliceTirIndex, arcillaS2, feOxideS2, ferrosoS2, feOxideL8, arcillaL8 ]), description: 'Stack_Multisensor', folder: 'exploracion_mineral', fileNamePrefix: 'stack_multisensor', region: roi, scale: 30, crs: 'EPSG:4326', maxPixels: 1e9 }); // 4. RGB mineralógico en 8 bits, listo para abrir en cualquier SIG var rgbMineral = ee.Image.cat([ alunitaIndex.max(caolinitaIndex).max(sericitaIndex), arcillaS2, feOxideS2 ]).unitScale(0.8, 1.6).multiply(255).byte(); Export.image.toDrive({ image: rgbMineral, description: 'RGB_Mineralogico', folder: 'exploracion_mineral', fileNamePrefix: 'rgb_mineralogico', region: roi, scale: 10, crs: 'EPSG:4326', maxPixels: 1e9 }); print('4 tareas de exportación creadas: abre la pestaña "Tasks" (arriba a la derecha) y pulsa RUN en cada una.');
Export.*.toDrive() solo crea la tarea. Tienes que ir a la pestaña Tasks (arriba a la derecha del Code Editor) y pulsar RUN en cada una para que realmente se generen los archivos en tu Google Drive, dentro de la carpeta exploracion_mineral.
Todas las fórmulas y afirmaciones técnicas de este curso están respaldadas por estas fuentes. Se verificaron una a una contra las publicaciones originales; la referencia "Agar & Coulter 2007" del material previo no correspondía a ninguna publicación localizable y fue sustituida por Rowan et al. (2003), la fuente correcta y verificable para ese índice.
Sistema mono-sensor: ASTER + DEM + Machine Learning
Los Módulos 1–6 enseñan el enfoque multisensor con ROI configurable. Este anexo documenta un caso real distinto: un sistema que profundiza en un solo sensor (ASTER) combinado con relieve (DEM) y clustering no supervisado (K-Means), aplicado a un polígono minero fijo. Sirve como segunda referencia — más profundidad mineralógica y estructural, a cambio de depender 100% de que ASTER cubra tu zona.
estructuralFav: pendientes 15°–40°, la ventana morfológica más favorable para trampas estructurales.| # | Síntoma / riesgo | Causa real | Corrección aplicada |
|---|---|---|---|
| 1 | "No aparece nada de ASTER" | Sin filterDate + CLOUDCOVER<10 muy estricto → fácil terminar con 0 escenas, sin ningún aviso. |
Diagnóstico en cascada: cuenta escenas, reintenta sin nubes, reintenta sin fecha, e informa la causa exacta con print(). |
| 2 | Ninguna imagen ASTER cruda visible | Solo se visualizaban productos derivados (índices), casi todos con shown=false. |
Se agregó una capa 📷 ASTER RGB visible por defecto, para confirmar cobertura a simple vista. |
| 3 | La capa "Brechas Conocidas" desaparecía al hacer clic | Map.layers().set(12, ...) con índice fijo — sobrescribía silenciosamente la capa que ocupaba esa posición. |
Capa dedicada creada una sola vez; el clic actualiza su contenido con markerLayer.setEeObject(point). |
| 4 | Clusters K-Means poco informativos | Un único unitScale(0,10000) para bandas de escalas muy distintas (DN 1–255, índices ~0.8–2.5, pendiente 0–90°) aplastaba casi todo a ~0. |
Cada banda se normaliza individualmente con percentiles 5–95 (normalize01), igual que en el Módulo 6. |
| 5 | Filtro de nubes descartaba casi todo | CLOUDCOVER<10 es muy exigente para zonas con nubosidad frecuente. |
Subido a 30% como punto de partida, con reintento automático si aún da 0. |
| Índice | Fórmula | Peso en favorabilidad Au |
|---|---|---|
| Alunita Rowan et al. 2003 — Cuprite | B7 / B5 | 30% |
| Sílice TIR Ninomiya 2003 | (B13/B12)+(B13/B14) | 25% |
| Caolinita Ninomiya 2002 | B6 / B5 | 15% |
| Óxidos de Fe Sabins 1999 | B2 / B1 | 15% |
| Sericita Ninomiya 2002 | B5 / B6 | 8% |
| Grupos OH Rowan & Mars 2003 | (B6+B9)/(B8×2) | 7% |
| Propilítica/Clorita Corbett & Leach 1998 | B4 / B5 | — |
| Carbonatos TIR Ninomiya 2003 | (B13+B11)/(B12×2) | — |
Este es un script monolítico (no está pensado para pegarse por fragmentos como en los Módulos 1–6). Cópialo completo de una sola vez en el Code Editor de GEE.
/*
================================================================================
SISTEMA DE EXPLORACIÓN MINERAL AUTÓNOMO - v2.1 Autor Spatialmind S.R.L.
Detección de Alteración Hidrotermal Asociada a Mineralización Aurífera
Sin Muestras de Campo - Basado 100% en Teledetección + ML No Supervisado
REFERENCIAS:
- Sabins (1999) — Remote Sensing: Principles and Interpretation
- Rowan et al. (2003) — Mapping hydrothermally altered rocks at Cuprite (Economic Geology)
- Rowan & Mars (2003) — Lithologic mapping in the Mountain Pass area
- Ninomiya (2003) — Silica index and carbonate index from ASTER TIR
- Corbett & Leach (1998) — Southwest Pacific Rim gold-copper systems
================================================================================
*/
// ════════════════════════════════════════════════════════════════════════════
// 0. CONFIGURACIÓN INICIAL
// ════════════════════════════════════════════════════════════════════════════
var aoiLocal = ee.FeatureCollection('projects/eddycc66/assets/POLIGONO_TIGRES_DE_ORO').geometry();
var brechas = ee.FeatureCollection('projects/eddycc66/assets/Brechas_dorado');
Map.centerObject(aoiLocal, 13);
Map.addLayer(aoiLocal, {color: 'white'}, '🔲 Límite del AOI', true);
// ════════════════════════════════════════════════════════════════════════════
// 1. PROCESAMIENTO TOPOGRÁFICO
// ════════════════════════════════════════════════════════════════════════════
var dem = ee.Image('projects/eddycc66/assets/dem_tigres_de_oro').clip(aoiLocal);
var slope = ee.Terrain.slope(dem);
var aspect = ee.Terrain.aspect(dem);
var hillshade = ee.Terrain.hillshade(dem);
// Zonas estructuralmente favorables: pendientes moderadas (15°–40°)
var estructuralFav = slope.gt(15).and(slope.lt(40)).rename('Estructural');
// ════════════════════════════════════════════════════════════════════════════
// 2. PROCESAMIENTO ASTER MULTI-ESPECTRAL — CON DIAGNÓSTICO Y REINTENTOS
// ════════════════════════════════════════════════════════════════════════════
var FECHA_INICIO = '2000-01-01';
var FECHA_FIN = '2008-04-28'; // límite real del SWIR (falló en abril 2008)
var MAX_NUBES = 30; // 10% era muy estricto; súbelo más si sigue en 0
var asterColl = ee.ImageCollection('ASTER/AST_L1T_003')
.filterBounds(aoiLocal)
.filterDate(FECHA_INICIO, FECHA_FIN)
.filter(ee.Filter.lt('CLOUDCOVER', MAX_NUBES));
var nEscenas = asterColl.size().getInfo();
print('① Escenas ASTER SWIR válidas (fecha ' + FECHA_INICIO + ' a ' + FECHA_FIN +
', nubes <' + MAX_NUBES + '%):', nEscenas);
if (nEscenas === 0) {
print('⚠ 0 escenas con ese filtro de nubes. Reintentando sin filtro de nubes...');
asterColl = ee.ImageCollection('ASTER/AST_L1T_003')
.filterBounds(aoiLocal)
.filterDate(FECHA_INICIO, FECHA_FIN);
nEscenas = asterColl.size().getInfo();
print('② Escenas ASTER SWIR sin filtro de nubes:', nEscenas);
}
if (nEscenas === 0) {
print('⚠ Sigue en 0 dentro de la ventana SWIR (2000–2008). Ampliando a TODO ' +
'el archivo ASTER (VNIR/TIR seguirán funcionando; el SWIR puede no ser confiable)...');
asterColl = ee.ImageCollection('ASTER/AST_L1T_003').filterBounds(aoiLocal);
nEscenas = asterColl.size().getInfo();
print('③ Escenas ASTER de cualquier fecha sobre el AOI:', nEscenas);
}
if (nEscenas === 0) {
print('🛑 CAUSA CONFIRMADA: no existe ninguna escena ASTER (de ningún tipo) sobre ' +
'tu polígono POLIGONO_TIGRES_DE_ORO en el catálogo de Google Earth Engine. ' +
'Por eso no aparece ninguna imagen ni índice derivado de ASTER — no es un ' +
'error de código, es que no hay dato fuente. Revisa que las coordenadas de ' +
'ese asset sean correctas, o usa Sentinel-2/Landsat en su lugar para esta zona.');
} else {
print('✅ Hay cobertura ASTER utilizable — si aun así no ves la capa "📷 ASTER RGB" ' +
'en el mapa, revisa que su casilla esté marcada en el panel Layers (arriba a la derecha del mapa).');
}
var asterMedian = asterColl.median().clip(aoiLocal);
// Bandas VNIR (15 m) + SWIR (30 m)
// B01=0.52–0.60μm B02=0.63–0.69μm B3N=0.78–0.86μm
// B04=1.60–1.70μm B05=2.145–2.185μm B06=2.185–2.225μm
// B07=2.235–2.285μm B08=2.295–2.365μm B09=2.360–2.430μm
var asterBands = ['B01','B02','B3N','B04','B05','B06','B07','B08','B09'];
var asterNames = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9'];
var vnirSwir = asterMedian.select(asterBands, asterNames);
// Bandas TIR (90 m)
// B10=8.125–8.475μm B11=8.475–8.825μm B12=8.925–9.275μm
// B13=10.25–10.95μm B14=10.95–11.65μm
var tirBands = asterMedian.select(['B10','B11','B12','B13','B14']);
// ── Capa cruda visible: esto es lo que faltaba en la v2.0 ────────────────
// Falso color estándar ASTER: R=NIR(B3) G=Rojo(B2) B=Verde(B1)
var asterRGB = vnirSwir.select(['B3', 'B2', 'B1']);
Map.addLayer(asterRGB, {min: 20, max: 130}, '📷 ASTER RGB (falso color, verifica cobertura)', true);
// ════════════════════════════════════════════════════════════════════════════
// 3. ÍNDICES MINERALÓGICOS
// ════════════════════════════════════════════════════════════════════════════
// ─── 3.1 SÍLICE (TIR) ─────────────────────────────────────────────────────
// Ninomiya (2003): Silica Index compuesto — más robusto que ratio simple
var siliceIndex = tirBands.select('B13').divide(tirBands.select('B12').add(0.001))
.add(tirBands.select('B13').divide(tirBands.select('B14').add(0.001)))
.rename('Silice_Index');
// ─── 3.2 ALUNITA (SWIR) ───────────────────────────────────────────────────
// Rowan et al. (2003, Cuprite): absorción ~2.17μm (B5), reflectancia ~2.26μm (B7)
var alunitaIndex = vnirSwir.select('B7')
.divide(vnirSwir.select('B5').add(0.001))
.rename('Alunita_Index');
// ─── 3.3 CAOLINITA (SWIR) ─────────────────────────────────────────────────
var caolintaIndex = vnirSwir.select('B6')
.divide(vnirSwir.select('B5').add(0.001))
.rename('Caolinita_Index');
// ─── 3.4 SERICITA / MUSCOVITA (SWIR) ─────────────────────────────────────
// Ninomiya (2002): absorción principal sericita en ~2.20μm (B5)
var sericitaIndex = vnirSwir.select('B5')
.divide(vnirSwir.select('B6').add(0.001))
.rename('Sericita_Index');
// ─── 3.5 ÓXIDOS DE HIERRO (VNIR) ──────────────────────────────────────────
var oxidosIndex = vnirSwir.select('B2')
.divide(vnirSwir.select('B1').add(0.001))
.rename('Oxidos_Index');
// ─── 3.6 GRUPOS OH — ARCILLAS + CARBONATOS (SWIR) ─────────────────────────
// Rowan & Mars (2003): (B6+B9)/(B8×2)
var ohIndex = vnirSwir.select('B6')
.add(vnirSwir.select('B9'))
.divide(vnirSwir.select('B8').multiply(2).add(0.001))
.rename('OH_Index');
// ─── 3.7 PROPILÍTICA / CLORITA (SWIR) ────────────────────────────────────
var propiliticaIndex = vnirSwir.select('B4')
.divide(vnirSwir.select('B5').add(0.001))
.rename('Propilitica_Index');
// ─── 3.8 CARBONATOS (TIR) ─────────────────────────────────────────────────
var carbonatoIndex = tirBands.select('B13').add(tirBands.select('B11'))
.divide(tirBands.select('B12').multiply(2).add(0.001))
.rename('Carbonato_Index');
// ─── 3.9 NDVI (para máscara de vegetación) ────────────────────────────────
var ndvi = vnirSwir.normalizedDifference(['B3','B2']).rename('NDVI');
// ════════════════════════════════════════════════════════════════════════════
// 4. FUNCIÓN DE NORMALIZACIÓN ROBUSTA (percentiles 5–95)
// ════════════════════════════════════════════════════════════════════════════
var normalize01 = function(img) {
var bandName = img.bandNames().get(0);
var stats = img.reduceRegion({
reducer: ee.Reducer.percentile([5, 95]),
geometry: aoiLocal,
scale: 90,
bestEffort: true,
maxPixels: 1e9
});
var vals = stats.values();
var minVal = ee.Number(vals.get(0));
var maxVal = ee.Number(vals.get(1));
var diff = maxVal.subtract(minVal);
var safeMax = ee.Algorithms.If(diff.gt(1e-10), maxVal, minVal.add(1));
return img
.subtract(minVal)
.divide(ee.Number(safeMax).subtract(minVal))
.clamp(0, 1)
.rename([bandName]);
};
// ════════════════════════════════════════════════════════════════════════════
// 5. MODELO DE FAVORABILIDAD PARA SISTEMA EPITERMAL AU
// ════════════════════════════════════════════════════════════════════════════
// Alunita 30% — alteración argílica avanzada, proxy más diagnóstico de Au
// Sílice 25% — silicificación hidrotermal
// Caolinita 15% — asociada a AAS y Au supérgeno
// Óxidos Fe 15% — gossans / halos de oxidación
// Sericita 8% — alteración fílica
// OH general 7% — halo externo de alteración
var favorabilidadAu = normalize01(alunitaIndex).multiply(0.30)
.add(normalize01(siliceIndex).multiply(0.25))
.add(normalize01(caolintaIndex).multiply(0.15))
.add(normalize01(oxidosIndex).multiply(0.15))
.add(normalize01(sericitaIndex).multiply(0.08))
.add(normalize01(ohIndex).multiply(0.07))
.rename('Favorabilidad_Au');
// ════════════════════════════════════════════════════════════════════════════
// 6. STACK DE VARIABLES PARA CLUSTERING ML — NORMALIZACIÓN CORREGIDA
// ════════════════════════════════════════════════════════════════════════════
var mlInput = ee.Image.cat([
vnirSwir.select(['B1','B2','B3','B4','B5','B6','B7','B8','B9']),
siliceIndex,
alunitaIndex,
caolintaIndex,
sericitaIndex,
oxidosIndex,
ohIndex,
propiliticaIndex,
slope,
estructuralFav
]).unmask(0);
// CORREGIDO: antes un único unitScale(0,10000) aplastaba casi todas las
// bandas a ~0 (DN 1-255, índices ~0.8-2.5 y pendiente 0-90° no comparten
// escala). Ahora cada banda se normaliza individualmente con percentiles.
var mlBandNames = mlInput.bandNames();
var mlNormalizadas = mlBandNames.map(function (name) {
return normalize01(mlInput.select([name]));
});
var mlInputNorm = ee.ImageCollection.fromImages(mlNormalizadas).toBands().rename(mlBandNames);
// ════════════════════════════════════════════════════════════════════════════
// 7. CLUSTERING NO SUPERVISADO — K-MEANS
// ════════════════════════════════════════════════════════════════════════════
var samplesForClustering = mlInputNorm.sample({
region: aoiLocal,
scale: 60,
numPixels: 3000,
seed: 42,
geometries: false
});
var clusterer = ee.Clusterer.wekaKMeans({
nClusters: 8,
init: 0,
canopies: false,
maxIterations: 100,
distanceFunction: 'Euclidean'
}).train(samplesForClustering);
var clusters = mlInputNorm.cluster(clusterer).rename('Cluster');
// ════════════════════════════════════════════════════════════════════════════
// 8. ZONA DE MÁXIMA PRIORIDAD MINERAL
// INSTRUCCIONES: inspecciona visualmente los clusters, anota cuáles
// coinciden con zonas de alta favorabilidad y actualiza prospectiveClusters
// ════════════════════════════════════════════════════════════════════════════
var prospectiveClusters = ee.List([2, 5, 6]); // ← AJUSTAR TRAS INSPECCIÓN VISUAL
var targetClusters = prospectiveClusters.iterate(function (clusterID, image) {
var mask = clusters.eq(ee.Number(clusterID));
return ee.Image(image).or(mask);
}, ee.Image(0));
targetClusters = ee.Image(targetClusters).selfMask().rename('Target_Clusters');
// Máscaras de calidad
var mascaraVegetacion = ndvi.lt(0.3); // excluir vegetación densa
var mascaraPendiente = slope.gt(8); // excluir planicies
// Zona final: umbral de favorabilidad + cluster ML + máscaras
var zonaProspectiva = favorabilidadAu.gt(0.55)
.and(targetClusters.eq(1))
.and(mascaraVegetacion)
.and(mascaraPendiente)
.selfMask()
.rename('Zona_Maxima_Prioridad');
// ════════════════════════════════════════════════════════════════════════════
// 9. VISUALIZACIÓN
// ════════════════════════════════════════════════════════════════════════════
Map.addLayer(hillshade, {min: 120, max: 250}, 'Relieve (Hillshade)', true, 0.35);
Map.addLayer(favorabilidadAu,
{min: 0, max: 1, palette: ['#0000FF', '#00FFFF', '#00FF00', '#FFFF00', '#FF8C00', '#FF0000']},
'1️⃣ Favorabilidad Au (Índices corregidos)', true);
Map.addLayer(clusters.randomVisualizer(), {}, '2️⃣ Clusters K-Means (8 clases)', true);
Map.addLayer(zonaProspectiva, {palette: ['#FF00FF']}, '🎯 ZONA MÁXIMA PRIORIDAD Au', true);
// Índices individuales (apagados por defecto — activa la casilla para diagnóstico)
Map.addLayer(siliceIndex,
{min: 1.0, max: 2.4, palette: ['navy', 'white', 'red']},
'Índice Sílice TIR (Ninomiya 2003)', false);
Map.addLayer(alunitaIndex,
{min: 0.9, max: 1.3, palette: ['blue', 'yellow', 'orange']},
'Índice Alunita SWIR', false);
Map.addLayer(sericitaIndex,
{min: 0.9, max: 1.2, palette: ['purple', 'white', 'green']},
'Índice Sericita SWIR', false);
Map.addLayer(oxidosIndex,
{min: 0.8, max: 1.4, palette: ['white', 'brown', 'darkred']},
'Índice Óxidos Fe (Gossans)', false);
Map.addLayer(caolintaIndex,
{min: 0.9, max: 1.2, palette: ['lightblue', 'cyan', 'blue']},
'Índice Caolinita SWIR', false);
Map.addLayer(propiliticaIndex,
{min: 0.8, max: 1.3, palette: ['darkgreen', 'lime', 'yellow']},
'Índice Propilítica/Clorita (B4/B5)', false);
Map.addLayer(ohIndex,
{min: 0.9, max: 1.15, palette: ['gray', 'pink', 'magenta']},
'Índice OH — Arcillas+Carbonatos', false);
// RGB falso color mineralógico: R=Alunita, G=Sericita, B=Óxidos
var rgbMineralogico = ee.Image.cat([
alunitaIndex.unitScale(0.9, 1.3),
sericitaIndex.unitScale(0.9, 1.2),
oxidosIndex.unitScale(0.8, 1.4)
]);
Map.addLayer(rgbMineralogico, {min: 0, max: 1}, 'RGB Mineralógico (Alunita/Sericita/Óxidos)', false);
Map.addLayer(brechas, {color: '00FF00'}, '✅ Brechas Conocidas (Validación)', true, 0.7);
// ════════════════════════════════════════════════════════════════════════════
// 10. UI — INSPECTOR DE CLUSTER EN TIEMPO REAL
// ════════════════════════════════════════════════════════════════════════════
var clusterPanel = ui.Panel({
style: {position: 'top-right', padding: '10px', width: '220px',
border: '1px solid #ccc', backgroundColor: 'white'}
});
clusterPanel.add(ui.Label('🔍 INSPECTOR DE CLUSTER', {fontWeight: 'bold', color: 'navy'}));
clusterPanel.add(ui.Label('Haz clic en el mapa', {fontSize: '12px', color: 'gray'}));
var clusterLabel = ui.Label('Cluster: —');
var favLabel = ui.Label('Favorabilidad: —');
var interpretaLabel = ui.Label('Tipo: —', {fontSize: '12px', color: 'gray'});
clusterPanel.add(clusterLabel);
clusterPanel.add(favLabel);
clusterPanel.add(interpretaLabel);
Map.add(clusterPanel);
// ════════════════════════════════════════════════════════════════════════════
// 11. UI — PANEL ESPECTRAL INTERACTIVO
// ════════════════════════════════════════════════════════════════════════════
var inspector = ui.Panel({
style: {width: '440px', position: 'bottom-left', padding: '10px',
border: '2px solid navy', backgroundColor: 'white'}
});
ui.root.insert(0, inspector);
inspector.add(ui.Label('📊 ANÁLISIS ESPECTRAL Y MINERALÓGICO',
{fontWeight: 'bold', fontSize: '15px', color: 'navy'}));
inspector.add(ui.Label('Haz clic en el mapa para analizar un píxel',
{fontSize: '12px', color: 'gray', margin: '0 0 8px 0'}));
// CORREGIDO: capa dedicada para el marcador de clic, en vez de
// Map.layers().set(12, ...) que sobrescribía "Brechas Conocidas".
var markerLayer = ui.Map.Layer(ee.Geometry.MultiPoint([]), {color: 'yellow'}, '📍 Punto analizado', false);
Map.layers().add(markerLayer);
// ════════════════════════════════════════════════════════════════════════════
// 12. EVENTO OnClick — INSPECCIÓN DE PÍXEL
// ════════════════════════════════════════════════════════════════════════════
Map.onClick(function (coords) {
var point = ee.Geometry.Point([coords.lon, coords.lat]);
// Actualizar inspector de cluster (panel derecho)
var clusterVal = clusters.reduceRegion({
reducer: ee.Reducer.first(), geometry: point, scale: 30
});
clusterVal.evaluate(function (r) {
if (r && r.Cluster !== null && r.Cluster !== undefined) {
clusterLabel.setValue('Cluster: ' + Math.round(r.Cluster));
}
});
inspector.clear();
inspector.add(ui.Label('⏳ Procesando...', {color: 'blue', fontWeight: 'bold'}));
markerLayer.setEeObject(point);
markerLayer.setShown(true);
// Firma espectral VNIR-SWIR
var chart = ui.Chart.image.regions({
image: vnirSwir,
regions: point,
reducer: ee.Reducer.mean(),
scale: 30,
xLabels: [0.56, 0.66, 0.81, 1.65, 2.165, 2.205, 2.26, 2.33, 2.395]
}).setOptions({
title: 'Firma Espectral VNIR-SWIR (ASTER)',
hAxis: {title: 'Longitud de Onda (μm)', titleTextStyle: {italic: false}},
vAxis: {title: 'Reflectancia (DN)', titleTextStyle: {italic: false}},
lineWidth: 3, pointSize: 6,
series: {0: {color: 'darkred'}},
legend: 'none',
backgroundColor: '#f5f5f5'
});
// Valores de todos los índices
var valores = ee.Image.cat([
favorabilidadAu,
siliceIndex,
alunitaIndex,
caolintaIndex,
sericitaIndex,
oxidosIndex,
ohIndex,
propiliticaIndex,
clusters,
ndvi
]).reduceRegion({
reducer: ee.Reducer.first(), geometry: point, scale: 30
});
valores.evaluate(function (v) {
inspector.clear();
inspector.add(ui.Label('📊 ANÁLISIS ESPECTRAL Y MINERALÓGICO',
{fontWeight: 'bold', fontSize: '15px', color: 'navy', margin: '0 0 8px 0'}));
inspector.add(chart);
inspector.add(ui.Label('─────────────────────────────────', {color: '#ccc'}));
inspector.add(ui.Label('📈 ÍNDICES MINERALÓGICOS:',
{fontWeight: 'bold', margin: '8px 0 4px 0'}));
var fav = (v && v.Favorabilidad_Au !== null) ? v.Favorabilidad_Au : 0;
var favColor = fav > 0.6 ? '#1a7a1a' : (fav > 0.4 ? '#cc7700' : '#cc0000');
inspector.add(ui.Label('• Favorabilidad Au: ' + fav.toFixed(3),
{fontWeight: 'bold', color: favColor}));
var safe = function (val) { return (v && v[val] !== null && v[val] !== undefined) ? v[val].toFixed(3) : 'N/D'; };
inspector.add(ui.Label('• Sílice TIR (Ninomiya): ' + safe('Silice_Index')));
inspector.add(ui.Label('• Alunita SWIR: ' + safe('Alunita_Index')));
inspector.add(ui.Label('• Caolinita SWIR: ' + safe('Caolinita_Index')));
inspector.add(ui.Label('• Sericita B5/B6: ' + safe('Sericita_Index')));
inspector.add(ui.Label('• Óxidos Fe: ' + safe('Oxidos_Index')));
inspector.add(ui.Label('• OH (Rowan & Mars): ' + safe('OH_Index')));
inspector.add(ui.Label('• Propilítica B4/B5: ' + safe('Propilitica_Index')));
inspector.add(ui.Label('• NDVI: ' + safe('NDVI')));
inspector.add(ui.Label('• Cluster ML: ' +
(v && v.Cluster !== null ? Math.round(v.Cluster) : 'N/D')));
inspector.add(ui.Label('─────────────────────────────────', {color: '#ccc'}));
inspector.add(ui.Label('🔬 INTERPRETACIÓN GEOLÓGICA:',
{fontWeight: 'bold', margin: '8px 0 4px 0'}));
var hayHallazgo = false;
if (v && v.Alunita_Index > 1.05) {
inspector.add(ui.Label('✓ Alteración argílica avanzada (AAS) — alta sulfuración Au',
{color: '#1a7a1a', fontWeight: 'bold'}));
hayHallazgo = true;
}
if (v && v.Silice_Index > 2.0) {
inspector.add(ui.Label('✓ Silicificación intensa — posible brecha/stockwork/veta',
{color: '#1a7a1a'}));
hayHallazgo = true;
}
if (v && v.Oxidos_Index > 1.15) {
inspector.add(ui.Label('✓ Gossan / halo de óxidos Fe — mineralización sulfurada oxidada',
{color: '#cc7700'}));
hayHallazgo = true;
}
if (v && v.Sericita_Index > 1.05) {
inspector.add(ui.Label('✓ Alteración fílica (sericita) — epitermal baja sulfuración',
{color: '#cc7700'}));
hayHallazgo = true;
}
if (v && v.Caolinita_Index > 1.05 && v.Alunita_Index > 1.0) {
inspector.add(ui.Label('✓ Caolinita + Alunita — zona de alteración supérgena sobre Au',
{color: '#1a7a1a'}));
hayHallazgo = true;
}
if (v && v.Propilitica_Index < 0.95) {
inspector.add(ui.Label('• Posible propilítica/clorita — halo externo distal',
{color: 'gray'}));
}
if (v && v.NDVI > 0.3) {
inspector.add(ui.Label('⚠ NDVI alto — vegetación puede enmascarar señal espectral',
{color: '#cc0000'}));
}
if (!hayHallazgo && fav < 0.4) {
inspector.add(ui.Label('✗ Baja probabilidad de alteración hidrotermal',
{color: '#cc0000'}));
}
favLabel.setValue('Favorabilidad: ' + fav.toFixed(3));
var tipoSistema = '—';
if (v && v.Alunita_Index > 1.05) tipoSistema = 'Alta sulfuración (Au)';
else if (v && v.Sericita_Index > 1.05) tipoSistema = 'Baja sulfuración (Au-Ag)';
else if (v && v.Oxidos_Index > 1.15) tipoSistema = 'Gossan / oxidado';
interpretaLabel.setValue('Tipo: ' + tipoSistema);
});
});
// ════════════════════════════════════════════════════════════════════════════
// 13. LEYENDA
// ════════════════════════════════════════════════════════════════════════════
var legend = ui.Panel({
style: {position: 'bottom-right', padding: '8px 14px', backgroundColor: 'white',
border: '1px solid #ccc'}
});
legend.add(ui.Label('EXPLORACIÓN Au — LEYENDA',
{fontWeight: 'bold', fontSize: '13px', color: 'navy', margin: '0 0 6px 0'}));
var addRow = function (color, label) {
var row = ui.Panel({layout: ui.Panel.Layout.Flow('horizontal'), style: {margin: '2px 0'}});
row.add(ui.Label('█', {color: color, fontSize: '18px', margin: '0 6px 0 0'}));
row.add(ui.Label(label, {fontSize: '12px'}));
legend.add(row);
};
addRow('#FF00FF', 'Zona Máxima Prioridad Au');
addRow('#FF0000', 'Alta Favorabilidad (>0.75)');
addRow('#FF8C00', 'Moderada Favorabilidad (0.55–0.75)');
addRow('#FFFF00', 'Baja-Moderada (0.35–0.55)');
addRow('#00FF00', 'Brechas conocidas (validación)');
legend.add(ui.Label('────────────────', {color: '#ccc', margin: '4px 0'}));
legend.add(ui.Label('RGB Mineralógico:', {fontWeight: 'bold', fontSize: '12px'}));
legend.add(ui.Label('R=Alunita G=Sericita B=Óxidos', {fontSize: '11px', color: 'gray'}));
Map.add(legend);
// ════════════════════════════════════════════════════════════════════════════
// 14. EXPORTACIONES
// NOTA: TIR se exporta a 90 m (resolución nativa), SWIR/VNIR a 30 m
// ════════════════════════════════════════════════════════════════════════════
// ── Producto 1: Stack completo VNIR+SWIR (30 m) ──────────────────────────
Export.image.toDrive({
image: ee.Image.cat([
zonaProspectiva.unmask(0).toFloat().rename('Zona_Prioridad_Au'),
favorabilidadAu.toFloat().rename('Favorabilidad_Au'),
clusters.toFloat().rename('Clusters_KMeans'),
alunitaIndex.toFloat().rename('Alunita_B7B5'),
caolintaIndex.toFloat().rename('Caolinita_B6B5'),
sericitaIndex.toFloat().rename('Sericita_B5B6'),
oxidosIndex.toFloat().rename('Oxidos_Fe_B2B1'),
ohIndex.toFloat().rename('OH_B6B9_B8'),
propiliticaIndex.toFloat().rename('Propilitica_B4B5')
]),
description: 'Stack_Completo_VNIR_SWIR_30m',
folder: 'GEE_Exploracion_Mineral_Au',
region: aoiLocal,
scale: 30,
maxPixels: 1e9,
crs: 'EPSG:4326'
});
// ── Producto 2: Índices TIR (90 m — resolución nativa) ────────────────────
Export.image.toDrive({
image: ee.Image.cat([
siliceIndex.toFloat().rename('Silice_Ninomiya2003'),
carbonatoIndex.toFloat().rename('Carbonatos_TIR')
]),
description: 'Indices_TIR_90m_Nativos',
folder: 'GEE_Exploracion_Mineral_Au',
region: aoiLocal,
scale: 90,
maxPixels: 1e9,
crs: 'EPSG:4326'
});
// ── Producto 3: Mapa de favorabilidad Au (30 m) ───────────────────────────
Export.image.toDrive({
image: favorabilidadAu.toFloat(),
description: 'Favorabilidad_Au_Epitermal',
folder: 'GEE_Exploracion_Mineral_Au',
region: aoiLocal,
scale: 30,
maxPixels: 1e9,
crs: 'EPSG:4326'
});
// ── Producto 4: Clusters K-Means (30 m) ──────────────────────────────────
Export.image.toDrive({
image: clusters.toFloat(),
description: 'Clusters_KMeans_8clases',
folder: 'GEE_Exploracion_Mineral_Au',
region: aoiLocal,
scale: 30,
maxPixels: 1e9,
crs: 'EPSG:4326'
});
// ── Producto 5: RGB mineralógico para visualización (30 m) ────────────────
var rgbExport = ee.Image.cat([
alunitaIndex.unitScale(0.9, 1.3).multiply(255),
sericitaIndex.unitScale(0.9, 1.2).multiply(255),
oxidosIndex.unitScale(0.8, 1.4).multiply(255)
]).toByte();
Export.image.toDrive({
image: rgbExport,
description: 'RGB_Mineralogico_Alunita_Sericita_Oxidos',
folder: 'GEE_Exploracion_Mineral_Au',
region: aoiLocal,
scale: 30,
maxPixels: 1e9,
crs: 'EPSG:4326'
});
// ── Producto 6: Zona prospectiva vectorizada (shapefile) ──────────────────
var vectores = zonaProspectiva.reduceToVectors({
geometry: aoiLocal,
scale: 30,
geometryType: 'polygon',
eightConnected: false,
maxPixels: 1e9,
bestEffort: true
});
Export.table.toDrive({
collection: vectores,
description: 'Zonas_Prospectivas_Au_Vector',
folder: 'GEE_Exploracion_Mineral_Au',
fileFormat: 'SHP'
});
// ════════════════════════════════════════════════════════════════════════════
// 15. ESTADÍSTICAS Y RESUMEN CONSOLA
// ════════════════════════════════════════════════════════════════════════════
var pixelCount = zonaProspectiva.reduceRegion({
reducer: ee.Reducer.count(),
geometry: aoiLocal,
scale: 30,
maxPixels: 1e9
});
print('══════════════════════════════════════════════════════');
print('🥇 EXPLORACIÓN MINERAL Au — SISTEMA CORREGIDO v2.1');
print('══════════════════════════════════════════════════════');
print('📦 EXPORTACIONES (6 productos, en la pestaña Tasks):');
print(' 1. Stack_Completo_VNIR_SWIR_30m.tif (9 bandas)');
print(' 2. Indices_TIR_90m_Nativos.tif (2 bandas, 90 m)');
print(' 3. Favorabilidad_Au_Epitermal.tif (continua 0-1)');
print(' 4. Clusters_KMeans_8clases.tif (0-7)');
print(' 5. RGB_Mineralogico_*.tif (visualización)');
print(' 6. Zonas_Prospectivas_Au_Vector.shp (polígonos)');
print('══════════════════════════════════════════════════════');
print('📊 Píxeles en zona de máxima prioridad Au:');
print(pixelCount);
print('🎯 Clusters prospectivos (ajustar tras inspección):');
print(prospectiveClusters);
print('══════════════════════════════════════════════════════');
print('💡 WORKFLOW RECOMENDADO:');
print(' PASO 1 → Revisa el mensaje "①②③" de arriba: confirma que SÍ hay ASTER');
print(' PASO 2 → Activar capa "Clusters K-Means" y hacer clic');
print(' en zonas de alta favorabilidad (rojo/naranja)');
print(' PASO 3 → Anotar IDs de clusters → actualizar prospectiveClusters');
print(' PASO 4 → Ajustar umbral .gt(0.55) si necesitas más/menos área');
print(' PASO 5 → Ejecutar exportaciones desde pestaña Tasks');
print(' PASO 6 → QGIS/ArcGIS → superponer con geología regional');
print(' PASO 7 → Validar con capas de brechas conocidas');
print('══════════════════════════════════════════════════════');
print('⚠ NOTA: ASTER detecta halos de ALTERACIÓN (proxies de Au).');
print(' La confirmación de oro requiere muestreo geoquímico de campo.');
print('══════════════════════════════════════════════════════');