Skip to main content

Interactive Map Examples

Advanced examples showing user interactions and dynamic map updates.

Click to Add Markers

import { HereMap, Marker } from '@rodrito/react-here-maps';
import { useMapContext } from '@rodrito/react-here-maps';
import { useState, useEffect } from 'react';

function ClickToAddMarkers() {
  const { map } = useMapContext();
  const [markers, setMarkers] = useState<Array<{ lat: number; lng: number; id: number }>>([]);

  useEffect(() => {
    if (!map.current) return;

    const handleClick = (evt: H.mapevents.Event) => {
      const coord = map.current!.screenToGeo(
        evt.currentPointer.viewportX,
        evt.currentPointer.viewportY
      );

      setMarkers((prev) => [
        ...prev,
        { lat: coord.lat, lng: coord.lng, id: Date.now() },
      ]);
    };

    map.current.addEventListener('tap', handleClick);
    return () => map.current?.removeEventListener('tap', handleClick);
  }, [map]);

  return (
    <>
      {markers.map((marker) => (
        <Marker key={marker.id} position={{ lat: marker.lat, lng: marker.lng }} />
      ))}
    </>
  );
}

export function ClickToAddMap() {
  return (
    <HereMap apikey="YOUR_API_KEY" options={{ center: { lat: 40.7128, lng: -74.0060 }, zoom: 12 }}>
      <ClickToAddMarkers />
    </HereMap>
  );
}

Geolocation Tracking

import { useState, useEffect } from 'react';

export function GeolocationMap() {
  const [position, setPosition] = useState<{ lat: number; lng: number } | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!navigator.geolocation) {
      setError('Geolocation not supported');
      return;
    }

    const watchId = navigator.geolocation.watchPosition(
      (pos) => {
        setPosition({
          lat: pos.coords.latitude,
          lng: pos.coords.longitude,
        });
      },
      (err) => setError(err.message),
      { enableHighAccuracy: true }
    );

    return () => navigator.geolocation.clearWatch(watchId);
  }, []);

  if (error) return <div>Error: {error}</div>;
  if (!position) return <div>Getting location...</div>;

  return (
    <HereMap apikey="YOUR_API_KEY" options={{ center: position, zoom: 15 }}>
      <Marker
        position={position}
        label="You are here"
        icon="/user-location.png"
      />
    </HereMap>
  );
}

Distance Measurement

import { useState, useEffect } from 'react';
import { useMapContext } from '@rodrito/react-here-maps';

function DistanceMeasurement() {
  const { map } = useMapContext();
  const [points, setPoints] = useState<Array<{ lat: number; lng: number }>>([]);
  const [distance, setDistance] = useState(0);

  useEffect(() => {
    if (!map.current) return;

    const handleClick = (evt: H.mapevents.Event) => {
      const coord = map.current!.screenToGeo(
        evt.currentPointer.viewportX,
        evt.currentPointer.viewportY
      );
      setPoints((prev) => [...prev, { lat: coord.lat, lng: coord.lng }]);
    };

    map.current.addEventListener('tap', handleClick);
    return () => map.current?.removeEventListener('tap', handleClick);
  }, [map]);

  useEffect(() => {
    if (points.length < 2) {
      setDistance(0);
      return;
    }

    let totalDistance = 0;
    for (let i = 0; i < points.length - 1; i++) {
      const p1 = new H.geo.Point(points[i].lat, points[i].lng);
      const p2 = new H.geo.Point(points[i + 1].lat, points[i + 1].lng);
      totalDistance += p1.distance(p2);
    }
    setDistance(totalDistance);
  }, [points]);

  return (
    <div>
      <div style={{ marginBottom: '1rem' }}>
        <p>Distance: {(distance / 1000).toFixed(2)} km</p>
        <button onClick={() => setPoints([])}>Clear</button>
      </div>

      {points.length > 1 && (
        <Polyline
          points={points}
          options={{
            style: {
              strokeColor: '#FF5733',
              lineWidth: 3,
            }
          }}
        />
      )}

      {points.map((point, i) => (
        <Marker key={i} position={point} label={`${i + 1}`} />
      ))}
    </div>
  );
}

