gotrackit 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. {gotrackit-0.2.2/src/gotrackit.egg-info → gotrackit-0.2.3}/PKG-INFO +16 -8
  2. {gotrackit-0.2.2 → gotrackit-0.2.3}/README.md +15 -7
  3. {gotrackit-0.2.2 → gotrackit-0.2.3}/pyproject.toml +1 -1
  4. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/MapMatch.py +82 -35
  5. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/gps/GpsArray.py +2 -2
  6. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/gps/GpsXfer.py +1 -1
  7. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/gps/LocGps.py +17 -15
  8. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/map/Link.py +21 -8
  9. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/map/Net.py +55 -11
  10. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/map/Node.py +11 -5
  11. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/model/Markov.py +42 -19
  12. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/NetGen.py +13 -0
  13. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/SaveStreets/streets.py +5 -0
  14. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/tools/geo_process.py +2 -1
  15. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/visualization.py +24 -11
  16. {gotrackit-0.2.2 → gotrackit-0.2.3/src/gotrackit.egg-info}/PKG-INFO +16 -8
  17. {gotrackit-0.2.2 → gotrackit-0.2.3}/LICENSE +0 -0
  18. {gotrackit-0.2.2 → gotrackit-0.2.3}/setup.cfg +0 -0
  19. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/__init__.py +0 -0
  20. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/GlobalVal.py +0 -0
  21. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/WrapsFunc.py +0 -0
  22. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/__init__.py +0 -0
  23. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/generation/GpsGen.py +0 -0
  24. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/generation/__init__.py +0 -0
  25. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/gps/GpsTrip.py +0 -0
  26. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/gps/__init__.py +0 -0
  27. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/map/__init__.py +0 -0
  28. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/model/__init__.py +0 -0
  29. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/GlobalVal.py +0 -0
  30. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Parse/__init__.py +0 -0
  31. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Parse/gd_car_path.py +0 -0
  32. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/PublicTools/GeoProcess.py +0 -0
  33. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/PublicTools/GraphAna.py +0 -0
  34. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/PublicTools/IndexAna.py +0 -0
  35. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/PublicTools/MapProcess.py +0 -0
  36. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/PublicTools/__init__.py +0 -0
  37. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/PublicTools/od.py +0 -0
  38. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/__init__.py +0 -0
  39. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/api/WebApi.py +0 -0
  40. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/api/__init__.py +0 -0
  41. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/request_path.py +0 -0
  42. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/usage/__init__.py +0 -0
  43. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/usage/bd_ts.py +0 -0
  44. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/Request/usage/gd_car_path.py +0 -0
  45. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/DupProcess/DupLinks.py +0 -0
  46. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/DupProcess/__init__.py +0 -0
  47. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/__init__.py +0 -0
  48. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/get_merged_link_seq.py +0 -0
  49. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/limit/__init__.py +0 -0
  50. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/limit/attr_limit.py +0 -0
  51. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/limit/direction_limit.py +0 -0
  52. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/limit/same_head_tail_limit.py +0 -0
  53. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/limit/two_degrees_group.py +0 -0
  54. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/merge_links.py +0 -0
  55. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Merge/merge_short.py +0 -0
  56. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/__init__.py +0 -0
  57. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/limit/__init__.py +0 -0
  58. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/limit/attr_limit.py +0 -0
  59. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/limit/direction_limit.py +0 -0
  60. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/limit/same_head_tail_limit.py +0 -0
  61. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/limit/two_degrees_group.py +0 -0
  62. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/MultiCoreMerge/merge_links_multi.py +0 -0
  63. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/SaveStreets/__init__.py +0 -0
  64. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Split/SplitPath.py +0 -0
  65. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Split/__init__.py +0 -0
  66. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Tools/__init__.py +0 -0
  67. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/Tools/process.py +0 -0
  68. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/__init__.py +0 -0
  69. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/conn.py +0 -0
  70. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/increment.py +0 -0
  71. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/net_reverse.py +0 -0
  72. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/optimize_net.py +0 -0
  73. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/RoadNet/save_file.py +0 -0
  74. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/__init__.py +0 -0
  75. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/book_mark.py +0 -0
  76. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netreverse/format_od.py +0 -0
  77. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netxfer/SumoConvert.py +0 -0
  78. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/netxfer/__init__.py +0 -0
  79. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/solver/Viterbi.py +0 -0
  80. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/solver/__init__.py +0 -0
  81. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/tools/__init__.py +0 -0
  82. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/tools/coord_trans.py +0 -0
  83. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/tools/group.py +0 -0
  84. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit/tools/save_file.py +0 -0
  85. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit.egg-info/SOURCES.txt +0 -0
  86. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit.egg-info/dependency_links.txt +0 -0
  87. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit.egg-info/requires.txt +0 -0
  88. {gotrackit-0.2.2 → gotrackit-0.2.3}/src/gotrackit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gotrackit
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: A Python Package for Map Matching Algorithm Based on Hidden Markov Model
5
5
  Author-email: Kai Tang <794568794@qq.com>
