pycityagent 2.0.0a1__tar.gz → 2.0.0a3__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.
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/PKG-INFO +1 -1
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/agent.py +1 -1
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/__init__.py +2 -0
- pycityagent-2.0.0a3/pycityagent/environment/sim/sim_env.py +145 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/simulator.py +110 -159
- pycityagent-2.0.0a3/pycityagent/environment/utils/__init__.py +10 -0
- pycityagent-2.0.0a3/pycityagent/environment/utils/base64.py +16 -0
- pycityagent-2.0.0a3/pycityagent/environment/utils/const.py +242 -0
- pycityagent-2.0.0a3/pycityagent/environment/utils/port.py +11 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/simulation.py +153 -87
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/workflow/__init__.py +5 -3
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/workflow/block.py +31 -4
- pycityagent-2.0.0a3/pycityagent/workflow/trigger.py +150 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pyproject.toml +1 -1
- pycityagent-2.0.0a1/pycityagent/environment/utils/__init__.py +0 -8
- pycityagent-2.0.0a1/pycityagent/workflow/trigger.py +0 -66
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/README.md +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/config.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/economy/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/economy/econ_client.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/interact/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/interact/interact.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/message/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sence/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sence/static.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sidecar/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sidecar/sidecarv2.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/aoi_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/client.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/clock_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/economy_services.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/lane_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/light_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/person_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/road_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/sim/social_service.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/utils/geojson.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/utils/grpc.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/utils/map_utils.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/environment/utils/protobuf.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/llm/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/llm/embedding.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/llm/llm.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/llm/llmconfig.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/llm/utils.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/const.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/memory.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/memory_base.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/profile.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/self_define.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/state.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/memory/utils.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/message/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/interview.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/survey/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/survey/manager.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/survey/models.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/ui/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/simulation/ui/interface.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/utils/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/utils/decorators.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/utils/parsers/__init__.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/utils/parsers/code_block_parser.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/utils/parsers/json_parser.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/utils/parsers/parser_base.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/workflow/prompt.py +0 -0
- {pycityagent-2.0.0a1 → pycityagent-2.0.0a3}/pycityagent/workflow/tool.py +0 -0
@@ -126,7 +126,7 @@ class Agent(ABC):
|
|
126
126
|
dialog = []
|
127
127
|
|
128
128
|
# 添加系统提示
|
129
|
-
system_prompt = f"
|
129
|
+
system_prompt = f"请以第一人称的方式回答问题,保持回答简洁明了。"
|
130
130
|
dialog.append({"role": "system", "content": system_prompt})
|
131
131
|
|
132
132
|
# 添加记忆上下文
|
@@ -12,6 +12,7 @@ from .lane_service import LaneService
|
|
12
12
|
from .road_service import RoadService
|
13
13
|
from .social_service import SocialService
|
14
14
|
from .light_service import LightService
|
15
|
+
from .sim_env import ControlSimEnv
|
15
16
|
|
16
17
|
__all__ = [
|
17
18
|
"CityClient",
|
@@ -24,4 +25,5 @@ __all__ = [
|
|
24
25
|
"EconomyPersonService",
|
25
26
|
"EconomyOrgService",
|
26
27
|
"LightService",
|
28
|
+
"ControlSimEnv",
|
27
29
|
]
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import logging
|
2
|
+
import os
|
3
|
+
import time
|
4
|
+
import atexit
|
5
|
+
from subprocess import DEVNULL, Popen
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from pycitydata.map import Map
|
9
|
+
|
10
|
+
from ..utils import encode_to_base64, find_free_port
|
11
|
+
|
12
|
+
__all__ = ["ControlSimEnv"]
|
13
|
+
|
14
|
+
|
15
|
+
def _generate_yaml_config(map_file: str, start_step: int, total_step: int) -> str:
|
16
|
+
map_file = os.path.abspath(map_file)
|
17
|
+
return f"""
|
18
|
+
input:
|
19
|
+
# 地图
|
20
|
+
map:
|
21
|
+
file: "{map_file}"
|
22
|
+
|
23
|
+
control:
|
24
|
+
step:
|
25
|
+
# 模拟器起始步
|
26
|
+
start: {start_step}
|
27
|
+
# 模拟总步数,结束步为起始步+总步数
|
28
|
+
total: {total_step}
|
29
|
+
# 每步的时间间隔
|
30
|
+
interval: 1
|
31
|
+
skip_overtime_trip_when_init: true
|
32
|
+
enable_platoon: false
|
33
|
+
enable_indoor: false
|
34
|
+
prefer_fixed_light: true
|
35
|
+
enable_collision_avoidance: false # 计算性能下降10倍,需要保证subloop>=5
|
36
|
+
enable_go_astray: true # 引入串行的路径规划调用,计算性能下降(幅度不确定)
|
37
|
+
lane_change_model: earliest # mobil (主动变道+强制变道,默认值) earliest (总是尽可能早地变道)
|
38
|
+
|
39
|
+
output:
|
40
|
+
"""
|
41
|
+
|
42
|
+
|
43
|
+
class ControlSimEnv:
|
44
|
+
def __init__(
|
45
|
+
self,
|
46
|
+
task_name: str,
|
47
|
+
map_file: str,
|
48
|
+
# person_file,
|
49
|
+
start_step: int,
|
50
|
+
total_step: int,
|
51
|
+
log_dir: str,
|
52
|
+
min_step_time: int = 1000,
|
53
|
+
timeout: int = 5,
|
54
|
+
simuletgo_addr: Optional[str] = None,
|
55
|
+
):
|
56
|
+
self._task_name = task_name
|
57
|
+
self._map_file = map_file
|
58
|
+
# self._person_file = person_file
|
59
|
+
self._start_step = start_step
|
60
|
+
self._total_step = total_step
|
61
|
+
self._log_dir = log_dir
|
62
|
+
self._min_step_time = min_step_time
|
63
|
+
self._timeout = timeout
|
64
|
+
self.m = Map(pb_path=map_file)
|
65
|
+
"""
|
66
|
+
地图数据
|
67
|
+
"""
|
68
|
+
|
69
|
+
# 检查二进制文件是否存在
|
70
|
+
# simulet-go: ~/.local/bin/simulet-go
|
71
|
+
self._simuletgo_path = os.path.expanduser("~/.local/bin/simulet-go")
|
72
|
+
if not os.path.exists(os.path.expanduser(self._simuletgo_path)):
|
73
|
+
raise FileNotFoundError("simulet-go not found, please install it first")
|
74
|
+
|
75
|
+
self._simuletgo_config = _generate_yaml_config(map_file, start_step, total_step)
|
76
|
+
self.simuletgo_port = None
|
77
|
+
self._simuletgo_proc = None
|
78
|
+
self._traffic_client = None
|
79
|
+
|
80
|
+
os.makedirs(log_dir, exist_ok=True)
|
81
|
+
|
82
|
+
self.simuletgo_addr = self.reset(simuletgo_addr)
|
83
|
+
|
84
|
+
def reset(
|
85
|
+
self,
|
86
|
+
simuletgo_addr: Optional[str] = None,
|
87
|
+
):
|
88
|
+
"""
|
89
|
+
Args:
|
90
|
+
- simuletgo_addr: str, simulet-go的地址。如果为None,则启动一个新的simulet-go
|
91
|
+
"""
|
92
|
+
|
93
|
+
# 三个地址必须同时为None或者同时不为None
|
94
|
+
if simuletgo_addr is None:
|
95
|
+
# 1. 启动simulet-go
|
96
|
+
# ./simulet-go -config-data configbase64 -job test -syncer http://localhost:53001 -listen :51102
|
97
|
+
assert self.simuletgo_port is None
|
98
|
+
assert self._simuletgo_proc is None
|
99
|
+
self.simuletgo_port = find_free_port()
|
100
|
+
config_base64 = encode_to_base64(self._simuletgo_config)
|
101
|
+
self._simuletgo_proc = Popen(
|
102
|
+
[
|
103
|
+
self._simuletgo_path,
|
104
|
+
"-config-data",
|
105
|
+
config_base64,
|
106
|
+
"-job",
|
107
|
+
self._task_name,
|
108
|
+
"-listen",
|
109
|
+
f":{self.simuletgo_port}",
|
110
|
+
"-run.min_step_time",
|
111
|
+
f"{self._min_step_time}",
|
112
|
+
"-output",
|
113
|
+
self._log_dir,
|
114
|
+
"-cache",
|
115
|
+
"",
|
116
|
+
"-log.level",
|
117
|
+
"error",
|
118
|
+
],
|
119
|
+
# 忽略输出
|
120
|
+
# stdout=DEVNULL,
|
121
|
+
)
|
122
|
+
logging.info(
|
123
|
+
f"start simulet-go at localhost:{self.simuletgo_port}, PID={self._simuletgo_proc.pid}"
|
124
|
+
)
|
125
|
+
simuletgo_addr = f"http://localhost:{self.simuletgo_port}"
|
126
|
+
atexit.register(self.close)
|
127
|
+
time.sleep(1)
|
128
|
+
elif simuletgo_addr is not None:
|
129
|
+
pass
|
130
|
+
else:
|
131
|
+
# raise ValueError(
|
132
|
+
# "simuletgo_addr, syncer_addr, routing_addr must be all None or all not None"
|
133
|
+
# )
|
134
|
+
pass
|
135
|
+
return simuletgo_addr
|
136
|
+
|
137
|
+
def close(self):
|
138
|
+
if self._simuletgo_proc is not None:
|
139
|
+
self._simuletgo_proc.terminate()
|
140
|
+
simuletgo_code = self._simuletgo_proc.wait()
|
141
|
+
logging.info(f"simulet-go exit with code {simuletgo_code}")
|
142
|
+
|
143
|
+
self.simuletgo_port = None
|
144
|
+
self._simuletgo_proc = None
|
145
|
+
self._traffic_client = None
|
@@ -2,15 +2,24 @@
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import logging
|
5
|
+
import os
|
6
|
+
from collections import defaultdict
|
7
|
+
from collections.abc import Sequence
|
5
8
|
from datetime import datetime, timedelta
|
6
9
|
from typing import Any, Optional, Tuple, Union, cast
|
7
10
|
|
8
11
|
from mosstool.type import TripMode
|
12
|
+
from mosstool.util.format_converter import coll2pb
|
9
13
|
from pycitydata.map import Map as SimMap
|
14
|
+
from pycityproto.city.map.v2 import map_pb2 as map_pb2
|
10
15
|
from pycityproto.city.person.v2 import person_pb2 as person_pb2
|
11
16
|
from pycityproto.city.person.v2 import person_service_pb2 as person_service
|
17
|
+
from pymongo import MongoClient
|
18
|
+
from shapely.geometry import Point
|
19
|
+
from shapely.strtree import STRtree
|
12
20
|
|
13
|
-
from .sim import CityClient
|
21
|
+
from .sim import CityClient, ControlSimEnv
|
22
|
+
from .utils.const import *
|
14
23
|
|
15
24
|
|
16
25
|
class Simulator:
|
@@ -25,18 +34,52 @@ class Simulator:
|
|
25
34
|
- 模拟器配置
|
26
35
|
- simulator config
|
27
36
|
"""
|
37
|
+
_mongo_uri, _mongo_db, _mongo_coll, _map_cache_dir = (
|
38
|
+
config["map_request"]["mongo_uri"],
|
39
|
+
config["map_request"]["mongo_db"],
|
40
|
+
config["map_request"]["mongo_coll"],
|
41
|
+
config["map_request"]["cache_dir"],
|
42
|
+
)
|
43
|
+
_mongo_client = MongoClient(_mongo_uri)
|
44
|
+
os.makedirs(_map_cache_dir, exist_ok=True)
|
45
|
+
_map_pb_path = os.path.join(_map_cache_dir, f"{_mongo_db}.{_mongo_coll}.pb") # type: ignore
|
46
|
+
_map_pb = map_pb2.Map()
|
47
|
+
if os.path.exists(_map_pb_path):
|
48
|
+
with open(_map_pb_path, "rb") as f:
|
49
|
+
_map_pb.ParseFromString(f.read())
|
50
|
+
else:
|
51
|
+
_map_pb = coll2pb(_mongo_client[_mongo_db][_mongo_coll], _map_pb)
|
52
|
+
with open(_map_pb_path, "wb") as f:
|
53
|
+
f.write(_map_pb.SerializeToString())
|
28
54
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
55
|
+
if 'simulator' in config:
|
56
|
+
if 'server' not in config["simulator"]:
|
57
|
+
self._sim_env = sim_env = ControlSimEnv(
|
58
|
+
task_name=config["simulator"].get("task", "citysim"),
|
59
|
+
map_file=_map_pb_path,
|
60
|
+
start_step=config["simulator"].get("start_step", 0),
|
61
|
+
total_step=2147000000,
|
62
|
+
log_dir=config["simulator"].get("log_dir", "./log"),
|
63
|
+
min_step_time=config["simulator"].get("min_step_time", 1000),
|
64
|
+
simuletgo_addr=config["simulator"].get("server", None),
|
65
|
+
)
|
66
|
+
|
67
|
+
# using local client
|
68
|
+
self._client = CityClient(sim_env.simuletgo_addr, secure=False)
|
69
|
+
"""
|
70
|
+
- 模拟器grpc客户端
|
71
|
+
- grpc client of simulator
|
72
|
+
"""
|
73
|
+
else:
|
74
|
+
self._client = CityClient(config['simulator']['server'], secure=False)
|
75
|
+
else:
|
76
|
+
logging.warning("No simulator config found, no simulator client will be used")
|
34
77
|
|
35
78
|
self.map = SimMap(
|
36
|
-
mongo_uri=
|
37
|
-
mongo_db=
|
38
|
-
mongo_coll=
|
39
|
-
cache_dir=
|
79
|
+
mongo_uri=_mongo_uri,
|
80
|
+
mongo_db=_mongo_db,
|
81
|
+
mongo_coll=_mongo_coll,
|
82
|
+
cache_dir=_map_cache_dir,
|
40
83
|
)
|
41
84
|
"""
|
42
85
|
- 模拟器地图对象
|
@@ -54,24 +97,14 @@ class Simulator:
|
|
54
97
|
- 模拟城市当前时间
|
55
98
|
- The current time of simulator
|
56
99
|
"""
|
57
|
-
self.poi_cate =
|
58
|
-
"10": "eat",
|
59
|
-
"13": "shopping",
|
60
|
-
"18": "sports",
|
61
|
-
"22": "excursion",
|
62
|
-
"16": "entertainment",
|
63
|
-
"20": "medical tratment",
|
64
|
-
"14": "trivialities",
|
65
|
-
"25": "financial",
|
66
|
-
"12": "government and political services",
|
67
|
-
"23": "cultural institutions",
|
68
|
-
"28": "residence",
|
69
|
-
}
|
100
|
+
self.poi_cate = POI_CATG_DICT
|
70
101
|
self.map_x_gap = None
|
71
102
|
self.map_y_gap = None
|
72
|
-
self._bbox:
|
103
|
+
self._bbox: tuple[float, float, float, float] = (-1, -1, -1, -1)
|
73
104
|
self.poi_matrix_centers = []
|
74
105
|
self._lock = asyncio.Lock()
|
106
|
+
# poi STRtree
|
107
|
+
self.set_poi_tree()
|
75
108
|
|
76
109
|
# * Agent相关
|
77
110
|
def FindAgentsByArea(self, req: dict, status=None):
|
@@ -101,146 +134,36 @@ class Simulator:
|
|
101
134
|
resp.motions = motions # type: ignore
|
102
135
|
return resp
|
103
136
|
|
104
|
-
def
|
105
|
-
self, row_number: int = 12, col_number: int = 10, radius: int = 10000
|
106
|
-
):
|
107
|
-
"""
|
108
|
-
初始化pois_matrix
|
109
|
-
|
110
|
-
Args:
|
111
|
-
- map (dict): 地图参数
|
112
|
-
east, west, north, south
|
113
|
-
- row_number (int): 行数
|
114
|
-
- col_number (int): 列数
|
115
|
-
- radius (int): 搜索半径, 单位m
|
116
|
-
"""
|
117
|
-
self.matrix_map = self.map
|
118
|
-
map_header = self.matrix_map.header
|
119
|
-
min_x, min_y, max_x, max_y = (
|
120
|
-
map_header[k] for k in ("west", "south", "east", "north")
|
121
|
-
)
|
122
|
-
self._bbox = (min_x, min_y, max_x, max_y)
|
123
|
-
logging.info(
|
124
|
-
f"Building Poi searching matrix, Row_number: {row_number}, Col_number: {col_number}, Radius: {radius}m"
|
125
|
-
)
|
126
|
-
self.map_x_gap = map_x_gap = (max_x - min_x) / col_number
|
127
|
-
self.map_y_gap = map_y_gap = (max_y - min_y) / row_number
|
128
|
-
self.poi_matrix_centers = poi_matrix_centers = [[] for _ in range(row_number)]
|
129
|
-
for i, i_centers in enumerate(poi_matrix_centers):
|
130
|
-
for j in range(col_number):
|
131
|
-
center_x = map_header["west"] + map_x_gap * j + map_x_gap / 2
|
132
|
-
center_y = map_header["south"] + map_y_gap * i + map_y_gap / 2
|
133
|
-
i_centers.append((center_x, center_y))
|
134
|
-
|
135
|
-
for pre, _ in self.poi_cate.items():
|
136
|
-
logging.info(f"Building matrix for Poi category: {pre}")
|
137
|
-
self.pois_matrix[pre] = []
|
138
|
-
for row_centers in self.poi_matrix_centers:
|
139
|
-
row_pois = []
|
140
|
-
for center in row_centers:
|
141
|
-
pois = self.map.query_pois(
|
142
|
-
center=center, radius=radius, category_prefix=pre
|
143
|
-
)
|
144
|
-
row_pois.append(pois)
|
145
|
-
self.pois_matrix[pre].append(row_pois)
|
146
|
-
logging.info("Finished")
|
147
|
-
|
148
|
-
def get_pois_from_matrix(self, center: Tuple[float, float], prefix: str):
|
149
|
-
"""
|
150
|
-
从poi搜索矩阵中快速获取poi
|
151
|
-
|
152
|
-
Args:
|
153
|
-
- center (Tuple[float, float]): 位置信息
|
154
|
-
- prefix (str): 类型前缀
|
155
|
-
"""
|
156
|
-
(min_x, min_y, max_x, max_y) = self._bbox
|
157
|
-
_x, _y = center
|
158
|
-
if self.map_x_gap is None or self.map_y_gap is None:
|
159
|
-
logging.warning("Set Poi Matrix first")
|
160
|
-
return
|
161
|
-
elif prefix not in self.poi_cate:
|
162
|
-
logging.warning(f"Wrong prefix, only {self.poi_cate.keys()} is usable")
|
163
|
-
return
|
164
|
-
elif not (min_x < _x < max_x) or not (min_y < _y < max_y):
|
165
|
-
logging.warning("Wrong center")
|
166
|
-
return
|
167
|
-
|
168
|
-
# 矩阵匹配
|
169
|
-
rows = int((_y - min_y) / self.map_y_gap)
|
170
|
-
cols = int((_x - min_x) / self.map_x_gap)
|
171
|
-
pois = self.pois_matrix[prefix][rows][cols]
|
172
|
-
return pois
|
173
|
-
|
174
|
-
def get_cat_from_pois(self, pois: list):
|
175
|
-
cat_2_num = {}
|
176
|
-
for poi in pois:
|
177
|
-
cate = poi["category"][:2]
|
178
|
-
if cate not in self.poi_cate:
|
179
|
-
continue
|
180
|
-
if cate in cat_2_num:
|
181
|
-
cat_2_num[cate] += 1
|
182
|
-
else:
|
183
|
-
cat_2_num[cate] = 1
|
184
|
-
max_cat = ""
|
185
|
-
max_num = 0
|
186
|
-
for key in cat_2_num.keys():
|
187
|
-
if cat_2_num[key] > max_num:
|
188
|
-
max_num = cat_2_num[key]
|
189
|
-
max_cat = self.poi_cate[key]
|
190
|
-
return max_cat
|
191
|
-
|
192
|
-
def get_poi_matrix_in_rec(
|
137
|
+
def set_poi_tree(
|
193
138
|
self,
|
194
|
-
center: Tuple[float, float],
|
195
|
-
radius: int = 2500,
|
196
|
-
rows: int = 5,
|
197
|
-
cols: int = 5,
|
198
139
|
):
|
199
140
|
"""
|
200
|
-
|
201
|
-
|
202
|
-
Args:
|
203
|
-
- center (Tuple[float, float]): 中心位置点
|
204
|
-
- radius (int): 半径
|
141
|
+
初始化pois_tree
|
205
142
|
"""
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
for i in range(rows):
|
214
|
-
matrix.append([])
|
215
|
-
for j in range(cols):
|
216
|
-
matrix[i].append([])
|
217
|
-
for poi in self.map.pois.values():
|
218
|
-
x = poi["position"]["x"]
|
219
|
-
y = poi["position"]["y"]
|
220
|
-
if west < x < east and south < y < north:
|
221
|
-
row_index = int((y - south) / x_gap)
|
222
|
-
col_index = int((x - west) / y_gap)
|
223
|
-
matrix[row_index][col_index].append(poi)
|
224
|
-
matrix_type = []
|
225
|
-
for i in range(rows):
|
226
|
-
for j in range(cols):
|
227
|
-
matrix_type.append(self.get_cat_from_pois(matrix[i][j]))
|
228
|
-
poi_total_number = []
|
229
|
-
poi_type_number = []
|
230
|
-
for i in range(rows):
|
231
|
-
for j in range(cols):
|
232
|
-
poi_total_number.append(len(matrix[i][j]))
|
233
|
-
number = 0
|
234
|
-
for poi in matrix[i][j]:
|
235
|
-
if (
|
236
|
-
poi["category"][:2] in self.poi_cate.keys()
|
237
|
-
and self.poi_cate[poi["category"][:2]]
|
238
|
-
== matrix_type[i * cols + j]
|
239
|
-
):
|
240
|
-
number += 1
|
241
|
-
poi_type_number.append(number)
|
143
|
+
poi_geos = []
|
144
|
+
tree_id_2_poi_and_catg: dict[int, tuple[dict, str]] = {}
|
145
|
+
for tree_id, poi in enumerate(self.map.pois.values()):
|
146
|
+
tree_id_2_poi_and_catg[tree_id] = (poi, poi["category"])
|
147
|
+
poi_geos.append(Point([poi["position"][k] for k in ["x", "y"]]))
|
148
|
+
self.tree_id_2_poi_and_catg = tree_id_2_poi_and_catg
|
149
|
+
self.pois_tree = STRtree(poi_geos)
|
242
150
|
|
243
|
-
|
151
|
+
def GetPoiCategories(
|
152
|
+
self,
|
153
|
+
center: Optional[Union[tuple[float, float], Point]] = None,
|
154
|
+
radius: Optional[float] = None,
|
155
|
+
) -> list[str]:
|
156
|
+
if center is not None and radius is not None:
|
157
|
+
if not isinstance(center, Point):
|
158
|
+
center = Point(center)
|
159
|
+
indices = self.pois_tree.query(center.buffer(radius))
|
160
|
+
else:
|
161
|
+
indices = list(self.tree_id_2_poi_and_catg.keys())
|
162
|
+
categories = []
|
163
|
+
for index in indices:
|
164
|
+
_, catg = self.tree_id_2_poi_and_catg[index]
|
165
|
+
categories.append(catg.split("|")[-1])
|
166
|
+
return list(set(categories))
|
244
167
|
|
245
168
|
async def GetTime(
|
246
169
|
self, format_time: bool = False, format: str = "%H:%M:%S"
|
@@ -367,3 +290,31 @@ class Simulator:
|
|
367
290
|
logging.debug(
|
368
291
|
f"Neither aoi or lane pos provided for person {person_id} position reset!!"
|
369
292
|
)
|
293
|
+
|
294
|
+
def GetAroundPoi(
|
295
|
+
self,
|
296
|
+
center: Union[tuple[float, float], Point],
|
297
|
+
radius: float,
|
298
|
+
poi_type: Union[str, list[str]],
|
299
|
+
):
|
300
|
+
if not isinstance(poi_type, Sequence):
|
301
|
+
poi_type = [poi_type]
|
302
|
+
transformed_poi_type = []
|
303
|
+
for t in poi_type:
|
304
|
+
if t not in self.poi_cate:
|
305
|
+
transformed_poi_type.append(t)
|
306
|
+
else:
|
307
|
+
transformed_poi_type += self.poi_cate[t]
|
308
|
+
poi_type_set = set(transformed_poi_type)
|
309
|
+
if not isinstance(center, Point):
|
310
|
+
center = Point(center)
|
311
|
+
# 获取半径内的poi
|
312
|
+
indices = self.pois_tree.query(center.buffer(radius))
|
313
|
+
# 过滤掉不满足类别前缀的poi
|
314
|
+
pois = []
|
315
|
+
for index in indices:
|
316
|
+
poi, catg = self.tree_id_2_poi_and_catg[index]
|
317
|
+
if catg.split("|")[-1] not in poi_type_set:
|
318
|
+
continue
|
319
|
+
pois.append(poi)
|
320
|
+
return pois
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import base64
|
2
|
+
|
3
|
+
__all__ = ["encode_to_base64"]
|
4
|
+
|
5
|
+
|
6
|
+
def encode_to_base64(input_string: str) -> str:
|
7
|
+
# 将字符串转换为字节
|
8
|
+
input_bytes = input_string.encode("utf-8")
|
9
|
+
|
10
|
+
# 将字节编码为Base64
|
11
|
+
base64_bytes = base64.b64encode(input_bytes)
|
12
|
+
|
13
|
+
# 将Base64字节转换为字符串
|
14
|
+
base64_string = base64_bytes.decode("utf-8")
|
15
|
+
|
16
|
+
return base64_string
|