Skip to main content
Glama

HRFCO Service

station-mapper.ts14.4 kB
// HRFCO 관측소 제원정보를 다운받아 매핑 규칙을 생성하는 모듈 interface StationInfo { obs_code: string; obs_name: string; river_name?: string; location?: string; address?: string; agency?: string; latitude?: number; longitude?: number; data_type: 'dam' | 'waterlevel' | 'rainfall'; } interface StationMapping { [key: string]: { code: string; data_type: 'dam' | 'waterlevel' | 'rainfall'; name: string; river?: string; location?: string; }; } export class StationMapper { private stationMapping: StationMapping = {}; private isInitialized = false; // HRFCO API에서 관측소 제원정보를 다운받아 매핑 생성 async initializeMapping(): Promise<void> { try { console.log('🔄 HRFCO 관측소 제원정보 다운로드 시작...'); // 1. 댐 정보 다운로드 const damStations = await this.fetchStationInfo('dam'); console.log(`📊 댐 관측소 ${damStations.length}개 다운로드 완료`); // 2. 수위관측소 정보 다운로드 const waterlevelStations = await this.fetchStationInfo('waterlevel'); console.log(`📊 수위관측소 ${waterlevelStations.length}개 다운로드 완료`); // 3. 우량관측소 정보 다운로드 const rainfallStations = await this.fetchStationInfo('rainfall'); console.log(`📊 우량관측소 ${rainfallStations.length}개 다운로드 완료`); // 4. 매핑 규칙 생성 this.createMappingRules(damStations, 'dam'); this.createMappingRules(waterlevelStations, 'waterlevel'); this.createMappingRules(rainfallStations, 'rainfall'); this.isInitialized = true; console.log(`✅ 총 ${Object.keys(this.stationMapping).length}개 관측소 매핑 완료`); } catch (error) { console.error('❌ 관측소 매핑 초기화 실패:', error); // 실패 시 데모 데이터로 폴백 this.createDemoMapping(); } } // 저장된 관측소 정보 파일에서 데이터 로드 private async fetchStationInfo(dataType: string): Promise<StationInfo[]> { try { // 저장된 파일에서 데이터 로드 시도 const fs = require('fs'); const path = require('path'); const fileName = `${dataType}-stations.json`; // 절대 경로 사용 const filePath = path.join(__dirname, 'data', fileName); // 상대 경로도 시도 const relativePath = path.join(process.cwd(), 'netlify', 'functions', 'data', fileName); console.log(`🔍 ${dataType} 파일 경로 확인: ${filePath}`); console.log(`🔍 ${dataType} 상대 경로 확인: ${relativePath}`); let actualPath = null; if (fs.existsSync(filePath)) { actualPath = filePath; } else if (fs.existsSync(relativePath)) { actualPath = relativePath; } if (actualPath) { console.log(`📁 ${dataType} 저장된 파일에서 로드: ${actualPath}`); const fileData = fs.readFileSync(actualPath, 'utf8'); const stations = JSON.parse(fileData); console.log(`✅ ${dataType} 관측소 ${stations.length}개 파일에서 로드 완료`); // 문경시 관련 관측소 로그 출력 const mungyeongStations = stations.filter((station: any) => station.obs_name && station.obs_name.includes('문경') ); if (mungyeongStations.length > 0) { console.log(`📍 문경시 관련 ${dataType} 관측소:`); mungyeongStations.forEach((station: any) => { console.log(` - ${station.obs_name}: ${station.obs_code}`); }); } return stations; } else { console.log(`⚠️ ${dataType} 저장된 파일이 없음: ${filePath} 또는 ${relativePath}`); throw new Error(`저장된 ${dataType} 파일이 없습니다`); } } catch (error) { console.warn(`⚠️ ${dataType} 파일 로드 실패, 데모 데이터 사용:`, error); return this.getDemoStationInfo(dataType); } } // 매핑 규칙 생성 private createMappingRules(stations: StationInfo[], dataType: 'dam' | 'waterlevel' | 'rainfall'): void { stations.forEach(station => { if (!station.obs_code || !station.obs_name) return; // 1. 정확한 이름으로 매핑 this.stationMapping[station.obs_name] = { code: station.obs_code, data_type: dataType, name: station.obs_name, river: station.river_name, location: station.location || station.address }; // 2. 지역명 + 관측소명 조합으로 매핑 if (station.location) { const locationKey = `${station.location}${station.obs_name}`; this.stationMapping[locationKey] = { code: station.obs_code, data_type: dataType, name: station.obs_name, river: station.river_name, location: station.location }; } // 3. 하천명 + 관측소명 조합으로 매핑 if (station.river_name) { const riverKey = `${station.river_name}${station.obs_name}`; this.stationMapping[riverKey] = { code: station.obs_code, data_type: dataType, name: station.obs_name, river: station.river_name, location: station.location }; } // 4. 관측소명에서 키워드 추출하여 매핑 const keywords = this.extractKeywords(station.obs_name); keywords.forEach(keyword => { if (keyword.length > 1) { // 1글자 키워드는 제외 this.stationMapping[keyword] = { code: station.obs_code, data_type: dataType, name: station.obs_name, river: station.river_name, location: station.location }; } }); }); } // 관측소명에서 키워드 추출 private extractKeywords(name: string): string[] { const keywords: string[] = []; // 댐, 대교, 관측소 등 접미사 제거 const cleanName = name.replace(/[댐대교관측소]/g, ''); // 2글자 이상의 연속된 문자 추출 for (let i = 0; i < cleanName.length - 1; i++) { for (let j = i + 2; j <= cleanName.length; j++) { const keyword = cleanName.substring(i, j); if (keyword.length >= 2) { keywords.push(keyword); } } } return keywords; } // 관측소 코드 찾기 findStationCode(query: string): { code: string; data_type: string; name: string } | null { if (!this.isInitialized) { console.warn('⚠️ StationMapper가 초기화되지 않았습니다'); return null; } // 1. 정확한 매칭 if (this.stationMapping[query]) { const mapping = this.stationMapping[query]; return { code: mapping.code, data_type: mapping.data_type, name: mapping.name }; } // 2. 부분 매칭 (포함 관계) for (const [key, mapping] of Object.entries(this.stationMapping)) { if (key.includes(query) || query.includes(key)) { return { code: mapping.code, data_type: mapping.data_type, name: mapping.name }; } } // 3. 키워드 매칭 const queryKeywords = this.extractKeywords(query); for (const keyword of queryKeywords) { if (this.stationMapping[keyword]) { const mapping = this.stationMapping[keyword]; return { code: mapping.code, data_type: mapping.data_type, name: mapping.name }; } } return null; } // 데모 데이터 생성 (API 실패 시 사용) private createDemoMapping(): void { console.log('📝 데모 매핑 데이터 생성 중...'); // 주요 댐들 const demoDams = [ { name: '대청댐', code: '1018680', river: '금강', location: '대전' }, { name: '소양댐', code: '1018681', river: '한강', location: '춘천' }, { name: '충주댐', code: '1018682', river: '한강', location: '충주' }, { name: '안동댐', code: '1018683', river: '낙동강', location: '안동' }, { name: '임하댐', code: '1018684', river: '낙동강', location: '안동' }, { name: '합천댐', code: '1018685', river: '낙동강', location: '합천' }, ]; // 주요 수위관측소들 const demoWaterlevels = [ { name: '한강대교', code: '1018700', river: '한강', location: '서울' }, { name: '잠실대교', code: '1018701', river: '한강', location: '서울' }, { name: '성산대교', code: '1018702', river: '한강', location: '서울' }, { name: '반포대교', code: '1018703', river: '한강', location: '서울' }, { name: '동작대교', code: '1018704', river: '한강', location: '서울' }, { name: '한남대교', code: '1018705', river: '한강', location: '서울' }, { name: '평림댐', code: '5002201', river: '영산강', location: '장성군' }, { name: '장성군(평림댐)', code: '5002680', river: '영산강', location: '장성군' }, { name: '광주광역시(평림교)', code: '5002677', river: '영산강', location: '광주광역시' }, ]; // 주요 우량관측소들 const demoRainfalls = [ { name: '서울우량관측소', code: '1018825', river: '한강', location: '서울' }, { name: '부산우량관측소', code: '1018826', river: '낙동강', location: '부산' }, { name: '대구우량관측소', code: '1018827', river: '낙동강', location: '대구' }, { name: '인천우량관측소', code: '1018828', river: '한강', location: '인천' }, { name: '광주우량관측소', code: '1018829', river: '영산강', location: '광주' }, { name: '대전우량관측소', code: '1018830', river: '금강', location: '대전' }, { name: '문경시(농암리)', code: '20054010', river: '낙동강', location: '문경시' }, { name: '문경시(화산리)', code: '20054080', river: '낙동강', location: '문경시' }, { name: '문경시(김용리)', code: '20054020', river: '낙동강', location: '문경시' }, { name: '문경시(진안리)', code: '20054070', river: '낙동강', location: '문경시' }, { name: '문경시(동로면사무소)', code: '20044030', river: '낙동강', location: '문경시' }, { name: '평림댐', code: '99999999', river: '영산강', location: '장성군' }, { name: '장성군(평림댐)', code: '50024051', river: '영산강', location: '장성군' }, { name: '가평군(가평교)', code: '1018832', river: '한강', location: '가평군' }, ]; // 데모 데이터로 매핑 생성 [...demoDams, ...demoWaterlevels, ...demoRainfalls].forEach(station => { const dataType = demoDams.includes(station as any) ? 'dam' : demoWaterlevels.includes(station as any) ? 'waterlevel' : 'rainfall'; // 1. 정확한 이름으로 매핑 this.stationMapping[station.name] = { code: station.code, data_type: dataType, name: station.name, river: station.river, location: station.location }; // 2. 지역명 + 관측소명 조합으로 매핑 if (station.location) { const locationKey = `${station.location}${station.name}`; this.stationMapping[locationKey] = { code: station.code, data_type: dataType, name: station.name, river: station.river, location: station.location }; } // 3. 관측소명에서 키워드 추출하여 매핑 const keywords = this.extractKeywords(station.name); keywords.forEach(keyword => { if (keyword.length > 1) { this.stationMapping[keyword] = { code: station.code, data_type: dataType, name: station.name, river: station.river, location: station.location }; } }); // 4. 지역명으로도 매핑 if (station.location) { this.stationMapping[station.location] = { code: station.code, data_type: dataType, name: station.name, river: station.river, location: station.location }; } }); this.isInitialized = true; console.log(`✅ 데모 매핑 데이터 ${Object.keys(this.stationMapping).length}개 생성 완료`); } // 데모 관측소 정보 생성 private getDemoStationInfo(dataType: string): StationInfo[] { const demoData: Record<string, StationInfo[]> = { dam: [ { obs_code: '1018680', obs_name: '대청댐', river_name: '금강', location: '대전', data_type: 'dam' }, { obs_code: '1018681', obs_name: '소양댐', river_name: '한강', location: '춘천', data_type: 'dam' }, { obs_code: '1018682', obs_name: '충주댐', river_name: '한강', location: '충주', data_type: 'dam' }, ], waterlevel: [ { obs_code: '1018700', obs_name: '한강대교', river_name: '한강', location: '서울', data_type: 'waterlevel' }, { obs_code: '1018701', obs_name: '잠실대교', river_name: '한강', location: '서울', data_type: 'waterlevel' }, { obs_code: '1018702', obs_name: '성산대교', river_name: '한강', location: '서울', data_type: 'waterlevel' }, ], rainfall: [ { obs_code: '1018825', obs_name: '서울우량관측소', river_name: '한강', location: '서울', data_type: 'rainfall' }, { obs_code: '1018826', obs_name: '부산우량관측소', river_name: '낙동강', location: '부산', data_type: 'rainfall' }, { obs_code: '1018827', obs_name: '대구우량관측소', river_name: '낙동강', location: '대구', data_type: 'rainfall' }, ] }; return demoData[dataType] || []; } // 매핑 통계 정보 getMappingStats(): { total: number; byType: Record<string, number> } { const stats = { total: 0, byType: { dam: 0, waterlevel: 0, rainfall: 0 } }; Object.values(this.stationMapping).forEach(mapping => { stats.total++; stats.byType[mapping.data_type]++; }); return stats; } } // 싱글톤 인스턴스 export const stationMapper = new StationMapper();

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kwenhwang/hrfco-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server