6
6
  License: LICENCE
@@ -38,26 +38,34 @@ Requires-Dist: geopy
38
38
 
39
39
  ~ 一个包搞定:路网获取、路网优化、宏微观地图匹配、匹配可视化、问题路段快速定位 ~
40
40
 
41
- 唐铠, 794568794@qq.com, tangkai@zhechengdata.com
41
+ Developed by Tang Kai, Email: 794568794@qq.com & tangkai@zhechengdata.com
42
42
  </div>
43
43
  <br>
44
44
 
45
45
 
46
46
 
47
- **版本状态:04.27即将更新更新: v0.2.2**
47
+ **版本状态:05.07已经更新: v0.2.3**
48
48
 
49
49
  更新命令:pip install --upgrade -i https://pypi.org/simple/ gotrackit
50
50
 
51
- - 向量化改造, 且引入FMM(Fast Map Matching)路径预存储机制, 大规模路网匹配效率大幅度提升
52
51
 
53
- - 完善报错机制, 遇到GPS脏数据不再报错停止, 而是跳过, 并且在所有的agents计算完毕后输出有问题的agent编号
52
+ - 效率优化, 相较于v0.2.2小幅度提升
53
+
54
+ - crs判断BUG修复、境外路网构建失败BUG修复
55
+
56
+ - 报错机制优化
57
+
58
+ - 输出文件目录指定参数html_fldr废除, 改为out_fldr
59
+
60
+ - 增加环路处理功能
61
+
62
+ - 增加匹配结果即时输出参数, 每匹配完一条轨迹可马上进行结果存储
54
63
 
55
- - BUG修复
56
64
 
57
65
  <br>
58
66
 
59
67
  <div align=center>
60
- ~ v0.2.2效率将大幅度提升 ~
68
+ ~ v0.2.2效率将大幅度提升, 最高可以提升10倍的性能 !~
61
69
  </div>
62
70
 
63
71
  <br>
@@ -155,7 +163,7 @@ v0.2.2多核效率对比:
155
163
  - 算法原理讲解部分不涉及复杂的公式推导,使用动画形式剖析算法原理,简洁明了。
156
164
 
157
165
  **匹配结果自动优化**
158
- - 对基于HMM匹配的初步路径进行了优化,对于不连通的位置会自动补路,对于实际路网不连通的位置会输出警告,方便用户检查路网。
166
+ - 对基于HMM匹配的初步路径进行了优化,对于不连通的位置会自动搜路补全,对于实际路网不连通的位置会输出警告信息,方便用户回溯问题。
159
167
 
160
168
 
161
169
 
@@ -16,26 +16,34 @@
16
16
 
17
17
  ~ 一个包搞定:路网获取、路网优化、宏微观地图匹配、匹配可视化、问题路段快速定位 ~
18
18
 
19
- 唐铠, 794568794@qq.com, tangkai@zhechengdata.com
19
+ Developed by Tang Kai, Email: 794568794@qq.com & tangkai@zhechengdata.com
20
20
  </div>
21
21
  <br>
22
22
 
23
23
 
24
24
 
25
- **版本状态:04.27即将更新更新: v0.2.2**
25
+ **版本状态:05.07已经更新: v0.2.3**
26
26
 
27
27
  更新命令:pip install --upgrade -i https://pypi.org/simple/ gotrackit
28
28
 
29
- - 向量化改造, 且引入FMM(Fast Map Matching)路径预存储机制, 大规模路网匹配效率大幅度提升
30
29
 
31
- - 完善报错机制, 遇到GPS脏数据不再报错停止, 而是跳过, 并且在所有的agents计算完毕后输出有问题的agent编号
30
+ - 效率优化, 相较于v0.2.2小幅度提升
31
+
32
+ - crs判断BUG修复、境外路网构建失败BUG修复
33
+
34
+ - 报错机制优化
35
+
36
+ - 输出文件目录指定参数html_fldr废除, 改为out_fldr
37
+
38
+ - 增加环路处理功能
39
+
40
+ - 增加匹配结果即时输出参数, 每匹配完一条轨迹可马上进行结果存储
32
41
 
