import logging import traceback from typing import List, Dict, Tuple, Optional, Union, Any from landmark_data import ALL_LANDMARKS, get_all_landmark_prompts from landmark_activities import LANDMARK_ACTIVITIES class LandmarkDataManager: """ 專門處理地標數據的載入、管理和查詢功能,包括地標信息、提示詞和活動建議 """ def __init__(self): """ initialize landmark related """ self.logger = logging.getLogger(__name__) self.landmark_data = {} self.landmark_prompts = [] self.landmark_id_to_index = {} self.is_enabled = False self._load_landmark_data() def _load_landmark_data(self): """ 載入地標數據和相關資訊 """ try: self.landmark_data = ALL_LANDMARKS self.landmark_prompts = get_all_landmark_prompts() self.logger.info(f"Loaded {len(self.landmark_prompts)} landmark prompts for classification") # 創建地標ID到索引的映射,可快速查找 self.landmark_id_to_index = {landmark_id: i for i, landmark_id in enumerate(ALL_LANDMARKS.keys())} self.is_enabled = True self.logger.info(f"Successfully loaded landmark data with {len(self.landmark_data)} landmarks") except ImportError: self.logger.warning("landmark_data.py not found. Landmark classification will be limited") self.landmark_data = {} self.landmark_prompts = [] self.landmark_id_to_index = {} self.is_enabled = False except Exception as e: self.logger.error(f"Error loading landmark data: {e}") self.logger.error(traceback.format_exc()) self.landmark_data = {} self.landmark_prompts = [] self.landmark_id_to_index = {} self.is_enabled = False def get_landmark_prompts(self) -> List[str]: """ 獲取所有地標提示詞 Returns: List[str]: 地標提示詞列表 """ return self.landmark_prompts def get_landmark_by_id(self, landmark_id: str) -> Dict[str, Any]: """ 根據地標ID獲取地標信息 Args: landmark_id: Landmark ID Returns: Dict[str, Any]: 地標詳細信息 """ return self.landmark_data.get(landmark_id, {}) def get_landmark_by_index(self, index: int) -> Tuple[str, Dict[str, Any]]: """ 根據索引獲取地標信息 Args: index: 地標在列表中的索引 Returns: Tuple[str, Dict[str, Any]]: (地標ID, 地標info) """ try: landmark_ids = list(self.landmark_data.keys()) if 0 <= index < len(landmark_ids): landmark_id = landmark_ids[index] return landmark_id, self.landmark_data[landmark_id] else: self.logger.warning(f"Index {index} out of range for landmark data") return None, {} except Exception as e: self.logger.error(f"Error getting landmark by index {index}: {e}") self.logger.error(traceback.format_exc()) return None, {} def get_landmark_index(self, landmark_id: str) -> Optional[int]: """ 獲取地標ID對應的index Args: landmark_id: 地標ID Returns: Optional[int]: 索引,如果不存在則返回None """ return self.landmark_id_to_index.get(landmark_id) def determine_landmark_type(self, landmark_id: str) -> str: """ 自動判斷地標類型,基於地標數據和命名 Args: landmark_id: 地標ID Returns: str: 地標類型,用於調整閾值 """ if not landmark_id: return "building" # 預設類型 try: # 獲取地標詳細數據 landmark_info = self.landmark_data.get(landmark_id, {}) # 獲取地標相關文本 landmark_id_lower = landmark_id.lower() landmark_name = landmark_info.get("name", "").lower() landmark_location = landmark_info.get("location", "").lower() landmark_aliases = [alias.lower() for alias in landmark_info.get("aliases", [])] # 合併所有文本數據用於特徵判斷 combined_text = " ".join([landmark_id_lower, landmark_name] + landmark_aliases) # 地標類型的特色特徵 type_features = { "skyscraper": ["skyscraper", "tall", "tower", "高樓", "摩天", "大厦", "タワー"], "tower": ["tower", "bell", "clock", "塔", "鐘樓", "タワー", "campanile"], "monument": ["monument", "memorial", "statue", "紀念", "雕像", "像", "memorial"], "natural": ["mountain", "lake", "canyon", "falls", "beach", "山", "湖", "峽谷", "瀑布", "海灘"], "temple": ["temple", "shrine", "寺", "神社", "廟"], "palace": ["palace", "castle", "宮", "城", "皇宮", "宫殿"], "distinctive": ["unique", "leaning", "slanted", "傾斜", "斜", "獨特", "傾く"] } # 檢查是否位於亞洲地區 asian_regions = ["china", "japan", "korea", "taiwan", "singapore", "vietnam", "thailand", "hong kong", "中國", "日本", "韓國", "台灣", "新加坡", "越南", "泰國", "香港"] is_asian = any(region in landmark_location for region in asian_regions) # 判斷地標類型 best_type = None max_matches = 0 for type_name, features in type_features.items(): # 計算特徵詞匹配數量 matches = sum(1 for feature in features if feature in combined_text) if matches > max_matches: max_matches = matches best_type = type_name # 處理亞洲地區特例 if is_asian and best_type == "tower": best_type = "skyscraper" # 亞洲地區的塔型建築閾值較低 # 特例處理:檢測傾斜建築 if any(term in combined_text for term in ["leaning", "slanted", "tilt", "inclined", "斜", "傾斜"]): return "distinctive" # 傾斜建築需要特殊處理 return best_type if best_type and max_matches > 0 else "building" # 預設為一般建築 except Exception as e: self.logger.error(f"Error determining landmark type for {landmark_id}: {e}") self.logger.error(traceback.format_exc()) return "building" def extract_landmark_specific_info(self, landmark_id: str) -> Dict[str, Any]: """ 提取特定地標的詳細信息,包括特色模板和活動建議 Args: landmark_id: 地標ID Returns: Dict[str, Any]: 地標特定信息 """ if not landmark_id or landmark_id == "unknown": return {"has_specific_activities": False} specific_info = {"has_specific_activities": False} try: # 從 landmark_data 中提取基本信息 landmark_data_source = self.landmark_data.get(landmark_id) # 處理地標基本數據 if landmark_data_source: # 提取正確的地標名稱 if "name" in landmark_data_source: specific_info["landmark_name"] = landmark_data_source["name"] # 提取所有可用的 prompts 作為特色模板 if "prompts" in landmark_data_source: specific_info["feature_templates"] = landmark_data_source["prompts"][:5] specific_info["primary_template"] = landmark_data_source["prompts"][0] # 提取別名info if "aliases" in landmark_data_source: specific_info["aliases"] = landmark_data_source["aliases"] # 提取位置信息 if "location" in landmark_data_source: specific_info["location"] = landmark_data_source["location"] # 提取其他相關信息 for key in ["year_built", "architectural_style", "significance", "description"]: if key in landmark_data_source: specific_info[key] = landmark_data_source[key] # 嘗試從 LANDMARK_ACTIVITIES 中提取活動建議 try: if landmark_id in LANDMARK_ACTIVITIES: activities = LANDMARK_ACTIVITIES[landmark_id] specific_info["landmark_specific_activities"] = activities specific_info["has_specific_activities"] = True self.logger.info(f"Found {len(activities)} specific activities for landmark {landmark_id}") else: self.logger.info(f"No specific activities found for landmark {landmark_id} in LANDMARK_ACTIVITIES") specific_info["has_specific_activities"] = False except ImportError: self.logger.warning("Could not import LANDMARK_ACTIVITIES from landmark_activities") specific_info["has_specific_activities"] = False except Exception as e: self.logger.error(f"Error loading landmark activities for {landmark_id}: {e}") self.logger.error(traceback.format_exc()) specific_info["has_specific_activities"] = False except Exception as e: self.logger.error(f"Error extracting landmark specific info for {landmark_id}: {e}") self.logger.error(traceback.format_exc()) return specific_info def get_landmark_count(self) -> int: """ 獲取地標總數 Returns: int: 地標數量 """ return len(self.landmark_data) def is_landmark_enabled(self) -> bool: """ 檢查地標功能是否啟用 Returns: bool: 地標功能狀態 """ return self.is_enabled def get_all_landmark_ids(self) -> List[str]: """ 獲取所有地標ID列表 Returns: List[str]: 地標ID列表 """ return list(self.landmark_data.keys()) def validate_landmark_id(self, landmark_id: str) -> bool: """ 驗證地標ID是否有效 Args: landmark_id: 要驗證的地標ID Returns: bool: ID是否有效 """ return landmark_id in self.landmark_data