export function DistanceMeasurementMap() {
  return (
    <HereMap apikey="YOUR_API_KEY" options={{ center: { lat: 40.7128, lng: -74.0060 }, zoom: 13 }}>
      <DistanceMeasurement />
    </HereMap>
  );
}

Search and Navigate

import { useState } from 'react';
import { useMapContext } from '@rodrito/react-here-maps';

function SearchBox() {
  const { platform, map } = useMapContext();
  const [query, setQuery] = useState('');
  const [result, setResult] = useState<{ lat: number; lng: number } | null>(null);

  const handleSearch = async () => {
    if (!platform.current || !query) return;

    const service = platform.current.getSearchService();
    service.geocode(
      { q: query },
      (res) => {
        if (res.items.length > 0) {
          const location = res.items[0].position;
          setResult(location);
          map.current?.setCenter(location);
          map.current?.setZoom(14);
        }
      },
      (error) => console.error('Search error:', error)
    );
  };

  return (
    <div style={{ position: 'absolute', top: '10px', left: '10px', zIndex: 1 }}>
      <input
        type="text"
        placeholder="Search location..."
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
        style={{
          padding: '8px',
          width: '250px',
          border: '1px solid #ccc',
          borderRadius: '4px',
        }}
      />
      <button onClick={handleSearch} style={{ marginLeft: '4px' }}>
        Search
      </button>

      {result && <Marker position={result} label="Search Result" />}
    </div>
  );
}

export function SearchMap() {
  return (
    <div style={{ position: 'relative', height: '500px' }}>
      <HereMap apikey="YOUR_API_KEY" options={{ center: { lat: 40.7128, lng: -74.0060 }, zoom: 12 }}>
        <SearchBox />
      </HereMap>
    </div>
  );
}

Draggable Route

import { useState } from 'react';

export function DraggableRoute() {
  const [waypoints, setWaypoints] = useState([
    { id: 1, lat: 40.7128, lng: -74.0060 },
    { id: 2, lat: 40.7589, lng: -73.9851 },
  ]);

  const updateWaypoint = (id: number, lat: number, lng: number) => {
    setWaypoints((prev) =>
      prev.map((wp) => (wp.id === id ? { ...wp, lat, lng } : wp))
    );
  };

  return (
    <HereMap apikey="YOUR_API_KEY" options={{ center: waypoints[0], zoom: 12 }}>
      <Polyline
        points={waypoints}
        options={{
          style: {
            strokeColor: '#1EA7FD',
            lineWidth: 4,
          }
        }}
      />

      {waypoints.map((wp, index) => (
        <Marker
          key={wp.id}
          position={{ lat: wp.lat, lng: wp.lng }}
          label={`${index + 1}`}
          draggable
          onDragEnd={(event) => {
            const coord = event.target.getGeometry();
            updateWaypoint(wp.id, coord.lat, coord.lng);
          }}
        />
      ))}
    </HereMap>
  );
}

Info Bubbles on Click

import { useEffect } from 'react';
import { useMapContext } from '@rodrito/react-here-maps';

function InfoBubbles() {
  const { map, ui } = useMapContext();

  const locations = [
    { lat: 40.7128, lng: -74.0060, name: 'NYC', info: 'New York City' },
    { lat: 40.7589, lng: -73.9851, name: 'Times Sq', info: 'Times Square' },
  ];

  const showBubble = (lat: number, lng: number, content: string) => {
    if (!ui.current) return;

    const bubble = new H.ui.InfoBubble(
      { lat, lng },
      { content: `<div style="padding: 10px;">${content}</div>` }
    );

    ui.current.addBubble(bubble);
  };

  return (
    <>
      {locations.map((loc, i) => (
        <Marker
          key={i}
          position={{ lat: loc.lat, lng: loc.lng }}
          label={loc.name}
          onClick={() => showBubble(loc.lat, loc.lng, loc.info)}
        />
      ))}
    </>
  );
}

export function InfoBubbleMap() {
  return (
    <HereMap apikey="YOUR_API_KEY" options={{ center: { lat: 40.7358, lng: -73.9955 }, zoom: 12 }}>
      <InfoBubbles />
    </HereMap>
  );
}

See Also