SpatialWebAgent commited on
Commit
a501022
·
verified ·
1 Parent(s): 997aa2e

Update utils/llm_coding.py

Browse files
Files changed (1) hide show
  1. utils/llm_coding.py +572 -572
utils/llm_coding.py CHANGED
@@ -1,583 +1,583 @@
1
- import requests
2
- import urllib3
3
- import json
4
- from utils import geoutil
5
- import regex_spatial
6
- from shapely.geometry import Polygon, MultiPoint, LineString, Point, mapping
7
- import re
8
- import geopandas as gpd
9
- from geocoder import geo_level1
10
- from openai import OpenAI
11
-
12
- import os
13
- api_key = os.getenv('api_key')
14
- client = OpenAI(
15
- api_key=api_key
16
- )
17
-
18
-
19
- model = "gpt-4o"
20
-
21
- north = ["north", "N'", "North", "NORTH"]
22
- south = ["south", "S'", "South", "SOUTH"]
23
- east = ["east", "E'", "East", "EAST"]
24
- west = ["west", "W'", "West", "WEST"]
25
- northeast = ["north-east", "NE'", "north east", "NORTH-EAST", "North East", "NORTH EAST"]
26
- southeast = ["south-east", "SE'", "south east", "SOUTH-EAST", "South East", "SOUTH EAST"]
27
- northwest = ["north-west", "NW'", "north west", "NORTH-WEST", "North West", "NORTH WEST"]
28
- southwest = ["south-west", "SW'", "south west", "SOUTH-WEST", "South West", "SOUTH WEST"]
29
- center = ["center","central", "downtown","midtown"]
30
- #
31
- #
32
- # def get_directional_coordinates(coordinates, direction, centroid, minimum, maximum, is_midmid):
33
- # direction_coordinates = get_directional_coordinates_by_angle(coordinates, direction, minimum, maximum)
34
- # midmid1, midmid2 = geoutil.get_midmid_point(centroid, direction_coordinates[0], direction_coordinates[-1],
35
- # is_midmid)
36
- # if direction in west:
37
- # maxi = max(p[2] for p in direction_coordinates)
38
- # mini = min(p[2] for p in direction_coordinates)
39
- # index_mini = 0
40
- # index_maxi = 0
41
- # for idx, p in enumerate(direction_coordinates):
42
- # if p[2] == mini:
43
- # index_mini = idx
44
- # if p[2] == maxi:
45
- # index_maxi = idx
46
- #
47
- # direction_coordinates.insert(index_maxi + 1, midmid2)
48
- # direction_coordinates.insert(index_mini + 1, midmid1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  # else:
50
- # direction_coordinates.append(midmid2)
51
- # direction_coordinates.append(midmid1)
52
- #
53
- # return direction_coordinates, midmid1, midmid2
54
- #
55
- #
56
- # def get_directional_coordinates_by_angle(coordinates, direction, minimum, maximum):
57
- # direction_coordinates = []
58
- # for p in coordinates:
59
- # if direction in east:
60
- # if p[2] >= minimum or p[2] <= maximum:
61
- # direction_coordinates.append(p)
62
- #
63
- # else:
64
- # if p[2] >= minimum and p[2] <= maximum:
65
- # direction_coordinates.append(p)
66
- # return direction_coordinates
67
- #
68
- #
69
- # def get_directional_coordinates_by_angle(coordinates, direction, minimum, maximum):
70
  # direction_coordinates = []
71
  # for p in coordinates:
72
- # if direction in east:
73
- # if p[2] >= minimum or p[2] <= maximum:
74
- # direction_coordinates.append(p)
75
- #
 
 
76
  # else:
77
- # if p[2] >= minimum and p[2] <= maximum:
78
- # direction_coordinates.append(p)
 
 
 
 
79
  # return direction_coordinates
80
- #
81
- #
82
- # def get_central(coordinates, centroid, direction, is_midmid):
83
- # n_min_max = get_min_max("north")
84
- # n_coordinates = get_directional_coordinates_by_angle(coordinates, "north", n_min_max[0], n_min_max[1])
85
- # n_mid1, n_mid2 = geoutil.get_midmid_point(centroid, n_coordinates[0], n_coordinates[-1], is_midmid)
86
- #
87
- # ne_min_max = get_min_max("north east")
88
- # ne_coordinates = get_directional_coordinates_by_angle(coordinates, "north east", ne_min_max[0], ne_min_max[1])
89
- # ne_mid1, ne_mid2 = geoutil.get_midmid_point(centroid, ne_coordinates[0], ne_coordinates[-1], is_midmid)
90
- #
91
- # e_min_max = get_min_max("east")
92
- # e_coordinates = get_directional_coordinates_by_angle(coordinates, "east", e_min_max[0], e_min_max[1])
93
- # e_mid1, e_mid2 = geoutil.get_midmid_point(centroid, e_coordinates[0], e_coordinates[-1], is_midmid)
94
- #
95
- # se_min_max = get_min_max("south east")
96
- # se_coordinates = get_directional_coordinates_by_angle(coordinates, "south east", se_min_max[0], se_min_max[1])
97
- # se_mid1, se_mid2 = geoutil.get_midmid_point(centroid, se_coordinates[0], se_coordinates[-1], is_midmid)
98
- #
99
- # s_min_max = get_min_max("south")
100
- # s_coordinates = get_directional_coordinates_by_angle(coordinates, "south", s_min_max[0], s_min_max[1])
101
- # s_mid1, s_mid2 = geoutil.get_midmid_point(centroid, s_coordinates[0], s_coordinates[-1], is_midmid)
102
- #
103
- # sw_min_max = get_min_max("south west")
104
- # sw_coordinates = get_directional_coordinates_by_angle(coordinates, "south west", sw_min_max[0], sw_min_max[1])
105
- # sw_mid1, sw_mid2 = geoutil.get_midmid_point(centroid, sw_coordinates[0], sw_coordinates[-1], is_midmid)
106
- #
107
- # w_min_max = get_min_max("west")
108
- # w_coordinates = get_directional_coordinates_by_angle(coordinates, "west", w_min_max[0], w_min_max[1])
109
- # w_mid1, w_mid2 = geoutil.get_midmid_point(centroid, w_coordinates[0], w_coordinates[-1], is_midmid)
110
- #
111
- # nw_min_max = get_min_max("north west")
112
- # nw_coordinates = get_directional_coordinates_by_angle(coordinates, "north west", nw_min_max[0], nw_min_max[1])
113
- # nw_mid1, nw_mid2 = geoutil.get_midmid_point(centroid, nw_coordinates[0], nw_coordinates[-1], is_midmid)
114
- #
115
- # central_coordindates = [e_mid1, e_mid2, ne_mid1, ne_mid2, n_mid1, n_mid2,
116
- # nw_mid1, nw_mid2, w_mid1, w_mid2, sw_mid1, sw_mid2,
117
- # s_mid1, s_mid2, se_mid1, se_mid2]
118
- # return central_coordindates
119
- #
120
- #
121
- # def get_min_max(direction):
122
- # regex = regex_spatial.get_directional_regex()
123
- # direction_list = regex.split("|")
124
- # if direction in direction_list:
125
- # if direction in east:
126
- # return (337, 22)
127
- # if direction in northeast:
128
- # return (22, 67)
129
- # if direction in north:
130
- # return (67, 112)
131
- # if direction in northwest:
132
- # return (112, 157)
133
- # if direction in west:
134
- # return (157, 202)
135
- # if direction in southwest:
136
- # return (202, 247)
137
- # if direction in south:
138
- # return (247, 292)
139
- # if direction in southeast:
140
- # return (292, 337)
141
- #
142
- # return None
143
- # def get_level1_coordinates(coordinates, centroid, direction, is_midmid):
144
- # min_max = get_min_max(direction)
145
  # if min_max is not None:
146
- # coordinates, mid1, mid2 = get_directional_coordinates(coordinates, direction, centroid, min_max[0], min_max[1], is_midmid)
147
- # return coordinates, centroid, mid1, mid2
148
- # elif direction.lower() in center:
149
- # return get_central(coordinates, centroid, direction, is_midmid), centroid, None, None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  # else:
151
- # return coordinates, centroid, None, None
152
- def to_standard_2d_list(data):
153
- arr = np.array(data)
154
-
155
- # 强制变成一维后 reshape,前提是元素总数是2的倍数
156
- flat = arr.flatten()
157
- if flat.size % 2 != 0:
158
- raise ValueError("元素个数不是2的倍数,不能 reshape 成 [N, 2] 格式")
159
-
160
- return flat.reshape(-1, 2).tolist()
161
-
162
-
163
- def get_geojson(ent, arr, centroid):
164
- poly_json = {}
165
- poly_json['type'] = 'FeatureCollection'
166
- poly_json['features'] = []
167
- coordinates= []
168
- coordinates.append(arr)
169
- poly_json['features'].append({
170
- 'type':'Feature',
171
- 'id': ent,
172
- 'properties': {
173
- 'centroid': centroid
174
- },
175
- 'geometry': {
176
- 'type':'Polygon',
177
- 'coordinates': coordinates
178
- }
179
- })
180
- return poly_json
181
-
182
-
183
- def get_coordinates(ent):
184
- request_url = 'https://nominatim.openstreetmap.org/search.php?q= ' +ent +'&polygon_geojson=1&accept-language=en&format=jsonv2'
185
- headers = {
186
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15"
187
- }
188
- page = requests.get(request_url, headers=headers, verify=False)
189
- json_content = json.loads(page.content)
190
- all_coordinates = json_content[0]['geojson']['coordinates'][0]
191
- centroid = (float(json_content[0]['lon']), float(json_content[0]['lat']))
192
- for p in all_coordinates:
193
- p2 = (p[0], p[1])
194
- angle = geoutil.calculate_bearing(centroid, p2)
195
- p.append(angle)
196
-
197
- geojson = get_geojson(ent, all_coordinates, centroid)
198
-
199
- return geojson['features'][0]['geometry']['coordinates'][0], geojson['features'][0]['properties']['centroid']
200
-
201
- def get_coordinates(location):
202
- request_url = f'https://nominatim.openstreetmap.org/search.php?q={location}&polygon_geojson=1&accept-language=en&format=jsonv2'
203
-
204
- print(request_url)
205
- headers = {"User-Agent": "Mozilla/5.0"}
206
- response = requests.get(request_url, headers=headers, verify=False)
207
- json_content = json.loads(response.content)
208
- # print(json_content)
209
- if json_content[0]['geojson']['type'] == 'Polygon':
210
- coordinates = json_content[0]['geojson']['coordinates'][0]
211
- elif json_content[0]['geojson']['type'] == 'Point':
212
- coordinates = json_content[0]['geojson']['coordinates']
213
- else:
214
- print(json_content[0]['geojson']['type'])
215
- centroid = (float(json_content[0]['lon']), float(json_content[0]['lat']))
216
- return (coordinates, centroid)
217
-
218
-
219
- # level3
220
- def get_directional_coordinates_by_angle(coordinates, centroid, direction, minimum, maximum):
221
- # minimum = 157
222
- # maximum = 202
223
-
224
- direction_coordinates = []
225
- for p in coordinates:
226
- angle = geoutil.calculate_bearing(centroid, p)
227
- p2 = (p[0], p[1], angle)
228
- if direction in geo_level1.east:
229
- if angle >= minimum or angle <= maximum:
230
- direction_coordinates.append(p2)
231
-
232
- else:
233
- if angle >= minimum and angle <= maximum:
234
- direction_coordinates.append(p2)
235
- # print(type(direction_coordinates[0]))
236
- # if(direction in geo_level1.west):
237
- # direction_coordinates.sort(key=lambda k: k[2], reverse=True)
238
-
239
- return direction_coordinates
240
- def get_level3(level3):
241
- digits = re.findall('[0-9]+', level3)[0]
242
- unit = re.findall('[A-Za-z]+', level3)[0]
243
- return digits, unit
244
-
245
- def get_direction_coordinates(coordinates, centroid, level1):
246
- min_max = geo_level1.get_min_max(level1)
247
- if min_max is not None:
248
- coord = get_directional_coordinates_by_angle(coordinates, centroid, level1, min_max[0], min_max[1])
249
- return coord
250
- return coordinates
251
- def sort_west(poly1, poly2, centroid):
252
- coords1 = mapping(poly1)["features"][0]["geometry"]["coordinates"]
253
- coords2 = mapping(poly2)["features"][0]["geometry"]["coordinates"]
254
- coord1 = []
255
- coord2 = []
256
- coord = []
257
- for c in coords1:
258
- pol = list(c[::-1])
259
- coord1.extend(pol)
260
- for c in coords2:
261
- pol = list(c[::-1])
262
- coord2.extend(pol)
263
- coo1 = []
264
- coo2 = []
265
- for p in coord1:
266
- angle = geoutil.calculate_bearing(centroid, p)
267
- if angle >= 157 and angle <= 202:
268
- coo1.append((p[0], p[1], angle))
269
- for p in coord2:
270
- angle = geoutil.calculate_bearing(centroid, p)
271
- if angle >= 157 and angle <= 202:
272
- coo2.append((p[0], p[1], angle))
273
- coo1.extend(coo2)
274
- return coo1
275
-
276
-
277
- def get_level3_coordinates(coordinates, level_3, level1):
278
- distance, unit = get_level3(level_3)
279
- kms = geoutil.get_kilometers(distance, unit)
280
- coord = []
281
-
282
- coords0, center = coordinates
283
-
284
- if not isinstance(coords0, list) or len(coords0) < 3:
285
-
286
- # 从原始点出发,根据方向移动距离 kms 得到新圆心
287
- lat_km = 111.32
288
- lon_km = 111.32 * np.cos(np.radians(center[1]))
289
-
290
- dx = dy = 0
291
-
292
- if level1 is not None:
293
- if level1 in geo_level1.east:
294
- dx = kms / lon_km
295
- elif level1 in geo_level1.west:
296
- dx = -kms / lon_km
297
- elif level1 in geo_level1.north:
298
- dy = kms / lat_km
299
- elif level1 in geo_level1.south:
300
- dy = -kms / lat_km
301
- # 你也可以支持 northeast、southwest 等复合方向
302
-
303
- new_center = (center[0] + dx, center[1] + dy)
304
-
305
- # 用固定半径画个圆(例如半径2km)
306
- r_km = 1 # 半径设为1km,你也可以设为其他值
307
-
308
- circle_points = []
309
- for theta in np.linspace(0, 360, num=100):
310
- theta_rad = np.radians(theta)
311
- d_lat = (np.sin(theta_rad) * r_km) / lat_km
312
- d_lon = (np.cos(theta_rad) * r_km) / lon_km
313
- circle_points.append((new_center[0] + d_lon, new_center[1] + d_lat))
314
-
315
- # 输出中心(使用新圆心)
316
- if circle_points:
317
- center_point = MultiPoint(circle_points).centroid
318
- center = (center_point.x, center_point.y)
319
- else:
320
- center = new_center
321
-
322
- return circle_points, center
323
-
324
- # 正常 polygon 流程
325
- poly1 = Polygon(coords0)
326
- polygon1 = gpd.GeoSeries(poly1)
327
-
328
- # 生成环形区域
329
- poly2 = polygon1.buffer(0.0095 * kms, join_style=2)
330
- poly3 = polygon1.buffer(0.013 * kms, join_style=2)
331
- poly = poly3.difference(poly2)
332
-
333
- # 获取坐标
334
- coords = mapping(poly)["features"][0]["geometry"]["coordinates"]
335
- for c in coords:
336
- pol = list(c[::-1])
337
- coord.extend(pol)
338
-
339
- # 方向裁剪
340
- if level1 is not None:
341
- coord = get_direction_coordinates(coord, coordinates[1], level1)
342
- if level1 in geo_level1.west:
343
- coord = sort_west(poly3, poly2, coordinates[1])
344
-
345
- # 计算质心
346
- if coord:
347
- center_point = MultiPoint(coord).centroid
348
- center = (center_point.x, center_point.y)
349
- else:
350
- center = coordinates[1]
351
-
352
- return coord, center
353
- # level 3 end
354
-
355
- # between
356
- def get_between_coordinates(coordinates1, coordinates2):
357
- """
358
- 计算两个区域之间的中间点,并生成一个等面积的圆形区域。
359
- 如果某个输入仅为点(坐标长度 < 3),则其面积设为 0;
360
- 如果两个输入都是点,则默认半径为 2km。
361
- :param coordinates1: 第一个区域的边界坐标和中心点
362
- :param coordinates2: 第二个区域的边界坐标和中心点
363
- :return: 圆形区域的坐标集和圆心
364
- """
365
-
366
- def is_valid_polygon(coords):
367
- return isinstance(coords, list) and len(coords) >= 3
368
-
369
- coords1, center1 = coordinates1
370
- coords2, center2 = coordinates2
371
-
372
- # 判断输入是否为合法多边形(>=3个点)
373
- if is_valid_polygon(coords1):
374
- poly1 = Polygon(coords1)
375
- area1 = poly1.area
376
- else:
377
- area1 = 0
378
-
379
- if is_valid_polygon(coords2):
380
- poly2 = Polygon(coords2)
381
- area2 = poly2.area
382
- else:
383
- area2 = 0
384
-
385
- # 计算中心点(两个中心的中点)
386
- midpoint = (
387
- (center1[0] + center2[0]) / 2,
388
- (center1[1] + center2[1]) / 2
389
- )
390
-
391
- # 如果两个区域都是点,则使用默认半径 2km
392
- if area1 == 0 and area2 == 0:
393
- r_km = 2
394
- else:
395
- avg_area = (area1 + area2) / 2
396
- r_km = np.sqrt(avg_area / np.pi) * 111.32 # 近似 km 半径
397
-
398
- # 经纬度距离换算因子
399
- lat_km = 111.32
400
- lon_km = 111.32 * np.cos(np.radians(midpoint[1]))
401
-
402
- # 生成圆形区域坐标(100个点)
403
- circle_points = []
404
- for theta in np.linspace(0, 360, num=100):
405
- theta_rad = np.radians(theta)
406
- d_lat = (np.sin(theta_rad) * r_km) / lat_km
407
- d_lon = (np.cos(theta_rad) * r_km) / lon_km
408
- circle_points.append((midpoint[0] + d_lon, midpoint[1] + d_lat))
409
-
410
- return circle_points, midpoint
411
- # between end
412
-
413
-
414
- def llmapi(text):
415
- system_prompt = (
416
- "你是一个资深的地理学家,你的任务是通过给定的一段自然语言,来选择正确的定位函数顺序以及他们的输入。\n"
417
- "你能选择的定位函数有:\n"
418
- "1. 相对定位(Relative Positioning):输入为地点坐标,方位,距离。输出为距离‘距离’输入的地点坐标的‘方位’的坐标。\n"
419
- "2. 中间定位(Between Positioning):输入为两个地点的坐标,输出为两个地点坐标的中点。\n"
420
- "请先进行思维链(CoT)推理,并最终用 JSON 格式输出你的答案,用 `<<<JSON>>>` 和 `<<<END>>>` 包裹起来。\n"
421
- "请确保所有输入仅包含:地点名称(字符串)、索引(整数)、方位(字符串,必须是英文)或距离(字符串,带单位),不允许返���诸如 'Chatswood 南4 km的坐标' 这样的内容。\n"
422
- "每个步骤编号都有 id 记录,然后如果某个输入是之前步骤的输出,那么输入对应步骤的 id。\n"
423
- "所有方向必须使用英文(如 south, west, northeast, etc.)。\n"
424
- "示例输出:\n"
425
- "<<<JSON>>>\n"
426
- "[{\"id\": 1, \"function\": \"Relative\", \"inputs\": [\"Chatswood\", \"south\", \"4 km\"]},"
427
- "{\"id\": 2, \"function\": \"Relative\", \"inputs\": [\"North Sydney\", \"west\", \"2 km\"]},"
428
- "{\"id\": 3, \"function\": \"Between\", \"inputs\": [1, 2]},"
429
- "{\"id\": 4, \"function\": \"Relative\", \"inputs\": [3, \"southwest\", \"5 km\"]}]\n"
430
- "<<<END>>>")
431
-
432
- messages = [
433
- {"role": "system", "content": system_prompt},
434
- {"role": "user", "content": text},
435
- ]
436
-
437
- chat_completion = client.chat.completions.create(
438
- messages=messages,
439
- model=model,
440
- )
441
-
442
- result = chat_completion.choices[0].message.content
443
- json_match = re.search(r'<<<JSON>>>\n(.*?)\n<<<END>>>', result, re.DOTALL)
444
-
445
- if json_match:
446
- # print(json.loads(json_match.group(1)))
447
- return json.loads(json_match.group(1))
448
- else:
449
- raise ValueError("LLM 输出未包含预期的 JSON 格式数据。")
450
- def llmapi(text):
451
- system_prompt = (
452
- "You are an experienced geographer. Your task is to determine the correct sequence of positioning functions and their inputs based on a given piece of natural language.\n"
453
- "The positioning functions you can choose from are:\n"
454
- "1. Relative Positioning: Inputs is (location coordinate or location name, direction, and distance). Outputs the coordinates that are in the given 'direction' and 'distance' from the input location.\n"
455
- "2. Between Positioning: Inputs is (location 1 coordinates or location 1 name, location 2 coordinates or location 2 name). Outputs the midpoint coordinate between the two locations.\n"
456
- "You can only use the given functions, and the inputs to the functions must obey the above properties. The given functions can be combined to solve complex situations."
457
- "First, perform chain-of-thought (CoT) reasoning, and finally output your answer in JSON format, wrapped between `<<<JSON>>>` and `<<<END>>>`.\n"
458
- "Make sure all inputs only include: location names (strings), step indices (integers), directions (strings, must be in English), or distances (strings with units). Do not return expressions like 'the coordinate 4 km south of Chatswood'.\n"
459
- "Each step must have an 'id'. If the input of a step is the output of a previous step, use that step’s 'id' as the input.\n"
460
- "All directions must be in English (e.g., south, west, northeast, etc.).\n"
461
- "Example output:\n"
462
- "<<<JSON>>>\n"
463
- "[{\"id\": 1, \"function\": \"Relative\", \"inputs\": [\"Chatswood\", \"south\", \"4 km\"]},"
464
- "{\"id\": 2, \"function\": \"Relative\", \"inputs\": [\"North Sydney\", \"west\", \"2 km\"]},"
465
- "{\"id\": 3, \"function\": \"Between\", \"inputs\": [1, 2]},"
466
- "{\"id\": 4, \"function\": \"Relative\", \"inputs\": [3, \"southwest\", \"5 km\"]}]\n"
467
- "<<<END>>>")
468
-
469
- messages = [
470
- {"role": "system", "content": system_prompt},
471
- {"role": "user", "content": text},
472
- ]
473
-
474
- chat_completion = client.chat.completions.create(
475
- messages=messages,
476
- model=model,
477
- )
478
-
479
- result = chat_completion.choices[0].message.content
480
- print(result)
481
- json_match = re.search(r'<<<JSON>>>\n(.*?)\n<<<END>>>', result, re.DOTALL)
482
-
483
- if json_match:
484
- return json.loads(json_match.group(1))
485
- else:
486
- raise ValueError("LLM 输出未包含预期的 JSON 格式数据。")
487
-
488
-
489
-
490
-
491
-
492
- def execute_steps(steps):
493
- data = {}
494
-
495
- for step in steps:
496
- step_id = step['id']
497
- function = step['function']
498
- inputs = step['inputs']
499
- # print('-' * 50)
500
- # print(function)
501
- # print(inputs)
502
-
503
-
504
- resolved_inputs = []
505
- for inp in inputs:
506
- if isinstance(inp, int):
507
- resolved_inputs.append(data[inp])
508
- else:
509
- resolved_inputs.append(inp)
510
- if function == "Relative":
511
- location, direction, distance = resolved_inputs
512
- if isinstance(location, str):
513
- location = get_coordinates(location)
514
-
515
- location = [to_standard_2d_list(location[0])] + list(location[1:])
516
- location = [[[151.214901,-33.859175]], (151.214901,-33.859175)]
517
- result = get_level3_coordinates(location, distance, direction)
518
- data[step_id] = result
519
-
520
- elif function == "Between":
521
-
522
-
523
- location1, location2 = resolved_inputs
524
- # print(location1)
525
- # print(111)
526
- # print(location2)
527
- if isinstance(location1, str):
528
- location1 = get_coordinates(location1)
529
-
530
- location1 = [to_standard_2d_list(location1[0])] + list(location1[1:])
531
- if isinstance(location2, str):
532
-
533
- location2 = get_coordinates(location2)
534
- location2 = [to_standard_2d_list(location2[0])] + list(location2[1:])
535
- result = get_between_coordinates(location1, location2)
536
-
537
- data[step_id] = result
538
-
539
- return data
540
-
541
-
542
-
543
- if __name__ == '__main__':
544
- # a = get_coordinates('Burwood')
545
- # a2 = get_coordinates('Glebe')
546
- # b = get_level3_coordinates(a, '5 km', 'east')
547
- # c = get_between_coordinates(a, a2)
548
-
549
- # 完整通道
550
- # 默认输入
551
- # default_input_text = "在Chatswood南边4公里与North Sydney 东边2公里的中间的西南5公里。"
552
- # default_input_text = "你是一位规划师,正在为华盛顿州的一项新森林监测站选址。两个潜在的参考位置分别是雷尼尔山国家公园(Mount Rainier National Park)和北喀斯喀特国家公园(North Cascades National Park)。首先,你想在这两个国家公园之间找到一个中间点。接着,你希望在这个中间点与北喀斯喀特国家公园之间,再取一个中间位置,以便确定最终的建设候选地。"
553
- # default_input_text = "在Chatswood和North Sydney的中间靠近North Sydney的四分之一位置"
554
- # default_input_text = "Plan a trip that involves determining the midpoint between Paris and London, and then finding another midpoint between this location and Paris to identify potential stopovers during travel."
555
- # default_input_text = "5km southwest of Chatswood, 4km south of Chatswood and 2km north of North Sydney."
556
-
557
-
558
-
559
- # 解析 LLM 结果
560
- # parsed_steps = llmapi(default_input_text)
561
- # parsed_steps = [{'id': 1, 'function': 'Relative', 'inputs': ['Chatswood', 'south', '4 km']}, {'id': 2, 'function': 'Relative', 'inputs': ['North Sydney', 'east', '2 km']}, {'id': 3, 'function': 'Between', 'inputs': [1, 2]}, {'id': 4, 'function': 'Relative', 'inputs': [3, 'south west', '5 km']}]
562
- # parsed_steps = [{"id": 1, "function": "Between", "inputs": ["Chatswood", "North Sydney"]},{"id": 2, "function": "Between", "inputs": [1, "North Sydney"]}]
563
- # parsed_steps = [{"id": 1, "function": "Relative", "inputs": ["Katoomba", "southeast", "3 km"]}, {"id": 2, "function": "Between", "inputs": [1, "Echo Point"]}]
564
- # parsed_steps = [{'id': 1, 'function': 'Relative', 'inputs': ['Scafell Pike', 'east', '9 km']}]
565
- # parsed_steps = [{'id': 1, 'function': 'Relative', 'inputs': ['Colosseum', 'northeast', '8 km']}, {'id': 2, 'function': 'Relative', 'inputs': [1, 'northeast', '2 km']}]
566
- parsed_steps = [
567
- {"id": 1, "function": "Between", "inputs": ["Statue of Liberty", "Eiffel Tower"]},
568
- {"id": 2, "function": "Relative", "inputs": [1, "west", "8 km"]}
569
- ]
570
 