33
- - BUG修复
34
42
 
35
43
  <br>
36
44
 
37
45
  <div align=center>
38
- ~ v0.2.2效率将大幅度提升 ~
46
+ ~ v0.2.2效率将大幅度提升, 最高可以提升10倍的性能 !~
39
47
  </div>
40
48
 
41
49
  <br>
@@ -133,7 +141,7 @@ v0.2.2多核效率对比:
133
141
  - 算法原理讲解部分不涉及复杂的公式推导,使用动画形式剖析算法原理,简洁明了。
134
142
 
135
143
  **匹配结果自动优化**
136
- - 对基于HMM匹配的初步路径进行了优化,对于不连通的位置会自动补路,对于实际路网不连通的位置会输出警告,方便用户检查路网。
144
+ - 对基于HMM匹配的初步路径进行了优化,对于不连通的位置会自动搜路补全,对于实际路网不连通的位置会输出警告信息,方便用户回溯问题。
137
145
 
138
146
 
139
147
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gotrackit"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  dependencies = ["geopandas>=0.14.1", "shapely", "networkx", "pandas", "numpy", "keplergl", "geopy"]
9
9
  requires-python = ">=3.8"
10
10
  authors = [
@@ -26,19 +26,19 @@ class MapMatch(object):
26
26
  time_format: str = "%Y-%m-%d %H:%M:%S", time_unit: str = 's',
27
27
  gps_buffer: float = 200.0, gps_route_buffer_gap: float = 15.0,
28
28
  max_increment_times: int = 2, increment_buffer: float = 15.0,
29
- beta: float = 20.0, gps_sigma: float = 20.0, dis_para: float = 0.1,
29
+ beta: float = 6.0, gps_sigma: float = 30.0, dis_para: float = 0.1,
30
30
  is_lower_f: bool = False, lower_n: int = 2,
31
31
  use_heading_inf: bool = False, heading_para_array: np.ndarray = None,
32
32
  dense_gps: bool = True, dense_interval: float = 80.0,
33
33
  dwell_l_length: float = 10.0, dwell_n: int = 2, del_dwell: bool = True,
34
34
  dup_threshold: float = 10.0,
35
35
  is_rolling_average: bool = False, window: int = 2,
36
- export_html: bool = False, use_gps_source: bool = False, html_fldr: str = None,
36
+ export_html: bool = False, use_gps_source: bool = False, out_fldr: str = None,
37
37
  export_geo_res: bool = False,
38
38
  node_num_threshold: int = 2000, top_k: int = 20, omitted_l: float = 6.0,
39
39
  link_width: float = 1.5, node_radius: float = 1.5,
40
40
  match_link_width: float = 5.0, gps_radius: float = 6.0, export_all_agents: bool = False,
41
- visualization_cache_times: int = 50, multi_core_save: bool = False):
41
+ visualization_cache_times: int = 50, multi_core_save: bool = False, instant_output: bool = False):
42
42
  """
43
43
  :param flag_name: 标记字符名称, 会用于标记输出的可视化文件, 默认"test"
44
44
  :param net: gotrackit路网对象, 必须指定
@@ -64,14 +64,15 @@ class MapMatch(object):
64
64
  :param window: 滑动窗口大小, 默认2
65
65
  :param export_html: 是否输出网页可视化结果html文件, 默认True
66
66
  :param use_gps_source: 是否在可视化结果中使用GPS源数据进行展示, 默认False
67
- :param html_fldr: 保存网页可视化结果的文件目录, 默认当前目录
67
+ :param out_fldr: 保存匹配结果的文件目录, 默认当前目录
68
68
  :param export_geo_res: 是否输出匹配结果的几何可视化文件, 默认False
69
69
  :param node_num_threshold: 默认2000
70
70
  :param omitted_l: 当某GPS点与前后GPS点的平均距离小于该距离(m)时, 该GPS点的方向限制作用被取消
71
71
  :param gps_radius: HTML可视化中GPS点的半径大小,单位米,默认8米
72
72
  :param export_all_agents: 是否将所有agent的可视化存储于一个html文件中
73
- :param visualization_cache_times: 每匹配完几辆车再进行结果的统一存储, 默认50
73
+ :param visualization_cache_times: 每匹配完几辆车再进行(html or geojson文件)结果的统一存储, 默认50
74
74
  :param multi_core_save: 是否启用多进程进行结果存储
75
+ :param instant_output: 是否每匹配完一条轨迹就存储csv匹配结果
75
76
  """
76
77
  # 坐标系投影
