You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
aisentinel/server/utils/sentinel.js

231 lines
7.4 KiB

import { createCanvas } from 'canvas';
import axios from 'axios';
class SentinelHubService {
constructor() {
this.baseUrl = 'https://services.sentinel-hub.com/ogc/wms';
this.instanceId = process.env.SENTINEL_INSTANCE_ID;
}
async getData(polygon, date = null) {
// Генерируем mock данные для демонстрации
const bbox = this.getBoundingBox(polygon);
return {
redBand: this.generateMockBandData(bbox, 0.1, 0.3),
nirBand: this.generateMockBandData(bbox, 0.4, 0.8),
date: date || new Date().toISOString().split('T')[0],
bbox: bbox
};
}
getBoundingBox(polygon) {
const coords = polygon.coordinates[0];
const lats = coords.map(coord => coord[1]);
const lons = coords.map(coord => coord[0]);
return {
minLon: Math.min(...lons),
minLat: Math.min(...lats),
maxLon: Math.max(...lons),
maxLat: Math.max(...lats)
};
}
generateMockBandData(bbox, min, max) {
// Генерируем mock растровые данные
const width = 200;
const height = 200;
const data = [];
// Создаем паттерн для более реалистичных данных
for (let y = 0; y < height; y++) {
const row = [];
for (let x = 0; x < width; x++) {
// Создаем паттерн с некоторой пространственной корреляцией
const baseValue = min + Math.random() * (max - min);
// Добавляем некоторые паттерны
const pattern1 = Math.sin(x * 0.1) * 0.1;
const pattern2 = Math.cos(y * 0.1) * 0.1;
const noise = (Math.random() - 0.5) * 0.2;
const value = Math.max(min, Math.min(max, baseValue + pattern1 + pattern2 + noise));
row.push(value);
}
data.push(row);
}
return {
data: data,
width: width,
height: height,
bbox: bbox
};
}
}
const sentinelService = new SentinelHubService();
export async function getSentinelData(polygon, date) {
return await sentinelService.getData(polygon, date);
}
export async function calculateNDVI(satelliteData) {
const { redBand, nirBand } = satelliteData;
const ndviValues = [];
// Создаем canvas для генерации изображения
const canvas = createCanvas(redBand.width, redBand.height);
const ctx = canvas.getContext('2d');
// Создаем ImageData для манипуляций с пикселями
const imageData = ctx.createImageData(redBand.width, redBand.height);
for (let y = 0; y < redBand.height; y++) {
for (let x = 0; x < redBand.width; x++) {
const nir = nirBand.data[y][x];
const red = redBand.data[y][x];
// Calculate NDVI
let ndvi = (nir - red) / (nir + red);
// Обрабатываем случаи деления на ноль
if (isNaN(ndvi) || !isFinite(ndvi)) {
ndvi = -1;
}
ndviValues.push(ndvi);
// Convert NDVI to color
const color = ndviToColor(ndvi);
const index = (y * redBand.width + x) * 4;
imageData.data[index] = color[0]; // R
imageData.data[index + 1] = color[1]; // G
imageData.data[index + 2] = color[2]; // B
imageData.data[index + 3] = color[3]; // A
}
}
// Применяем ImageData к canvas
ctx.putImageData(imageData, 0, 0);
// Конвертируем в base64
const ndviImage = canvas.toDataURL('image/png');
return {
ndviValues,
ndviImage,
width: redBand.width,
height: redBand.height,
bbox: redBand.bbox
};
}
function ndviToColor(ndvi) {
// Convert NDVI value to RGBA color
if (ndvi < -0.2) return [140, 140, 140, 200]; // Gray - water/clouds
if (ndvi < 0) return [210, 180, 140, 200]; // Tan - soil
if (ndvi < 0.1) return [255, 255, 200, 200]; // Light yellow - very sparse
if (ndvi < 0.3) return [255, 255, 0, 200]; // Yellow - sparse vegetation
if (ndvi < 0.6) return [0, 255, 0, 200]; // Green - moderate vegetation
return [0, 100, 0, 200]; // Dark green - dense vegetation
}
// Альтернативная функция для генерации градиентного изображения
export function generateGradientNDVI(width, height) {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
// Создаем горизонтальный градиент
const gradient = ctx.createLinearGradient(0, 0, width, 0);
gradient.addColorStop(0.0, '#8C8C8C'); // Вода/Облака
gradient.addColorStop(0.2, '#D2B48C'); // Почва
gradient.addColorStop(0.4, '#FFFF00'); // Слабая растительность
gradient.addColorStop(0.7, '#00FF00'); // Умеренная растительность
gradient.addColorStop(1.0, '#006400'); // Густая растительность
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// Добавляем вертикальные вариации
const verticalGradient = ctx.createLinearGradient(0, 0, 0, height);
verticalGradient.addColorStop(0, 'rgba(255,255,255,0.3)');
verticalGradient.addColorStop(1, 'rgba(0,0,0,0.3)');
ctx.fillStyle = verticalGradient;
ctx.fillRect(0, 0, width, height);
// Добавляем некоторые паттерны для реалистичности
ctx.fillStyle = 'rgba(0,0,0,0.1)';
for (let i = 0; i < 50; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const size = Math.random() * 10 + 5;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
return canvas.toDataURL('image/png');
}
// Функция для создания паттернов поля
export function generateFieldPatternNDVI(width, height) {
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
// Заливаем базовым цветом (умеренная растительность)
ctx.fillStyle = '#00FF00';
ctx.fillRect(0, 0, width, height);
// Добавляем полосы разной растительности
const stripeHeight = height / 8;
for (let i = 0; i < 8; i++) {
const y = i * stripeHeight;
if (i % 4 === 0) {
// Густая растительность
ctx.fillStyle = '#006400';
} else if (i % 4 === 2) {
// Слабая растительность
ctx.fillStyle = '#FFFF00';
} else {
// Умеренная растительность
ctx.fillStyle = '#00FF00';
}
ctx.fillRect(0, y, width, stripeHeight);
}
// Добавляем случайные пятна проблемных зон
ctx.fillStyle = '#D2B48C';
for (let i = 0; i < 15; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const size = Math.random() * 20 + 10;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
// Добавляем водные объекты
ctx.fillStyle = '#8C8C8C';
for (let i = 0; i < 3; i++) {
const x = Math.random() * width * 0.8;
const y = Math.random() * height * 0.8;
const w = Math.random() * 30 + 20;
const h = Math.random() * 30 + 20;
ctx.fillRect(x, y, w, h);
}
return canvas.toDataURL('image/png');
}
export default {
getSentinelData,
calculateNDVI,
generateGradientNDVI,
generateFieldPatternNDVI
};