-
-
Save nillpo/e88715f75998ae7fad97cc50b8f79463 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * BLASTAM - Computer Program for Forecasting Occurrence of Rice Leaf Blast | |
| * Based on the research by Takashi HAYASHI and Yukio KOSHIMIZU (1988) | |
| * Tohoku Agricultural Experiment Station Research Report No.78 | |
| */ | |
| interface WeatherData { | |
| precipitation: number; // 降水量 (mm) | |
| windVelocity: number; // 風速 (m/s) | |
| sunshineHours: number; // 日照時間 (hours) | |
| temperature: number; // 気温 (°C) | |
| date: Date; | |
| } | |
| interface BlastForecastResult { | |
| infectionProbability: number; // 感染確率 (0-1) | |
| riskLevel: 'Low' | 'Medium' | 'High' | 'Very High'; | |
| wetPeriodHours: number; // 濡れ時間 | |
| favorableConditions: boolean; | |
| details: { | |
| precipitationFactor: number; | |
| windFactor: number; | |
| sunshineFactor: number; | |
| temperatureFactor: number; | |
| }; | |
| } | |
| class BlastamModel { | |
| private readonly CRITICAL_WET_PERIOD = 6; // 臨界濡れ時間(時間) | |
| private readonly OPTIMAL_TEMP_MIN = 20; // 最適温度範囲下限 | |
| private readonly OPTIMAL_TEMP_MAX = 28; // 最適温度範囲上限 | |
| /** | |
| * いもち病発生予察を実行 | |
| * @param weatherData 24時間分の気象データ | |
| * @returns 予察結果 | |
| */ | |
| public forecast(weatherData: WeatherData[]): BlastForecastResult { | |
| if (weatherData.length !== 24) { | |
| throw new Error('24時間分の気象データが必要です'); | |
| } | |
| // 濡れ時間の計算 | |
| const wetPeriodHours = this.calculateWetPeriod(weatherData); | |
| // 各気象要素の評価 | |
| const precipitationFactor = this.evaluatePrecipitation(weatherData); | |
| const windFactor = this.evaluateWind(weatherData); | |
| const sunshineFactor = this.evaluateSunshine(weatherData); | |
| const temperatureFactor = this.evaluateTemperature(weatherData); | |
| // 総合的な感染確率の計算 | |
| const infectionProbability = this.calculateInfectionProbability( | |
| wetPeriodHours, | |
| precipitationFactor, | |
| windFactor, | |
| sunshineFactor, | |
| temperatureFactor | |
| ); | |
| // 好適条件の判定 | |
| const favorableConditions = this.assessFavorableConditions( | |
| wetPeriodHours, | |
| precipitationFactor, | |
| temperatureFactor | |
| ); | |
| // リスクレベルの決定 | |
| const riskLevel = this.determineRiskLevel(infectionProbability); | |
| return { | |
| infectionProbability, | |
| riskLevel, | |
| wetPeriodHours, | |
| favorableConditions, | |
| details: { | |
| precipitationFactor, | |
| windFactor, | |
| sunshineFactor, | |
| temperatureFactor | |
| } | |
| }; | |
| } | |
| /** | |
| * 濡れ時間を計算 | |
| * 降雨、高湿度、露の条件を考慮 | |
| */ | |
| private calculateWetPeriod(weatherData: WeatherData[]): number { | |
| let wetHours = 0; | |
| for (const data of weatherData) { | |
| // 降雨がある場合 | |
| if (data.precipitation > 0) { | |
| wetHours += 1; | |
| continue; | |
| } | |
| // 高湿度条件の推定(気温と日照から) | |
| // 低温かつ日照少ない場合は高湿度と仮定 | |
| const isHighHumidity = data.temperature < 25 && data.sunshineHours < 0.5; | |
| // 夜間の露形成(日照時間0で気温が低い場合) | |
| const isDewFormation = data.sunshineHours === 0 && data.temperature < 20; | |
| if (isHighHumidity || isDewFormation) { | |
| wetHours += 1; | |
| } | |
| } | |
| return wetHours; | |
| } | |
| /** | |
| * 降水量因子の評価 | |
| */ | |
| private evaluatePrecipitation(weatherData: WeatherData[]): number { | |
| const totalPrecipitation = weatherData.reduce((sum, data) => sum + data.precipitation, 0); | |
| // 降水量による重み付け | |
| if (totalPrecipitation === 0) return 0.1; | |
| if (totalPrecipitation < 5) return 0.3; | |
| if (totalPrecipitation < 10) return 0.6; | |
| if (totalPrecipitation < 20) return 0.8; | |
| return 1.0; | |
| } | |
| /** | |
| * 風速因子の評価 | |
| */ | |
| private evaluateWind(weatherData: WeatherData[]): number { | |
| const avgWindVelocity = weatherData.reduce((sum, data) => sum + data.windVelocity, 0) / weatherData.length; | |
| // 適度な風速が胞子飛散に有利 | |
| if (avgWindVelocity < 1) return 0.3; | |
| if (avgWindVelocity < 3) return 0.8; | |
| if (avgWindVelocity < 5) return 1.0; | |
| if (avgWindVelocity < 7) return 0.7; | |
| return 0.4; // 強風は不利 | |
| } | |
| /** | |
| * 日照因子の評価 | |
| */ | |
| private evaluateSunshine(weatherData: WeatherData[]): number { | |
| const totalSunshine = weatherData.reduce((sum, data) => sum + data.sunshineHours, 0); | |
| // 日照時間が少ないほど感染に有利 | |
| if (totalSunshine < 3) return 1.0; | |
| if (totalSunshine < 6) return 0.7; | |
| if (totalSunshine < 9) return 0.4; | |
| return 0.1; | |
| } | |
| /** | |
| * 温度因子の評価 | |
| */ | |
| private evaluateTemperature(weatherData: WeatherData[]): number { | |
| const avgTemperature = weatherData.reduce((sum, data) => sum + data.temperature, 0) / weatherData.length; | |
| // 最適温度範囲での評価 | |
| if (avgTemperature >= this.OPTIMAL_TEMP_MIN && avgTemperature <= this.OPTIMAL_TEMP_MAX) { | |
| return 1.0; | |
| } | |
| // 最適範囲からの距離に応じて重み付け | |
| const distanceFromOptimal = Math.min( | |
| Math.abs(avgTemperature - this.OPTIMAL_TEMP_MIN), | |
| Math.abs(avgTemperature - this.OPTIMAL_TEMP_MAX) | |
| ); | |
| if (distanceFromOptimal <= 2) return 0.8; | |
| if (distanceFromOptimal <= 5) return 0.5; | |
| if (distanceFromOptimal <= 8) return 0.2; | |
| return 0.05; | |
| } | |
| /** | |
| * 感染確率を計算 | |
| */ | |
| private calculateInfectionProbability( | |
| wetPeriodHours: number, | |
| precipitationFactor: number, | |
| windFactor: number, | |
| sunshineFactor: number, | |
| temperatureFactor: number | |
| ): number { | |
| // 濡れ時間が臨界値未満の場合は感染確率が大幅に低下 | |
| const wetPeriodFactor = wetPeriodHours >= this.CRITICAL_WET_PERIOD ? 1.0 : | |
| Math.pow(wetPeriodHours / this.CRITICAL_WET_PERIOD, 2); | |
| // 各因子の重み付け合成 | |
| const baseInfectionRate = ( | |
| precipitationFactor * 0.3 + | |
| windFactor * 0.2 + | |
| sunshineFactor * 0.2 + | |
| temperatureFactor * 0.3 | |
| ); | |
| // 濡れ時間因子を乗算 | |
| const infectionProbability = baseInfectionRate * wetPeriodFactor; | |
| return Math.min(Math.max(infectionProbability, 0), 1); | |
| } | |
| /** | |
| * 好適条件の判定 | |
| */ | |
| private assessFavorableConditions( | |
| wetPeriodHours: number, | |
| precipitationFactor: number, | |
| temperatureFactor: number | |
| ): boolean { | |
| return wetPeriodHours >= this.CRITICAL_WET_PERIOD && | |
| precipitationFactor >= 0.5 && | |
| temperatureFactor >= 0.6; | |
| } | |
| /** | |
| * リスクレベルの決定 | |
| */ | |
| private determineRiskLevel(infectionProbability: number): 'Low' | 'Medium' | 'High' | 'Very High' { | |
| if (infectionProbability < 0.2) return 'Low'; | |
| if (infectionProbability < 0.4) return 'Medium'; | |
| if (infectionProbability < 0.7) return 'High'; | |
| return 'Very High'; | |
| } | |
| /** | |
| * 複数日間の予察を実行 | |
| */ | |
| public forecastMultipleDays(weatherDataByDay: WeatherData[][]): BlastForecastResult[] { | |
| return weatherDataByDay.map(dayData => this.forecast(dayData)); | |
| } | |
| /** | |
| * 感染好適期間の判定 | |
| */ | |
| public findInfectionPeriods(results: BlastForecastResult[]): Array<{start: number, end: number, maxProbability: number}> { | |
| const periods: Array<{start: number, end: number, maxProbability: number}> = []; | |
| let currentPeriodStart = -1; | |
| let maxProbInPeriod = 0; | |
| for (let i = 0; i < results.length; i++) { | |
| const result = results[i]; | |
| if (result.favorableConditions) { | |
| if (currentPeriodStart === -1) { | |
| currentPeriodStart = i; | |
| maxProbInPeriod = result.infectionProbability; | |
| } else { | |
| maxProbInPeriod = Math.max(maxProbInPeriod, result.infectionProbability); | |
| } | |
| } else { | |
| if (currentPeriodStart !== -1) { | |
| periods.push({ | |
| start: currentPeriodStart, | |
| end: i - 1, | |
| maxProbability: maxProbInPeriod | |
| }); | |
| currentPeriodStart = -1; | |
| maxProbInPeriod = 0; | |
| } | |
| } | |
| } | |
| // 最後の期間の処理 | |
| if (currentPeriodStart !== -1) { | |
| periods.push({ | |
| start: currentPeriodStart, | |
| end: results.length - 1, | |
| maxProbability: maxProbInPeriod | |
| }); | |
| } | |
| return periods; | |
| } | |
| } | |
| // 使用例 | |
| export { BlastamModel, WeatherData, BlastForecastResult }; | |
| // サンプル使用例 | |
| /* | |
| const model = new BlastamModel(); | |
| // サンプルデータ(24時間分) | |
| const sampleWeatherData: WeatherData[] = Array.from({length: 24}, (_, i) => ({ | |
| precipitation: i < 6 ? 2.5 : 0, // 最初の6時間降雨 | |
| windVelocity: 2.0 + Math.sin(i * Math.PI / 12), // 風速変動 | |
| sunshineHours: i >= 6 && i <= 18 ? 0.5 : 0, // 日中のみ日照 | |
| temperature: 22 + 3 * Math.sin((i - 6) * Math.PI / 12), // 温度変動 | |
| date: new Date(2024, 5, 15, i, 0, 0) | |
| })); | |
| const result = model.forecast(sampleWeatherData); | |
| console.log('感染確率:', (result.infectionProbability * 100).toFixed(1) + '%'); | |
| console.log('リスクレベル:', result.riskLevel); | |
| console.log('濡れ時間:', result.wetPeriodHours + '時間'); | |
| console.log('好適条件:', result.favorableConditions ? 'あり' : 'なし'); | |
| */ |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
葉いもち発生予察のコンピュータプログラム(BLASTAM)の開発