Files
rogaining_srv/rog/gpx_route_views.py
2025-08-27 15:01:06 +09:00

358 lines
10 KiB
Python

"""
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 = '''<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="GifuRogaining" xmlns="http://www.topografix.com/GPX/1/1">
<metadata>
<name>{}</name>
<desc>Generated test route for rogaining simulation</desc>
<time>{}</time>
</metadata>'''.format(route_name, datetime.now().isoformat())
# トラックセグメント
track_points = []
for waypoint in waypoints:
track_points.append(''' <trkpt lat="{}" lon="{}">
<ele>{}</ele>
<time>{}</time>
<name>CP{} - {}</name>
</trkpt>'''.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(''' <wpt lat="{}" lon="{}">
<ele>{}</ele>
<time>{}</time>
<name>CP{}</name>
<desc>{}</desc>
<sym>Flag, Blue</sym>
</wpt>'''.format(
waypoint['lat'],
waypoint['lng'],
waypoint.get('elevation', 0),
waypoint['timestamp'],
waypoint['cp_number'],
waypoint['description']
))
gpx_content = f'''{gpx_header}
<trk>
<name>{route_name}</name>
<desc>Test route for rogaining simulation</desc>
<trkseg>
{chr(10).join(track_points)}
</trkseg>
</trk>
{chr(10).join(waypoint_elements)}
</gpx>'''
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)
})