Finish basic API implementation
This commit is contained in:
357
rog/gpx_route_views.py
Normal file
357
rog/gpx_route_views.py
Normal file
@ -0,0 +1,357 @@
|
||||
"""
|
||||
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)
|
||||
})
|
||||
Reference in New Issue
Block a user