tbdavid2019 commited on
Commit
454ef0a
·
1 Parent(s): 9d3bc81

✨ 新增距離計算功能,優化搜尋結果排序及顯示距離資訊

Browse files
Files changed (2) hide show
  1. .DS_Store +0 -0
  2. index.html +65 -9
.DS_Store ADDED
Binary file (6.15 kB). View file
 
index.html CHANGED
@@ -239,6 +239,35 @@
239
  let activeInfoWindow = null;
240
  let userLocation = null;
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  function initMap() {
243
  const defaultLoc = { lat: 25.0330, lng: 121.5654 }; // 預設台北市中心
244
  map = new google.maps.Map(document.getElementById("map"), {
@@ -260,6 +289,10 @@
260
  new google.maps.Marker({ map, position: userLocation, icon: 'https://maps.google.com/mapfiles/ms/icons/blue-dot.png', title: '您的位置' });
261
  }, () => {
262
  console.log("使用者拒絕提供位置資訊。");
 
 
 
 
263
  });
264
  }
265
 
@@ -295,25 +328,36 @@
295
  const request = {
296
  query: query,
297
  location: userLocation || new google.maps.LatLng(25.0330, 121.5654),
298
- radius: 50000,
299
  fields: ['name', 'geometry', 'place_id', 'rating', 'user_ratings_total', 'vicinity', 'url']
300
  };
301
 
