@cablate/mcp-google-map 0.0.4 → 0.0.6
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.
- package/README.md +353 -113
- package/dist/chunk-U2CGP7BJ.js +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +3 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +1 -197
- package/package.json +73 -47
- package/LICENSE +0 -21
- package/dist/index.cjs +0 -24181
- package/dist/maps-tools/mapsTools.js +0 -167
- package/dist/maps-tools/searchPlaces.js +0 -144
- package/dist/maps-tools/toolclass.js +0 -248
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
export const SEARCH_NEARBY_TOOL = {
|
|
2
|
-
name: "search_nearby",
|
|
3
|
-
description: "搜尋附近的地點",
|
|
4
|
-
inputSchema: {
|
|
5
|
-
type: "object",
|
|
6
|
-
properties: {
|
|
7
|
-
center: {
|
|
8
|
-
type: "object",
|
|
9
|
-
properties: {
|
|
10
|
-
value: { type: "string", description: "地址、地標名稱或經緯度座標(經緯度座標格式: lat,lng)" },
|
|
11
|
-
isCoordinates: { type: "boolean", description: "是否為經緯度座標", default: false },
|
|
12
|
-
},
|
|
13
|
-
required: ["value"],
|
|
14
|
-
description: "搜尋中心點",
|
|
15
|
-
},
|
|
16
|
-
keyword: {
|
|
17
|
-
type: "string",
|
|
18
|
-
description: "搜尋關鍵字(例如:餐廳、咖啡廳)",
|
|
19
|
-
},
|
|
20
|
-
radius: {
|
|
21
|
-
type: "number",
|
|
22
|
-
description: "搜尋半徑(公尺)",
|
|
23
|
-
default: 1000,
|
|
24
|
-
},
|
|
25
|
-
openNow: {
|
|
26
|
-
type: "boolean",
|
|
27
|
-
description: "是否只顯示營業中的地點",
|
|
28
|
-
default: false,
|
|
29
|
-
},
|
|
30
|
-
minRating: {
|
|
31
|
-
type: "number",
|
|
32
|
-
description: "最低評分要求(0-5)",
|
|
33
|
-
minimum: 0,
|
|
34
|
-
maximum: 5,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
required: ["center"],
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
export const GEOCODE_TOOL = {
|
|
41
|
-
name: "maps_geocode",
|
|
42
|
-
description: "將地址轉換為座標",
|
|
43
|
-
inputSchema: {
|
|
44
|
-
type: "object",
|
|
45
|
-
properties: {
|
|
46
|
-
address: {
|
|
47
|
-
type: "string",
|
|
48
|
-
description: "要轉換的地址或地標名稱",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
required: ["address"],
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
export const REVERSE_GEOCODE_TOOL = {
|
|
55
|
-
name: "maps_reverse_geocode",
|
|
56
|
-
description: "將座標轉換為地址",
|
|
57
|
-
inputSchema: {
|
|
58
|
-
type: "object",
|
|
59
|
-
properties: {
|
|
60
|
-
latitude: {
|
|
61
|
-
type: "number",
|
|
62
|
-
description: "緯度",
|
|
63
|
-
},
|
|
64
|
-
longitude: {
|
|
65
|
-
type: "number",
|
|
66
|
-
description: "經度",
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
required: ["latitude", "longitude"],
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
export const DISTANCE_MATRIX_TOOL = {
|
|
73
|
-
name: "maps_distance_matrix",
|
|
74
|
-
description: "計算多個起點和終點之間的距離和時間",
|
|
75
|
-
inputSchema: {
|
|
76
|
-
type: "object",
|
|
77
|
-
properties: {
|
|
78
|
-
origins: {
|
|
79
|
-
type: "array",
|
|
80
|
-
items: {
|
|
81
|
-
type: "string",
|
|
82
|
-
},
|
|
83
|
-
description: "起點地址或座標列表",
|
|
84
|
-
},
|
|
85
|
-
destinations: {
|
|
86
|
-
type: "array",
|
|
87
|
-
items: {
|
|
88
|
-
type: "string",
|
|
89
|
-
},
|
|
90
|
-
description: "終點地址或座標列表",
|
|
91
|
-
},
|
|
92
|
-
mode: {
|
|
93
|
-
type: "string",
|
|
94
|
-
enum: ["driving", "walking", "bicycling", "transit"],
|
|
95
|
-
description: "交通模式",
|
|
96
|
-
default: "driving",
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
required: ["origins", "destinations"],
|
|
100
|
-
},
|
|
101
|
-
};
|
|
102
|
-
export const DIRECTIONS_TOOL = {
|
|
103
|
-
name: "maps_directions",
|
|
104
|
-
description: "獲取兩點之間的路線指引",
|
|
105
|
-
inputSchema: {
|
|
106
|
-
type: "object",
|
|
107
|
-
properties: {
|
|
108
|
-
origin: {
|
|
109
|
-
type: "string",
|
|
110
|
-
description: "起點地址或座標",
|
|
111
|
-
},
|
|
112
|
-
destination: {
|
|
113
|
-
type: "string",
|
|
114
|
-
description: "終點地址或座標",
|
|
115
|
-
},
|
|
116
|
-
mode: {
|
|
117
|
-
type: "string",
|
|
118
|
-
enum: ["driving", "walking", "bicycling", "transit"],
|
|
119
|
-
description: "交通模式",
|
|
120
|
-
default: "driving",
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
required: ["origin", "destination"],
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
export const ELEVATION_TOOL = {
|
|
127
|
-
name: "maps_elevation",
|
|
128
|
-
description: "獲取位置的海拔數據",
|
|
129
|
-
inputSchema: {
|
|
130
|
-
type: "object",
|
|
131
|
-
properties: {
|
|
132
|
-
locations: {
|
|
133
|
-
type: "array",
|
|
134
|
-
items: {
|
|
135
|
-
type: "object",
|
|
136
|
-
properties: {
|
|
137
|
-
latitude: {
|
|
138
|
-
type: "number",
|
|
139
|
-
description: "緯度",
|
|
140
|
-
},
|
|
141
|
-
longitude: {
|
|
142
|
-
type: "number",
|
|
143
|
-
description: "經度",
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
required: ["latitude", "longitude"],
|
|
147
|
-
},
|
|
148
|
-
description: "要獲取海拔數據的位置列表",
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
required: ["locations"],
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
export const GET_PLACE_DETAILS_TOOL = {
|
|
155
|
-
name: "get_place_details",
|
|
156
|
-
description: "獲取特定地點的詳細資訊",
|
|
157
|
-
inputSchema: {
|
|
158
|
-
type: "object",
|
|
159
|
-
properties: {
|
|
160
|
-
placeId: {
|
|
161
|
-
type: "string",
|
|
162
|
-
description: "Google Maps 地點 ID",
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
required: ["placeId"],
|
|
166
|
-
},
|
|
167
|
-
};
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { GoogleMapsTools } from "./toolclass.js";
|
|
2
|
-
export class PlacesSearcher {
|
|
3
|
-
constructor() {
|
|
4
|
-
this.mapsTools = new GoogleMapsTools();
|
|
5
|
-
}
|
|
6
|
-
async searchNearby(params) {
|
|
7
|
-
try {
|
|
8
|
-
const location = await this.mapsTools.getLocation(params.center);
|
|
9
|
-
console.error(location);
|
|
10
|
-
const places = await this.mapsTools.searchNearbyPlaces({
|
|
11
|
-
location,
|
|
12
|
-
keyword: params.keyword,
|
|
13
|
-
radius: params.radius,
|
|
14
|
-
openNow: params.openNow,
|
|
15
|
-
minRating: params.minRating,
|
|
16
|
-
});
|
|
17
|
-
return {
|
|
18
|
-
location: location,
|
|
19
|
-
success: true,
|
|
20
|
-
data: places.map((place) => ({
|
|
21
|
-
name: place.name,
|
|
22
|
-
place_id: place.place_id,
|
|
23
|
-
address: place.formatted_address,
|
|
24
|
-
location: place.geometry.location,
|
|
25
|
-
rating: place.rating,
|
|
26
|
-
total_ratings: place.user_ratings_total,
|
|
27
|
-
open_now: place.opening_hours?.open_now,
|
|
28
|
-
})),
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
return {
|
|
33
|
-
success: false,
|
|
34
|
-
error: error instanceof Error ? error.message : "搜尋時發生錯誤",
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async getPlaceDetails(placeId) {
|
|
39
|
-
try {
|
|
40
|
-
const details = await this.mapsTools.getPlaceDetails(placeId);
|
|
41
|
-
return {
|
|
42
|
-
success: true,
|
|
43
|
-
data: {
|
|
44
|
-
name: details.name,
|
|
45
|
-
address: details.formatted_address,
|
|
46
|
-
location: details.geometry?.location,
|
|
47
|
-
rating: details.rating,
|
|
48
|
-
total_ratings: details.user_ratings_total,
|
|
49
|
-
open_now: details.opening_hours?.open_now,
|
|
50
|
-
phone: details.formatted_phone_number,
|
|
51
|
-
website: details.website,
|
|
52
|
-
price_level: details.price_level,
|
|
53
|
-
reviews: details.reviews?.map((review) => ({
|
|
54
|
-
rating: review.rating,
|
|
55
|
-
text: review.text,
|
|
56
|
-
time: review.time,
|
|
57
|
-
author_name: review.author_name,
|
|
58
|
-
})),
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
return {
|
|
64
|
-
success: false,
|
|
65
|
-
error: error instanceof Error ? error.message : "獲取詳細資訊時發生錯誤",
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
async geocode(address) {
|
|
70
|
-
try {
|
|
71
|
-
const result = await this.mapsTools.geocode(address);
|
|
72
|
-
return {
|
|
73
|
-
success: true,
|
|
74
|
-
data: result,
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
error: error instanceof Error ? error.message : "地址轉換座標時發生錯誤",
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
async reverseGeocode(latitude, longitude) {
|
|
85
|
-
try {
|
|
86
|
-
const result = await this.mapsTools.reverseGeocode(latitude, longitude);
|
|
87
|
-
return {
|
|
88
|
-
success: true,
|
|
89
|
-
data: result,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
return {
|
|
94
|
-
success: false,
|
|
95
|
-
error: error instanceof Error ? error.message : "座標轉換地址時發生錯誤",
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
async calculateDistanceMatrix(origins, destinations, mode = "driving") {
|
|
100
|
-
try {
|
|
101
|
-
const result = await this.mapsTools.calculateDistanceMatrix(origins, destinations, mode);
|
|
102
|
-
return {
|
|
103
|
-
success: true,
|
|
104
|
-
data: result,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
return {
|
|
109
|
-
success: false,
|
|
110
|
-
error: error instanceof Error ? error.message : "計算距離矩陣時發生錯誤",
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
async getDirections(origin, destination, mode = "driving") {
|
|
115
|
-
try {
|
|
116
|
-
const result = await this.mapsTools.getDirections(origin, destination, mode);
|
|
117
|
-
return {
|
|
118
|
-
success: true,
|
|
119
|
-
data: result,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
catch (error) {
|
|
123
|
-
return {
|
|
124
|
-
success: false,
|
|
125
|
-
error: error instanceof Error ? error.message : "獲取路線指引時發生錯誤",
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
async getElevation(locations) {
|
|
130
|
-
try {
|
|
131
|
-
const result = await this.mapsTools.getElevation(locations);
|
|
132
|
-
return {
|
|
133
|
-
success: true,
|
|
134
|
-
data: result,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
return {
|
|
139
|
-
success: false,
|
|
140
|
-
error: error instanceof Error ? error.message : "獲取海拔數據時發生錯誤",
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
@@ -1,248 +0,0 @@
|
|
|
1
|
-
import { Client, Language } from "@googlemaps/google-maps-services-js";
|
|
2
|
-
import dotenv from "dotenv";
|
|
3
|
-
// 確保環境變數被載入
|
|
4
|
-
dotenv.config();
|
|
5
|
-
export class GoogleMapsTools {
|
|
6
|
-
constructor() {
|
|
7
|
-
this.defaultLanguage = Language.zh_TW;
|
|
8
|
-
this.client = new Client({});
|
|
9
|
-
if (!process.env.GOOGLE_MAPS_API_KEY) {
|
|
10
|
-
throw new Error("Google Maps API Key is required");
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
async searchNearbyPlaces(params) {
|
|
14
|
-
const searchParams = {
|
|
15
|
-
location: params.location,
|
|
16
|
-
radius: params.radius || 1000,
|
|
17
|
-
keyword: params.keyword,
|
|
18
|
-
opennow: params.openNow,
|
|
19
|
-
language: this.defaultLanguage,
|
|
20
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
21
|
-
};
|
|
22
|
-
try {
|
|
23
|
-
const response = await this.client.placesNearby({
|
|
24
|
-
params: searchParams,
|
|
25
|
-
});
|
|
26
|
-
let results = response.data.results;
|
|
27
|
-
// 如果有最低評分要求,進行過濾
|
|
28
|
-
if (params.minRating) {
|
|
29
|
-
results = results.filter((place) => (place.rating || 0) >= (params.minRating || 0));
|
|
30
|
-
}
|
|
31
|
-
return results;
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
console.error("Error in searchNearbyPlaces:", error);
|
|
35
|
-
throw new Error("搜尋附近地點時發生錯誤");
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
async getPlaceDetails(placeId) {
|
|
39
|
-
try {
|
|
40
|
-
const response = await this.client.placeDetails({
|
|
41
|
-
params: {
|
|
42
|
-
place_id: placeId,
|
|
43
|
-
fields: ["name", "rating", "formatted_address", "opening_hours", "reviews", "geometry", "formatted_phone_number", "website", "price_level", "photos"],
|
|
44
|
-
language: this.defaultLanguage,
|
|
45
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
return response.data.result;
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
console.error("Error in getPlaceDetails:", error);
|
|
52
|
-
throw new Error("獲取地點詳細資訊時發生錯誤");
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
async geocodeAddress(address) {
|
|
56
|
-
try {
|
|
57
|
-
const response = await this.client.geocode({
|
|
58
|
-
params: {
|
|
59
|
-
address: address,
|
|
60
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
61
|
-
language: this.defaultLanguage,
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
if (response.data.results.length === 0) {
|
|
65
|
-
throw new Error("找不到該地址的位置");
|
|
66
|
-
}
|
|
67
|
-
const result = response.data.results[0];
|
|
68
|
-
const location = result.geometry.location;
|
|
69
|
-
return {
|
|
70
|
-
lat: location.lat,
|
|
71
|
-
lng: location.lng,
|
|
72
|
-
formatted_address: result.formatted_address,
|
|
73
|
-
place_id: result.place_id,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
console.error("Error in geocodeAddress:", error);
|
|
78
|
-
throw new Error("地址轉換座標時發生錯誤");
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
parseCoordinates(coordString) {
|
|
82
|
-
const coords = coordString.split(",").map((c) => parseFloat(c.trim()));
|
|
83
|
-
if (coords.length !== 2 || isNaN(coords[0]) || isNaN(coords[1])) {
|
|
84
|
-
throw new Error("無效的座標格式,請使用「緯度,經度」格式");
|
|
85
|
-
}
|
|
86
|
-
return { lat: coords[0], lng: coords[1] };
|
|
87
|
-
}
|
|
88
|
-
async getLocation(center) {
|
|
89
|
-
if (center.isCoordinates) {
|
|
90
|
-
return this.parseCoordinates(center.value);
|
|
91
|
-
}
|
|
92
|
-
return this.geocodeAddress(center.value);
|
|
93
|
-
}
|
|
94
|
-
// 新增公開方法用於地址轉座標
|
|
95
|
-
async geocode(address) {
|
|
96
|
-
try {
|
|
97
|
-
const result = await this.geocodeAddress(address);
|
|
98
|
-
return {
|
|
99
|
-
location: { lat: result.lat, lng: result.lng },
|
|
100
|
-
formatted_address: result.formatted_address || "",
|
|
101
|
-
place_id: result.place_id || "",
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
console.error("Error in geocode:", error);
|
|
106
|
-
throw new Error("地址轉換座標時發生錯誤");
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
async reverseGeocode(latitude, longitude) {
|
|
110
|
-
try {
|
|
111
|
-
const response = await this.client.reverseGeocode({
|
|
112
|
-
params: {
|
|
113
|
-
latlng: { lat: latitude, lng: longitude },
|
|
114
|
-
language: this.defaultLanguage,
|
|
115
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
if (response.data.results.length === 0) {
|
|
119
|
-
throw new Error("找不到該座標的地址");
|
|
120
|
-
}
|
|
121
|
-
const result = response.data.results[0];
|
|
122
|
-
return {
|
|
123
|
-
formatted_address: result.formatted_address,
|
|
124
|
-
place_id: result.place_id,
|
|
125
|
-
address_components: result.address_components,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
console.error("Error in reverseGeocode:", error);
|
|
130
|
-
throw new Error("座標轉換地址時發生錯誤");
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
async calculateDistanceMatrix(origins, destinations, mode = "driving") {
|
|
134
|
-
try {
|
|
135
|
-
const response = await this.client.distancematrix({
|
|
136
|
-
params: {
|
|
137
|
-
origins: origins,
|
|
138
|
-
destinations: destinations,
|
|
139
|
-
mode: mode,
|
|
140
|
-
language: this.defaultLanguage,
|
|
141
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
const result = response.data;
|
|
145
|
-
if (result.status !== "OK") {
|
|
146
|
-
throw new Error(`距離矩陣計算失敗: ${result.status}`);
|
|
147
|
-
}
|
|
148
|
-
const distances = [];
|
|
149
|
-
const durations = [];
|
|
150
|
-
result.rows.forEach((row) => {
|
|
151
|
-
const distanceRow = [];
|
|
152
|
-
const durationRow = [];
|
|
153
|
-
row.elements.forEach((element) => {
|
|
154
|
-
if (element.status === "OK") {
|
|
155
|
-
distanceRow.push({
|
|
156
|
-
value: element.distance.value,
|
|
157
|
-
text: element.distance.text,
|
|
158
|
-
});
|
|
159
|
-
durationRow.push({
|
|
160
|
-
value: element.duration.value,
|
|
161
|
-
text: element.duration.text,
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
distanceRow.push(null);
|
|
166
|
-
durationRow.push(null);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
distances.push(distanceRow);
|
|
170
|
-
durations.push(durationRow);
|
|
171
|
-
});
|
|
172
|
-
return {
|
|
173
|
-
distances: distances,
|
|
174
|
-
durations: durations,
|
|
175
|
-
origin_addresses: result.origin_addresses,
|
|
176
|
-
destination_addresses: result.destination_addresses,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
console.error("Error in calculateDistanceMatrix:", error);
|
|
181
|
-
throw new Error("計算距離矩陣時發生錯誤");
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
async getDirections(origin, destination, mode = "driving") {
|
|
185
|
-
try {
|
|
186
|
-
const response = await this.client.directions({
|
|
187
|
-
params: {
|
|
188
|
-
origin: origin,
|
|
189
|
-
destination: destination,
|
|
190
|
-
mode: mode,
|
|
191
|
-
language: this.defaultLanguage,
|
|
192
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
const result = response.data;
|
|
196
|
-
if (result.status !== "OK") {
|
|
197
|
-
throw new Error(`路線指引獲取失敗: ${result.status}`);
|
|
198
|
-
}
|
|
199
|
-
if (result.routes.length === 0) {
|
|
200
|
-
throw new Error("找不到路線");
|
|
201
|
-
}
|
|
202
|
-
const route = result.routes[0];
|
|
203
|
-
const legs = route.legs[0];
|
|
204
|
-
return {
|
|
205
|
-
routes: result.routes,
|
|
206
|
-
summary: route.summary,
|
|
207
|
-
total_distance: {
|
|
208
|
-
value: legs.distance.value,
|
|
209
|
-
text: legs.distance.text,
|
|
210
|
-
},
|
|
211
|
-
total_duration: {
|
|
212
|
-
value: legs.duration.value,
|
|
213
|
-
text: legs.duration.text,
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
catch (error) {
|
|
218
|
-
console.error("Error in getDirections:", error);
|
|
219
|
-
throw new Error("獲取路線指引時發生錯誤");
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
async getElevation(locations) {
|
|
223
|
-
try {
|
|
224
|
-
const formattedLocations = locations.map((loc) => ({
|
|
225
|
-
lat: loc.latitude,
|
|
226
|
-
lng: loc.longitude,
|
|
227
|
-
}));
|
|
228
|
-
const response = await this.client.elevation({
|
|
229
|
-
params: {
|
|
230
|
-
locations: formattedLocations,
|
|
231
|
-
key: process.env.GOOGLE_MAPS_API_KEY || "",
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
const result = response.data;
|
|
235
|
-
if (result.status !== "OK") {
|
|
236
|
-
throw new Error(`海拔數據獲取失敗: ${result.status}`);
|
|
237
|
-
}
|
|
238
|
-
return result.results.map((item, index) => ({
|
|
239
|
-
elevation: item.elevation,
|
|
240
|
-
location: formattedLocations[index],
|
|
241
|
-
}));
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
console.error("Error in getElevation:", error);
|
|
245
|
-
throw new Error("獲取海拔數據時發生錯誤");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|