Spaces:
Sleeping
Sleeping
| // Real-time environmental data integration | |
| class RealTimeEnvironmentalData { | |
| constructor() { | |
| this.apiKeys = { | |
| openWeather: process.env.REACT_APP_OPENWEATHER_API_KEY, | |
| airQuality: process.env.REACT_APP_AIRQUALITY_API_KEY, | |
| nasa: process.env.REACT_APP_NASA_API_KEY | |
| }; | |
| this.cache = new Map(); | |
| this.cacheTimeout = 5 * 60 * 1000; // 5 minutes | |
| } | |
| // Real-time air quality data from multiple sources | |
| async getAirQualityData(lat, lon) { | |
| const cacheKey = `air_${lat}_${lon}`; | |
| if (this.isCached(cacheKey)) { | |
| return this.cache.get(cacheKey).data; | |
| } | |
| try { | |
| // Primary: OpenWeatherMap Air Pollution API | |
| const response = await fetch( | |
| `https://api.openweathermap.org/data/2.5/air_pollution?lat=${lat}&lon=${lon}&appid=${this.apiKeys.openWeather}` | |
| ); | |
| const data = await response.json(); | |
| const processedData = { | |
| timestamp: new Date().toISOString(), | |
| location: { lat, lon }, | |
| aqi: data.list[0].main.aqi, | |
| components: { | |
| co: data.list[0].components.co, | |
| no: data.list[0].components.no, | |
| no2: data.list[0].components.no2, | |
| o3: data.list[0].components.o3, | |
| so2: data.list[0].components.so2, | |
| pm2_5: data.list[0].components.pm2_5, | |
| pm10: data.list[0].components.pm10, | |
| nh3: data.list[0].components.nh3 | |
| }, | |
| healthRisk: this.calculateHealthRisk(data.list[0].main.aqi), | |
| recommendations: this.getHealthRecommendations(data.list[0].main.aqi) | |
| }; | |
| this.cache.set(cacheKey, { data: processedData, timestamp: Date.now() }); | |
| return processedData; | |
| } catch (error) { | |
| console.warn('Failed to fetch real-time air quality data:', error); | |
| return this.getFallbackAirQualityData(lat, lon); | |
| } | |
| } | |
| // Real-time weather and climate data | |
| async getClimateData(lat, lon) { | |
| const cacheKey = `climate_${lat}_${lon}`; | |
| if (this.isCached(cacheKey)) { | |
| return this.cache.get(cacheKey).data; | |
| } | |
| try { | |
| const [current, forecast] = await Promise.all([ | |
| fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${this.apiKeys.openWeather}&units=metric`), | |
| fetch(`https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${this.apiKeys.openWeather}&units=metric`) | |
| ]); | |
| const currentData = await current.json(); | |
| const forecastData = await forecast.json(); | |
| const processedData = { | |
| timestamp: new Date().toISOString(), | |
| location: { lat, lon, name: currentData.name }, | |
| current: { | |
| temperature: currentData.main.temp, | |
| humidity: currentData.main.humidity, | |
| pressure: currentData.main.pressure, | |
| windSpeed: currentData.wind.speed, | |
| windDirection: currentData.wind.deg, | |
| visibility: currentData.visibility, | |
| uvIndex: currentData.uvi || 'N/A', | |
| cloudCover: currentData.clouds.all | |
| }, | |
| forecast: forecastData.list.slice(0, 5).map(item => ({ | |
| datetime: item.dt_txt, | |
| temperature: item.main.temp, | |
| humidity: item.main.humidity, | |
| description: item.weather[0].description, | |
| precipitation: item.rain ? item.rain['3h'] || 0 : 0 | |
| })), | |
| climateIndicators: this.calculateClimateIndicators(currentData, forecastData) | |
| }; | |
| this.cache.set(cacheKey, { data: processedData, timestamp: Date.now() }); | |
| return processedData; | |
| } catch (error) { | |
| console.warn('Failed to fetch real-time climate data:', error); | |
| return this.getFallbackClimateData(lat, lon); | |
| } | |
| } | |
| // Real biodiversity data from GBIF and iNaturalist | |
| async getBiodiversityData(lat, lon, radius = 10) { | |
| const cacheKey = `biodiversity_${lat}_${lon}_${radius}`; | |
| if (this.isCached(cacheKey)) { | |
| return this.cache.get(cacheKey).data; | |
| } | |
| try { | |
| // GBIF API for species occurrence data | |
| const gbifResponse = await fetch( | |
| `https://api.gbif.org/v1/occurrence/search?decimalLatitude=${lat}&decimalLongitude=${lon}&radius=${radius}&limit=100` | |
| ); | |
| const gbifData = await gbifResponse.json(); | |
| // Process GBIF data | |
| const speciesData = gbifData.results.reduce((acc, occurrence) => { | |
| if (occurrence.species && occurrence.scientificName) { | |
| const key = occurrence.species; | |
| if (!acc[key]) { | |
| acc[key] = { | |
| scientificName: occurrence.scientificName, | |
| commonName: occurrence.vernacularName || 'Unknown', | |
| kingdom: occurrence.kingdom, | |
| phylum: occurrence.phylum, | |
| class: occurrence.class, | |
| order: occurrence.order, | |
| family: occurrence.family, | |
| genus: occurrence.genus, | |
| occurrences: 0, | |
| lastSeen: null, | |
| conservationStatus: this.getConservationStatus(occurrence.species) | |
| }; | |
| } | |
| acc[key].occurrences++; | |
| if (!acc[key].lastSeen || new Date(occurrence.eventDate) > new Date(acc[key].lastSeen)) { | |
| acc[key].lastSeen = occurrence.eventDate; | |
| } | |
| } | |
| return acc; | |
| }, {}); | |
| const processedData = { | |
| timestamp: new Date().toISOString(), | |
| location: { lat, lon, radius }, | |
| totalSpecies: Object.keys(speciesData).length, | |
| totalOccurrences: gbifData.count, | |
| species: Object.values(speciesData), | |
| biodiversityIndex: this.calculateBiodiversityIndex(Object.values(speciesData)), | |
| threatLevel: this.assessThreatLevel(Object.values(speciesData)), | |
| recommendations: this.getBiodiversityRecommendations(Object.values(speciesData)) | |
| }; | |
| this.cache.set(cacheKey, { data: processedData, timestamp: Date.now() }); | |
| return processedData; | |
| } catch (error) { | |
| console.warn('Failed to fetch real-time biodiversity data:', error); | |
| return this.getFallbackBiodiversityData(lat, lon); | |
| } | |
| } | |
| // Real-time carbon emissions data | |
| async getCarbonEmissionsData(country = 'US') { | |
| const cacheKey = `carbon_${country}`; | |
| if (this.isCached(cacheKey)) { | |
| return this.cache.get(cacheKey).data; | |
| } | |
| try { | |
| // CO2 Signal API for real-time carbon intensity | |
| const response = await fetch( | |
| `https://api.co2signal.com/v1/latest?countryCode=${country}`, | |
| { | |
| headers: { | |
| 'auth-token': process.env.REACT_APP_CO2_SIGNAL_API_KEY | |
| } | |
| } | |
| ); | |
| const data = await response.json(); | |
| const processedData = { | |
| timestamp: new Date().toISOString(), | |
| country: country, | |
| carbonIntensity: data.data.carbonIntensity, | |
| fossilFuelPercentage: data.data.fossilFuelPercentage, | |
| renewablePercentage: 100 - data.data.fossilFuelPercentage, | |
| energyMix: { | |
| nuclear: data.data.powerConsumptionBreakdown?.nuclear || 0, | |
| geothermal: data.data.powerConsumptionBreakdown?.geothermal || 0, | |
| biomass: data.data.powerConsumptionBreakdown?.biomass || 0, | |
| coal: data.data.powerConsumptionBreakdown?.coal || 0, | |
| wind: data.data.powerConsumptionBreakdown?.wind || 0, | |
| solar: data.data.powerConsumptionBreakdown?.solar || 0, | |
| hydro: data.data.powerConsumptionBreakdown?.hydro || 0, | |
| gas: data.data.powerConsumptionBreakdown?.gas || 0, | |
| oil: data.data.powerConsumptionBreakdown?.oil || 0 | |
| }, | |
| trend: this.calculateCarbonTrend(data.data.carbonIntensity), | |
| recommendations: this.getCarbonRecommendations(data.data.carbonIntensity) | |
| }; | |
| this.cache.set(cacheKey, { data: processedData, timestamp: Date.now() }); | |
| return processedData; | |
| } catch (error) { | |
| console.warn('Failed to fetch real-time carbon data:', error); | |
| return this.getFallbackCarbonData(country); | |
| } | |
| } | |
| // Helper methods | |
| isCached(key) { | |
| const cached = this.cache.get(key); | |
| return cached && (Date.now() - cached.timestamp) < this.cacheTimeout; | |
| } | |
| calculateHealthRisk(aqi) { | |
| if (aqi <= 1) return 'Good'; | |
| if (aqi <= 2) return 'Fair'; | |
| if (aqi <= 3) return 'Moderate'; | |
| if (aqi <= 4) return 'Poor'; | |
| return 'Very Poor'; | |
| } | |
| getHealthRecommendations(aqi) { | |
| const recommendations = { | |
| 1: ['Air quality is good. Enjoy outdoor activities!'], | |
| 2: ['Air quality is fair. Sensitive individuals should consider reducing outdoor activities.'], | |
| 3: ['Air quality is moderate. Consider wearing a mask during outdoor activities.'], | |
| 4: ['Air quality is poor. Limit outdoor activities, especially for sensitive groups.'], | |
| 5: ['Air quality is very poor. Avoid outdoor activities. Keep windows closed.'] | |
| }; | |
| return recommendations[aqi] || recommendations[5]; | |
| } | |
| calculateClimateIndicators(current, forecast) { | |
| const temps = forecast.list.map(item => item.main.temp); | |
| const avgTemp = temps.reduce((a, b) => a + b, 0) / temps.length; | |
| const tempVariation = Math.max(...temps) - Math.min(...temps); | |
| return { | |
| temperatureTrend: avgTemp > current.main.temp ? 'Rising' : 'Falling', | |
| temperatureVariation: tempVariation, | |
| extremeWeatherRisk: tempVariation > 15 ? 'High' : tempVariation > 10 ? 'Medium' : 'Low', | |
| climateStability: tempVariation < 5 ? 'Stable' : tempVariation < 10 ? 'Moderate' : 'Unstable' | |
| }; | |
| } | |
| calculateBiodiversityIndex(species) { | |
| if (species.length === 0) return 0; | |
| const totalOccurrences = species.reduce((sum, s) => sum + s.occurrences, 0); | |
| let shannonIndex = 0; | |
| species.forEach(s => { | |
| const proportion = s.occurrences / totalOccurrences; | |
| if (proportion > 0) { | |
| shannonIndex -= proportion * Math.log(proportion); | |
| } | |
| }); | |
| return Math.round(shannonIndex * 100) / 100; | |
| } | |
| getConservationStatus(speciesKey) { | |
| // This would typically query IUCN Red List API | |
| // For now, return random status for demo | |
| const statuses = ['Least Concern', 'Near Threatened', 'Vulnerable', 'Endangered', 'Critically Endangered']; | |
| return statuses[Math.floor(Math.random() * statuses.length)]; | |
| } | |
| assessThreatLevel(species) { | |
| const threatenedCount = species.filter(s => | |
| ['Vulnerable', 'Endangered', 'Critically Endangered'].includes(s.conservationStatus) | |
| ).length; | |
| const threatRatio = threatenedCount / species.length; | |
| if (threatRatio > 0.5) return 'High'; | |
| if (threatRatio > 0.3) return 'Medium'; | |
| return 'Low'; | |
| } | |
| getBiodiversityRecommendations(species) { | |
| const recommendations = []; | |
| const threatenedCount = species.filter(s => | |
| ['Vulnerable', 'Endangered', 'Critically Endangered'].includes(s.conservationStatus) | |
| ).length; | |
| if (threatenedCount > 0) { | |
| recommendations.push(`${threatenedCount} threatened species detected - consider conservation actions`); | |
| } | |
| if (species.length < 10) { | |
| recommendations.push('Low species diversity - habitat restoration may be beneficial'); | |
| } | |
| recommendations.push('Support local conservation efforts and sustainable practices'); | |
| return recommendations; | |
| } | |
| calculateCarbonTrend(intensity) { | |
| if (intensity < 200) return 'Very Low'; | |
| if (intensity < 400) return 'Low'; | |
| if (intensity < 600) return 'Moderate'; | |
| if (intensity < 800) return 'High'; | |
| return 'Very High'; | |
| } | |
| getCarbonRecommendations(intensity) { | |
| const recommendations = []; | |
| if (intensity > 600) { | |
| recommendations.push('High carbon intensity - consider renewable energy options'); | |
| recommendations.push('Reduce energy consumption during peak hours'); | |
| } else if (intensity > 400) { | |
| recommendations.push('Moderate carbon intensity - good time for energy-efficient activities'); | |
| } else { | |
| recommendations.push('Low carbon intensity - optimal time for energy-intensive activities'); | |
| } | |
| return recommendations; | |
| } | |
| // Fallback data methods for when APIs fail | |
| getFallbackAirQualityData(lat, lon) { | |
| return { | |
| timestamp: new Date().toISOString(), | |
| location: { lat, lon }, | |
| aqi: 2, | |
| components: { | |
| co: 233.4, | |
| no: 0.01, | |
| no2: 13.4, | |
| o3: 54.3, | |
| so2: 7.8, | |
| pm2_5: 8.9, | |
| pm10: 15.2, | |
| nh3: 2.1 | |
| }, | |
| healthRisk: 'Fair', | |
| recommendations: ['Air quality is fair. Sensitive individuals should consider reducing outdoor activities.'], | |
| dataSource: 'Fallback - API unavailable' | |
| }; | |
| } | |
| getFallbackClimateData(lat, lon) { | |
| return { | |
| timestamp: new Date().toISOString(), | |
| location: { lat, lon, name: 'Unknown Location' }, | |
| current: { | |
| temperature: 22, | |
| humidity: 65, | |
| pressure: 1013, | |
| windSpeed: 3.2, | |
| windDirection: 180, | |
| visibility: 10000, | |
| uvIndex: 5, | |
| cloudCover: 40 | |
| }, | |
| forecast: [], | |
| climateIndicators: { | |
| temperatureTrend: 'Stable', | |
| temperatureVariation: 8, | |
| extremeWeatherRisk: 'Low', | |
| climateStability: 'Stable' | |
| }, | |
| dataSource: 'Fallback - API unavailable' | |
| }; | |
| } | |
| getFallbackBiodiversityData(lat, lon) { | |
| return { | |
| timestamp: new Date().toISOString(), | |
| location: { lat, lon, radius: 10 }, | |
| totalSpecies: 0, | |
| totalOccurrences: 0, | |
| species: [], | |
| biodiversityIndex: 0, | |
| threatLevel: 'Unknown', | |
| recommendations: ['Real-time biodiversity data unavailable. Consider manual species observation.'], | |
| dataSource: 'Fallback - API unavailable' | |
| }; | |
| } | |
| getFallbackCarbonData(country) { | |
| return { | |
| timestamp: new Date().toISOString(), | |
| country: country, | |
| carbonIntensity: 400, | |
| fossilFuelPercentage: 60, | |
| renewablePercentage: 40, | |
| energyMix: { | |
| nuclear: 20, | |
| geothermal: 1, | |
| biomass: 5, | |
| coal: 25, | |
| wind: 8, | |
| solar: 6, | |
| hydro: 15, | |
| gas: 18, | |
| oil: 2 | |
| }, | |
| trend: 'Stable', | |
| recommendations: ['Carbon intensity is moderate. Consider renewable energy options.'], | |
| dataSource: 'Fallback - API unavailable' | |
| }; | |
| } | |
| } | |
| export default new RealTimeEnvironmentalData(); |