Servidor REST em TypeScript/Express para servir tiles de arquivos GeoTIFF multiespectrais com cálculo dinâmico de índices espectrais e colormaps científicos otimizados.
🎯 Objetivo: API performática para visualização de dados espectrais em mapas web (Leaflet, OpenLayers, Mapbox)
🚀 Performance Otimizada - ~2ms por tile (com cache), 6.8 tiles/s
📊 10 Índices Espectrais - NDVI, NDWI, EVI, SAVI e mais
🎨 5 Colormaps Científicos - Viridis, RdYlGn, RdYlBu, Spectral, Greys
🎯 Parser de Equações - Equações customizadas pixel-por-pixel
🗺️ Tiles XYZ - Compatível com Leaflet, OpenLayers, MapBox
💾 Cache Inteligente - LUT (lookup table) de 256 cores pré-computadas
🔄 Detecção Automática - Reconhece metadados GDAL (Red, Green, NIR, etc.)
npm installCrie o arquivo .env:
PORT=3001
DATA_DIR=./data
DEFAULT_GEOTIFF=odm_orthophoto_multi.tif
# Para testes
TEST_TIFFID=odm_orthophoto_multi.tif
TEST_Z=21
TEST_X=381005
TEST_Y=585528
TEST_SIZE=512npm start # Produção
npm run dev # Desenvolvimento (watch mode)npm run test-spectral # Gera 4 imagens de índices (NDVI, EVI, NDWI, Custom)
npm run test-tile # Testa tile RGB simplesGET /tile/:tiffId/:z/:x/:yParâmetros:
tiffId: Nome do arquivo GeoTIFF (sem extensão)z/x/y: Coordenadas do tile (zoom/x/y)size: Tamanho do tile (padrão: 256)
Exemplo:
curl "http://localhost:3001/tile/odm_orthophoto/20/381004/585533?size=512" -o tile.pngGET /index/:tiffId/:z/:x/:y?indexName=NDVI&colormap=RdYlGnParâmetros obrigatórios:
tiffId: Arquivo GeoTIFF multiespectral (ex:odm_orthophoto_multi.tif)z/x/y: Coordenadas do tile
Parâmetros opcionais:
indexName: Nome do índice (NDVI, EVI, etc.) - obrigatório se não usarequationequation: Equação customizada - obrigatório se não usarindexNamecolormap: Paleta de cores (padrão:RdYlGn)size: Tamanho do tile (padrão: 256)format: Formato de saída (png,jpeg,webp)
Exemplos:
# NDVI (vegetação)
curl "http://localhost:3001/index/odm_orthophoto_multi.tif/21/381005/585528?indexName=NDVI&colormap=RdYlGn" -o ndvi.png
# NDWI (água)
curl "http://localhost:3001/index/odm_orthophoto_multi.tif/21/381005/585528?indexName=NDWI&colormap=RdYlBu" -o ndwi.png
# EVI (biomassa)
curl "http://localhost:3001/index/odm_orthophoto_multi.tif/21/381005/585528?indexName=EVI" -o evi.png
# Equação customizada
curl "http://localhost:3001/index/odm_orthophoto_multi.tif/21/381005/585528?equation=(green-red)/(green+red)&colormap=viridis" -o custom.pngGET /index/listResposta:
{
"success": true,
"data": {
"count": 10,
"indices": [
{
"name": "Normalized Difference Vegetation Index",
"equation": "(nir - red) / (nir + red)",
"abbreviation": "NDVI"
}
]
}
}GET /vari/:tiffId/:z/:x/:yÍndice de vegetação para imagens RGB (sem banda NIR).
Exemplo:
curl "http://localhost:3001/vari/odm_orthophoto/20/381004/585533" -o vari.png| Índice | Equação | Aplicação | Range | Colormap |
|---|---|---|---|---|
| NDVI | (nir - red) / (nir + red) |
Saúde vegetal | 0.2-0.9 | RdYlGn |
| NDWI | (green - nir) / (green + nir) |
Detecção de água | -1 a 1 | RdYlBu |
| EVI | 2.5 * ((nir - red) / (nir + 6*red - 7.5*blue + 1)) |
Biomassa vegetal | -1 a 1 | RdYlGn |
| SAVI | ((nir - red) / (nir + red + 0.5)) * 1.5 |
Solo exposto | -1 a 1 | RdYlGn |
| VARI | (green - red) / (green + red - blue) |
Vegetação RGB | -1 a 1 | RdYlGn |
| NDMI | (nir - swir1) / (nir + swir1) |
Umidade | -1 a 1 | RdYlBu |
| NBR | (nir - swir2) / (nir + swir2) |
Áreas queimadas | -1 a 1 | Spectral |
| GNDVI | (nir - green) / (nir + green) |
Clorofila | -1 a 1 | RdYlGn |
| NDRE | (nir - rededge) / (nir + rededge) |
Stress vegetal | -1 a 1 | RdYlGn |
| MSAVI | (2*nir + 1 - sqrt((2*nir+1)^2 - 8*(nir-red))) / 2 |
SAVI melhorado | -1 a 1 | RdYlGn |
- 🟢 0.7 - 1.0 = Verde escuro = Vegetação densa e saudável
- 🟡 0.4 - 0.7 = Amarelo/Verde claro = Vegetação moderada
- 🔴 0.0 - 0.4 = Vermelho = Vegetação esparsa ou solo exposto
- ⚫ < 0.0 = Preto = Água ou superfícies artificiais
Importante: O NDVI usa visualRange: [0.2, 0.9] ao invés do range teórico [-1, 1] para melhor contraste na visualização de áreas vegetadas.
| Colormap | Descrição | Uso Recomendado |
|---|---|---|
| viridis | Perceptualmente uniforme (azul→verde→amarelo) | Dados contínuos, temperatura |
| RdYlGn | Divergente (vermelho→amarelo→verde) | NDVI, EVI, vegetação |
| RdYlBu | Divergente (vermelho→amarelo→azul) | NDWI, água vs solo |
| Spectral | Arco-íris científico | Multiplos fenômenos |
| Greys | Escala de cinza | Dados monocromáticos |
# Vegetação (valores altos = saudável)
?colormap=RdYlGn
# Água (valores altos = água)
?colormap=RdYlBu
# Dados contínuos sem polaridade
?colormap=viridisO sistema pré-computa 256 cores para cada colormap, resultando em performance de ~2ms por tile:
// Lookup Table (LUT) - 256 cores pré-calculadas
const lut = getColormapLUT('RdYlGn'); // Cache hit: ~0.001ms
// Mapeamento vetorizado (sem loops)
const rgb = lut[Math.floor(normalizedValue * 255)];Você pode criar índices customizados usando o parser de expressões:
// Bandas disponíveis: red, green, blue, nir, rededge, swir1, swir2
// Operadores: +, -, *, /, ^, sqrt, abs, log, exp, sin, cos, tan
// Exemplos
(nir - red) / (nir + red) // NDVI
sqrt(nir * green) / red // Custom
abs(nir - red) + 0.5 * green // Complex# Codificação URL: espaços = %20, + = %2B
curl "http://localhost:3001/index/odm_orthophoto_multi.tif/21/381005/585528?equation=(nir-red)/(nir%2Bred)&colormap=viridis"O sistema reconhece automaticamente metadados GDAL:
# Exemplo de metadata no GeoTIFF
Band 1: DESCRIPTION=Red
Band 2: DESCRIPTION=Green
Band 3: DESCRIPTION=NIR
Band 4: DESCRIPTION=RedEdgeAliases reconhecidos:
red→ Band 1green→ Band 2blue→ Band 3 (se presente)nir→ Band 3 ou 4 (depende do arquivo)rededge→ Band 4 ou 5swir1,swir2→ Bandas SWIR (se presentes)
# Primeiro acesso (cold start)
🗂️ Cache miss - loading: odm_orthophoto_multi.tif
⏱️ GeoTIFF loaded in 1150ms
# Acessos subsequentes (cached)
🗂️ Cache hit - GeoTIFF already loaded: odm_orthophoto_multi.tif
⏱️ GeoTIFF loaded in 0.01ms
# Colormap (LUT cached)
⏱️ Colormap applied in 2ms| Métrica | Valor |
|---|---|
| Cold start | ~1150ms |
| Cache hit | ~0.01ms (GeoTIFF) |
| Colormap (LUT) | ~2ms |
| Total (cached) | ~2ms |
| Throughput | 6.8 tiles/s |
| Speedup | 500x vs não-otimizado |
- GeoTIFF Cache: Mantém objetos GeoTIFF em memória (Map)
- LUT Cache: 256 cores pré-computadas por colormap
- Vetorização: Evita loops com lookup direto:
lut[index]
src/
├── index.ts # Entry point (Express server)
├── config/
│ └── appConfig.ts # Configurações (.env)
├── controllers/
│ ├── GeoTiffController.ts # Controller para tiles RGB
│ ├── SpectralIndexController.ts # Controller para índices espectrais
│ └── TileController.ts # Controller para VARI (RGB)
├── routes/
│ ├── index.ts # Router principal
│ ├── geotiffRoutes.ts # Rotas /tile
│ ├── spectralIndexRoutes.ts # Rotas /index
│ ├── tileRoutes.ts # (deprecated)
│ └── variRoutes.ts # Rotas /vari
├── services/
│ ├── GeoTiffManager.ts # Cache e gerenciamento de GeoTIFFs
│ └── TileService.ts # Lógica de geração de tiles
├── utils/
│ ├── bandMetadata.ts # Parser de metadados GDAL
│ ├── colorMaps.ts # Sistema de colormaps (chroma-js)
│ ├── expressionParser.ts # Parser de equações matemáticas
│ ├── spectralIndices.ts # Definições dos 10 índices
│ ├── tileUtils.ts # Conversão de coordenadas (proj4)
│ └── variUtils.ts # Lógica VARI (RGB)
└── types/
└── index.ts # TypeScript interfaces
scripts/
├── test-spectral-indices.js # Testa NDVI, EVI, NDWI, Custom
└── test-tile.js # Testa tile RGB
data/
├── odm_orthophoto.tif # RGB (15020×29317)
└── odm_orthophoto_multi.tif # Multiespectral (11979×12939, R+G+NIR+RedEdge)
| Dependência | Versão | Uso |
|---|---|---|
| chroma-js | ^3.1.2 | Colormaps científicos (viridis, RdYlGn, etc.) |
| express | ^5.1.0 | Servidor REST |
| geotiff | ^2.1.4-beta.0 | Leitura de GeoTIFF multiband |
| sharp | ^0.34.4 | Processamento de imagens (resize, encode) |
| proj4 | ^2.19.10 | Conversão de coordenadas |
| global-mercator | ^3.1.0 | Cálculo de tiles XYZ |
npm run test-spectralGera 4 imagens em img/:
ndvi.png- NDVI com RdYlGn (padrão)evi.png- EVI com RdYlGnndwi.png- NDWI com RdYlBucustom_equation.png- Equação customizada com viridis
🌿 Teste de Índices Espectrais Multiespectrais
📋 Listando índices disponíveis...
✅ Total de índices: 10
📊 Testando: NDVI
URL: http://localhost:3001/index/odm_orthophoto_multi.tif/21/381005/585528?indexName=NDVI&size=512
✅ Sucesso! Arquivo: /home/alisson/Global-drones/Project-geotiff/img/ndvi.png
📊 Tamanho: 1118 bytes
🎉 Todos os testes concluídos!
// NDVI Layer
const ndviLayer = L.tileLayer(
'http://localhost:3001/index/odm_orthophoto_multi.tif/{z}/{x}/{y}?indexName=NDVI&colormap=RdYlGn',
{ maxZoom: 22 }
);
// RGB Layer
const rgbLayer = L.tileLayer(
'http://localhost:3001/tile/odm_orthophoto/{z}/{x}/{y}',
{ maxZoom: 22 }
);
// Control
L.control.layers({
'RGB': rgbLayer,
'NDVI': ndviLayer
}).addTo(map);graph TB
A[Cliente: /index/21/381005/585528?indexName=NDVI] --> B{Cache GeoTIFF?}
B -->|Miss| C[Carrega do disco ~1150ms]
B -->|Hit| D[Retorna do cache ~0.01ms]
C --> E[Parse metadados GDAL]
D --> E
E --> F[Mapeia bandas: nir→Band3, red→Band1]
F --> G[Calcula NDVI pixel-por-pixel]
G --> H{Cache LUT?}
H -->|Miss| I[Gera 256 cores com chroma-js]
H -->|Hit| J[Usa LUT do cache]
I --> K[Aplica colormap vetorizado ~2ms]
J --> K
K --> L[Encode PNG com sharp]
L --> M[Retorna imagem]
Causa: Usando arquivo RGB (odm_orthophoto.tif) ao invés do multiespectral.
Solução:
# Corrija o .env
TEST_TIFFID=odm_orthophoto_multi.tif
# Ou use o arquivo correto na URL
/index/odm_orthophoto_multi.tif/21/381005/585528?indexName=NDVICausa: Range não otimizado para dados reais.
Solução: O sistema já usa visualRange: [0.2, 0.9] automaticamente para NDVI. Se ainda estiver errado:
- Verifique o colormap:
?colormap=RdYlGn(verde = alto, vermelho = baixo) - Teste outros colormaps:
?colormap=viridis
Causa: Cache frio ou arquivo muito grande.
Solução:
- Primeiro acesso é sempre lento (~1s)
- Acessos subsequentes são rápidos (~2ms)
- Use tiles menores:
?size=256ao invés de?size=512
- ✅ Performance: Sistema de cache LUT (500x speedup)
- ✅ Simplificação: Reduzidos colormaps de 12 para 5 essenciais
- ✅ Colormaps: Migrado para chroma-js (biblioteca profissional)
- ✅ Visual Range: NDVI otimizado para [0.2, 0.9]
- ✅ Cleanup: Removidos scripts de debug e logs verbosos
- 🎉 Release inicial
- 📊 10 índices espectrais
- 🎨 12 colormaps científicos
- 🎯 Parser de equações customizadas
MIT License - use livremente!
Desenvolvido para análise de dados espectrais de drones/satélites.
Repositório: Global-drones/Project-geotiff
⭐ Se este projeto foi útil, considere dar uma estrela no GitHub!