Back To Articles

在 Next.js 中串接 Google Maps API

🧑🏻‍💻 Sean Huang 📅 December 25, 2024

Article Image

在現代 Web 開發中,地圖服務是許多應用的重要元件。本文將介紹如何在 Next.js 中使用 Google Maps API,並展示一個包含地圖標記和互動功能的範例。

設定 Google Maps API

1. 啟用 Google Maps API

第一步是前往 Google Cloud Console,開啟 Google Maps API 並建立 API 金鑰。記得限制金鑰的使用範圍以提高安全性。

2. 設定環境變數

將 API 金鑰儲存到 .env.local 檔案中,例如:NEXT_PUBLIC_MAPS_API_KEY=你的API金鑰

開發地圖元件

1. 安裝 Google Maps 套件

安裝必要的套件,包含 Google Maps API 的 Loader,以及 GeoJSON 的 TypeScript 型別支援套件:

npm install @googlemaps/js-api-loader @types/geojson

2. 建立地圖元件

以下是 Map 元件的完整程式碼,它負責載入地圖並顯示標記,同時也綁定了 Click 事件,後續可以再依照需求加上互動功能:

import { Loader } from '@googlemaps/js-api-loader'
import { FeatureCollection, Point } from 'geojson'
import { useEffect, useRef } from 'react'

interface MapProps {
  mapData: FeatureCollection | null
  handleClick: (marker: { lat: number; lng: number }) => void
}

const Map = ({ mapData, handleClick }: MapProps) => {
  const mapRef = useRef<HTMLDivElement>(null)

  useEffect(() => {
    // Import needed libraries
    const initMap = async () => {
      const loader = new Loader({
        apiKey: process.env.NEXT_PUBLIC_MAPS_API_KEY || '',
        version: 'weekly',
        libraries: ['places'],
      })
      const { Map } = await loader.importLibrary('maps')
      const { AdvancedMarkerElement } = await loader.importLibrary('marker')

      // Map options
      const mapOptions: google.maps.MapOptions = {
        center: {
          lat: 22.61204890135084,
          lng: 120.30288007791076,
        },
        zoom: 13,
        mapTypeId: 'roadmap',
        mapId: 'YOUR_MAP_ID',
      }

      // Setup the map
      const map = new Map(mapRef.current as HTMLDivElement, mapOptions)

      // Create markers
      if (!mapData) return
      mapData.features.forEach(feature => {
        const coordinates = (feature.geometry as Point).coordinates
        // Create custom pin element for each feature
        const customPin = document.createElement('div')
        customPin.innerHTML = `<div id="marker-${feature.properties?.id}">TEST</div>`
        const faPin = new PinElement({
          glyph: customPin,
        })
        const marker = new AdvancedMarkerElement({
          map,
          position: {
            lat: coordinates[0], // 經度
            lng: coordinates[1], // 緯度
          },
          title: `${feature.properties?.name} - ${feature.properties?.site}`,
          content: faPin.element,
          gmpClickable: true,
        })
        // Add a click listener for each marker
        marker.addListener('click', () => {
          // ...
          // handle click event
        })
      })
    }

    initMap()
  }, [mapData, handleClick])

  return <div ref={mapRef} style={{ height: '500px', width: '100%' }} />
}

export default Map

3. 整合至頁面上

完成地圖元件後,就可以在 page.tsx 中呼叫 Map 元件,實際載入地圖:

'use client'

import { useState, useEffect } from 'react'
import Map from '@/features/map/components'

const HomePage = () => {
  const [mapData, setMapData] = useState<FeatureCollection | null>(null)

  useEffect(() => {
    // 模擬 API 呼叫載入地圖資料
    fetch('/api/map')
      .then(res => res.json())
      .then(data => setMapData(data))
  }, [])

  const handleMarkerClick = (marker: { lat: number; lng: number }) => {
    console.log('Marker clicked:', marker)
  }

  return (
    <div>
      <h1>Google Maps in Next.js</h1>
      {mapData ? (
        <Map mapData={mapData} handleClick={handleMarkerClick} />
      ) : (
        <p>Loading map...</p>
      )}
    </div>
  )
}

export default HomePage

處理地圖資料

GeoJSON 是一種處理地理資訊的 JSON 格式,我們可以根據格式規範,建立以下地圖資料:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "geometry": { "type": "Point", "coordinates": [120.30288, 22.61205] },
      "properties": { "name": "Location 1", "site": "Site 1" }
    }
  ]
}

可以使用 Next.js 的 API Route /api/map 模擬提供這些資料,並在地圖上渲染出標記。

結論

透過上述步驟,就可以在 Next.js 中成功整合 Google Maps API,實現地圖顯示、標記點擊等互動功能。這只是一個簡單的基礎實作,後續可以再進行更多擴展,像是整合更多自訂功能,例如搜尋和導航等等。