CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-google-maps

React.js Google Maps integration component library providing comprehensive Google Maps functionality through React components

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

visualization.mddocs/

Visualization

Data visualization component for displaying heatmaps and other visual representations of geographic data on Google Maps. Requires the visualization library.

Capabilities

HeatmapLayer

Component for displaying heatmap visualizations of geographic data points with density-based coloring.

/**
 * Heatmap layer component for data visualization
 * Requires: visualization library in Google Maps API URL
 */
class HeatmapLayer extends Component<HeatmapLayerProps> {
  /** Returns the heatmap data array */
  getData(): google.maps.MVCArray<google.maps.LatLng | google.maps.visualization.WeightedLocation>;
}

interface HeatmapLayerProps {
  // Default props
  defaultData?: google.maps.MVCArray<google.maps.LatLng | google.maps.visualization.WeightedLocation> | Array<google.maps.LatLng | google.maps.visualization.WeightedLocation>;
  defaultOptions?: google.maps.visualization.HeatmapLayerOptions;
  
  // Controlled props
  data?: google.maps.MVCArray<google.maps.LatLng | google.maps.visualization.WeightedLocation> | Array<google.maps.LatLng | google.maps.visualization.WeightedLocation>;
  options?: google.maps.visualization.HeatmapLayerOptions;
}

Usage Example:

import HeatmapLayer from "react-google-maps/lib/components/visualization/HeatmapLayer";

const HeatmapVisualization = () => {
  // Sample data points for San Francisco crime data
  const [heatmapData] = useState([
    { location: new google.maps.LatLng(37.782551, -122.445368), weight: 0.5 },
    { location: new google.maps.LatLng(37.782745, -122.444586), weight: 2 },
    { location: new google.maps.LatLng(37.782842, -122.443688), weight: 3 },
    { location: new google.maps.LatLng(37.782919, -122.442815), weight: 2 },
    { location: new google.maps.LatLng(37.782992, -122.442112), weight: 3 },
    { location: new google.maps.LatLng(37.783100, -122.441461), weight: 0.5 },
    { location: new google.maps.LatLng(37.783206, -122.440829), weight: 2 },
    { location: new google.maps.LatLng(37.783273, -122.440324), weight: 3 },
    { location: new google.maps.LatLng(37.783316, -122.440023), weight: 3 },
    { location: new google.maps.LatLng(37.783357, -122.439794), weight: 2 },
    // ... more data points
  ]);

  const [heatmapOptions, setHeatmapOptions] = useState({
    radius: 20,
    opacity: 0.6,
    gradient: [
      'rgba(0, 255, 255, 0)',
      'rgba(0, 255, 255, 1)',
      'rgba(0, 191, 255, 1)',
      'rgba(0, 127, 255, 1)',
      'rgba(0, 63, 255, 1)',
      'rgba(0, 0, 255, 1)',
      'rgba(0, 0, 223, 1)',
      'rgba(0, 0, 191, 1)',
      'rgba(0, 0, 159, 1)',
      'rgba(0, 0, 127, 1)',
      'rgba(63, 0, 91, 1)',
      'rgba(127, 0, 63, 1)',
      'rgba(191, 0, 31, 1)',
      'rgba(255, 0, 0, 1)'
    ]
  });

  return (
    <div>
      {/* Heatmap controls */}
      <div style={{ padding: '10px', backgroundColor: '#f5f5f5' }}>
        <div style={{ marginBottom: '10px' }}>
          <label style={{ marginRight: '10px' }}>
            Radius: {heatmapOptions.radius}
            <input
              type="range"
              min="10"
              max="50"
              value={heatmapOptions.radius}
              onChange={(e) => setHeatmapOptions(prev => ({
                ...prev,
                radius: parseInt(e.target.value)
              }))}
              style={{ marginLeft: '10px' }}
            />
          </label>
          
          <label style={{ marginLeft: '20px' }}>
            Opacity: {heatmapOptions.opacity}
            <input
              type="range"
              min="0.1"
              max="1"
              step="0.1"
              value={heatmapOptions.opacity}
              onChange={(e) => setHeatmapOptions(prev => ({
                ...prev,
                opacity: parseFloat(e.target.value)
              }))}
              style={{ marginLeft: '10px' }}
            />
          </label>
        </div>
      </div>

      <GoogleMap 
        defaultZoom={13} 
        defaultCenter={{ lat: 37.7831, lng: -122.4421 }}
        style={{ height: '500px' }}
      >
        <HeatmapLayer
          data={heatmapData}
          options={heatmapOptions}
        />
      </GoogleMap>
    </div>
  );
};

Google Maps Visualization API Setup

API Requirements

The visualization components require the visualization library to be loaded:

// Include 'visualization' in the libraries parameter
const googleMapURL = `https://maps.googleapis.com/maps/api/js?key=${API_KEY}&libraries=places,visualization`;

