|
|
#tyx
|
|
|
"""
|
|
|
owntracks 应用的视图函数
|
|
|
处理位置数据的接收、展示和查询
|
|
|
"""
|
|
|
|
|
|
import datetime
|
|
|
import itertools
|
|
|
import json
|
|
|
import logging
|
|
|
from datetime import timezone
|
|
|
from itertools import groupby
|
|
|
|
|
|
import django
|
|
|
import requests
|
|
|
from django.contrib.auth.decorators import login_required
|
|
|
from django.http import HttpResponse
|
|
|
from django.http import JsonResponse
|
|
|
from django.shortcuts import render
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
|
|
from .models import OwnTrackLog
|
|
|
|
|
|
# 获取当前模块的日志器
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
@csrf_exempt # 免除CSRF保护,便于外部应用调用
|
|
|
def manage_owntrack_log(request):
|
|
|
"""
|
|
|
接收和处理 OwnTracks 客户端发送的位置数据
|
|
|
POST请求:接收JSON格式的位置数据并存入数据库
|
|
|
"""
|
|
|
try:
|
|
|
# 解析请求体中的JSON数据
|
|
|
s = json.loads(request.read().decode('utf-8'))
|
|
|
tid = s['tid'] # 用户标识
|
|
|
lat = s['lat'] # 纬度
|
|
|
lon = s['lon'] # 经度
|
|
|
|
|
|
# 记录接收到的数据到日志
|
|
|
logger.info(
|
|
|
'tid:{tid}.lat:{lat}.lon:{lon}'.format(
|
|
|
tid=tid, lat=lat, lon=lon))
|
|
|
|
|
|
# 验证必要字段是否存在
|
|
|
if tid and lat and lon:
|
|
|
# 创建新的位置记录
|
|
|
m = OwnTrackLog()
|
|
|
m.tid = tid
|
|
|
m.lat = lat
|
|
|
m.lon = lon
|
|
|
m.save() # 保存到数据库
|
|
|
return HttpResponse('ok') # 返回成功响应
|
|
|
else:
|
|
|
return HttpResponse('data error') # 数据不完整
|
|
|
except Exception as e:
|
|
|
# 记录错误信息
|
|
|
logger.error(e)
|
|
|
return HttpResponse('error') # 返回错误响应
|
|
|
|
|
|
|
|
|
@login_required # 要求用户登录
|
|
|
def show_maps(request):
|
|
|
"""
|
|
|
显示位置轨迹地图页面
|
|
|
仅限超级用户访问,支持按日期筛选
|
|
|
"""
|
|
|
if request.user.is_superuser:
|
|
|
# 设置默认日期为当前UTC日期
|
|
|
defaultdate = str(datetime.datetime.now(timezone.utc).date())
|
|
|
# 获取请求中的日期参数,若无则使用默认日期
|
|
|
date = request.GET.get('date', defaultdate)
|
|
|
|
|
|
# 构建模板上下文
|
|
|
context = {
|
|
|
'date': date
|
|
|
}
|
|
|
# 渲染地图展示页面
|
|
|
return render(request, 'owntracks/show_maps.html', context)
|
|
|
else:
|
|
|
# 非超级用户返回403禁止访问
|
|
|
from django.http import HttpResponseForbidden
|
|
|
return HttpResponseForbidden()
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
def show_log_dates(request):
|
|
|
"""
|
|
|
显示所有有位置记录的日期列表
|
|
|
用于在地图页面中选择特定日期查看轨迹
|
|
|
"""
|
|
|
# 获取所有记录的创建时间
|
|
|
dates = OwnTrackLog.objects.values_list('creation_time', flat=True)
|
|
|
|
|
|
# 处理日期数据:格式化为YYYY-MM-DD,去重并排序
|
|
|
results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates))))
|
|
|
|
|
|
# 构建模板上下文
|
|
|
context = {
|
|
|
'results': results
|
|
|
}
|
|
|
# 渲染日期列表页面
|
|
|
return render(request, 'owntracks/show_log_dates.html', context)
|
|
|
|
|
|
|
|
|
def convert_to_amap(locations):
|
|
|
"""
|
|
|
将GPS坐标转换为高德地图坐标系统
|
|
|
由于GPS坐标系与高德地图不同,需要进行坐标转换
|
|
|
|
|
|
Args:
|
|
|
locations: OwnTrackLog 对象列表,包含经纬度信息
|
|
|
|
|
|
Returns:
|
|
|
str: 转换后的坐标字符串,格式为"经度,纬度;经度,纬度;..."
|
|
|
"""
|
|
|
convert_result = []
|
|
|
it = iter(locations) # 创建迭代器
|
|
|
|
|
|
# 每次处理30个坐标(高德API限制)
|
|
|
item = list(itertools.islice(it, 30))
|
|
|
while item:
|
|
|
# 将坐标格式化为高德API要求的格式
|
|
|
datas = ';'.join(
|
|
|
set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
|
|
|
|
|
|
# 高德地图API密钥(实际使用中应放在配置文件中)
|
|
|
key = '8440a376dfc9743d8924bf0ad141f28e'
|
|
|
api = 'http://restapi.amap.com/v3/assistant/coordinate/convert'
|
|
|
query = {
|
|
|
'key': key,
|
|
|
'locations': datas,
|
|
|
'coordsys': 'gps' # 指定源坐标系为GPS
|
|
|
}
|
|
|
|
|
|
# 调用高德坐标转换API
|
|
|
rsp = requests.get(url=api, params=query)
|
|
|
result = json.loads(rsp.text)
|
|
|
|
|
|
# 检查API响应是否包含转换结果
|
|
|
if "locations" in result:
|
|
|
convert_result.append(result['locations'])
|
|
|
|
|
|
# 获取下一批坐标
|
|
|
item = list(itertools.islice(it, 30))
|
|
|
|
|
|
# 合并所有转换结果
|
|
|
return ";".join(convert_result)
|
|
|
|
|
|
|
|
|
@login_required
|
|
|
def get_datas(request):
|
|
|
"""
|
|
|
提供位置数据的JSON API接口
|
|
|
返回指定日期的用户轨迹数据,用于在地图上绘制路径
|
|
|
|
|
|
Returns:
|
|
|
JsonResponse: 包含用户轨迹数据的JSON响应
|
|
|
"""
|
|
|
# 获取当前时间并设置为UTC时区
|
|
|
now = django.utils.timezone.now().replace(tzinfo=timezone.utc)
|
|
|
|
|
|
# 设置查询日期为今天零点
|
|
|
querydate = django.utils.timezone.datetime(
|
|
|
now.year, now.month, now.day, 0, 0, 0)
|
|
|
|
|
|
# 如果请求中指定了日期,使用指定日期
|
|
|
if request.GET.get('date', None):
|
|
|
date = list(map(lambda x: int(x), request.GET.get('date').split('-')))
|
|
|
querydate = django.utils.timezone.datetime(
|
|
|
date[0], date[1], date[2], 0, 0, 0)
|
|
|
|
|
|
# 计算查询结束时间(第二天零点)
|
|
|
nextdate = querydate + datetime.timedelta(days=1)
|
|
|
|
|
|
# 查询指定日期范围内的所有位置记录
|
|
|
models = OwnTrackLog.objects.filter(
|
|
|
creation_time__range=(querydate, nextdate))
|
|
|
|
|
|
result = list() # 初始化结果列表
|
|
|
|
|
|
# 如果有数据,按用户分组处理
|
|
|
if models and len(models):
|
|
|
# 按用户ID分组,每个用户的轨迹单独处理
|
|
|
for tid, item in groupby(
|
|
|
sorted(models, key=lambda k: k.tid), key=lambda k: k.tid):
|
|
|
|
|
|
d = dict() # 创建用户数据字典
|
|
|
d["name"] = tid # 设置用户名
|
|
|
|
|
|
paths = list() # 初始化路径点列表
|
|
|
|
|
|
# 选项1:使用高德转换后的经纬度(当前被注释)
|
|
|
# locations = convert_to_amap(
|
|
|
# sorted(item, key=lambda x: x.creation_time))
|
|
|
# for i in locations.split(';'):
|
|
|
# paths.append(i.split(','))
|
|
|
|
|
|
# 选项2:使用GPS原始经纬度(当前使用)
|
|
|
# 按时间排序后添加到路径中
|
|
|
for location in sorted(item, key=lambda x: x.creation_time):
|
|
|
paths.append([str(location.lon), str(location.lat)])
|
|
|
|
|
|
d["path"] = paths # 设置用户轨迹路径
|
|
|
result.append(d) # 添加到结果列表
|
|
|
|
|
|
# 返回JSON格式的响应
|
|
|
return JsonResponse(result, safe=False) # safe=False允许非字典对象被序列化 |