pycityagent 1.0.0__py3-none-any.whl → 2.0.0a2__py3-none-any.whl

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 (110) hide show
  1. pycityagent/__init__.py +7 -3
  2. pycityagent/agent.py +180 -284
  3. pycityagent/economy/__init__.py +5 -0
  4. pycityagent/economy/econ_client.py +307 -0
  5. pycityagent/environment/__init__.py +7 -0
  6. pycityagent/environment/interact/interact.py +141 -0
  7. pycityagent/environment/sence/__init__.py +0 -0
  8. pycityagent/{brain → environment/sence}/static.py +1 -1
  9. pycityagent/environment/sidecar/__init__.py +8 -0
  10. pycityagent/environment/sidecar/sidecarv2.py +109 -0
  11. pycityagent/environment/sim/__init__.py +29 -0
  12. pycityagent/environment/sim/aoi_service.py +38 -0
  13. pycityagent/environment/sim/client.py +126 -0
  14. pycityagent/environment/sim/clock_service.py +43 -0
  15. pycityagent/environment/sim/economy_services.py +191 -0
  16. pycityagent/environment/sim/lane_service.py +110 -0
  17. pycityagent/environment/sim/light_service.py +120 -0
  18. pycityagent/environment/sim/person_service.py +294 -0
  19. pycityagent/environment/sim/road_service.py +38 -0
  20. pycityagent/environment/sim/sim_env.py +145 -0
  21. pycityagent/environment/sim/social_service.py +58 -0
  22. pycityagent/environment/simulator.py +320 -0
  23. pycityagent/environment/utils/__init__.py +10 -0
  24. pycityagent/environment/utils/base64.py +16 -0
  25. pycityagent/environment/utils/const.py +242 -0
  26. pycityagent/environment/utils/geojson.py +26 -0
  27. pycityagent/environment/utils/grpc.py +57 -0
  28. pycityagent/environment/utils/map_utils.py +157 -0
  29. pycityagent/environment/utils/port.py +11 -0
  30. pycityagent/environment/utils/protobuf.py +39 -0
  31. pycityagent/llm/__init__.py +6 -0
  32. pycityagent/llm/embedding.py +136 -0
  33. pycityagent/llm/llm.py +430 -0
  34. pycityagent/llm/llmconfig.py +15 -0
  35. pycityagent/llm/utils.py +6 -0
  36. pycityagent/memory/__init__.py +11 -0
  37. pycityagent/memory/const.py +41 -0
  38. pycityagent/memory/memory.py +453 -0
  39. pycityagent/memory/memory_base.py +168 -0
  40. pycityagent/memory/profile.py +165 -0
  41. pycityagent/memory/self_define.py +165 -0
  42. pycityagent/memory/state.py +173 -0
  43. pycityagent/memory/utils.py +27 -0
  44. pycityagent/message/__init__.py +0 -0
  45. pycityagent/simulation/__init__.py +7 -0
  46. pycityagent/simulation/interview.py +36 -0
  47. pycityagent/simulation/simulation.py +352 -0
  48. pycityagent/simulation/survey/__init__.py +9 -0
  49. pycityagent/simulation/survey/manager.py +67 -0
  50. pycityagent/simulation/survey/models.py +49 -0
  51. pycityagent/simulation/ui/__init__.py +3 -0
  52. pycityagent/simulation/ui/interface.py +602 -0
  53. pycityagent/utils/__init__.py +0 -0
  54. pycityagent/utils/decorators.py +89 -0
  55. pycityagent/utils/parsers/__init__.py +12 -0
  56. pycityagent/utils/parsers/code_block_parser.py +37 -0
  57. pycityagent/utils/parsers/json_parser.py +86 -0
  58. pycityagent/utils/parsers/parser_base.py +60 -0
  59. pycityagent/workflow/__init__.py +24 -0
  60. pycityagent/workflow/block.py +164 -0
  61. pycityagent/workflow/prompt.py +72 -0
  62. pycityagent/workflow/tool.py +246 -0
  63. pycityagent/workflow/trigger.py +150 -0
  64. pycityagent-2.0.0a2.dist-info/METADATA +208 -0
  65. pycityagent-2.0.0a2.dist-info/RECORD +69 -0
  66. {pycityagent-1.0.0.dist-info → pycityagent-2.0.0a2.dist-info}/WHEEL +1 -2
  67. pycityagent/ac/__init__.py +0 -6
  68. pycityagent/ac/ac.py +0 -50
  69. pycityagent/ac/action.py +0 -14
  70. pycityagent/ac/controled.py +0 -13
  71. pycityagent/ac/converse.py +0 -31
  72. pycityagent/ac/idle.py +0 -17
  73. pycityagent/ac/shop.py +0 -80
  74. pycityagent/ac/trip.py +0 -37
  75. pycityagent/brain/__init__.py +0 -10
  76. pycityagent/brain/brain.py +0 -52
  77. pycityagent/brain/brainfc.py +0 -10
  78. pycityagent/brain/memory.py +0 -541
  79. pycityagent/brain/persistence/social.py +0 -1
  80. pycityagent/brain/persistence/spatial.py +0 -14
  81. pycityagent/brain/reason/shop.py +0 -37
  82. pycityagent/brain/reason/social.py +0 -148
  83. pycityagent/brain/reason/trip.py +0 -67
  84. pycityagent/brain/reason/user.py +0 -122
  85. pycityagent/brain/retrive/social.py +0 -6
  86. pycityagent/brain/scheduler.py +0 -408
  87. pycityagent/brain/sence.py +0 -375
  88. pycityagent/cc/__init__.py +0 -5
  89. pycityagent/cc/cc.py +0 -102
  90. pycityagent/cc/conve.py +0 -6
  91. pycityagent/cc/idle.py +0 -20
  92. pycityagent/cc/shop.py +0 -6
  93. pycityagent/cc/trip.py +0 -13
  94. pycityagent/cc/user.py +0 -13
  95. pycityagent/hubconnector/__init__.py +0 -3
  96. pycityagent/hubconnector/hubconnector.py +0 -137
  97. pycityagent/image/__init__.py +0 -3
  98. pycityagent/image/image.py +0 -158
  99. pycityagent/simulator.py +0 -161
  100. pycityagent/st/__init__.py +0 -4
  101. pycityagent/st/st.py +0 -96
  102. pycityagent/urbanllm/__init__.py +0 -3
  103. pycityagent/urbanllm/urbanllm.py +0 -132
  104. pycityagent-1.0.0.dist-info/LICENSE +0 -21
  105. pycityagent-1.0.0.dist-info/METADATA +0 -181
  106. pycityagent-1.0.0.dist-info/RECORD +0 -48
  107. pycityagent-1.0.0.dist-info/top_level.txt +0 -1
  108. /pycityagent/{brain/persistence/__init__.py → config.py} +0 -0
  109. /pycityagent/{brain/reason → environment/interact}/__init__.py +0 -0
  110. /pycityagent/{brain/retrive → environment/message}/__init__.py +0 -0
