# 既存のインポート部分に追加 from rest_framework.decorators import api_view from rest_framework.response import Response from rest_framework import status from rog.models import NewEvent2, Entry,Location, GpsLog import logging from django.db.models import F, Q from django.conf import settings import os from urllib.parse import urljoin logger = logging.getLogger(__name__) """ get_start_point() 解説: この実装では以下の処理を行っています: 1.イベントコードのパラメータを受け取ります 2.パラメータが不足している場合はエラーを返します 3.指定されたイベントの存在を確認します 4. イベントのスタートポイント情報を取得します: - 緯度・経度 - スタート地点の名前 - スタート地点の説明 5.GeoJSON形式のデータも提供します(座標情報がある場合) - スタート地点をPointとして表現 - プロパティとして名前や説明も含む このエンドポイントは、アプリでイベントのスタート地点を地図上に表示したり、 スタート地点の詳細情報を表示したりするために使用できます。 これにより、参加者はどこに集合すればよいかを正確に把握できます。 """ @api_view(['GET']) def get_start_point(request): """ イベントのスタートポイント情報を取得 パラメータ: - event: イベントコード """ logger.info("get_start_point called") # リクエストからパラメータを取得 event_code = request.query_params.get('event') logger.debug(f"Parameters: event={event_code}") # パラメータ検証 if not event_code: logger.warning("Missing required event parameter") return Response({ "status": "ERROR", "message": "イベントコードが必要です" }, status=status.HTTP_400_BAD_REQUEST) try: # イベントの存在確認 event = NewEvent2.objects.filter(event_name=event_code).first() if not event: logger.warning(f"Event not found: {event_code}") return Response({ "status": "ERROR", "message": "指定されたイベントが見つかりません" }, status=status.HTTP_404_NOT_FOUND) # スタートポイント情報の取得 start_point = { "event_code": event_code, "event_name": event.event_name } # 座標情報があれば追加 if event.start_latitude is not None and event.start_longitude is not None: start_point["latitude"] = event.start_latitude start_point["longitude"] = event.start_longitude # 名前や説明があれば追加 if hasattr(event, 'start_name') and event.start_name: start_point["name"] = event.start_name if hasattr(event, 'start_description') and event.start_description: start_point["description"] = event.start_description # GeoJSON形式のデータも作成 geo_json = None if event.start_latitude is not None and event.start_longitude is not None: geo_json = { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": { "type": "Point", "coordinates": [event.start_longitude, event.start_latitude] }, "properties": { "name": event.start_name if hasattr(event, 'start_name') else "スタート地点", "description": event.start_description if hasattr(event, 'start_description') else "", "event_code": event_code } } ] } return Response({ "status": "OK", "start_point": start_point, "geo_json": geo_json }) except Exception as e: logger.error(f"Error in get_start_point: {str(e)}") return Response({ "status": "ERROR", "message": "サーバーエラーが発生しました" }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) """ 解説 この実装では以下の処理を行っています: 1.緯度、経度、チーム名、イベントコードのパラメータを受け取ります 2.パラメータが不足している場合はエラーを返します 3.指定されたイベントとチームの存在を確認します 4.チームのウェイポイントデータを取得し、指定された座標からの距離を計算します 5.最も近いウェイポイントを特定し、以下の情報を含む分析結果を作成します: - 最近接ポイントの詳細(位置、時間、標高、精度、速度など) - 速度分析(前後のポイントから計算した速度と移動タイプの推測) - 周辺ポイント(時間的・空間的に近いウェイポイント) - 最も近いチェックポイントとの関係(オプション) 実装のポイント: - ハバーサイン公式を使用して、2点間の地理的距離を正確に計算 - 速度から移動タイプを推測(静止、歩行、ジョギング、ランニングなど) - チェックポイントとの近接性分析も行い、チームが通過済みかどうかも確認 - 周辺ポイントも提供することで、より広い範囲での動きを把握可能 このエンドポイントは、特定地点での移動状況を詳細に分析したい場合に役立ちます。 例えば、チームの移動戦略の分析や、特定地点でのパフォーマンス評価などに使用できます。 """ @api_view(['GET']) def analyze_point(request): """ 指定地点の情報を分析(速度、移動タイプなど) パラメータ: - lat: 緯度 - lng: 経度 - team_name: チーム名 - event_code: イベントコード """ logger.info("analyze_point called") # リクエストからパラメータを取得 latitude = request.query_params.get('lat') longitude = request.query_params.get('lng') team_name = request.query_params.get('team_name') event_code = request.query_params.get('event_code') logger.debug(f"Parameters: lat={latitude}, lng={longitude}, team_name={team_name}, event_code={event_code}") # パラメータ検証 if not all([latitude, longitude, team_name, event_code]): logger.warning("Missing required parameters") return Response({ "status": "ERROR", "message": "緯度、経度、チーム名、イベントコードが全て必要です" }, status=status.HTTP_400_BAD_REQUEST) try: # 数値型に変換 try: lat = float(latitude) lng = float(longitude) except ValueError: logger.warning(f"Invalid coordinate values: lat={latitude}, lng={longitude}") return Response({ "status": "ERROR", "message": "緯度と経度は数値である必要があります" }, status=status.HTTP_400_BAD_REQUEST) # イベントの存在確認 event = NewEvent2.objects.filter(event_name=event_code).first() if not event: logger.warning(f"Event not found: {event_code}") return Response({ "status": "ERROR", "message": "指定されたイベントが見つかりません" }, status=status.HTTP_404_NOT_FOUND) # チームの存在確認 entry = Entry.objects.filter( event=event, team_name=team_name ).first() if not entry: logger.warning(f"Team '{team_name}' not found in event: {event_code}") return Response({ "status": "ERROR", "message": "指定されたチームが見つかりません" }, status=status.HTTP_404_NOT_FOUND) # 最も近い位置にあるウェイポイントを見つける # 指定座標からの距離を計算するヘルパー関数 def calculate_distance(waypoint): wp_lat = waypoint.latitude wp_lng = waypoint.longitude # ハバーサイン公式で距離を計算 R = 6371.0 # 地球の半径(km) lat1_rad = radians(lat) lon1_rad = radians(lng) lat2_rad = radians(wp_lat) lon2_rad = radians(wp_lng) dlon = lon2_rad - lon1_rad dlat = lat2_rad - lat1_rad a = sin(dlat / 2)**2 + cos(lat1_rad) * cos(lat2_rad) * sin(dlon / 2)**2 c = 2 * atan2(sqrt(a), sqrt(1 - a)) distance = R * c * 1000 # メートル単位に変換 return distance # チームの全ウェイポイントを取得 all_waypoints = Waypoint.objects.filter(entry=entry).order_by('recorded_at') if not all_waypoints.exists(): logger.warning(f"No waypoints found for team {team_name}") return Response({ "status": "WARNING", "message": "このチームのウェイポイントデータがありません", "team_name": team_name, "event_code": event_code, "latitude": lat, "longitude": lng }) # 各ウェイポイントについて指定座標からの距離を計算 waypoints_with_distance = [(wp, calculate_distance(wp)) for wp in all_waypoints] # 距離でソート waypoints_with_distance.sort(key=lambda x: x[1]) # 最も近いウェイポイント nearest_waypoint, nearest_distance = waypoints_with_distance[0] # 分析結果 analysis = { "nearest_point": { "latitude": nearest_waypoint.latitude, "longitude": nearest_waypoint.longitude, "timestamp": nearest_waypoint.recorded_at.strftime("%Y-%m-%d %H:%M:%S"), "distance": round(nearest_distance, 2), # メートル単位 "altitude": nearest_waypoint.altitude, "accuracy": nearest_waypoint.accuracy, "speed": nearest_waypoint.speed } } # 前後のウェイポイントを取得して速度分析 waypoint_index = list(all_waypoints).index(nearest_waypoint) # 前のウェイポイント(あれば) if waypoint_index > 0: prev_waypoint = all_waypoints[waypoint_index - 1] time_diff = (nearest_waypoint.recorded_at - prev_waypoint.recorded_at).total_seconds() if time_diff > 0: # 2点間の距離を計算 prev_distance = calculate_distance(prev_waypoint) # 速度を計算(m/s) speed_mps = abs(nearest_distance - prev_distance) / time_diff analysis["speed_analysis"] = { "speed_mps": round(speed_mps, 2), "speed_kmh": round(speed_mps * 3.6, 2), # km/h に変換 "time_diff_seconds": time_diff } # 移動タイプを推測 if speed_mps < 1.5: mobility_type = "静止またはゆっくり歩行" elif speed_mps < 2.5: mobility_type = "歩行" elif speed_mps < 4.0: mobility_type = "速歩き" elif speed_mps < 7.0: mobility_type = "ジョギング" elif speed_mps < 12.0: mobility_type = "ランニング" else: mobility_type = "自転車または車両" analysis["speed_analysis"]["mobility_type"] = mobility_type # 周辺の他のウェイポイントも取得(時間範囲内で) # 前後30分以内のウェイポイントを検索 time_range = timedelta(minutes=30) nearby_waypoints = [] for wp in all_waypoints: if abs((wp.recorded_at - nearest_waypoint.recorded_at).total_seconds()) <= time_range.total_seconds(): # 距離も近いものだけ(1km以内) distance = calculate_distance(wp) if distance <= 1000: nearby_waypoints.append({ "latitude": wp.latitude, "longitude": wp.longitude, "timestamp": wp.recorded_at.strftime("%Y-%m-%d %H:%M:%S"), "distance": round(distance, 2), "altitude": wp.altitude, "speed": wp.speed }) analysis["nearby_points"] = nearby_waypoints analysis["nearby_points_count"] = len(nearby_waypoints) # チェックポイントとの関係を分析(オプション) try: # イベントのチェックポイント定義を取得 event_cps = Location.objects.filter(event=event) # チームが通過したチェックポイントを取得 team_cps = GpsLog.objects.filter(entry=entry) if event_cps.exists(): # 指定地点から最も近いチェックポイントを見つける closest_cp = None closest_cp_distance = float('inf') for cp in event_cps: if cp.latitude is not None and cp.longitude is not None: # チェックポイントからの距離を計算 cp_lat = cp.latitude cp_lng = cp.longitude R = 6371.0 lat1_rad = radians(lat) lon1_rad = radians(lng) lat2_rad = radians(cp_lat) lon2_rad = radians(cp_lng) dlon = lon2_rad - lon1_rad dlat = lat2_rad - lat1_rad a = sin(dlat / 2)**2 + cos(lat1_rad) * cos(lat2_rad) * sin(dlon / 2)**2 c = 2 * atan2(sqrt(a), sqrt(1 - a)) distance = R * c * 1000 # メートル単位 if distance < closest_cp_distance: closest_cp = cp closest_cp_distance = distance if closest_cp: # チームがこのチェックポイントを通過したかを確認 team_cp = team_cps.filter(cp_number=closest_cp.cp_number).first() visited = team_cp is not None analysis["nearest_checkpoint"] = { "cp_number": closest_cp.cp_number, "cp_name": closest_cp.cp_name, "cp_point": closest_cp.cp_point, "distance": round(closest_cp_distance, 2), "visited": visited, "checkin_time": team_cp.checkin_time.strftime("%Y-%m-%d %H:%M:%S") if visited and team_cp.checkin_time else None } except: # Location モデルがない場合などはスキップ pass return Response({ "status": "OK", "team_name": team_name, "event_code": event_code, "latitude": lat, "longitude": lng, "analysis": analysis }) except Exception as e: logger.error(f"Error in analyze_point: {str(e)}") return Response({ "status": "ERROR", "message": "サーバーエラーが発生しました" }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)