77
78
  self.plain_crs = net.planar_crs
@@ -115,7 +116,7 @@ class MapMatch(object):
115
116
 
116
117
  self.export_html = export_html
117
118
  self.export_geo_res = export_geo_res
118
- self.html_fldr = html_fldr
119
+ self.out_fldr = r'./' if out_fldr is None else out_fldr
119
120
 
120
121
  self.may_error_list = dict()
121
122
  self.error_list = list()
@@ -134,6 +135,7 @@ class MapMatch(object):
134
135
  self.visualization_cache_times = visualization_cache_times
135
136
  self.multi_core_save = multi_core_save
136
137
  self.sub_net_buffer = self.gps_buffer + self.gps_route_buffer_gap + max_increment_times * increment_buffer
138
+ self.instant_output = instant_output
137
139
 
138
140
  def execute(self) -> tuple[pd.DataFrame, dict, list]:
139
141
  match_res_df = pd.DataFrame()
@@ -152,29 +154,49 @@ class MapMatch(object):
152
154
  for agent_id, _gps_df in self.gps_df.groupby(gps_field.AGENT_ID_FIELD):
153
155
  agent_count += 1
154
156
  print(rf'- gotrackit ------> No.{agent_count}: agent: {agent_id} ')
155
- gps_obj = GpsPointsGdf(gps_points_df=_gps_df, time_format=self.time_format,
156
- buffer=self.gps_buffer, time_unit=self.time_unit,
157
- plane_crs=self.plain_crs,
158
- max_increment_times=self.max_increment_times, increment_buffer=self.increment_buffer,
159
- dense_gps=self.dense_gps, dense_interval=self.dense_interval,
160
- dwell_l_length=self.dwell_l_length, dwell_n=self.dwell_n)
157
+ try:
158
+ gps_obj = GpsPointsGdf(gps_points_df=_gps_df, time_format=self.time_format,
159
+ buffer=self.gps_buffer, time_unit=self.time_unit,
160
+ plane_crs=self.plain_crs,
161
+ max_increment_times=self.max_increment_times,
162
+ increment_buffer=self.increment_buffer,
163
+ dense_gps=self.dense_gps, dense_interval=self.dense_interval,
164
+ dwell_l_length=self.dwell_l_length, dwell_n=self.dwell_n)
165
+ except Exception as e:
166
+ print('构建按GPS对象出错...')
167
+ print(repr(e))
168
+ self.error_list.append(agent_id)
169
+ continue
170
+
161
171
  del _gps_df
162
172
 
163
- if self.del_dwell:
164
- gps_obj.del_dwell_points()
173
+ # gps-process
174
+ try:
175
+ if self.del_dwell:
176
+ print(rf'gps-preprocessing: del dwell')
177
+ gps_obj.del_dwell_points()
178
+
179
+ # 降频处理
180
+ if self.is_lower_f:
181
+ print(rf'gps-preprocessing: lower frequency, size: {self.lower_n}')
182
+ gps_obj.lower_frequency(n=self.lower_n)
165
183
 
166
- # 降频处理
167
- if self.is_lower_f:
168
- print(rf'lower {self.lower_n} - frequency')
169
- gps_obj.lower_frequency(n=self.lower_n)
184
+ if self.is_rolling_average:
185
+ print(rf'gps-preprocessing: rolling average, window size: {self.rolling_window}')
186
+ gps_obj.rolling_average(window=self.rolling_window)
170
187
 
171
- if self.is_rolling_average:
172
- print(rf'rolling average by window size - {self.rolling_window}')
173
- gps_obj.rolling_average(window=self.rolling_window)
188
+ if self.dense_gps:
189
+ print(rf'gps-preprocessing: dense gps by interval: {self.dense_interval}m')
190
+ gps_obj.dense()
191
+ except Exception as e:
192
+ print(rf'gps数据预处理出错:{repr(e)}')
193
+ self.error_list.append(agent_id)
194
+ continue
174
195
 
175
- if self.dense_gps:
176
- print(rf'dense gps by interval - {self.dense_interval}m')
177
- gps_obj.dense()
196
+ if len(gps_obj.gps_gdf) <= 1:
197
+ print(r'经过数据预处理后GPS观测点数据不足2个')
198
+ self.error_list.append(agent_id)
199
+ continue
178
200
 
179
201
  # 依据当前的GPS数据(源数据)做一个子网络
180
202
  if self.use_sub_net:
@@ -199,15 +221,39 @@ class MapMatch(object):
199
221
  top_k=self.top_k, omitted_l=self.omitted_l)