@@ -0,0 +1,320 @@
1
+ """Simulator: 城市模拟器类及其定义"""
2
+
3
+ import asyncio
4
+ import logging
5
+ import os
6
+ from collections import defaultdict
7
+ from collections.abc import Sequence
8
+ from datetime import datetime, timedelta
9
+ from typing import Any, Optional, Tuple, Union, cast
10
+
11
+ from mosstool.type import TripMode
12
+ from mosstool.util.format_converter import coll2pb
13
+ from pycitydata.map import Map as SimMap
14
+ from pycityproto.city.map.v2 import map_pb2 as map_pb2
15
+ from pycityproto.city.person.v2 import person_pb2 as person_pb2
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
20
+
21
+ from .sim import CityClient, ControlSimEnv
22
+ from .utils.const import *
23
+
24
+
25
+ class Simulator:
26
+ """
27
+ - 模拟器主类
28
+ - Simulator Class
29
+ """
30
+
31
+ def __init__(self, config, secure: bool = False) -> None:
32
+ self.config = config
33
+ """
34
+ - 模拟器配置
35
+ - simulator config
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())
54
+
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")
77
+
78
+ self.map = SimMap(
79
+ mongo_uri=_mongo_uri,
80
+ mongo_db=_mongo_db,
81
+ mongo_coll=_mongo_coll,
82
+ cache_dir=_map_cache_dir,
83
+ )
84
+ """
85
+ - 模拟器地图对象
86
+ - Simulator map object
87
+ """
88
+
89
+ self.pois_matrix: dict[str, list[list[list]]] = {}
90
+ """
91
+ pois的基于区块的划分——方便快速粗略地查询poi
92
+ 通过Simulator.set_pois_matrix()初始化
93
+ """
94
+
95
+ self.time: int = 0
96
+ """
97
+ - 模拟城市当前时间
98
+ - The current time of simulator
99
+ """
100
+ self.poi_cate = POI_CATG_DICT
101
+ self.map_x_gap = None
102
+ self.map_y_gap = None
103
+ self._bbox: tuple[float, float, float, float] = (-1, -1, -1, -1)
104
+ self.poi_matrix_centers = []
105
+ self._lock = asyncio.Lock()
106
+ # poi STRtree
107
+ self.set_poi_tree()
108
+
109
+ # * Agent相关
110
+ def FindAgentsByArea(self, req: dict, status=None):
111
+ """
112
+ 通过区域范围查找agent/person
113
+ Get agents/persons in the provided area
114
+
115
+ Args:
116
+ - req (dict): 用于描述区域的请求 https://cityproto.sim.fiblab.net/#city.person.1.GetPersonByLongLatBBoxRequest
117
+ - status (int): 用于限制agent/person状态 if 'status' is not None, then you get those persons in 'status' https://cityproto.sim.fiblab.net/#city.agent.v2.Status
118
+
119
+ Returns:
120
+ - https://cityproto.sim.fiblab.net/#city.person.1.GetPersonByLongLatBBoxResponse
121
+ """
122
+ loop = asyncio.get_event_loop()
123
+ resp = loop.run_until_complete(
124
+ self._client.person_service.GetPersonByLongLatBBox(req=req)
125
+ )
126
+ loop.close()
127
+ if status == None:
128
+ return resp
129
+ else:
130
+ motions = []
131
+ for agent in resp.motions: # type: ignore
132
+ if agent.status in status:
133
+ motions.append(agent)
134
+ resp.motions = motions # type: ignore
135
+ return resp
136
+
137
+ def set_poi_tree(
138
+ self,
139
+ ):
140
+ """
141
+ 初始化pois_tree
142
+ """
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)
150
+
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))
167
+
168
+ async def GetTime(
169
+ self, format_time: bool = False, format: str = "%H:%M:%S"
170
+ ) -> Union[int, str]:
171
+ """
172
+ 获取模拟器当前时间 Get current time of simulator
173
+ 默认返回以00:00:00为始的, 以s为单位的时间(int)
174
+ 支持格式化时间
175
+
176
+ Args:
177
+ - format_time (bool): 是否格式化 format or not
178
+ - format (str): 格式化模板,默认为"Hour:Minute:Second" the formation
179
+
180
+ Returns:
181
+ - time Union[int, str]: 时间 time in second(int) or formatted time(str)
182
+ """
183
+ t_sec = await self._client.clock_service.Now({})
184
+ t_sec = cast(dict[str, int], t_sec)
185
+ self.time = t_sec["t"]
186
+ if format_time:
187
+ current_date = datetime.now().date()
188
+ start_of_day = datetime.combine(current_date, datetime.min.time())
189
+ current_time = start_of_day + timedelta(seconds=t_sec["t"])
190
+ formatted_time = current_time.strftime(format)
191
+ return formatted_time
192
+ else:
193
+ return t_sec["t"]
194
+
195
+ async def GetPerson(self, person_id: int) -> dict:
196
+ return await self._client.person_service.GetPerson(
197
+ req={"person_id": person_id}
198
+ ) # type:ignore
199
+
200
+ async def AddPerson(self, person: Any) -> dict:
201
+ if isinstance(person, person_pb2.Person):
202
+ req = person_service.AddPersonRequest(person=person)
203
+ else:
204
+ req = person
205
+ return await self._client.person_service.AddPerson(req) # type:ignore
206
+
207
+ async def SetAoiSchedules(
208
+ self,
209
+ person_id: int,
210
+ target_positions: Union[
211
+ list[Union[int, tuple[int, int]]], Union[int, tuple[int, int]]
212
+ ],
213
+ departure_times: Optional[list[float]] = None,
214
+ modes: Optional[list[TripMode]] = None,
215
+ ):
216
+ cur_time = float(await self.GetTime())
217
+ if not isinstance(target_positions, list):
218
+ target_positions = [target_positions]
219
+ if departure_times is None:
220
+ departure_times = [cur_time for _ in range(len(target_positions))]
221
+ else:
222
+ for _ in range(len(target_positions) - len(departure_times)):
223
+ departure_times.append(cur_time)
224
+ if modes is None:
225
+ modes = [
226
+ TripMode.TRIP_MODE_DRIVE_ONLY for _ in range(len(target_positions))
227
+ ]
228
+ else:
229
+ for _ in range(len(target_positions) - len(modes)):
230
+ modes.append(TripMode.TRIP_MODE_DRIVE_ONLY)
231
+ _schedules = []
232
+ for target_pos, _time, _mode in zip(target_positions, departure_times, modes):
233
+ if isinstance(target_pos, int):
234
+ aoi_id = target_pos
235
+ end = {
236
+ "aoi_position": {
237
+ "aoi_id": aoi_id,
238
+ }
239
+ }
240
+ else:
241
+ aoi_id, poi_id = target_pos
242
+ end = {"aoi_position": {"aoi_id": aoi_id, "poi_id": poi_id}}
243
+ # activity = ""
244
+ trips = [
245
+ {
246
+ "mode": _mode,
247
+ "end": end,
248
+ "departure_time": _time,
249
+ },
250
+ ]
251
+ _schedules.append(
252
+ {"trips": trips, "loop_count": 1, "departure_time": _time}
253
+ )
254
+ req = {"person_id": person_id, "schedules": _schedules}
255
+ await self._client.person_service.SetSchedule(req)
256
+
257
+ async def ResetPersonPosition(
258
+ self,
259
+ person_id: int,
260
+ aoi_id: Optional[int] = None,
261
+ poi_id: Optional[int] = None,
262
+ lane_id: Optional[int] = None,
263
+ s: Optional[float] = None,
264
+ ):
265
+ reset_position = {}
266
+ if aoi_id is not None:
267
+ reset_position["aoi_position"] = {"aoi_id": aoi_id}
268
+ if poi_id is not None:
269
+ reset_position["aoi_position"]["poi_id"] = poi_id
270
+ logging.debug(
271
+ f"Setting person {person_id} pos to AoiPosition {reset_position}"
272
+ )
273
+ await self._client.person_service.ResetPersonPosition(
274
+ {"person_id": person_id, "position": reset_position}
275
+ )
276
+ elif lane_id is not None:
277
+ reset_position["lane_position"] = {
278
+ "lane_id": lane_id,
279
+ "s": 0.0,
280
+ }
281
+ if s is not None:
282
+ reset_position["lane_position"]["s"] = s
283
+ logging.debug(
284
+ f"Setting person {person_id} pos to LanePosition {reset_position}"
285
+ )
286
+ await self._client.person_service.ResetPersonPosition(
287
+ {"person_id": person_id, "position": reset_position}
288
+ )
289
+ else:
290
+ logging.debug(
291
+ f"Neither aoi or lane pos provided for person {person_id} position reset!!"
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,10 @@
1
+ """
2
+ 实用工具
3
+ utilities
4
+ """
5
+
6
+ from .geojson import wrap_feature_collection
7
+ from .base64 import encode_to_base64
8
+ from .port import find_free_port
9
+
10
+ __all__ = ["wrap_feature_collection","find_free_port","find_free_port",]
@@ -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
@@ -0,0 +1,242 @@
1
+ POI_CATG_DICT = {
2
+ "户外活动": [
3
+ "bandstand",
4
+ "beach_resort",
5
+ "bird_hide",
6
+ "bleachers",
7
+ "firepit",
8
+ "fishing",
9
+ "garden",
10
+ "nature_reserve",
11
+ "park",
12
+ "picnic_table",
13
+ "playground",
14
+ "resort",
15
+ "summer_camp",
16
+ "swimming_area",
17
+ "water_park",
18
+ "wildlife_hide",
19
+ ],
20
+ "室内娱乐场所": [
21
+ "adult_gaming_centre",
22
+ "amusement_arcade",
23
+ "bowling_alley",
24
+ "disc_golf_course",
25
+ "escape_game",
26
+ "fitness_centre",
27
+ "fitness_station",
28
+ "golf_course",
29
+ "miniature_golf",
30
+ "sauna",
31
+ "tanning_salon",
32
+ "trampoline_park",
33
+ "bar",
34
+ "biergarten",
35
+ "cafe",
36
+ "fast_food",
37
+ "food_court",
38
+ "ice_cream",
39
+ "pub",
40
+ "restaurant",
41
+ "arts_centre",
42
+ "brothel",
43
+ "casino",
44
+ "cinema",
45
+ "community_centre",
46
+ "conference_centre",
47
+ "events_venue",
48
+ "exhibition_centre",
49
+ "fountain",
50
+ "gambling",
51
+ "love_hotel",
52
+ "music_venue",
53
+ "nightclub",
54
+ "planetarium",
55
+ "public_bookcase",
56
+ "social_centre",
57
+ "stage",
58
+ "stripclub",
59
+ "studio",
60
+ "swingerclub",
61
+ "theatre",
62
+ ],
63
+ "体育设施": [
64
+ "horse_riding",
65
+ "ice_rink",
66
+ "marina",
67
+ "pitch",
68
+ "sports_centre",
69
+ "sports_hall",
70
+ "stadium",
71
+ "track",
72
+ "swimming_pool",
73
+ ],
74
+ "水上活动": [
75
+ "beach_resort",
76
+ "ice_rink",
77
+ "marina",
78
+ "slipway",
79
+ "swimming_area",
80
+ "swimming_pool",
81
+ "water_park",
82
+ ],
83
+ "自然与野生动物观赏": [
84
+ "bird_hide",
85
+ "nature_reserve",
86
+ "wildlife_hide",
87
+ "hunting_stand",
88
+ ],
89
+ "儿童游乐区": ["playground", "summer_camp", "miniature_golf", "dog_park"],
90
+ "餐饮服务": [
91
+ "bar",
92
+ "biergarten",
93
+ "cafe",
94
+ "fast_food",
95
+ "food_court",
96
+ "ice_cream",
97
+ "pub",
98
+ "restaurant",
99
+ ],
100
+ "教育机构": [
101
+ "college",
102
+ "dancing_school",
103
+ "driving_school",
104
+ "first_aid_school",
105
+ "kindergarten",
106
+ "language_school",
107
+ "library",
108
+ "surf_school",
109
+ "toy_library",
110
+ "research_institute",
111
+ "training",
112
+ "music_school",
113
+ "school",
114
+ "traffic_park",
115
+ "university",
116
+ ],
117
+ "交通设施": [
118
+ "bicycle_parking",
119
+ "bicycle_repair_station",
120
+ "bicycle_rental",
121
+ "bicycle_wash",
122
+ "boat_rental",
123
+ "boat_sharing",
124
+ "bus_station",
125
+ "car_rental",
126
+ "car_sharing",
127
+ "car_wash",
128
+ "compressed_air",
129
+ "vehicle_inspection",
130
+ "charging_station",
131
+ "driver_training",
132
+ "ferry_terminal",
133
+ "fuel",
134
+ "grit_bin",
135
+ "motorcycle_parking",
136
+ "parking",
137
+ "parking_entrance",
138
+ "parking_space",
139
+ "taxi",
140
+ "weighbridge",
141
+ ],
142
+ "金融服务": [
143
+ "atm",
144
+ "payment_terminal",
145
+ "bank",
146
+ "bureau_de_change",
147
+ "money_transfer",
148
+ "payment_centre",
149
+ ],
150
+ "医疗保健": [
151
+ "baby_hatch",
152
+ "clinic",
153
+ "dentist",
154
+ "doctors",
155
+ "hospital",
156
+ "nursing_home",
157
+ "pharmacy",
158
+ "social_facility",
159
+ "veterinary",
160
+ ],
161
+ "文化艺术": [
162
+ "arts_centre",
163
+ "brothel",
164
+ "casino",
165
+ "cinema",
166
+ "community_centre",
167
+ "conference_centre",
168
+ "events_venue",
169
+ "exhibition_centre",
170
+ "fountain",
171
+ "gambling",
172
+ "love_hotel",
173
+ "music_venue",
174
+ "nightclub",
175
+ "planetarium",
176
+ "public_bookcase",
177
+ "social_centre",
178
+ "stage",
179
+ "stripclub",
180
+ "studio",
181
+ "swingerclub",
182
+ "theatre",
183
+ ],
184
+ "公共服务": [
185
+ "bbq",
186
+ "bench",
187
+ "dog_toilet",
188
+ "dressing_room",
189
+ "drinking_water",
190
+ "give_box",
191
+ "lounge",
192
+ "mailroom",
193
+ "parcel_locker",
194
+ "shelter",
195
+ "shower",
196
+ "telephone",
197
+ "toilets",
198
+ "water_point",
199
+ "watering_place",
200
+ "sanitary_dump_station",
201
+ "recycling",
202
+ "waste_basket",
203
+ "waste_disposal",
204
+ "waste_transfer_station",
205
+ "post_box",
206
+ "post_depot",
207
+ "post_office",
208
+ "courthouse",
209
+ "fire_station",
210
+ "police",
211
+ "prison",
212
+ "ranger_station",
213
+ "townhall",
214
+ ],
215
+ "其他特殊用途": [
216
+ "animal_boarding",
217
+ "animal_breeding",
218
+ "animal_shelter",
219
+ "animal_training",
220
+ "baking_oven",
221
+ "clock",
222
+ "crematorium",
223
+ "dive_centre",
224
+ "funeral_hall",
225
+ "grave_yard",
226
+ "hunting_stand",
227
+ "internet_cafe",
228
+ "kitchen",
229
+ "kneipp_water_cure",
230
+ "lounger",
231
+ "marketplace",
232
+ "monastery",
233
+ "mortuary",
234
+ "photo_booth",
235
+ "place_of_mourning",
236
+ "place_of_worship",
237
+ "public_bath",
238
+ "public_building",
239
+ "refugee_site",
240
+ "vending_machine",
241
+ ],
242
+ }
@@ -0,0 +1,26 @@
1
+ from typing import List
2
+
3
+ __all__ = ["wrap_feature_collection"]
4
+
5
+
6
+ def wrap_feature_collection(features: List[dict], name: str):
7
+ """
8
+ 将 GeoJSON Feature 集合包装为 FeatureCollection
9
+ Wrap GeoJSON Feature collection as FeatureCollection
10
+
11
+ Args:
12
+ - features: GeoJSON Feature 集合。GeoJSON Feature collection.
13
+ - name: FeatureCollection 名称。FeatureCollection name.
14
+
15
+ Returns:
16
+ - dict: GeoJSON FeatureCollection
17
+ """
18
+ return {
19
+ "type": "FeatureCollection",
20
+ "name": name,
21
+ "crs": {
22
+ "type": "name",
23
+ "properties": {"name": "urn:ogc:def:crs:OGC:1.3:CRS84"},
24
+ },
25
+ "features": features,
26
+ }
@@ -0,0 +1,57 @@
1
+ import grpc
2
+
3
+ __all__ = ["create_channel", "create_aio_channel"]
4
+
5
+
6
+ def create_channel(server_address: str, secure: bool = False) -> grpc.Channel:
7
+ """
8
+ 创建一个grpc的channel
9
+ Create a grpc channel
10
+
11
+ Args:
12
+ - server_address (str): 服务器地址。server address.
13
+ - secure (bool, optional): 是否使用安全连接. Defaults to False. Whether to use a secure connection. Defaults to False.
14
+
15
+ Returns:
16
+ - grpc.Channel: grpc的channel。grpc channel.
17
+ """
18
+ if server_address.startswith("http://"):
19
+ server_address = server_address.split("//")[1]
20
+ if secure:
21
+ raise ValueError("secure channel must use `https` or not use `http`")
22
+ elif server_address.startswith("https://"):
23
+ server_address = server_address.split("//")[1]
24
+ if not secure:
25
+ secure = True
26
+
27
+ if secure:
28
+ return grpc.secure_channel(server_address, grpc.ssl_channel_credentials())
29
+ else:
30
+ return grpc.insecure_channel(server_address)
31
+
32
+
33
+ def create_aio_channel(server_address: str, secure: bool = False) -> grpc.aio.Channel:
34
+ """
35
+ 创建一个grpc的异步channel
36
+ Create a grpc asynchronous channel
37
+
38
+ Args:
39
+ - server_address (str): 服务器地址。server address.
40
+ - secure (bool, optional): 是否使用安全连接. Defaults to False. Whether to use a secure connection. Defaults to False.
41
+
42
+ Returns:
43
+ - grpc.aio.Channel: grpc的异步channel。grpc asynchronous channel.
44
+ """
45
+ if server_address.startswith("http://"):
46
+ server_address = server_address.split("//")[1]
47
+ if secure:
48
+ raise ValueError("secure channel must use `https` or not use `http`")
49
+ elif server_address.startswith("https://"):
50
+ server_address = server_address.split("//")[1]
51
+ if not secure:
52
+ secure = True
53
+
54
+ if secure:
55
+ return grpc.aio.secure_channel(server_address, grpc.ssl_channel_credentials())
56
+ else:
57
+ return grpc.aio.insecure_channel(server_address)