""" GPX Test Route API Views GPXシミュレーション用のテストルートデータ取得 """ import json import logging from datetime import datetime, timedelta from rest_framework import status from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import AllowAny from rest_framework.response import Response from .models import NewEvent2, Location logger = logging.getLogger(__name__) @api_view(['GET']) @permission_classes([AllowAny]) def gpx_test_data(request): """ GPXシミュレーション用のテストルートデータ取得 GET /api/routes/gpx-test-data Parameters: - event_code: イベントコード - route_type: ルートタイプ (sample, short, long) """ try: event_code = request.GET.get('event_code') route_type = request.GET.get('route_type', 'sample') if not event_code: return Response({ 'error': 'event_code parameter is required' }, status=status.HTTP_400_BAD_REQUEST) # イベントの存在確認 try: event = NewEvent2.objects.get(event_name=event_code) except NewEvent2.DoesNotExist: return Response({ 'error': f'Event "{event_code}" not found' }, status=status.HTTP_404_NOT_FOUND) # ルートタイプに応じたテストデータ生成 routes = [] if route_type == 'sample': routes = _generate_sample_route(event_code) elif route_type == 'short': routes = _generate_short_route(event_code) elif route_type == 'long': routes = _generate_long_route(event_code) else: routes = _generate_sample_route(event_code) return Response({ 'routes': routes, 'event_code': event_code, 'route_type': route_type, 'generated_at': datetime.now().isoformat() }) except Exception as e: logger.error(f"GPX test data error: {e}") return Response({ 'error': 'Internal server error', 'message': str(e) }, status=status.HTTP_500_INTERNAL_SERVER_ERROR) def _generate_sample_route(event_code): """サンプルルート生成""" # 岐阜市内の主要ポイント waypoints = [ { "lat": 35.4122, "lng": 136.7514, "timestamp": "2025-09-15T10:00:00Z", "cp_number": 1, "description": "岐阜公園", "elevation": 15 }, { "lat": 35.4089, "lng": 136.7581, "timestamp": "2025-09-15T10:15:00Z", "cp_number": 2, "description": "岐阜城天守閣", "elevation": 329 }, { "lat": 35.4091, "lng": 136.7456, "timestamp": "2025-09-15T10:30:00Z", "cp_number": 3, "description": "長良川うかいミュージアム", "elevation": 12 }, { "lat": 35.4187, "lng": 136.7598, "timestamp": "2025-09-15T10:45:00Z", "cp_number": 4, "description": "岐阜市歴史博物館", "elevation": 18 }, { "lat": 35.4122, "lng": 136.7514, "timestamp": "2025-09-15T11:00:00Z", "cp_number": 0, "description": "岐阜公園(ゴール)", "elevation": 15 } ] gpx_data = _generate_gpx_xml(waypoints, "岐阜市内サンプルルート") return [{ "route_name": "岐阜市内サンプルルート", "description": "チェックポイント1-4を巡回するテストルート", "estimated_time": "60分", "total_distance": "約3.2km", "elevation_gain": "約314m", "difficulty": "中級", "waypoints": waypoints, "gpx_data": gpx_data }] def _generate_short_route(event_code): """短距離ルート生成""" waypoints = [ { "lat": 35.4122, "lng": 136.7514, "timestamp": "2025-09-15T10:00:00Z", "cp_number": 1, "description": "岐阜公園(スタート)", "elevation": 15 }, { "lat": 35.4150, "lng": 136.7545, "timestamp": "2025-09-15T10:10:00Z", "cp_number": 2, "description": "信長の居館跡", "elevation": 25 }, { "lat": 35.4122, "lng": 136.7514, "timestamp": "2025-09-15T10:20:00Z", "cp_number": 0, "description": "岐阜公園(ゴール)", "elevation": 15 } ] gpx_data = _generate_gpx_xml(waypoints, "岐阜公園周辺ショートルート") return [{ "route_name": "岐阜公園周辺ショートルート", "description": "初心者向けの短距離ルート", "estimated_time": "20分", "total_distance": "約0.8km", "elevation_gain": "約10m", "difficulty": "初級", "waypoints": waypoints, "gpx_data": gpx_data }] def _generate_long_route(event_code): """長距離ルート生成""" waypoints = [ { "lat": 35.4122, "lng": 136.7514, "timestamp": "2025-09-15T10:00:00Z", "cp_number": 1, "description": "岐阜公園(スタート)", "elevation": 15 }, { "lat": 35.4089, "lng": 136.7581, "timestamp": "2025-09-15T10:20:00Z", "cp_number": 2, "description": "岐阜城天守閣", "elevation": 329 }, { "lat": 35.3978, "lng": 136.7456, "timestamp": "2025-09-15T10:45:00Z", "cp_number": 3, "description": "長良川河川敷", "elevation": 8 }, { "lat": 35.4234, "lng": 136.7345, "timestamp": "2025-09-15T11:15:00Z", "cp_number": 4, "description": "金華橋", "elevation": 10 }, { "lat": 35.4391, "lng": 136.7598, "timestamp": "2025-09-15T11:45:00Z", "cp_number": 5, "description": "護国神社", "elevation": 35 }, { "lat": 35.4187, "lng": 136.7698, "timestamp": "2025-09-15T12:10:00Z", "cp_number": 6, "description": "岐阜メモリアルセンター", "elevation": 22 }, { "lat": 35.4122, "lng": 136.7514, "timestamp": "2025-09-15T12:30:00Z", "cp_number": 0, "description": "岐阜公園(ゴール)", "elevation": 15 } ] gpx_data = _generate_gpx_xml(waypoints, "岐阜市内ロングルート") return [{ "route_name": "岐阜市内ロングルート", "description": "上級者向けの長距離チャレンジルート", "estimated_time": "150分", "total_distance": "約8.5km", "elevation_gain": "約321m", "difficulty": "上級", "waypoints": waypoints, "gpx_data": gpx_data }] def _generate_gpx_xml(waypoints, route_name): """GPXファイル形式のXMLを生成""" gpx_header = ''' {} Generated test route for rogaining simulation '''.format(route_name, datetime.now().isoformat()) # トラックセグメント track_points = [] for waypoint in waypoints: track_points.append(''' {} CP{} - {} '''.format( waypoint['lat'], waypoint['lng'], waypoint.get('elevation', 0), waypoint['timestamp'], waypoint['cp_number'], waypoint['description'] )) # ウェイポイント waypoint_elements = [] for waypoint in waypoints: waypoint_elements.append(''' {} CP{} {} Flag, Blue '''.format( waypoint['lat'], waypoint['lng'], waypoint.get('elevation', 0), waypoint['timestamp'], waypoint['cp_number'], waypoint['description'] )) gpx_content = f'''{gpx_header} {route_name} Test route for rogaining simulation {chr(10).join(track_points)} {chr(10).join(waypoint_elements)} ''' return gpx_content @api_view(['GET']) @permission_classes([AllowAny]) def available_routes(request): """利用可能なテストルート一覧取得""" event_code = request.GET.get('event_code') routes_info = [ { "route_type": "sample", "name": "岐阜市内サンプルルート", "description": "標準的なテストルート", "estimated_time": "60分", "difficulty": "中級", "checkpoint_count": 4 }, { "route_type": "short", "name": "岐阜公園周辺ショートルート", "description": "初心者向けの短距離ルート", "estimated_time": "20分", "difficulty": "初級", "checkpoint_count": 2 }, { "route_type": "long", "name": "岐阜市内ロングルート", "description": "上級者向けの長距離ルート", "estimated_time": "150分", "difficulty": "上級", "checkpoint_count": 6 } ] return Response({ "available_routes": routes_info, "event_code": event_code, "total_routes": len(routes_info) })