200
222
 
201
223
  # 求解参数
202
- is_success = hmm_obj.generate_markov_para(add_single_ft)
224
+ try:
225
+ is_success = hmm_obj.generate_markov_para(add_single_ft)
226
+ except Exception as e:
227
+ print(rf'解算HMM参数出错:{repr(e)}')
228
+ is_success = False
203
229
  if not is_success:
230
+ self.error_list.append(agent_id)
204
231
  continue
205
- hmm_obj.solve()
206
- _match_res_df = hmm_obj.acquire_res()
232
+
233
+ # 求解模型
234
+ try:
235
+ hmm_obj.solve()
236
+ except Exception as e:
237
+ print(rf'求解HMM模型出错:{repr(e)}')
238
+ self.error_list.append(agent_id)
239
+ continue
240
+
241
+ # 获取结果
242
+ try:
243
+ _match_res_df = hmm_obj.acquire_res()
244
+ except Exception as e:
245
+ print(rf'获取匹配结果出错:{repr(e)}')
246
+ self.error_list.append(agent_id)
247
+ continue
248
+ if self.instant_output:
249
+ _match_res_df.to_csv(os.path.join(self.out_fldr, rf'{agent_id}_match_res.csv'),
250
+ encoding='utf_8_sig', index=False)
251
+ else:
252
+ match_res_df = pd.concat([match_res_df, _match_res_df])
253
+
207
254
  hmm_obj.format_war_info()
208
255
  if hmm_obj.is_warn:
209
256
  self.may_error_list[agent_id] = hmm_obj.format_warn_info
210
- match_res_df = pd.concat([match_res_df, _match_res_df])
211
257
 
212
258
  # if export files
213
259
  if self.export_html or self.export_geo_res:
@@ -216,8 +262,9 @@ class MapMatch(object):
216
262
  export_visualization(hmm_obj_list=hmm_res_list, use_gps_source=self.use_gps_source,
217
263
  export_geo=self.export_geo_res, export_html=self.export_html,
218
264
  gps_radius=self.gps_radius, export_all_agents=self.export_all_agents,
219
- out_fldr=self.html_fldr, flag_name=self.flag_name,
220
- multi_core_save=self.multi_core_save)
265
+ out_fldr=self.out_fldr, flag_name=self.flag_name,
266
+ multi_core_save=self.multi_core_save, sub_net_buffer=self.sub_net_buffer,
267
+ dup_threshold=self.dup_threshold)
221
268
  del hmm_res_list
222
269
  hmm_res_list = []
223
270
 
@@ -232,11 +279,11 @@ class MapMatch(object):
232
279
  pool = multiprocessing.Pool(processes=n)
233
280
  result_list = []
234
281
  for i in range(0, n):
235
- core_html_fldr = os.path.join(self.html_fldr, rf'core{i}')
236
- if os.path.exists(core_html_fldr):
282
+ core_out_fldr = os.path.join(self.out_fldr, rf'core{i}')
283
+ if os.path.exists(core_out_fldr):
237
284
  pass
238
285
  else:
239
- os.makedirs(core_html_fldr)
286
+ os.makedirs(core_out_fldr)
240
287
 
241
288
  agent_id_list = agent_group[i]
242
289
  gps_df = self.gps_df[self.gps_df[gps_field.AGENT_ID_FIELD].isin(agent_id_list)]
@@ -254,7 +301,7 @@ class MapMatch(object):
254
301
  dup_threshold=self.dup_threshold, is_rolling_average=self.is_rolling_average,
255
302
  window=self.rolling_window,
256
303
  export_html=self.export_html,
257
- use_gps_source=self.use_gps_source, html_fldr=core_html_fldr,
304
+ use_gps_source=self.use_gps_source, out_fldr=core_out_fldr,
258
305
  export_geo_res=self.export_geo_res,
259
306
  node_num_threshold=self.node_num_threshold,
260
307
  top_k=self.top_k, omitted_l=self.omitted_l, link_width=self.link_width,
@@ -262,7 +309,7 @@ class MapMatch(object):
262
309
  match_link_width=self.match_link_width, gps_radius=self.gps_radius,
263
310
  export_all_agents=self.export_all_agents,
264
311
  visualization_cache_times=self.visualization_cache_times,
265
- multi_core_save=False)
312
+ multi_core_save=False, instant_output=self.instant_output)
266
313
  result = pool.apply_async(mmp.execute,
267
314
  args=())
268
315
  result_list.append(result)
@@ -70,7 +70,7 @@ class GpsArray(object):
70
70
  return self.__crs
