point.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. from fastapi import APIRouter, Request, Depends, Query, HTTPException, status
  4. from common.security import valid_access_token
  5. from fastapi.responses import JSONResponse
  6. from sqlalchemy.orm import Session
  7. from sqlalchemy import and_, or_,text,literal
  8. from sqlalchemy.sql import func
  9. from sqlalchemy.future import select
  10. from common.auth_user import *
  11. from pydantic import BaseModel
  12. from database import get_db
  13. from typing import List
  14. from models import *
  15. from utils import *
  16. from utils.ry_system_util import *
  17. from utils.video_util import *
  18. from collections import defaultdict
  19. import traceback
  20. from concurrent.futures import ThreadPoolExecutor, as_completed
  21. from multiprocessing import Pool, cpu_count
  22. import json
  23. import time
  24. import math
  25. router = APIRouter()
  26. @router.post("/get_info")
  27. @router.get("/get_info")
  28. async def get_infos(
  29. body = Depends(remove_xss_json),
  30. # zoom_level: float = Query(..., description="Zoom level for clustering"),
  31. # latitude_min: float = Query(..., description="Minimum latitude"),
  32. # latitude_max: float = Query(..., description="Maximum latitude"),
  33. # longitude_min: float = Query(..., description="Minimum longitude"),
  34. # longitude_max: float = Query(..., description="Maximum longitude"),
  35. # dict_value: str = Query(None),
  36. # option:str = Query(None),
  37. db: Session = Depends(get_db)
  38. ):
  39. try:
  40. # 根据缩放级别动态调整分组粒度
  41. zoom_level = float(body['zoom_level'])
  42. zoom_levels = {
  43. 3: 10000, # 全国范围
  44. 4: 5000,
  45. 5: 2500,
  46. 6: 1250,
  47. 7: 825,
  48. 8: 412.5,
  49. 9: 256.25,
  50. 10: 178.125,
  51. 11: 69.0625,
  52. 12: 29.53125,
  53. 13: 13.765625,
  54. 14: 5.8828125,
  55. 15: 2.44140625,
  56. 16: 1.220703125,
  57. 17: 0.6103515625,
  58. 18: 0.30517578125
  59. }
  60. distance_threshold=zoom_levels[int(zoom_level-1)]
  61. # distance_threshold = 100000 / (2.2 ** zoom_level) # 例如:每缩放一级,距离阈值减半
  62. dict_value= body['dict_value'].split(',')
  63. latitude_min = float(body['latitude_min'])
  64. latitude_max = float(body['latitude_max'])
  65. longitude_min = float(body['longitude_min'])
  66. longitude_max = float(body['longitude_max'])
  67. option = body['option'].split(',')
  68. print("1",time.time())
  69. videos = get_videos(db,dict_value,latitude_min,latitude_max,longitude_min,longitude_max)
  70. infos = get_points(db,option,latitude_min,latitude_max,longitude_min,longitude_max)
  71. # 动态分组逻辑
  72. groups = group_points(videos+infos, distance_threshold)
  73. print("4",time.time())
  74. return {"code": 200,
  75. "msg": "操作成功",
  76. "data": groups}
  77. except Exception as e:
  78. # 处理异常
  79. traceback.print_exc()
  80. raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
  81. @router.post("/get_details")
  82. @router.get("/get_details")
  83. async def get_details(
  84. body = Depends(remove_xss_json),
  85. # center_latitude: float = Query(..., description="网格中心点的纬度"),
  86. # center_longitude: float = Query(..., description="网格中心点的经度"),
  87. # zoom_level: float = Query(..., description="缩放级别"),
  88. db: Session = Depends(get_db)
  89. ):
  90. try:
  91. # 计算网格大小
  92. zoom_level = float(body['zoom_level'])
  93. zoom_levels = {
  94. 3: 10000, # 全国范围
  95. 4: 5000,
  96. 5: 2500,
  97. 6: 1250,
  98. 7: 825,
  99. 8: 412.5,
  100. 9: 256.25,
  101. 10: 178.125,
  102. 11: 69.0625,
  103. 12: 29.53125,
  104. 13: 13.765625,
  105. 14: 5.8828125,
  106. 15: 2.44140625,
  107. 16: 1.220703125,
  108. 17: 0.6103515625,
  109. 18: 0.30517578125
  110. }
  111. distance_threshold=zoom_levels[int(zoom_level-1)]
  112. # distance_threshold = 1000 / (1.5 ** zoom_level) # 例如:每缩放一级,距离阈值减半
  113. grid_size = calculate_grid_size(distance_threshold) # 地球半径为6371公里
  114. center_latitude = float(body['latitude'])
  115. center_longitude = float(body['longitude'])
  116. dict_value = body['dict_value'].split(',')
  117. option = body['option'].split(',')
  118. # 计算网格的经纬度范围
  119. latitude_min, latitude_max, longitude_min, longitude_max = get_grid_bounds_from_center(center_latitude, center_longitude, grid_size)
  120. videos = get_videos(db,dict_value,latitude_min,latitude_max,longitude_min,longitude_max)
  121. infos = get_points(db,option,latitude_min,latitude_max,longitude_min,longitude_max)
  122. return {"code": 200,
  123. "msg": "操作成功",
  124. "data": videos+infos }#{"videos":videos,"points":infos}}
  125. except Exception as e:
  126. # 处理异常
  127. traceback.print_exc()
  128. raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
  129. def calculate_grid_size(distance_threshold):
  130. # 假设地球半径为6371公里,将距离阈值转换为经纬度的差值
  131. # 这里假设纬度变化对距离的影响较小,仅根据经度计算网格大小
  132. earth_radius = 6371 # 地球半径,单位为公里
  133. grid_size = distance_threshold / earth_radius
  134. return grid_size
  135. def get_grid_key(latitude, longitude, grid_size):
  136. # 根据经纬度和网格大小计算网格键
  137. return (math.floor(latitude / grid_size), math.floor(longitude / grid_size))
  138. def get_grid_bounds_from_center(center_latitude, center_longitude, grid_size):
  139. half_grid_size = grid_size / 2
  140. min_latitude = center_latitude - half_grid_size
  141. max_latitude = center_latitude + half_grid_size
  142. min_longitude = center_longitude - half_grid_size
  143. max_longitude = center_longitude + half_grid_size
  144. return min_latitude, max_latitude, min_longitude, max_longitude
  145. def calculate_distance(point1, point2):
  146. # 使用 Haversine 公式计算两点之间的距离
  147. from math import radians, sin, cos, sqrt, atan2
  148. R = 6371 # 地球半径(公里)
  149. lat1, lon1 = radians(point1.latitude), radians(point1.longitude)
  150. lat2, lon2 = radians(point2.latitude), radians(point2.longitude)
  151. dlat = lat2 - lat1
  152. dlon = lon2 - lon1
  153. a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
  154. c = 2 * atan2(sqrt(a), sqrt(1 - a))
  155. return R * c
  156. def group_points(points, distance_threshold):
  157. grid_size = calculate_grid_size(distance_threshold)
  158. grid = defaultdict(lambda:{"count":0}) #,"list":[]
  159. groups = []
  160. tmp = defaultdict(list)
  161. for point in points:
  162. grid_key = get_grid_key(float(point.latitude), float(point.longitude), grid_size)
  163. lovalue = str(point.latitude)+str(point.longitude)
  164. if lovalue not in tmp['%s-%s'%grid_key]:
  165. tmp['%s-%s'%grid_key].append(lovalue)
  166. grid['%s-%s'%grid_key]['count']+=1
  167. if grid['%s-%s'%grid_key]['count']>1 and len(tmp['%s-%s'%grid_key])<3:
  168. grid['%s-%s'%grid_key]['dataType'] = ''
  169. grid['%s-%s'%grid_key]['id'] = ""
  170. if len(tmp['%s-%s'%grid_key])<2:
  171. grid['%s-%s'%grid_key]['name'] = '多数据点位'
  172. grid['%s-%s' % grid_key]['type'] ='1'
  173. else:
  174. grid['%s-%s' % grid_key]['name'] = '聚合点位'
  175. grid['%s-%s' % grid_key]['type'] = '3'
  176. # grid['%s-%s'%grid_key]['latitude'] = float(point.latitude) #(grid_key[0] + 0.5) * grid_size
  177. # grid['%s-%s'%grid_key]['longitude'] = float(point.longitude) #(grid_key[1] + 0.5) * grid_size
  178. elif grid['%s-%s'%grid_key]['count']==1:
  179. if point.dataType=='video':
  180. grid['%s-%s' % grid_key]['id'] = point.gbIndexCode
  181. else:
  182. grid['%s-%s' % grid_key]['id'] = point.id
  183. grid['%s-%s'%grid_key]['dataType'] = point.dataType
  184. grid['%s-%s'%grid_key]['name'] = point.name
  185. grid['%s-%s'%grid_key]['type'] ='2'
  186. grid['%s-%s'%grid_key]['latitude'] = float(point.latitude)
  187. grid['%s-%s'%grid_key]['longitude'] = float(point.longitude)
  188. groups = list(grid.values())
  189. return groups
  190. def get_videos(db:Session,dict_value,latitude_min,latitude_max,longitude_min,longitude_max):
  191. que = True
  192. if len(dict_value)>0:
  193. videolist = []
  194. for value in dict_value:
  195. tag_info = get_dict_data_info(db, 'video_type', value)
  196. if tag_info:
  197. if tag_info.dict_label == '全量视频':
  198. break
  199. else:
  200. videolist += [i.video_code for i in tag_get_video_tag_list(db, value)]
  201. else:
  202. que = TPVideoInfo.gbIndexCode.in_(videolist)
  203. # 查询分组
  204. query = (
  205. select(
  206. TPVideoInfo.gbIndexCode,
  207. TPVideoInfo.latitude,
  208. TPVideoInfo.longitude,
  209. TPVideoInfo.name,
  210. TPVideoInfo.status,
  211. literal('video').label("dataType")
  212. )
  213. .select_from(TPVideoInfo).where(
  214. and_(
  215. TPVideoInfo.latitude >= latitude_min,
  216. TPVideoInfo.latitude <= latitude_max,
  217. TPVideoInfo.longitude >= longitude_min,
  218. TPVideoInfo.longitude <= longitude_max,
  219. TPVideoInfo.longitude > 0,
  220. TPVideoInfo.latitude > 0, que
  221. )
  222. )
  223. .order_by(TPVideoInfo.status.asc())
  224. )
  225. result = db.execute(query)
  226. videos = result.fetchall()
  227. return videos
  228. def get_points(db:Session,option,latitude_min,latitude_max,longitude_min,longitude_max):
  229. # 使用参数化查询避免 SQL 注入
  230. if isinstance(option, list):
  231. option = tuple(option)
  232. query = text("""
  233. SELECT
  234. A.`name`,A.`id`,A.dataType,A.longitude,A.latitude,A.infoType
  235. FROM (
  236. SELECT
  237. *,
  238. ROW_NUMBER() OVER (PARTITION BY longitude, latitude, `name`
  239. ORDER BY longitude, latitude, `name`) AS rn
  240. FROM
  241. `point_data`
  242. WHERE
  243. longitude > 0
  244. AND latitude BETWEEN :latitude_min AND :latitude_max
  245. AND longitude BETWEEN :longitude_min AND :longitude_max
  246. AND dataType IN :option
  247. ) AS A
  248. WHERE rn = 1
  249. """)
  250. # 执行查询并传递参数
  251. result = db.execute(query, {
  252. 'latitude_min': latitude_min,
  253. 'latitude_max': latitude_max,
  254. 'longitude_min': longitude_min,
  255. 'longitude_max': longitude_max,
  256. 'option': option
  257. })
  258. infos = result.fetchall()
  259. return infos