Add map controller with controls

Add a map controller with controls

This example demonstrates how to set up a basic map with controls UI controls using the MapsGL Map Controller Provider.

Key Features

  • Basic map initialization with Mapbox GL
  • MapsGL controller integration
  • Zoom in/out controls
  • Data inspector
  • Geolocation support

Implementation

The map controller uses several components working together. Here's how to implement it:

  1. First, create the basic map component with Mapbox initialization:
import { useState, useRef } from 'react';
 
export const App = () => {
    const [map, setMap] = useState<mapboxgl.Map | null>(null);
    const containerRef = useRef<HTMLDivElement>(null);
 
    // Initialize map
    useEffect(() => {
        mapboxgl.accessToken = 'YOUR_MAPBOX_KEY';
        const mapEl = new mapboxgl.Map({
            container: containerRef.current || '',
            style: 'mapbox://styles/mapbox//streets-v10',
            center: [-74.5, 40],
            zoom: 3
        });
        setMap(mapEl);
    }, []);
 
    return (
       <div ref={containerRef} className="w-full h-full" />
    );
}
  1. Add the MapsGLMapControllerProvider to enable map interactions. Optionally, wrap your application in the Anchor.Position component for global positioning styles:
import { MapsGLMapControllerProvider, Anchor } from '@xweather/maps-ui-sdk';
 
export const App = () => {
    const [map, setMap] = useState<mapboxgl.Map | null>(null);
    const containerRef = useRef<HTMLDivElement>(null);
 
    // ... map initialization ...
 
    return (
        <Anchor.Position offset={12}>
            <MapsGLMapControllerProvider
                accessKeys={accessKeys}
                strategy="mapbox"
                map={map}
            >
                {/* Control panel will go here */}
            </MapsGLMapControllerProvider>
            <div ref={containerRef} className="w-full h-full" />
        </Anchor.Position>
    );
}
  1. Create a button component for the controls with the IconButton component:
import { IconButton, IconProps } from '@xweather/maps-ui-sdk';
 
interface MapControlButtonProps {
    icon: ComponentType<IconProps>;
    isActive?: boolean;
    onClick: () => void;
}
 
const MapControlButton = ({ icon, onClick, isActive }: MapControlButtonProps) => {
    const classNameBase = 'rounded-md hover:text-white text-2xl text-[#C1C7CD] size-7';
    const activeClass = isActive ? 'border-2 border-[#4ED9C2]' : ''; return (
 
        <IconButton
            className={`${classNameBase} ${activeClass}`}
            icon={icon}
            onClick={onClick}
        />
    );
};
  1. Create a specialized GeoLocate button with loading state.

The GeoLocate button is a special case that requires handling an asynchronous operation. We'll add a LoadingSpinner to provide visual feedback while waiting for the browser's Geolocation API (opens in a new tab):

import { useState } from 'react';
import { HStack, LoadingSpinner, getCurrentLocation, GeoLocateIcon } from '@xweather/maps-ui-sdk';
 
const GeoLocateButton = ({ map }) => {
    // Track loading state for geolocation request
    const [isLoading, setIsLoading] = useState(false);
 
    const handleGeoLocate = async () => {
        try {
            setIsLoading(true);
            // Use the utility function to access browser geolocation
            const { lat, lon } = await getCurrentLocation();
            // Animate the map to the user's location
            map?.flyTo({
                center: [lon, lat],
                essential: true,
                zoom: 7
            });
        } catch (error) {
            console.error('Error getting location:', error);
        } finally {
            setIsLoading(false);
        }
    };
    return (
        <HStack className="relative items-center justify-center">
            {isLoading && <LoadingSpinner
                className="absolute"
                size={34}
            />}
            <MapControlButton
                icon={GeoLocateIcon}
                onClick={handleGeoLocate}
            />
        </HStack>
    );
};

This component:

  • Uses React's useState to track the geolocation loading state
  • Shows a loading spinner while waiting for the geolocation request
  • Uses the getCurrentLocation utility which handles the geolocation request and browser permissions, returning the user's current location if successful
  • Animates the map to the user's location when coordinates are received
  • Handles errors gracefully and ensures the loading spinner is always removed when the operation completes
  1. Finally, implement the control panel component by combining the MapControlButton and GeoLocateButton with the useMapsGLMapControllerContext hook.
import { useMapsGLMapControllerContext } from '@xweather/maps-ui-sdk';
 
const MapControlPanel = () => {
    const { map, toggleDataInspector, isDataInspectorVisible } = useMapsGLMapControllerContext();
 
    return (
        <Anchor.BottomLeft offsetY={40} className="z-10">
            <VStack className="bg-black rounded-xl p-2 gap-1.5">
                <MapControlButton
                    icon={AddIcon}
                    onClick={() => map?.zoomIn()}
                />
                <MapControlButton
                    icon={SubtractIcon}
                    onClick={() => map?.zoomOut()}
                />
                <MapControlButton
                    icon={TargetIcon}
                    onClick={() => toggleDataInspector('move', true)}
                    isActive={isDataInspectorVisible}
                />
                <GeoLocateButton map={map} />
            </VStack>
        </Anchor.BottomLeft>
    );
};

This component:

  • Provides zoom controls for adjusting the map view
  • Includes a data inspector toggle that can be activated on click or mouse move
  • Uses the isDataInspectorVisible state to style the inspector button when active
  • Integrates the GeoLocate button we created in the previous step
  • Positions all controls consistently in an accessible location on the map