71
71
 
72
72
  def to_plane_prj(self) -> None:
73
- if self.gps_points_gdf.crs.srs == self.plane_crs:
73
+ if self.gps_points_gdf.crs.srs.upper() == self.plane_crs:
74
74
  self.__crs = self.plane_crs
75
75
  pass
76
76
  else:
@@ -78,7 +78,7 @@ class GpsArray(object):
78
78
  self.__crs = self.plane_crs
79
79
 
80
80
  def to_geo_prj(self) -> None:
81
- if self.gps_points_gdf.crs.srs == self.geo_crs:
81
+ if self.gps_points_gdf.crs.srs.upper() == self.geo_crs:
82
82
  self.__crs = self.geo_crs
83
83
  pass
84
84
  else:
@@ -35,7 +35,7 @@ class Route2Gps(object):
35
35
  :return:
36
36
  """
37
37
 
38
- assert path_gdf.crs.srs == 'EPSG:4326'
38
+ assert path_gdf.crs.srs.upper() == 'EPSG:4326'
39
39
  assert len(path_o_time_df) >= 1
40
40
  assert {path_id_field, seq_field, time_cost_field, geometry_field}.issubset(set(path_gdf.columns))
41
41
  assert {path_id_field, o_time_field}.issubset(set(path_o_time_df.columns))
@@ -87,6 +87,7 @@ class GpsPointsGdf(object):
87
87
  except ValueError:
88
88
  self.__gps_points_gdf[gps_field.TIME_FIELD] = \
89
89
  pd.to_datetime(self.__gps_points_gdf[gps_field.TIME_FIELD], unit=time_unit)
90
+
90
91
  self.__gps_points_gdf.sort_values(by=[gps_field.TIME_FIELD], ascending=[True], inplace=True)
91
92
  self.__gps_points_gdf[gps_field.POINT_SEQ_FIELD] = [i for i in range(len(self.__gps_points_gdf))]
92
93
  self.__gps_points_gdf[gps_field.ORIGIN_POINT_SEQ_FIELD] = self.__gps_points_gdf[gps_field.POINT_SEQ_FIELD]
@@ -152,15 +153,20 @@ class GpsPointsGdf(object):
152
153
  # 距离差
153
154
  self.__gps_points_gdf[next_p_field] = self.__gps_points_gdf[geometry_field].shift(-1).fillna(
154
155
  self.__gps_points_gdf[geometry_field])
155
- self.__gps_points_gdf[dis_gap_field] = self.__gps_points_gdf.apply(
156
- lambda row: row[next_p_field].distance(row[geometry_field]), axis=1)
156
+ # self.__gps_points_gdf[dis_gap_field] = self.__gps_points_gdf.apply(
157
+ # lambda row: row[next_p_field].distance(row[geometry_field]), axis=1)
158
+ self.__gps_points_gdf[dis_gap_field] = self.__gps_points_gdf[next_p_field].distance(
159
+ self.__gps_points_gdf[geometry_field])
157
160
 
158
161
  def calc_adj_time_gap(self) -> None:
159
162
  # 时间差
160
163
  self.__gps_points_gdf[next_time_field] = self.__gps_points_gdf[time_field].shift(-1).fillna(
161
164
  self.__gps_points_gdf[time_field])
162
- self.__gps_points_gdf[time_gap_field] = self.__gps_points_gdf.apply(
163
- lambda row: (row[next_time_field] - row[time_field]).seconds, axis=1)
165
+ # self.__gps_points_gdf[time_gap_field] = self.__gps_points_gdf.apply(
166
+ # lambda row: (row[next_time_field] - row[time_field]).seconds, axis=1)
167
+ self.__gps_points_gdf[time_gap_field] = self.__gps_points_gdf[next_time_field] - self.__gps_points_gdf[
168
+ time_field]
169
+ self.__gps_points_gdf[time_gap_field] = self.__gps_points_gdf[time_gap_field].apply(lambda x: x.seconds)
164
170
 
165
171
  def calc_pre_next_dis(self) -> pd.DataFrame():
166
172
  self.calc_adj_dis_gap()
@@ -289,14 +295,10 @@ class GpsPointsGdf(object):
289
295
 
290
296
  def get_gps_array_buffer(self, buffer: float = 200.0, dup_threshold: float = 10.0) -> Polygon or None:
291
297
  """输出gps路径的buffer范围面域"""
292
- if len(self.__gps_points_gdf) <= 1:
293
- print(r'经过数据预处理后GPS观测点数据不足2个')
294
- return None
295
- else:
296
- gps_route_l = gpd.GeoSeries(LineString(self.__gps_points_gdf[gps_field.GEOMETRY_FIELD].to_list()))
297
- simplify_gps_route_l = gps_route_l.remove_repeated_points(dup_threshold)
298
- gps_array_buffer = simplify_gps_route_l[0].buffer(buffer)
299
- return gps_array_buffer
298
+ gps_route_l = gpd.GeoSeries(LineString(self.__gps_points_gdf[gps_field.GEOMETRY_FIELD].to_list()))
299
+ simplify_gps_route_l = gps_route_l.remove_repeated_points(dup_threshold)
300
+ gps_array_buffer = simplify_gps_route_l[0].buffer(buffer)
301
+ return gps_array_buffer
300
302
 
301
303
  def generate_candidate_link(self, net: Net = None) -> tuple[pd.DataFrame, list[int]]:
302
304
  """
