|
|
|
|
#!/usr/bin/python
|
|
|
|
|
# -*- coding: UTF-8 -*-
|
|
|
|
|
|
|
|
|
|
from math import *
|
|
|
|
|
import queue
|
|
|
|
|
import sys
|
|
|
|
|
import time
|
|
|
|
|
import os
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
import pymssql
|
|
|
|
|
import pandas as pd
|
|
|
|
|
import numpy as np
|
|
|
|
|
import datetime
|
|
|
|
|
import math
|
|
|
|
|
sys.path.append(r'C:\Users\Administrator\Desktop')
|
|
|
|
|
from CarRecord import *
|
|
|
|
|
from Arc import *
|
|
|
|
|
from ArcMany import *
|
|
|
|
|
from CarPath import *
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
arc_objects=[]
|
|
|
|
|
map_graph=[]
|
|
|
|
|
car_id_count=[]
|
|
|
|
|
#通过经纬度计算两点之间的距离
|
|
|
|
|
def DistanceBetween(geo1, geo2):
|
|
|
|
|
return pow(pow(float(geo1[0]) - float(geo2[0]), 2) + pow(float(geo1[1]) - float(geo2[1]), 2), 0.5)
|
|
|
|
|
|
|
|
|
|
# 知道三个顶点坐标ABC,看角ACB是否是锐角三角形
|
|
|
|
|
def CheckOxygon(A, C, B):
|
|
|
|
|
a = DistanceBetween(C, B)
|
|
|
|
|
b = DistanceBetween(A, C)
|
|
|
|
|
c = DistanceBetween(A, B)
|
|
|
|
|
if a ** 2 + b ** 2 < c ** 2:
|
|
|
|
|
return False
|
|
|
|
|
else:
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
# 计算起点到终点线段方向与x轴的夹角 逆时针
|
|
|
|
|
# geo_list 第一个参数-1终点 0起点 第二个参数 0--y 1--x
|
|
|
|
|
def CalDirection(geo_list):
|
|
|
|
|
if geo_list[-1][0] == geo_list[0][0]:
|
|
|
|
|
if geo_list[-1][1] > geo_list[0][1]:
|
|
|
|
|
return float(0)
|
|
|
|
|
else:
|
|
|
|
|
return float(180)
|
|
|
|
|
else:
|
|
|
|
|
# tan=slope
|
|
|
|
|
# arctan(tan x)=x
|
|
|
|
|
slope = (geo_list[-1][1] - geo_list[0][1]) / (geo_list[-1][0] - geo_list[0][0])
|
|
|
|
|
if geo_list[-1][0] > geo_list[0][0]: # 如果射线在第1,2象限
|
|
|
|
|
return 90 - (atan(slope) / pi * 180) # 由于角度是跟x轴的夹角,所以需要90-
|
|
|
|
|
else: # 如果射线在第3,4象限
|
|
|
|
|
return 90 - (atan(slope) / pi * 180) + 180
|
|
|
|
|
|
|
|
|
|
# 传入geo1, geo2为直线y = kx + d上两点,求geo3在直线上的射影(即作垂直后与直线的交点)
|
|
|
|
|
# 如果形成钝角三角形,则垂点取geo1,geo2中靠近geo3的那个 就是直接过去
|
|
|
|
|
def CalProjection(geo1, geo2, geo3):
|
|
|
|
|
geo1 = [float(ele) for ele in geo1]
|
|
|
|
|
geo2 = [float(ele) for ele in geo2]
|
|
|
|
|
geo3 = [float(ele) for ele in geo3]
|
|
|
|
|
a = DistanceBetween(geo2, geo3)
|
|
|
|
|
b = DistanceBetween(geo1, geo3)
|
|
|
|
|
c = DistanceBetween(geo1, geo2)
|
|
|
|
|
if (a**2 + c**2) <= b**2: #钝角三角形,且geo3靠近geo2,包括点在线段延长线上的情况
|
|
|
|
|
return geo2
|
|
|
|
|
elif b**2 + c**2 <= a**2: #钝角三角形,且geo3靠近geo1,包括点在线段延长线上的情况
|
|
|
|
|
return geo1
|
|
|
|
|
elif a + b == c: # 说明点在线段上
|
|
|
|
|
return geo3
|
|
|
|
|
else:
|
|
|
|
|
if geo1[0] == geo2[0]: # 如果直线竖直(0---x;1---y)
|
|
|
|
|
return [geo1[0], geo3[1]]
|
|
|
|
|
elif geo1[1] == geo2[1]: # 如果直线水平
|
|
|
|
|
return [geo3[0], geo1[1]]
|
|
|
|
|
else:
|
|
|
|
|
k = (geo1[1] - geo2[1]) / (geo1[0] - geo2[0]) # y = kx+d中的k
|
|
|
|
|
d = geo1[1] - k * geo1[0] # y = kx+d中的d
|
|
|
|
|
x4 = (k * geo3[1] - k * d + geo3[0]) / (1 + k**2) #先求交点经度
|
|
|
|
|
y4 = k * x4 + d #再求交点纬度
|
|
|
|
|
return [x4, y4]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#逐个计算路段的点、与路段延长点、出发点的的夹角
|
|
|
|
|
def CalProjectionOfArc(arc, geo):
|
|
|
|
|
length = len(arc.geometry_list)
|
|
|
|
|
# 先找一个虚拟点,其为点arc.geometry_list[-2]到arc.geometry_list[-1]连线延长线上延长相等距离的点virtual_p
|
|
|
|
|
virtual_p = [(2 * arc.geometry_list[-1][i] - arc.geometry_list[-2][i]) for i in range(2)]
|
|
|
|
|
# 找从哪个i开始,三角形ACB变成钝角,其中点A为car_record.geo(出发点),点C为arc.geometry_list[i](路段的点),点B为virtual_p(路段延长点)
|
|
|
|
|
i = 0
|
|
|
|
|
while i < length - 1 and CheckOxygon(geo, arc.geometry_list[i], virtual_p): # 判断是否是锐角
|
|
|
|
|
i += 1
|
|
|
|
|
# 如果最小的i为0或者len() - 1,则取最靠近目标点的两个点计算垂点
|
|
|
|
|
# 即使第一个点就出现钝角三角形的情况,calProjection函数中会处理
|
|
|
|
|
if i == 0: # 如果垂点不在线段上
|
|
|
|
|
projection = CalProjection(arc.geometry_list[i], arc.geometry_list[i + 1], geo)
|
|
|
|
|
else: # 如果垂点在线段上
|
|
|
|
|
projection = CalProjection(arc.geometry_list[i - 1], arc.geometry_list[i], geo)
|
|
|
|
|
return projection
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 传入geo1, geo2为直线y = kx + d上两点,求geo3到直线的距离
|
|
|
|
|
# 如果形成钝角三角形,则垂点取geo1,geo2中靠近geo3的那个点与geo3的距离
|
|
|
|
|
def CalDistance(geo1, geo2, geo3):
|
|
|
|
|
geo1 = [float(ele) for ele in geo1]
|
|
|
|
|
geo2 = [float(ele) for ele in geo2]
|
|
|
|
|
geo3 = [float(ele) for ele in geo3]
|
|
|
|
|
a = DistanceBetween(geo2, geo3)
|
|
|
|
|
b = DistanceBetween(geo1, geo3)
|
|
|
|
|
c = DistanceBetween(geo1, geo2)
|
|
|
|
|
if (a ** 2 + c ** 2) <= b ** 2: # 钝角三角形,且geo3靠近geo2,包括点在线段延长线上的情况
|
|
|
|
|
return DistanceBetween(geo2, geo3) * 5 # *5是geo点跑到外面去的panelty
|
|
|
|
|
elif b ** 2 + c ** 2 <= a ** 2: # 钝角三角形,且geo3靠近geo1,包括点在线段延长线上的情况
|
|
|
|
|
return DistanceBetween(geo1, geo3) * 5
|
|
|
|
|
elif a + b == c: # 说明点在线段上
|
|
|
|
|
return 0
|
|
|
|
|
else:
|
|
|
|
|
if geo1[0] == geo2[0]: # 如果直线竖直
|
|
|
|
|
return abs(geo3[0] - geo1[0])
|
|
|
|
|
elif geo1[1] == geo2[1]: # 如果直线水平
|
|
|
|
|
return abs(geo3[1] - geo1[1])
|
|
|
|
|
else:
|
|
|
|
|
k = (geo1[1] - geo2[1]) / (geo1[0] - geo2[0]) # y = kx+d中的k
|
|
|
|
|
d = geo1[1] - k * geo1[0] # y = kx+d中的d
|
|
|
|
|
dist = abs(k * geo3[0] + d - geo3[1]) / sqrt(1 + k ** 2) # 点到直线距离公式
|
|
|
|
|
return dist
|
|
|
|
|
|
|
|
|
|
# 如果起点定位跟路线的相差很远或者方向不对,则没有必要取该条arc
|
|
|
|
|
def CheckNecessary(arc, car_record):
|
|
|
|
|
# 计算该地图中最长的路,取一半的大一点,即1000,防止出现漏判的情况,因为下面这个if是只取每段道路的坐标的起点和终点来跟目标点计算距离
|
|
|
|
|
if DistanceActual(arc.geometry_list[0], car_record.geo) > 1000:
|
|
|
|
|
if DistanceActual(arc.geometry_list[-1], car_record.geo) > 1000:
|
|
|
|
|
return False
|
|
|
|
|
# 下面if是计算这条线路上有一gps点跟目标点的最短距离小于100m才返回True。
|
|
|
|
|
min_dist = float("inf")
|
|
|
|
|
for geo1 in arc.geometry_list:
|
|
|
|
|
tmp = DistanceActual(geo1, car_record.geo)
|
|
|
|
|
if tmp < min_dist:
|
|
|
|
|
min_dist = tmp
|
|
|
|
|
if min_dist < 10:#起点与路线距离的判断
|
|
|
|
|
return True
|
|
|
|
|
else:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# 计算GPS匹配到该路段的成本系数cost
|
|
|
|
|
def CalCost(arc, car_record):
|
|
|
|
|
cost = float("inf")#正无穷
|
|
|
|
|
# CheckNecessary函数首先排除与起点定位点距离相差远的路段。
|
|
|
|
|
if CheckNecessary(arc, car_record):
|
|
|
|
|
# arc.geometry_list为路段点集合,length记录点个数。
|
|
|
|
|
length = len(arc.geometry_list)
|
|
|
|
|
# 先找一个虚拟点,其为点arc.geometry_list[-2]到arc.geometry_list[-1]连线延长线上延长相等距离的点
|
|
|
|
|
# 虚拟点virtual_p在道路最后一个点之后。
|
|
|
|
|
virtual_p = [(2 * arc.geometry_list[-1][i] - arc.geometry_list[-2][i]) for i in range(2)]
|
|
|
|
|
# 找从哪个i开始,三角形ACB变成钝角,其中点A为car_record.geo,点C为arc.geometry_list[i],点B为virtual_p
|
|
|
|
|
i = 0
|
|
|
|
|
# CheckOxygon检查起点与线路第i点的连线,与线路的夹角是否为锐角,是锐角继续循环。/LOU
|
|
|
|
|
while i < length - 1 and CheckOxygon(car_record.geo, arc.geometry_list[i], virtual_p):
|
|
|
|
|
i += 1
|
|
|
|
|
# 如果最小的i为0或者len() - 1,则取最靠近目标点的两个点计算垂点
|
|
|
|
|
# 即使出现钝角三角形的情况,calProjection函数中会处理
|
|
|
|
|
if i == 0:
|
|
|
|
|
# dist为起点定位点与道路的距离,direction_road为线路方向,作为之后计算cost的直接、间接的参数。/LOU
|
|
|
|
|
dist = CalDistance(arc.geometry_list[i], arc.geometry_list[i + 1], car_record.geo)
|
|
|
|
|
|
|
|
|
|
direction_road = arc.direction
|
|
|
|
|
else: # 如果垂点在线段上
|
|
|
|
|
dist = CalDistance(arc.geometry_list[i - 1], arc.geometry_list[i], car_record.geo)
|
|
|
|
|
direction_road = CalDirection(arc.geometry_list[i - 1: i + 1])
|
|
|
|
|
# 综合考虑起点与道路距离,得出起点定位点与路段匹配的cost。
|
|
|
|
|
cost = dist
|
|
|
|
|
return cost
|
|
|
|
|
|
|
|
|
|
# 给定car_geo和car_direction,根据最短距离,计算在所有道路的Cost,返回Cost最小的道路id
|
|
|
|
|
def CalMinCostArcID(car_record):
|
|
|
|
|
min_cost = float("inf")
|
|
|
|
|
id = 0
|
|
|
|
|
# arc_objects存储着所有边的Arc对象实例,即所有路段信息。
|
|
|
|
|
for arc in arc_objects:
|
|
|
|
|
cost = CalCost(arc, car_record)
|
|
|
|
|
if cost < min_cost:
|
|
|
|
|
min_cost = cost
|
|
|
|
|
id = arc.id
|
|
|
|
|
return id
|
|
|
|
|
|
|
|
|
|
# 计算时长
|
|
|
|
|
def SecondsBetween(t1, t2):
|
|
|
|
|
big = datetime.datetime.strptime(str(t2), "%Y-%m-%d %H:%M:%S")#例:print(now.strftime('%a, %b %d %H:%M'))输出Mon, May 08 20:22
|
|
|
|
|
small = datetime.datetime.strptime(str(t1), "%Y-%m-%d %H:%M:%S")
|
|
|
|
|
return (big - small).total_seconds()#用秒统计
|
|
|
|
|
|
|
|
|
|
# 返回两个点的实际距离,x,y与米的换算约为1:111000。(不同的比例尺数值不同)
|
|
|
|
|
def DistanceActual(geo1, geo2):
|
|
|
|
|
return pow(pow(float(geo1[0]) - float(geo2[0]), 2) + pow(float(geo1[1]) - float(geo2[1]), 2), 0.5)*111000
|
|
|
|
|
|
|
|
|
|
# 输入arc_cover是arc_id和cover的列表,该函数输出这个arc_cover包含的道路总长
|
|
|
|
|
def CalLenCover(arc_cover):
|
|
|
|
|
travel_distance = 0
|
|
|
|
|
for k in range(len(arc_cover)):
|
|
|
|
|
# arc_objects存储着所有边的Arc对象实例,即所有路段信息。
|
|
|
|
|
travel_distance += arc_objects[arc_cover[k][0] - 1].len * arc_cover[k][1]
|
|
|
|
|
return round(travel_distance,1)
|
|
|
|
|
|
|
|
|
|
# 输入arc_list是arc_id的列表,该函数输出这个arc_list包含的道路总长
|
|
|
|
|
def CalLen(arc_list):
|
|
|
|
|
travel_distance = 0
|
|
|
|
|
for k in range(len(arc_list)):
|
|
|
|
|
travel_distance += arc_objects[arc_list[k] - 1].len
|
|
|
|
|
return round(travel_distance,1)
|
|
|
|
|
|
|
|
|
|
# 拿着集合找最短的
|
|
|
|
|
def findMinDist(dist, collected):
|
|
|
|
|
min_dist = float("inf")
|
|
|
|
|
min_key = -1
|
|
|
|
|
for key in dist:
|
|
|
|
|
if dist[key] < min_dist and (key not in collected.keys()):
|
|
|
|
|
min_dist = dist[key]
|
|
|
|
|
min_key = key
|
|
|
|
|
return min_key
|
|
|
|
|
|
|
|
|
|
# 用单源最短路径算法,求两条边的最短距离的arc_list
|
|
|
|
|
# graph:邻近路网关系图;start_arc_id:起点路段;end_arc_id:终点路段
|
|
|
|
|
def dijkstra(graph, start_arc_id, end_arc_id):
|
|
|
|
|
if start_arc_id == end_arc_id: # 当前一路段与后匹配路段一样
|
|
|
|
|
return [start_arc_id]
|
|
|
|
|
dist = {}
|
|
|
|
|
path = {}
|
|
|
|
|
collected = {}
|
|
|
|
|
# 初始化距离为无穷大
|
|
|
|
|
for key in graph:
|
|
|
|
|
dist[key] = float("inf")
|
|
|
|
|
for arc in graph[key]:
|
|
|
|
|
if not arc.to_node in dist.keys(): # 如果邻近路网关系图中没有路段的起点等于arc的终点,则距离设为无穷
|
|
|
|
|
dist[arc.to_node] = float("inf")
|
|
|
|
|
from_node = arc_objects[start_arc_id - 1].to_node # 起始路段起点
|
|
|
|
|
to_node = arc_objects[end_arc_id - 1].from_node # 终止路段终点
|
|
|
|
|
if from_node in graph.keys(): # 如果graph中有路段起点与from_node一致
|
|
|
|
|
for arc in graph[from_node]: # 遍历路段起点与from_node一致的路段
|
|
|
|
|
dist[arc.to_node] = arc.len # 更新始路段起点到该点的距离
|
|
|
|
|
path[arc.to_node] = from_node # 更新始路段起点到该点的路径
|
|
|
|
|
dist[from_node] = 0 # 起始路段起点到起始路段起点距离为0
|
|
|
|
|
collected[from_node] = True
|
|
|
|
|
while True:
|
|
|
|
|
node = findMinDist(dist, collected)
|
|
|
|
|
if node == -1:
|
|
|
|
|
break
|
|
|
|
|
collected[node] = True
|
|
|
|
|
if node in graph.keys():
|
|
|
|
|
for arc in graph[node]:
|
|
|
|
|
if arc.to_node not in collected.keys():
|
|
|
|
|
if dist[arc.to_node] > dist[node] + arc.len:
|
|
|
|
|
dist[arc.to_node] = dist[node] + arc.len
|
|
|
|
|
path[arc.to_node] = node
|
|
|
|
|
arc_list = [end_arc_id]# 先放入终点
|
|
|
|
|
while to_node in path.keys():
|
|
|
|
|
for arc in graph[path[to_node]]:
|
|
|
|
|
if arc.to_node == to_node:
|
|
|
|
|
arc_list.append(arc.id)
|
|
|
|
|
to_node = path[to_node]
|
|
|
|
|
arc_list.append(start_arc_id) #最后放入起点
|
|
|
|
|
arc_list = arc_list[::-1] #两级反转
|
|
|
|
|
return arc_list
|
|
|
|
|
|
|
|
|
|
#arc:上一个点所匹配的路径,car_record:点对象,dist:arc终点到点的距离
|
|
|
|
|
def BFSFindPathArc(arc, car_record, dist):
|
|
|
|
|
min_cost = 60 # min_cost初始值为60近似为无限大
|
|
|
|
|
arc_list = [] # arc_list是点从上一个点走过的路径集合
|
|
|
|
|
min_arc_id = 0 # min_arc_id是得到最小cost的路段
|
|
|
|
|
visited = {} # visited 里面记录着已经搜索过计算过cost的路段
|
|
|
|
|
graph_around = {} # graph_around是连接着上一匹配路段的临近路段集合。
|
|
|
|
|
q = queue.Queue() # q是一个队列,里面存放需要计算cost的备选路段
|
|
|
|
|
q.put(arc) # 将arc放入队列
|
|
|
|
|
visited[arc.id] = True # 表示arc已经搜索过
|
|
|
|
|
while not q.empty(): # 当q里还有需要计算cost的路段,则继续循环
|
|
|
|
|
arc1 = q.get() # 将q里最后放进去的路段拿出并从q里删除
|
|
|
|
|
# 生成arc附近连通的道路的图,后面用来找最短路径
|
|
|
|
|
if arc1.from_node in graph_around.keys():
|
|
|
|
|
graph_around[arc1.from_node].append(arc1) # 将起点ID相同的路段放入同一个索引
|
|
|
|
|
else:
|
|
|
|
|
graph_around[arc1.from_node] = [arc1]
|
|
|
|
|
cost = CalCost(arc1, car_record) # 计算arc1的cost
|
|
|
|
|
if cost < min_cost:
|
|
|
|
|
min_cost = cost # 更新最小cost
|
|
|
|
|
min_arc_id = arc1.id # 更新最小cost的路段
|
|
|
|
|
if arc1.to_node in map_graph.keys(): # 如果arc1路段的终点在地图临近关系表map_graph里
|
|
|
|
|
for arc2 in map_graph[arc1.to_node]: # 在以arc1路段终点为起点的路段集合里做循环,以arc2变量表示
|
|
|
|
|
if arc2.id not in visited.keys(): # 如果arc2不在visited集合中
|
|
|
|
|
if DistanceBetween(arc.geometry_list[-1], arc2.geometry_list[0]) < dist*1.5:
|
|
|
|
|
# 如果arc1终点到arc2的起点的距离 小于 arc终点到GPS点的距离
|
|
|
|
|
q.put(arc2) # 将arc2放进队列
|
|
|
|
|
visited[arc2.id] = True # 将arc2标记已搜索
|
|
|
|
|
if min_arc_id != 0:
|
|
|
|
|
arc_list = dijkstra(graph_around, arc.id, min_arc_id) # 计算前一匹配路段到现匹配路段的最短路径
|
|
|
|
|
return arc_list
|
|
|
|
|
|
|
|
|
|
# 计算car_i经过的路径,将路段index记录在car_i_start_end_inde_list中,路径以j表示
|
|
|
|
|
def CarRecordsDivide(car_i):
|
|
|
|
|
# car_id_count存储着从1到100每一条OD的定位数量 /LOU
|
|
|
|
|
index_start = sum(car_id_count[:car_i-1]) # 第i条OD数据的起始索引位置
|
|
|
|
|
index_end = sum(car_id_count[:car_i]) # 第i条OD数据的终止索引位置
|
|
|
|
|
car_i_start_end_index_list = []
|
|
|
|
|
car_i_start_end_index_list.append([index_start, index_end])
|
|
|
|
|
return car_i_start_end_index_list
|
|
|
|
|
|
|
|
|
|
#计算属于一天中的第几个小时
|
|
|
|
|
def Time_stage(gps_time):
|
|
|
|
|
today_time=datetime.datetime(2022, 7, 9, 0, 0, 0)
|
|
|
|
|
return math.ceil(SecondsBetween(today_time, gps_time)/3600)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|