const MapWithVisualization = withScriptjs(withGoogleMap(() => (
  <GoogleMap defaultZoom={10} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
    <HeatmapLayer data={heatmapData} />
  </GoogleMap>
)));

<MapWithVisualization
  googleMapURL={googleMapURL}
  loadingElement={<div style={{ height: "100%" }} />}
  containerElement={<div style={{ height: "400px" }} />}
  mapElement={<div style={{ height: "100%" }} />}
/>

Heatmap Configuration Options

Data Formats

The heatmap accepts data in multiple formats:

// Simple LatLng array
const simpleData = [
  new google.maps.LatLng(37.782551, -122.445368),
  new google.maps.LatLng(37.782745, -122.444586),
  new google.maps.LatLng(37.782842, -122.443688)
];

// Weighted locations with intensity values
const weightedData = [
  { location: new google.maps.LatLng(37.782551, -122.445368), weight: 0.5 },
  { location: new google.maps.LatLng(37.782745, -122.444586), weight: 2.0 },
  { location: new google.maps.LatLng(37.782842, -122.443688), weight: 3.0 }
];

// Using plain objects (will be converted to LatLng)
const objectData = [
  { lat: 37.782551, lng: -122.445368 },
  { lat: 37.782745, lng: -122.444586 },
  { lat: 37.782842, lng: -122.443688 }
];

Styling Options

Customize heatmap appearance through options:

const heatmapOptions = {
  // Point radius in pixels
  radius: 20,
  
  // Layer opacity (0-1)
  opacity: 0.6,
  
  // Maximum intensity for color mapping
  maxIntensity: 100,
  
  // Dissipating effect (points fade over time)
  dissipating: true,
  
  // Custom color gradient
  gradient: [
    'rgba(0, 255, 255, 0)',    // Transparent cyan
    'rgba(0, 255, 255, 1)',    // Cyan
    'rgba(0, 191, 255, 1)',    // Light blue
    'rgba(0, 127, 255, 1)',    // Blue
    'rgba(0, 63, 255, 1)',     // Dark blue
    'rgba(0, 0, 255, 1)',      // Pure blue
    'rgba(0, 0, 223, 1)',      // Dark blue
    'rgba(0, 0, 191, 1)',      // Darker blue
    'rgba(0, 0, 159, 1)',      // Even darker blue
    'rgba(0, 0, 127, 1)',      // Very dark blue
    'rgba(63, 0, 91, 1)',      // Purple-blue
    'rgba(127, 0, 63, 1)',     // Purple
    'rgba(191, 0, 31, 1)',     // Red-purple
    'rgba(255, 0, 0, 1)'       // Red
  ]
};

Advanced Visualization Patterns

Dynamic Heatmap Data

Update heatmap data based on user interactions or time:

const DynamicHeatmap = () => {
  const [allData, setAllData] = useState([]); // Complete dataset
  const [filteredData, setFilteredData] = useState([]);
  const [timeRange, setTimeRange] = useState({ start: 0, end: 24 });

  useEffect(() => {
    // Load data from API
    fetchHeatmapData().then(data => {
      setAllData(data);
      setFilteredData(data);
    });
  }, []);

  const filterDataByTime = (start, end) => {
    const filtered = allData.filter(point => {
      const hour = point.timestamp.getHours();
      return hour >= start && hour <= end;
    });
    setFilteredData(filtered);
  };

  return (
    <div>
      <div style={{ padding: '10px' }}>
        <label>
          Time Range: {timeRange.start}:00 - {timeRange.end}:00
        </label>
        <input
          type="range"
          min="0"
          max="23"
          value={timeRange.start}
          onChange={(e) => {
            const start = parseInt(e.target.value);
            setTimeRange(prev => ({ ...prev, start }));
            filterDataByTime(start, timeRange.end);
          }}
        />
        <input
          type="range"
          min="0"
          max="23"
          value={timeRange.end}
          onChange={(e) => {
            const end = parseInt(e.target.value);
            setTimeRange(prev => ({ ...prev, end }));
            filterDataByTime(timeRange.start, end);
          }}
        />
      </div>

      <GoogleMap defaultZoom={12} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
        <HeatmapLayer
          data={filteredData.map(point => ({
            location: new google.maps.LatLng(point.lat, point.lng),
            weight: point.intensity
          }))}
        />
      </GoogleMap>
    </div>
  );
};

Multi-Category Heatmaps

Display different categories of data with toggleable layers:

const MultiCategoryHeatmap = () => {
  const [categories] = useState({
    crime: { color: ['rgba(255,0,0,0)', 'rgba(255,0,0,1)'], data: crimeData },
    traffic: { color: ['rgba(0,255,0,0)', 'rgba(0,255,0,1)'], data: trafficData },
    business: { color: ['rgba(0,0,255,0)', 'rgba(0,0,255,1)'], data: businessData }
  });
  
  const [visibleCategories, setVisibleCategories] = useState({
    crime: true,
    traffic: false,
    business: false
  });

  const toggleCategory = (categoryName) => {
    setVisibleCategories(prev => ({
      ...prev,
      [categoryName]: !prev[categoryName]
    }));
  };

  return (
    <div>
      <div style={{ padding: '10px' }}>
        {Object.keys(categories).map(categoryName => (
          <label key={categoryName} style={{ marginRight: '20px' }}>
            <input
              type="checkbox"
              checked={visibleCategories[categoryName]}
              onChange={() => toggleCategory(categoryName)}
            />
            {categoryName.charAt(0).toUpperCase() + categoryName.slice(1)}
          </label>
        ))}
      </div>

      <GoogleMap defaultZoom={12} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
        {Object.entries(categories).map(([categoryName, category]) => 
          visibleCategories[categoryName] && (
            <HeatmapLayer
              key={categoryName}
              data={category.data}
              options={{
                radius: 25,
                opacity: 0.6,
                gradient: category.color
              }}
            />
          )
        )}
      </GoogleMap>
    </div>
  );
};

Real-time Heatmap Updates

Stream real-time data to update heatmap visualization:

const RealtimeHeatmap = () => {
  const [heatmapData, setHeatmapData] = useState([]);
  const [isStreaming, setIsStreaming] = useState(false);

  useEffect(() => {
    let interval;
    
    if (isStreaming) {
      interval = setInterval(() => {
        // Simulate real-time data updates
        const newPoint = {
          location: new google.maps.LatLng(
            37.7749 + (Math.random() - 0.5) * 0.1,
            -122.4194 + (Math.random() - 0.5) * 0.1
          ),
          weight: Math.random() * 5
        };
        
        setHeatmapData(prevData => {
          const updatedData = [...prevData, newPoint];
          // Keep only last 100 points for performance
          return updatedData.slice(-100);
        });
      }, 1000);
    }
    
    return () => {
      if (interval) clearInterval(interval);
    };
  }, [isStreaming]);

  return (
    <div>
      <div style={{ padding: '10px' }}>
        <button onClick={() => setIsStreaming(!isStreaming)}>
          {isStreaming ? 'Stop' : 'Start'} Real-time Updates
        </button>
        <span style={{ marginLeft: '20px' }}>
          Data Points: {heatmapData.length}
        </span>
      </div>

      <GoogleMap defaultZoom={12} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
        <HeatmapLayer
          data={heatmapData}
          options={{
            radius: 30,
            opacity: 0.8,
            dissipating: true,
            maxIntensity: 10
          }}
        />
      </GoogleMap>
    </div>
  );
};

Heatmap with Data Analysis

Combine heatmap with statistical analysis:

const AnalyticalHeatmap = () => {
  const [heatmapData, setHeatmapData] = useState([]);
  const [statistics, setStatistics] = useState({});

  const analyzeData = (data) => {
    const weights = data.map(point => point.weight || 1);
    const total = weights.reduce((sum, weight) => sum + weight, 0);
    const average = total / weights.length;
    const max = Math.max(...weights);
    const min = Math.min(...weights);
    
    return {
      totalPoints: data.length,
      totalWeight: total.toFixed(2),
      averageWeight: average.toFixed(2),
      maxWeight: max.toFixed(2),
      minWeight: min.toFixed(2)
    };
  };

  useEffect(() => {
    if (heatmapData.length > 0) {
      setStatistics(analyzeData(heatmapData));
    }
  }, [heatmapData]);

  return (
    <div style={{ display: 'flex' }}>
      <div style={{ flex: 1 }}>
        <GoogleMap defaultZoom={12} defaultCenter={{ lat: 37.7749, lng: -122.4194 }}>
          <HeatmapLayer data={heatmapData} />
        </GoogleMap>
      </div>
      
      <div style={{ width: '300px', padding: '20px', backgroundColor: '#f5f5f5' }}>
        <h3>Data Analysis</h3>
        <table style={{ width: '100%' }}>
          <tbody>
            <tr>
              <td>Total Points:</td>
              <td>{statistics.totalPoints || 0}</td>
            </tr>
            <tr>
              <td>Total Weight:</td>
              <td>{statistics.totalWeight || 0}</td>
            </tr>
            <tr>
              <td>Average Weight:</td>
              <td>{statistics.averageWeight || 0}</td>
            </tr>
            <tr>
              <td>Max Weight:</td>
              <td>{statistics.maxWeight || 0}</td>
            </tr>
            <tr>
              <td>Min Weight:</td>
              <td>{statistics.minWeight || 0}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </div>
  );
};

Install with Tessl CLI

npx tessl i tessl/npm-react-google-maps

docs

addons.md

advanced.md

core-components.md

drawing.md

hocs.md

index.md

layers.md

places.md

shapes.md

visualization.md

tile.json