571
- # 执行步骤
572
- result = execute_steps(parsed_steps)
573
- # 输出最终计算结果
574
- print(result)
575
- print('-' * 100)
576
- print(result[(max(result.keys()))][0])
577
- # 通道结束
578
 
579
- # location = get_coordinates('Chatswood')
580
- # result = get_level3_coordinates(location, '4 km', 'north west')
581
- # print(result)
582
 
583
 
 
1
+ # import requests
2
+ # import urllib3
3
+ # import json
4
+ # from utils import geoutil
5
+ # import regex_spatial
6
+ # from shapely.geometry import Polygon, MultiPoint, LineString, Point, mapping
7
+ # import re
8
+ # import geopandas as gpd
9
+ # from geocoder import geo_level1
10
+ # from openai import OpenAI
11
+
12
+ # import os
13
+ # api_key = os.getenv('api_key')
14
+ # client = OpenAI(
15
+ # api_key=api_key
16
+ # )
17
+
18
+
19
+ # model = "gpt-4o"
20
+
21
+ # north = ["north", "N'", "North", "NORTH"]
22
+ # south = ["south", "S'", "South", "SOUTH"]
23
+ # east = ["east", "E'", "East", "EAST"]
24
+ # west = ["west", "W'", "West", "WEST"]
25
+ # northeast = ["north-east", "NE'", "north east", "NORTH-EAST", "North East", "NORTH EAST"]
26
+ # southeast = ["south-east", "SE'", "south east", "SOUTH-EAST", "South East", "SOUTH EAST"]
27
+ # northwest = ["north-west", "NW'", "north west", "NORTH-WEST", "North West", "NORTH WEST"]
28
+ # southwest = ["south-west", "SW'", "south west", "SOUTH-WEST", "South West", "SOUTH WEST"]
29
+ # center = ["center","central", "downtown","midtown"]
30
+ # #
31
+ # #
32
+ # # def get_directional_coordinates(coordinates, direction, centroid, minimum, maximum, is_midmid):
33
+ # # direction_coordinates = get_directional_coordinates_by_angle(coordinates, direction, minimum, maximum)
34
+ # # midmid1, midmid2 = geoutil.get_midmid_point(centroid, direction_coordinates[0], direction_coordinates[-1],
35
+ # # is_midmid)
36
+ # # if direction in west:
37
+ # # maxi = max(p[2] for p in direction_coordinates)
38
+ # # mini = min(p[2] for p in direction_coordinates)
39
+ # # index_mini = 0
40
+ # # index_maxi = 0
41
+ # # for idx, p in enumerate(direction_coordinates):
42
+ # # if p[2] == mini:
43
+ # # index_mini = idx
44
+ # # if p[2] == maxi:
45
+ # # index_maxi = idx
46
+ # #
47
+ # # direction_coordinates.insert(index_maxi + 1, midmid2)
48
+ # # direction_coordinates.insert(index_mini + 1, midmid1)
49
+ # # else:
50
+ # # direction_coordinates.append(midmid2)
51
+ # # direction_coordinates.append(midmid1)
52
+ # #
53
+ # # return direction_coordinates, midmid1, midmid2
54
+ # #
55
+ # #
56
+ # # def get_directional_coordinates_by_angle(coordinates, direction, minimum, maximum):
57
+ # # direction_coordinates = []
58
+ # # for p in coordinates:
59
+ # # if direction in east:
60
+ # # if p[2] >= minimum or p[2] <= maximum:
61
+ # # direction_coordinates.append(p)
62
+ # #
63
+ # # else:
64
+ # # if p[2] >= minimum and p[2] <= maximum:
65
+ # # direction_coordinates.append(p)
66
+ # # return direction_coordinates
67
+ # #
68
+ # #
69
+ # # def get_directional_coordinates_by_angle(coordinates, direction, minimum, maximum):
70
+ # # direction_coordinates = []
71
+ # # for p in coordinates:
72
+ # # if direction in east:
73
+ # # if p[2] >= minimum or p[2] <= maximum:
74
+ # # direction_coordinates.append(p)
75
+ # #
76
+ # # else:
77
+ # # if p[2] >= minimum and p[2] <= maximum:
78
+ # # direction_coordinates.append(p)
79
+ # # return direction_coordinates
80
+ # #
81
+ # #
82
+ # # def get_central(coordinates, centroid, direction, is_midmid):
83
+ # # n_min_max = get_min_max("north")
84
+ # # n_coordinates = get_directional_coordinates_by_angle(coordinates, "north", n_min_max[0], n_min_max[1])
85
+ # # n_mid1, n_mid2 = geoutil.get_midmid_point(centroid, n_coordinates[0], n_coordinates[-1], is_midmid)
86
+ # #
87
+ # # ne_min_max = get_min_max("north east")
88
+ # # ne_coordinates = get_directional_coordinates_by_angle(coordinates, "north east", ne_min_max[0], ne_min_max[1])
89
+ # # ne_mid1, ne_mid2 = geoutil.get_midmid_point(centroid, ne_coordinates[0], ne_coordinates[-1], is_midmid)
90
+ # #
91
+ # # e_min_max = get_min_max("east")
92
+ # # e_coordinates = get_directional_coordinates_by_angle(coordinates, "east", e_min_max[0], e_min_max[1])
93
+ # # e_mid1, e_mid2 = geoutil.get_midmid_point(centroid, e_coordinates[0], e_coordinates[-1], is_midmid)
94
+ # #
95
+ # # se_min_max = get_min_max("south east")
96
+ # # se_coordinates = get_directional_coordinates_by_angle(coordinates, "south east", se_min_max[0], se_min_max[1])
97
+ # # se_mid1, se_mid2 = geoutil.get_midmid_point(centroid, se_coordinates[0], se_coordinates[-1], is_midmid)
98
+ # #
99
+ # # s_min_max = get_min_max("south")
100
+ # # s_coordinates = get_directional_coordinates_by_angle(coordinates, "south", s_min_max[0], s_min_max[1])
101
+ # # s_mid1, s_mid2 = geoutil.get_midmid_point(centroid, s_coordinates[0], s_coordinates[-1], is_midmid)
102
+ # #
103
+ # # sw_min_max = get_min_max("south west")
104
+ # # sw_coordinates = get_directional_coordinates_by_angle(coordinates, "south west", sw_min_max[0], sw_min_max[1])
105
+ # # sw_mid1, sw_mid2 = geoutil.get_midmid_point(centroid, sw_coordinates[0], sw_coordinates[-1], is_midmid)
106
+ # #
107
+ # # w_min_max = get_min_max("west")
108
+ # # w_coordinates = get_directional_coordinates_by_angle(coordinates, "west", w_min_max[0], w_min_max[1])
109
+ # # w_mid1, w_mid2 = geoutil.get_midmid_point(centroid, w_coordinates[0], w_coordinates[-1], is_midmid)
110
+ # #
111
+ # # nw_min_max = get_min_max("north west")
112
+ # # nw_coordinates = get_directional_coordinates_by_angle(coordinates, "north west", nw_min_max[0], nw_min_max[1])
113
+ # # nw_mid1, nw_mid2 = geoutil.get_midmid_point(centroid, nw_coordinates[0], nw_coordinates[-1], is_midmid)
114
+ # #
115
+ # # central_coordindates = [e_mid1, e_mid2, ne_mid1, ne_mid2, n_mid1, n_mid2,
116
+ # # nw_mid1, nw_mid2, w_mid1, w_mid2, sw_mid1, sw_mid2,
117
+ # # s_mid1, s_mid2, se_mid1, se_mid2]
118
+ # # return central_coordindates
119
+ # #
120
+ # #
121
+ # # def get_min_max(direction):
122
+ # # regex = regex_spatial.get_directional_regex()
123
+ # # direction_list = regex.split("|")
124
+ # # if direction in direction_list:
125
+ # # if direction in east:
126
+ # # return (337, 22)
127
+ # # if direction in northeast:
128
+ # # return (22, 67)
129
+ # # if direction in north:
130
+ # # return (67, 112)
131
+ # # if direction in northwest:
132
+ # # return (112, 157)
133
+ # # if direction in west:
134
+ # # return (157, 202)
135
+ # # if direction in southwest:
136
+ # # return (202, 247)
137
+ # # if direction in south:
138
+ # # return (247, 292)
139
+ # # if direction in southeast:
140
+ # # return (292, 337)
141
+ # #
142
+ # # return None
143
+ # # def get_level1_coordinates(coordinates, centroid, direction, is_midmid):
144
+ # # min_max = get_min_max(direction)
145
+ # # if min_max is not None:
146
+ # # coordinates, mid1, mid2 = get_directional_coordinates(coordinates, direction, centroid, min_max[0], min_max[1], is_midmid)
147
+ # # return coordinates, centroid, mid1, mid2
148
+ # # elif direction.lower() in center:
149
+ # # return get_central(coordinates, centroid, direction, is_midmid), centroid, None, None
150
+ # # else:
151
+ # # return coordinates, centroid, None, None
152
+ # def to_standard_2d_list(data):
153
+ # arr = np.array(data)
154
+
155
+ # # 强制变成一维后 reshape,前提是元素总数是2的倍数
156
+ # flat = arr.flatten()
157
+ # if flat.size % 2 != 0:
158
+ # raise ValueError("元素个数不是2的倍数,不能 reshape 成 [N, 2] 格式")
159
+
160
+ # return flat.reshape(-1, 2).tolist()
161
+
162
+
163
+ # def get_geojson(ent, arr, centroid):
164
+ # poly_json = {}
165
+ # poly_json['type'] = 'FeatureCollection'
166
+ # poly_json['features'] = []
167
+ # coordinates= []
168
+ # coordinates.append(arr)
169
+ # poly_json['features'].append({
170
+ # 'type':'Feature',
171
+ # 'id': ent,
172
+ # 'properties': {
173
+ # 'centroid': centroid
174
+ # },
175
+ # 'geometry': {
176
+ # 'type':'Polygon',
177
+ # 'coordinates': coordinates
178
+ # }
179
+ # })
180
+ # return poly_json
181
+
182
+
183
+ # def get_coordinates(ent):
184
+ # request_url = 'https://nominatim.openstreetmap.org/search.php?q= ' +ent +'&polygon_geojson=1&accept-language=en&format=jsonv2'
185
+ # headers = {
186
+ # "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15"
187
+ # }
188
+ # page = requests.get(request_url, headers=headers, verify=False)
189
+ # json_content = json.loads(page.content)
190
+ # all_coordinates = json_content[0]['geojson']['coordinates'][0]
191
+ # centroid = (float(json_content[0]['lon']), float(json_content[0]['lat']))
192
+ # for p in all_coordinates:
193
+ # p2 = (p[0], p[1])
194
+ # angle = geoutil.calculate_bearing(centroid, p2)
195
+ # p.append(angle)
196
+
197
+ # geojson = get_geojson(ent, all_coordinates, centroid)
198
+
199
+ # return geojson['features'][0]['geometry']['coordinates'][0], geojson['features'][0]['properties']['centroid']
200
+
201
+ # def get_coordinates(location):
202
+ # request_url = f'https://nominatim.openstreetmap.org/search.php?q={location}&polygon_geojson=1&accept-language=en&format=jsonv2'
203
+
204
+ # print(request_url)
205
+ # headers = {"User-Agent": "Mozilla/5.0"}
206
+ # response = requests.get(request_url, headers=headers, verify=False)
207
+ # json_content = json.loads(response.content)
208
+ # # print(json_content)
209
+ # if json_content[0]['geojson']['type'] == 'Polygon':
210
+ # coordinates = json_content[0]['geojson']['coordinates'][0]
211
+ # elif json_content[0]['geojson']['type'] == 'Point':
212
+ # coordinates = json_content[0]['geojson']['coordinates']
213
  # else:
214
+ # print(json_content[0]['geojson']['type'])
215
+ # centroid = (float(json_content[0]['lon']), float(json_content[0]['lat']))
216
+ # return (coordinates, centroid)
217
+
218
+
219
+ # # level3
220
+ # def get_directional_coordinates_by_angle(coordinates, centroid, direction, minimum, maximum):
221
+ # # minimum = 157
222
+ # # maximum = 202
223
+
 
 
 
 
 
 
 
 
 
 
224
  # direction_coordinates = []
225
  # for p in coordinates:
226
+ # angle = geoutil.calculate_bearing(centroid, p)
227
+ # p2 = (p[0], p[1], angle)
228
+ # if direction in geo_level1.east:
229
+ # if angle >= minimum or angle <= maximum:
230
+ # direction_coordinates.append(p2)
231
+
232
  # else:
233
+ # if angle >= minimum and angle <= maximum:
234
+ # direction_coordinates.append(p2)
235
+ # # print(type(direction_coordinates[0]))
236
+ # # if(direction in geo_level1.west):
237
+ # # direction_coordinates.sort(key=lambda k: k[2], reverse=True)
238
+
239
  # return direction_coordinates
240
+ # def get_level3(level3):
241
+ # digits = re.findall('[0-9]+', level3)[0]
242
+ # unit = re.findall('[A-Za-z]+', level3)[0]
243
+ # return digits, unit
244
+
245
+ # def get_direction_coordinates(coordinates, centroid, level1):
246
+ # min_max = geo_level1.get_min_max(level1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  # if min_max is not None:
248
+ # coord = get_directional_coordinates_by_angle(coordinates, centroid, level1, min_max[0], min_max[1])
249
+ # return coord
250
+ # return coordinates
251
+ # def sort_west(poly1, poly2, centroid):
252
+ # coords1 = mapping(poly1)["features"][0]["geometry"]["coordinates"]
253
+ # coords2 = mapping(poly2)["features"][0]["geometry"]["coordinates"]
254
+ # coord1 = []
255
+ # coord2 = []
256
+ # coord = []
257
+ # for c in coords1:
258
+ # pol = list(c[::-1])
259
+ # coord1.extend(pol)
260
+ # for c in coords2:
261
+ # pol = list(c[::-1])
262
+ # coord2.extend(pol)
263
+ # coo1 = []
264
+ # coo2 = []
265
+ # for p in coord1:
266
+ # angle = geoutil.calculate_bearing(centroid, p)
267
+ # if angle >= 157 and angle <= 202:
268
+ # coo1.append((p[0], p[1], angle))
269
+ # for p in coord2:
270
+ # angle = geoutil.calculate_bearing(centroid, p)
271
+ # if angle >= 157 and angle <= 202:
272
+ # coo2.append((p[0], p[1], angle))
273
+ # coo1.extend(coo2)
274
+ # return coo1
275
+
276
+
277
+ # def get_level3_coordinates(coordinates, level_3, level1):
278
+ # distance, unit = get_level3(level_3)
279
+ # kms = geoutil.get_kilometers(distance, unit)
280
+ # coord = []
281
+
282
+ # coords0, center = coordinates
283
+
284
+ # if not isinstance(coords0, list) or len(coords0) < 3:
285
+
286
+ # # 从原始点出发,根据方向移动距离 kms 得到新圆心
287
+ # lat_km = 111.32
288
+ # lon_km = 111.32 * np.cos(np.radians(center[1]))
289
+
290
+ # dx = dy = 0
291
+
292
+ # if level1 is not None:
293
+ # if level1 in geo_level1.east:
294
+ # dx = kms / lon_km
295
+ # elif level1 in geo_level1.west:
296
+ # dx = -kms / lon_km
297
+ # elif level1 in geo_level1.north:
298
+ # dy = kms / lat_km
299
+ # elif level1 in geo_level1.south:
300
+ # dy = -kms / lat_km
301
+ # # 你也可以支持 northeast、southwest 等复合方向
302
+
303
+ # new_center = (center[0] + dx, center[1] + dy)
304
+
305
+ # # 用固定半径画个圆(例如半径2km)
306
+ # r_km = 1 # 半径设为1km,你也可以设为其他值
307
+
308
+ # circle_points = []
309
+ # for theta in np.linspace(0, 360, num=100):
310
+ # theta_rad = np.radians(theta)
311
+ # d_lat = (np.sin(theta_rad) * r_km) / lat_km
312
+ # d_lon = (np.cos(theta_rad) * r_km) / lon_km
313
+ # circle_points.append((new_center[0] + d_lon, new_center[1] + d_lat))
314
+
315
+ # # 输出中心(使用新圆心)
316
+ # if circle_points:
317
+ # center_point = MultiPoint(circle_points).centroid
318
+ # center = (center_point.x, center_point.y)
319
+ # else:
320
+ # center = new_center
321
+
322
+ # return circle_points, center
323
+
324
+ # # 正常 polygon 流程
325
+ # poly1 = Polygon(coords0)
326
+ # polygon1 = gpd.GeoSeries(poly1)
327
+
328
+ # # 生成环形区域
329
+ # poly2 = polygon1.buffer(0.0095 * kms, join_style=2)
330
+ # poly3 = polygon1.buffer(0.013 * kms, join_style=2)
331
+ # poly = poly3.difference(poly2)
332
+
333
+ # # 获取坐标
334
+ # coords = mapping(poly)["features"][0]["geometry"]["coordinates"]
335
+ # for c in coords:
336
+ # pol = list(c[::-1])
337
+ # coord.extend(pol)
338
+
339
+ # # 方向裁剪
340
+ # if level1 is not None:
341
+ # coord = get_direction_coordinates(coord, coordinates[1], level1)
342
+ # if level1 in geo_level1.west:
343
+ # coord = sort_west(poly3, poly2, coordinates[1])
344
+
345
+ # # 计算质心
346
+ # if coord:
347
+ # center_point = MultiPoint(coord).centroid
348
+ # center = (center_point.x, center_point.y)
349
  # else:
350
+ # center = coordinates[1]
351
+
352
+ # return coord, center
353
+ # # level 3 end
354
+
355
+ # # between
356
+ # def get_between_coordinates(coordinates1, coordinates2):
357
+ # """
358
+ # 计算两个区域之间的中间点,并生成一个等面积的圆形区域。
359
+ # 如果某个输入仅为点(坐标长度 < 3),则其面积设为 0;
360
+ # 如果两个输入都是点,则默认半径为 2km。
361
+ # :param coordinates1: 第一个区域的边界坐标和中心点
362
+ # :param coordinates2: 第二个区域的边界坐标和中心点
363
+ # :return: 圆形区域的坐标集和圆心
364
+ # """
365
+
366
+ # def is_valid_polygon(coords):
367
+ # return isinstance(coords, list) and len(coords) >= 3
368
+
369
+ # coords1, center1 = coordinates1
370
+ # coords2, center2 = coordinates2
371
+
372
+ # # 判断输入是否为合法多边形(>=3个点)
373
+ # if is_valid_polygon(coords1):
374
+ # poly1 = Polygon(coords1)
375
+ # area1 = poly1.area
376
+ # else:
377
+ # area1 = 0
378
+
379
+ # if is_valid_polygon(coords2):
380
+ # poly2 = Polygon(coords2)
381
+ # area2 = poly2.area
382
+ # else:
383
+ # area2 = 0
384
+
385
+ # # 计算中心点(两个中心的中点)
386
+ # midpoint = (
387
+ # (center1[0] + center2[0]) / 2,
388
+ # (center1[1] + center2[1]) / 2
389
+ # )
390
+
391
+ # # 如果两个区域都是点,则使用默认半径 2km
392
+ # if area1 == 0 and area2 == 0:
393
+ # r_km = 2
394
+ # else:
395
+ # avg_area = (area1 + area2) / 2
396
+ # r_km = np.sqrt(avg_area / np.pi) * 111.32 # 近似 km 半径
397
+
398
+ # # 经纬度距离换算因子
399
+ # lat_km = 111.32
400
+ # lon_km = 111.32 * np.cos(np.radians(midpoint[1]))
401
+
402
+ # # 生成圆形区域坐标(100个点)
403
+ # circle_points = []
404
+ # for theta in np.linspace(0, 360, num=100):
405
+ # theta_rad = np.radians(theta)
406
+ # d_lat = (np.sin(theta_rad) * r_km) / lat_km
407
+ # d_lon = (np.cos(theta_rad) * r_km) / lon_km
408
+ # circle_points.append((midpoint[0] + d_lon, midpoint[1] + d_lat))
409
+
410
+ # return circle_points, midpoint
411
+ # # between end
412
+
413
+
414
+ # def llmapi(text):
415
+ # system_prompt = (
416
+ # "你是一个资深的地理学家,你的任务是通过给定的一段自然语言,来选择正确的定位函数顺序以及他们的输入。\n"
417
+ # "你能选择的定位函数有:\n"
418
+ # "1. 相对定位(Relative Positioning):输入为地点坐标,方位,距离。输出为距离‘距离’输入的地点坐标的‘方位’的坐标。\n"
419
+ # "2. 中间定位(Between Positioning):输入为两个地点的坐标,输出为两个地点坐标的中点。\n"
420
+ # "请先进行思维链(CoT)推理,并最终用 JSON 格式输出你的答案,用 `<<<JSON>>>` 和 `<<<END>>>` 包裹起来。\n"
421
+ # "请确保所有输入仅包含:地点名称(字符串)、索引(整数)、方位(字符串,必须是英文)或距离(字符串,带单位),不允许返回诸如 'Chatswood 南4 km的坐标' 这样的内容。\n"
422
+ # "每个步骤编号都有 id 记录,然后如果某个输入是之前步骤的输出,那么输入对应步骤的 id。\n"
423
+ # "所有方向必须使用英文(如 south, west, northeast, etc.)。\n"
424
+ # "示例输出:\n"
425
+ # "<<<JSON>>>\n"
426
+ # "[{\"id\": 1, \"function\": \"Relative\", \"inputs\": [\"Chatswood\", \"south\", \"4 km\"]},"
427
+ # "{\"id\": 2, \"function\": \"Relative\", \"inputs\": [\"North Sydney\", \"west\", \"2 km\"]},"
428
+ # "{\"id\": 3, \"function\": \"Between\", \"inputs\": [1, 2]},"
429
+ # "{\"id\": 4, \"function\": \"Relative\", \"inputs\": [3, \"southwest\", \"5 km\"]}]\n"
430
+ # "<<<END>>>")
431
+
432
+ # messages = [
433
+ # {"role": "system", "content": system_prompt},
434
+ # {"role": "user", "content": text},
435
+ # ]
436
+
437
+ # chat_completion = client.chat.completions.create(
438
+ # messages=messages,
439
+ # model=model,
440
+ # )
441
+
442
+ # result = chat_completion.choices[0].message.content
443
+ # json_match = re.search(r'<<<JSON>>>\n(.*?)\n<<<END>>>', result, re.DOTALL)
444
+
445
+ # if json_match:
446
+ # # print(json.loads(json_match.group(1)))
447
+ # return json.loads(json_match.group(1))
448
+ # else:
449
+ # raise ValueError("LLM 输出未包含预期的 JSON 格式数据。")
450
+ # def llmapi(text):
451
+ # system_prompt = (
452
+ # "You are an experienced geographer. Your task is to determine the correct sequence of positioning functions and their inputs based on a given piece of natural language.\n"
453
+ # "The positioning functions you can choose from are:\n"
454
+ # "1. Relative Positioning: Inputs is (location coordinate or location name, direction, and distance). Outputs the coordinates that are in the given 'direction' and 'distance' from the input location.\n"
455
+ # "2. Between Positioning: Inputs is (location 1 coordinates or location 1 name, location 2 coordinates or location 2 name). Outputs the midpoint coordinate between the two locations.\n"
456
+ # "You can only use the given functions, and the inputs to the functions must obey the above properties. The given functions can be combined to solve complex situations."
457
+ # "First, perform chain-of-thought (CoT) reasoning, and finally output your answer in JSON format, wrapped between `<<<JSON>>>` and `<<<END>>>`.\n"
458
+ # "Make sure all inputs only include: location names (strings), step indices (integers), directions (strings, must be in English), or distances (strings with units). Do not return expressions like 'the coordinate 4 km south of Chatswood'.\n"
459
+ # "Each step must have an 'id'. If the input of a step is the output of a previous step, use that step’s 'id' as the input.\n"
460
+ # "All directions must be in English (e.g., south, west, northeast, etc.).\n"
461
+ # "Example output:\n"
462
+ # "<<<JSON>>>\n"
463
+ # "[{\"id\": 1, \"function\": \"Relative\", \"inputs\": [\"Chatswood\", \"south\", \"4 km\"]},"
464
+ # "{\"id\": 2, \"function\": \"Relative\", \"inputs\": [\"North Sydney\", \"west\", \"2 km\"]},"
465
+ # "{\"id\": 3, \"function\": \"Between\", \"inputs\": [1, 2]},"
466
+ # "{\"id\": 4, \"function\": \"Relative\", \"inputs\": [3, \"southwest\", \"5 km\"]}]\n"
467
+ # "<<<END>>>")
468
+
469
+ # messages = [
470
+ # {"role": "system", "content": system_prompt},
471
+ # {"role": "user", "content": text},
472
+ # ]
473
+
474
+ # chat_completion = client.chat.completions.create(
475
+ # messages=messages,
476
+ # model=model,
477
+ # )
478
+
479
+ # result = chat_completion.choices[0].message.content
480
+ # print(result)
481
+ # json_match = re.search(r'<<<JSON>>>\n(.*?)\n<<<END>>>', result, re.DOTALL)
482
+
483
+ # if json_match:
484
+ # return json.loads(json_match.group(1))
485
+ # else:
486
+ # raise ValueError("LLM 输出未包含预期的 JSON 格式数据。")
487
+
488
+
489
+
490
+
491
+
492
+ # def execute_steps(steps):
493
+ # data = {}
494
+
495
+ # for step in steps:
496
+ # step_id = step['id']
497
+ # function = step['function']
498
+ # inputs = step['inputs']
499
+ # # print('-' * 50)
500
+ # # print(function)
501
+ # # print(inputs)
502
+
503
+
504
+ # resolved_inputs = []
505
+ # for inp in inputs:
506
+ # if isinstance(inp, int):
507
+ # resolved_inputs.append(data[inp])
508
+ # else:
509
+ # resolved_inputs.append(inp)
510
+ # if function == "Relative":
511
+ # location, direction, distance = resolved_inputs
512
+ # if isinstance(location, str):
513
+ # location = get_coordinates(location)
514
+
515
+ # location = [to_standard_2d_list(location[0])] + list(location[1:])
516
+ # location = [[[151.214901,-33.859175]], (151.214901,-33.859175)]
517
+ # result = get_level3_coordinates(location, distance, direction)
518
+ # data[step_id] = result
519
+
520
+ # elif function == "Between":
521
+
522
+
523
+ # location1, location2 = resolved_inputs
524
+ # # print(location1)
525
+ # # print(111)
526
+ # # print(location2)
527
+ # if isinstance(location1, str):
528
+ # location1 = get_coordinates(location1)
529
+
530
+ # location1 = [to_standard_2d_list(location1[0])] + list(location1[1:])
531
+ # if isinstance(location2, str):
532
+
533
+ # location2 = get_coordinates(location2)
534
+ # location2 = [to_standard_2d_list(location2[0])] + list(location2[1:])
535
+ # result = get_between_coordinates(location1, location2)
536
+
537
+ # data[step_id] = result
538
+
539
+ # return data
540
+
541
+
542
+
543
+ # if __name__ == '__main__':
544
+ # # a = get_coordinates('Burwood')
545
+ # # a2 = get_coordinates('Glebe')
546
+ # # b = get_level3_coordinates(a, '5 km', 'east')
547
+ # # c = get_between_coordinates(a, a2)
548
+
549
+ # # 完整通道
550
+ # # 默认输入
551
+ # # default_input_text = "在Chatswood南边4公里与North Sydney 东边2公里的中间的西南5公里。"
552
+ # # default_input_text = "你是一位规划师,正在为华盛顿州的一项新森林监测站选址。两个潜在的参考位置分别是雷尼尔山国家公园(Mount Rainier National Park)和北喀斯喀特国家公园(North Cascades National Park)。首先,你想在这两个国家公园之间找到一个中间点。接着,你希望在这个中间点与北喀斯喀特国家公园之间,再取一个中间位置,以便确定最终的建设候选地。"
553
+ # # default_input_text = "在Chatswood和North Sydney的中间靠近North Sydney的四分之一位置"
554
+ # # default_input_text = "Plan a trip that involves determining the midpoint between Paris and London, and then finding another midpoint between this location and Paris to identify potential stopovers during travel."
555
+ # # default_input_text = "5km southwest of Chatswood, 4km south of Chatswood and 2km north of North Sydney."
556
+
557
+
558
+
559
+ # # 解析 LLM 结果
560
+ # # parsed_steps = llmapi(default_input_text)
561
+ # # parsed_steps = [{'id': 1, 'function': 'Relative', 'inputs': ['Chatswood', 'south', '4 km']}, {'id': 2, 'function': 'Relative', 'inputs': ['North Sydney', 'east', '2 km']}, {'id': 3, 'function': 'Between', 'inputs': [1, 2]}, {'id': 4, 'function': 'Relative', 'inputs': [3, 'south west', '5 km']}]
562
+ # # parsed_steps = [{"id": 1, "function": "Between", "inputs": ["Chatswood", "North Sydney"]},{"id": 2, "function": "Between", "inputs": [1, "North Sydney"]}]
563
+ # # parsed_steps = [{"id": 1, "function": "Relative", "inputs": ["Katoomba", "southeast", "3 km"]}, {"id": 2, "function": "Between", "inputs": [1, "Echo Point"]}]
564
+ # # parsed_steps = [{'id': 1, 'function': 'Relative', 'inputs': ['Scafell Pike', 'east', '9 km']}]
565
+ # # parsed_steps = [{'id': 1, 'function': 'Relative', 'inputs': ['Colosseum', 'northeast', '8 km']}, {'id': 2, 'function': 'Relative', 'inputs': [1, 'northeast', '2 km']}]
566
+ # parsed_steps = [
567
+ # {"id": 1, "function": "Between", "inputs": ["Statue of Liberty", "Eiffel Tower"]},
568
+ # {"id": 2, "function": "Relative", "inputs": [1, "west", "8 km"]}
569
+ # ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
 
571
+ # # 执行步骤
572
+ # result = execute_steps(parsed_steps)
573
+ # # 输出最终计算结果
574
+ # print(result)
575
+ # print('-' * 100)
576
+ # print(result[(max(result.keys()))][0])
577
+ # # 通道结束
578
 
579
+ # # location = get_coordinates('Chatswood')
580
+ # # result = get_level3_coordinates(location, '4 km', 'north west')
581
+ # # print(result)
582
 
583