302
  placesService.textSearch(request, (results, status) => {
303
  loader.style.display = 'none';
304
 
305
- if (status === google.maps.places.PlacesServiceStatus.OK && results) {
 
 
 
 
 
 
 
 
 
306
  // 限制最多 10 個結果
307
- const limitedResults = results.slice(0, 10);
308
-
309
- // 設置地圖中心為第一個結果
310
- if (limitedResults.length > 0) {
 
 
311
  map.setCenter(limitedResults[0].geometry.location);
312
  map.setZoom(13);
313
  }
314
 
315
  // 為每個結果獲取詳細信息(包含評論)
316
- limitedResults.forEach((place, index) => {
317
  const detailsRequest = {
318
  placeId: place.place_id,
319
  fields: ['name', 'rating', 'user_ratings_total', 'vicinity', 'geometry', 'place_id', 'reviews', 'url']
@@ -321,6 +365,9 @@
321
 
322
  placesService.getDetails(detailsRequest, (placeDetails, detailsStatus) => {
323
  if (detailsStatus === google.maps.places.PlacesServiceStatus.OK) {
 
 
 
324
  createMarker(placeDetails);
325
  createCard(placeDetails);
326
  }
@@ -355,10 +402,15 @@
355
  reviewsHtml += '</ul></div>';
356
  }
357
 
 
 
 
 
358
  card.innerHTML = `
359
  <h4>${place.name}</h4>
360
  <p>⭐ ${place.rating || '無評分'} (${place.user_ratings_total || 0} 則評論)</p>
361
  <p>📍 <a href="${place.url}" target="_blank">${place.vicinity}</a></p>
 
362
  ${reviewsHtml}
363
  `;
364
 
@@ -380,12 +432,16 @@
380
  title: place.name
381
  });
382
 
 
 
 
 
383
  const infoWindowContent = `
384
  <div class="custom-infowindow">
385
  <span class="close-btn" onclick="this.parentElement.parentElement.style.display='none';">&times;</span>
386
  <strong>${place.name}</strong><br>
387
  ⭐ ${place.rating || 'N/A'} | 評論: ${place.user_ratings_total || 0}<br>
388
- <a href="${place.url}" target="_blank">在 Google 地圖上查看</a>
389
  </div>
390
  `;
391
 
@@ -478,4 +534,4 @@
478
  </script>
479
  <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB9zIkabAHoudMDAwbKom6URgjmUuejEpo&callback=initMap&libraries=places,marker&v=weekly"></script>
480
  </body>
481
- </html>
 
239
  let activeInfoWindow = null;
240
  let userLocation = null;
241
 
242
+ const EARTH_RADIUS_METERS = 6371000;
243
+
244
+ function toRadians(degrees) {
245
+ return degrees * (Math.PI / 180);
246
+ }
247
+
248
+ function calculateDistanceMeters(origin, destination) {
249
+ if (!origin || !destination) {
250
+ return Number.POSITIVE_INFINITY;
251
+ }
252
+
253
+ const lat1 = origin.lat;
254
+ const lng1 = origin.lng;
255
+ const lat2 = typeof destination.lat === 'function' ? destination.lat() : destination.lat;
256
+ const lng2 = typeof destination.lng === 'function' ? destination.lng() : destination.lng;
257
+
258
+ if ([lat1, lng1, lat2, lng2].some(value => typeof value !== 'number')) {
259
+ return Number.POSITIVE_INFINITY;
260
+ }
261
+
262
+ const dLat = toRadians(lat2 - lat1);
263
+ const dLng = toRadians(lng2 - lng1);
264
+ const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
265
+ Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) *
266
+ Math.sin(dLng / 2) * Math.sin(dLng / 2);
267
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
268
+ return EARTH_RADIUS_METERS * c;
269
+ }
270
+
271
  function initMap() {
272
  const defaultLoc = { lat: 25.0330, lng: 121.5654 }; // 預設台北市中心
273
  map = new google.maps.Map(document.getElementById("map"), {
 
289
  new google.maps.Marker({ map, position: userLocation, icon: 'https://maps.google.com/mapfiles/ms/icons/blue-dot.png', title: '您的位置' });
290
  }, () => {
291
  console.log("使用者拒絕提供位置資訊。");
292
+ }, {
293
+ enableHighAccuracy: true,
294
+ timeout: 10000,
295
+ maximumAge: 300000
296
  });
297
  }
298
 
 
328
  const request = {
329
  query: query,
330
  location: userLocation || new google.maps.LatLng(25.0330, 121.5654),
331
+ radius: 40000,
332
  fields: ['name', 'geometry', 'place_id', 'rating', 'user_ratings_total', 'vicinity', 'url']
333
  };
334
 
335
  placesService.textSearch(request, (results, status) => {
336
  loader.style.display = 'none';
337
 
338
+ if (status === google.maps.places.PlacesServiceStatus.OK && results && results.length) {
339
+ let processedResults = results.slice();
340
+
341
+ if (userLocation) {
342
+ processedResults.forEach(place => {
343
+ place.distanceFromUser = calculateDistanceMeters(userLocation, place.geometry.location);
344
+ });
345
+ processedResults.sort((a, b) => (a.distanceFromUser || Infinity) - (b.distanceFromUser || Infinity));
346
+ }
347
+
348
  // 限制最多 10 個結果
349
+ const limitedResults = processedResults.slice(0, 10);
350
+
351
+ if (userLocation) {
352
+ map.setCenter(userLocation);
353
+ map.setZoom(14);
354
+ } else if (limitedResults.length > 0) {
355
  map.setCenter(limitedResults[0].geometry.location);
356
  map.setZoom(13);
357
  }
358
 
359
  // 為每個結果獲取詳細信息(包含評論)
360
+ limitedResults.forEach(place => {
361
  const detailsRequest = {
362
  placeId: place.place_id,
363
  fields: ['name', 'rating', 'user_ratings_total', 'vicinity', 'geometry', 'place_id', 'reviews', 'url']
 
365
 
366
  placesService.getDetails(detailsRequest, (placeDetails, detailsStatus) => {
367
  if (detailsStatus === google.maps.places.PlacesServiceStatus.OK) {
368
+ if (typeof place.distanceFromUser === 'number') {
369
+ placeDetails.distanceFromUser = place.distanceFromUser;
370
+ }
371
  createMarker(placeDetails);
372
  createCard(placeDetails);
373
  }
 
402
  reviewsHtml += '</ul></div>';
403
  }
404
 
405
+ const distanceText = typeof place.distanceFromUser === 'number'
406
+ ? `<p>📏 距離:約 ${(place.distanceFromUser / 1000).toFixed(1)} 公里</p>`
407
+ : '';
408
+
409
  card.innerHTML = `
410
  <h4>${place.name}</h4>
411
  <p>⭐ ${place.rating || '無評分'} (${place.user_ratings_total || 0} 則評論)</p>
412
  <p>📍 <a href="${place.url}" target="_blank">${place.vicinity}</a></p>
413
+ ${distanceText}
414
  ${reviewsHtml}
415
  `;
416
 
 
432
  title: place.name
433
  });
434
 
435
+ const distanceInfo = typeof place.distanceFromUser === 'number'
436
+ ? `<br>距離:約 ${(place.distanceFromUser / 1000).toFixed(1)} 公里`
437
+ : '';
438
+
439
  const infoWindowContent = `
440
  <div class="custom-infowindow">
441
  <span class="close-btn" onclick="this.parentElement.parentElement.style.display='none';">&times;</span>
442
  <strong>${place.name}</strong><br>
443
  ⭐ ${place.rating || 'N/A'} | 評論: ${place.user_ratings_total || 0}<br>
444
+ <a href="${place.url}" target="_blank">在 Google 地圖上查看</a>${distanceInfo}
445
  </div>
446
  `;
447
 
 
534
  </script>
535
  <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB9zIkabAHoudMDAwbKom6URgjmUuejEpo&callback=initMap&libraries=places,marker&v=weekly"></script>
536
  </body>
537
+ </html>