Skip to main content

Amap (Gaode Maps)

This guide explains how to integrate Amap map services into your superun application. The integration uses an Edge Function to securely fetch API configuration, then initializes the map on the frontend. For more details on the Amap API, refer to the official documentation:

Environment Variables

Calling the SupabaseEdgeFunctionSecretsCreate tool will open the secrets configuration panel. The plugin_secret_prefix should be SUPERUN. In this panel, you can:
  • Use shared keys — superun provides ready-to-use shared test keys. Check the option to get started quickly without applying for your own keys.
  • Enter your own keys — For production use, enter your own API keys in the input fields.
Required secrets:
  • AMAP_WEB_API_KEY - Amap Web API Key
  • AMAP_JS_API_KEY - Amap JavaScript API Key
  • AMAP_SECURITY_CODE - Amap Security Code

Step 1: Create an Edge Function

Create a Supabase Edge Function to securely return the Amap API configuration:
// supabase/functions/amap-config/index.ts

serve(async (req) => {
  // Return Amap configuration
  return new Response(
    JSON.stringify({
      webApiKey: Deno.env.get("SUPERUN_AMAP_WEB_API_KEY"),
      jsApiKey: Deno.env.get("SUPERUN_AMAP_JS_API_KEY"),
      securityCode: Deno.env.get("SUPERUN_AMAP_SECURITY_CODE"),
    }),
    {
      headers: { "Content-Type": "application/json" },
    }
  );
});

Step 2: Install the Amap Loader

Install the Amap JavaScript API loader:
npm i @amap/amap-jsapi-loader@1.0.1 --save

Step 3: Frontend Integration

import { useEffect, useRef, useState } from "react";
import AMapLoader from "@amap/amap-jsapi-loader";
import { supabase } from "@/integrations/supabase/client";

export default function AmapComponent({
  center = [116.397428, 39.90923],
  zoom = 13,
  mapStyle = "amap://styles/dark",
  onMapReady,
  onMapError,
  className = "h-full w-full",
}: AmapComponentProps) {
  const mapContainerRef = useRef<HTMLDivElement>(null);
  const mapInstanceRef = useRef<any>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const initMap = async () => {
      try {
        // Step 1: Fetch API keys from Edge Function
        const { data, error } = await supabase.functions.invoke("amap-config");
        
        if (error) throw error;
        
        if (!data?.jsApiKey) {
          throw new Error("No API key returned");
        }

        // Step 2: Configure the security code
        (window as any)._AMapSecurityConfig = {
          securityJsCode: data.securityCode,
        };

        // Step 3: Load Amap using the official loader
        setIsLoading(true);
        
        const AMap = await AMapLoader.load({
          key: data.jsApiKey,
          version: "2.0",
          plugins: [
            "AMap.Geolocation",
            "AMap.Scale",
            "AMap.ToolBar",
            "AMap.Marker",
            "AMap.InfoWindow",
          ],
        });

        // Step 4: Initialize the map
        if (!mapContainerRef.current) return;
        
        const map = new AMap.Map(mapContainerRef.current, {
          zoom,
          center,
          mapStyle,
          viewMode: "3D",
        });

        mapInstanceRef.current = map;
        setIsLoading(false);

        // Step 5: Invoke callback with the map instance
        if (onMapReady) {
          onMapReady(map);
        }
      } catch (error) {
        console.error("Failed to initialize Amap:", error);
        setIsLoading(false);
        if (onMapError) {
          onMapError(error as Error);
        }
      }
    };

    initMap();

    // Cleanup: destroy map on unmount
    return () => {
      if (mapInstanceRef.current) {
        mapInstanceRef.current.destroy();
        mapInstanceRef.current = null;
      }
    };
  }, []); // Run once on mount

  return (
    <div className="relative h-full w-full">
      <div ref={mapContainerRef} className={className} />
      
      {/* Loading overlay */}
      {isLoading && (
        <div className="absolute inset-0 flex items-center justify-center bg-stone-900/90">
          <div className="text-center">
            <div className="mb-4 h-12 w-12 animate-spin rounded-full border-4 border-stone-700 border-t-amber-500 mx-auto" />
            <p className="text-sm text-stone-400">Loading map...</p>
          </div>
        </div>
      )}
    </div>
  );
}