@@ -305,7 +307,7 @@ class GpsPointsGdf(object):
305
307
  :return: GPS候选路段信息, 未匹配到候选路段的gps点id
306
308
  """
307
309
  gps_buffer_gdf = self.__gps_points_gdf[[gps_field.POINT_SEQ_FIELD, gps_field.GEOMETRY_FIELD]].copy()
308
- if gps_buffer_gdf.crs.srs != self.plane_crs:
310
+ if gps_buffer_gdf.crs.srs.upper() != self.plane_crs:
309
311
  gps_buffer_gdf = gps_buffer_gdf.to_crs(self.plane_crs)
310
312
 
311
313
  single_link_gdf = net.get_link_data()[[net_field.SINGLE_LINK_ID_FIELD, net_field.FROM_NODE_FIELD,
@@ -344,7 +346,7 @@ class GpsPointsGdf(object):
344
346
  return candidate_link, remain_gps_list
345
347
 
346
348
  def to_plane_prj(self) -> None:
347
- if self.__gps_points_gdf.crs.srs == self.plane_crs:
349
+ if self.__gps_points_gdf.crs.srs.upper() == self.plane_crs:
348
350
  self.__crs = self.plane_crs
349
351
  pass
350
352
  else:
@@ -352,7 +354,7 @@ class GpsPointsGdf(object):
352
354
  self.__crs = self.plane_crs
353
355
 
354
356
  def to_geo_prj(self) -> None:
355
- if self.__gps_points_gdf.crs.srs == self.geo_crs:
357
+ if self.__gps_points_gdf.crs.srs.upper() == self.geo_crs:
356
358
  self.__crs = self.geo_crs
357
359
  pass
358
360
  else:
@@ -7,7 +7,6 @@
7
7
  路网线层存储与相关方法
8
8
  """
9
9
 
10
- import warnings
11
10
  import numpy as np
12
11
  import pandas as pd
13
12
  import networkx as nx
@@ -20,7 +19,6 @@ from ..netreverse.RoadNet.Tools.process import merge_double_link
20
19
  net_field = NetField()
21
20
  prj_const = PrjConst()
22
21
 
23
-
24
22
  geo_crs = prj_const.PRJ_CRS
25
23
  link_id_field = net_field.LINK_ID_FIELD
26
24
  dir_field = net_field.DIRECTION_FIELD
@@ -34,7 +32,7 @@ link_vec_field = net_field.LINK_VEC_FIELD
34
32
  class Link(object):
35
33
  def __init__(self, link_gdf: gpd.GeoDataFrame = None, planar_crs: str = None,
36
34
  weight_field: str = None, is_check: bool = True, not_conn_cost: float = 999.0,
37
- init_available_link: bool = True):
35
+ init_available_link: bool = True, delete_circle: bool = True):
38
36
 
39
37
  self.not_conn_cost = not_conn_cost
40
38
  self.geo_crs = geo_crs
@@ -43,6 +41,7 @@ class Link(object):
43
41
  self.link_gdf.index = self.link_gdf[link_id_field]
44
42
  self.weight_field = weight_field
45
43
  self.__available_link_id = []
44
+ self.delete_circle = delete_circle
46
45
  if is_check:
47
46
  self.check()
48
47
  self.max_link_id = 999
@@ -59,7 +58,8 @@ class Link(object):
59
58
  self.done_link_vec = False
60
59
 
61
60
  def check(self):
62
- assert self.link_gdf.crs.srs == self.geo_crs, rf'Link层数据必须为WGS84 - EPSG:4326, 实际输入: {self.link_gdf.crs.srs}'
61
+ assert self.link_gdf.crs.srs.upper() == self.geo_crs, \
62
+ rf'Link层数据必须为WGS84 - EPSG:4326, 实际输入: {self.link_gdf.crs.srs}'
63
63
  gap_set = {net_field.LINK_ID_FIELD, net_field.FROM_NODE_FIELD,
64
64
  net_field.TO_NODE_FIELD, net_field.DIRECTION_FIELD, self.weight_field,
65
65
  net_field.GEOMETRY_FIELD} - set(self.link_gdf.columns)
@@ -73,6 +73,13 @@ class Link(object):
73
73
  assert len(self.link_gdf[self.link_gdf[col].isna()]) == 0, rf'线层Link字段{col}有空值...'
74
74
  self.link_gdf[col] = self.link_gdf[col].astype(int)
75
75
 
76
+ # 环路检测
77
+ if self.delete_circle:
78
+ circle_idx = self.link_gdf[from_node_field] == self.link_gdf[to_node_field]
79
+ if not self.link_gdf[circle_idx].empty:
80
+ print(rf'检测到线层数据有环路, 自动删除...')
81
+ self.link_gdf.drop(index=self.link_gdf[circle_idx].index, inplace=True, axis=0)
82
+
76
83
  def init_link(self):
77
84
  """
78
85
  初始化Link, 这里会创建一个single层的link, 并将single_link设置为索引
@@ -111,7 +118,9 @@ class Link(object):
111
118
  neg_link[net_field.GEOMETRY_FIELD] = neg_link[net_field.GEOMETRY_FIELD].apply(
112
119
  lambda line_geo: LineString(list(line_geo.coords)[::-1]))
113
120
  self.__single_link_gdf = pd.concat([link_gdf, neg_link])
114
- self.__single_link_gdf.reset_index(inplace=True, drop=True)
121
+ # self.__single_link_gdf.reset_index(inplace=True, drop=True)
122
+ self.__single_link_gdf.drop_duplicates(subset=[from_node_field, to_node_field], keep='first', inplace=True)
123
+ self.__single_link_gdf.reset_index(inplace=True, drop=True)
115
124
  self.__single_link_gdf[net_field.SINGLE_LINK_ID_FIELD] = [i for i in range(1, len(self.__single_link_gdf) + 1)]
116
125
  self.__single_link_gdf['path'] = self.__single_link_gdf.apply(
117
126
  lambda row: [row[net_field.FROM_NODE_FIELD], row[net_field.TO_NODE_FIELD]], axis=1)
@@ -199,7 +208,7 @@ class Link(object):
199
208
  self.link_gdf.index = self.link_gdf[link_id_field]
200
209
 
201
210
  def append_link_gdf(self, link_gdf: gpd.GeoDataFrame = None) -> None:
202
- assert link_gdf.crs.srs == self.crs
211
+ assert link_gdf.crs.srs.upper() == self.crs
203
212
  assert set(link_gdf[link_id_field]) & set(self.link_gdf[link_id_field]) == set()
204
213
  self.link_gdf = pd.concat(
205
214
  [self.link_gdf, link_gdf])
@@ -313,7 +322,7 @@ class Link(object):
313
322
  return self.__ft_link_mapping
314
323
 
315
324
  def to_plane_prj(self) -> None:
316
- if self.link_gdf.crs.srs == self.planar_crs:
325
+ if self.check_same_crs(self.link_gdf, self.planar_crs):
317
326
  pass
318
327
  else:
319
328
  if self.__single_link_gdf is None or self.__single_link_gdf.empty:
@@ -323,7 +332,7 @@ class Link(object):
323
332
  self.link_gdf = self.link_gdf.to_crs(self.planar_crs)
324
333
 
325
334
  def to_geo_prj(self) -> None:
326
- if self.link_gdf.crs.srs == self.geo_crs:
335
+ if self.check_same_crs(self.link_gdf, self.geo_crs):
327
336
  pass
328
337
  else:
329
338
  if self.__single_link_gdf is None or self.__single_link_gdf.empty:
@@ -332,6 +341,10 @@ class Link(object):
332
341
  self.__single_link_gdf = self.__single_link_gdf.to_crs(self.geo_crs)
333
342
  self.link_gdf = self.link_gdf.to_crs(self.geo_crs)
334
343
 
344
+ @staticmethod
345
+ def check_same_crs(gdf: gpd.GeoDataFrame = None, format_crs: str = None) -> bool:
346
+ return gdf.crs.srs.upper() == format_crs
347
+
335
348
  @property
336
349
  def crs(self):
337
350
  return self.link_gdf.crs.srs