dawarich/HEXAGON_GRID_README.md
2025-08-24 11:08:56 +02:00

7.8 KiB

Hexagonal Grid Overlay Implementation

This implementation adds a hexagonal grid overlay to the Leaflet map in your Ruby on Rails + PostGIS project. The grid displays ~1km hexagons that dynamically load based on the current map viewport.

Components

1. Backend - Rails API Controller

File: app/controllers/api/v1/maps/hexagons_controller.rb

Endpoint: GET /api/v1/maps/hexagons

Authentication: Requires valid API key

Parameters:

  • api_key: User's API key (required)
  • min_lon, min_lat, max_lon, max_lat: Bounding box coordinates

Features:

  • Generates hexagons using PostGIS ST_HexagonGrid
  • 1km edge-to-edge hexagon size (~500m center-to-edge)
  • Maximum 5000 hexagons per request for performance
  • Validates bounding box size and coordinates
  • Handles edge cases (large areas, invalid coordinates)
  • Returns GeoJSON FeatureCollection

2. Frontend - JavaScript Module

File: app/javascript/maps/hexagon_grid.js

Key Features:

  • Efficient viewport-based loading with debouncing
  • Zoom-level restrictions (min: 8, max: 16)
  • Automatic cleanup and memory management
  • Hover effects and click handling
  • Request cancellation for pending requests

3. Integration

File: app/javascript/controllers/maps_controller.js (modified)

Integration Points:

  • Import and initialize hexagon grid
  • Add to layer control
  • Event handling for layer toggle
  • Cleanup on disconnect

Usage

Basic Usage

The hexagon grid will be available as a layer in the map's layer control panel. Users can toggle it on/off via the "Hexagon Grid" checkbox.

Programmatic Control

// Show hexagons
controller.hexagonGrid.show();

// Hide hexagons  
controller.hexagonGrid.hide();

// Toggle visibility
controller.hexagonGrid.toggle();

// Update styling
controller.hexagonGrid.updateStyle({
  fillColor: '#ff0000',
  fillOpacity: 0.2,
  color: '#ff0000',
  weight: 2,
  opacity: 0.8
});

PostGIS SQL Example

Here's the core SQL that generates the hexagon grid:

WITH bbox_geom AS (
  SELECT ST_MakeEnvelope(-74.0, 40.7, -73.9, 40.8, 4326) as geom
),
bbox_utm AS (
  SELECT 
    ST_Transform(geom, 3857) as geom_utm,
    geom as geom_wgs84
  FROM bbox_geom
),
hex_grid AS (
  SELECT 
    (ST_HexagonGrid(500, bbox_utm.geom_utm)).geom as hex_geom_utm
  FROM bbox_utm
)
SELECT 
  ST_AsGeoJSON(ST_Transform(hex_geom_utm, 4326)) as geojson,
  row_number() OVER () as id
FROM hex_grid
WHERE ST_Intersects(
  hex_geom_utm,
  (SELECT geom_utm FROM bbox_utm)
)
LIMIT 5000;

Performance Considerations

Backend Optimizations

  1. Request Limiting: Maximum 5000 hexagons per request
  2. Area Validation: Rejects requests for areas > 250,000 km²
  3. Coordinate Validation: Validates lat/lng bounds
  4. Efficient PostGIS: Uses ST_HexagonGrid with proper indexing

Frontend Optimizations

  1. Debounced Loading: 300ms delay prevents excessive API calls
  2. Viewport-based Loading: Only loads visible hexagons
  3. Request Cancellation: Cancels pending requests when new ones start
  4. Memory Management: Clears old hexagons before loading new ones
  5. Zoom Restrictions: Prevents loading at inappropriate zoom levels

Edge Cases and Solutions

1. Large Bounding Boxes

Problem: User zooms out too far, requesting millions of hexagons Solution:

  • Backend validates area size (max 250,000 km²)
  • Returns 400 error with user-friendly message
  • Frontend handles error gracefully

2. Crossing the International Date Line

Problem: Bounding box crosses longitude 180/-180 Detection: min_lon > max_lon Solution: Currently handled by PostGIS coordinate system transformation

3. Polar Regions

Problem: Hexagon distortion near poles Detection: Latitude > ±85° Note: Current implementation works with Web Mercator (EPSG:3857) limitations

4. Network Issues

Problem: API requests fail or timeout Solutions:

  • Request cancellation prevents multiple concurrent requests
  • Error handling with console logging
  • Graceful degradation (no hexagons shown, but map still works)

5. Performance on Low-End Devices

Problem: Too many hexagons cause rendering slowness Solutions:

  • Zoom level restrictions prevent overloading
  • Limited hexagon count per request
  • Efficient DOM manipulation with LayerGroup

Configuration Options

HexagonGrid Constructor Options

const options = {
  apiEndpoint: '/api/v1/maps/hexagons',
  style: {
    fillColor: '#3388ff',
    fillOpacity: 0.1,
    color: '#3388ff',
    weight: 1,
    opacity: 0.5
  },
  debounceDelay: 300,    // ms to wait before loading
  maxZoom: 16,           // Don't show beyond this zoom
  minZoom: 8             // Don't show below this zoom
};

Backend Configuration

Edit app/controllers/api/v1/maps/hexagons_controller.rb:

# Change hexagon size (in meters, center to edge)
hex_size = 500  # For ~1km edge-to-edge

# Change maximum hexagons per request
MAX_HEXAGONS_PER_REQUEST = 5000

# Change area limit (km²)
area_km2 > 250_000

Testing

Manual Testing Steps

  1. Basic Functionality:

    • Open map at various zoom levels
    • Toggle "Hexagon Grid" layer on/off
    • Verify hexagons load dynamically when panning
  2. Performance Testing:

    • Zoom to maximum level and pan rapidly
    • Verify no memory leaks or excessive API calls
    • Test on slow connections
  3. Edge Case Testing:

    • Zoom out very far (should show error handling)
    • Test near International Date Line
    • Test in polar regions
  4. API Testing:

    # Test valid request
    curl "http://localhost:3000/api/v1/maps/hexagons?api_key=YOUR_KEY&min_lon=-74&min_lat=40.7&max_lon=-73.9&max_lat=40.8"
    
    # Test invalid bounding box
    curl "http://localhost:3000/api/v1/maps/hexagons?api_key=YOUR_KEY&min_lon=-180&min_lat=-90&max_lon=180&max_lat=90"
    

Troubleshooting

Common Issues

  1. Hexagons not appearing:

    • Check console for API errors
    • Verify API key is valid
    • Check zoom level is within min/max range
  2. Performance issues:

    • Reduce MAX_HEXAGONS_PER_REQUEST
    • Increase minZoom to prevent loading at low zoom levels
    • Check for JavaScript errors preventing cleanup
  3. Database errors:

    • Ensure PostGIS extension is installed
    • Verify ST_HexagonGrid function is available (PostGIS 3.1+)
    • Check coordinate system support

Debug Information

Enable debug logging:

// Add to hexagon_grid.js constructor
console.log('HexagonGrid initialized with options:', options);

// Add to loadHexagons method
console.log('Loading hexagons for bounds:', bounds);

Future Enhancements

Potential Improvements

  1. Caching: Add Redis caching for frequently requested areas
  2. Clustering: Group nearby hexagons at low zoom levels
  3. Data Visualization: Color hexagons based on data (point density, etc.)
  4. Custom Shapes: Allow other grid patterns (squares, triangles)
  5. Persistent Settings: Remember user's hexagon visibility preference

Performance Optimizations

  1. Server-side Caching: Cache generated hexagon grids
  2. Tile-based Loading: Load hexagons in tile-like chunks
  3. Progressive Enhancement: Load lower resolution first, then refine
  4. WebWorker Integration: Move heavy calculations to background thread

Dependencies

Required

  • PostGIS 3.1+: For ST_HexagonGrid function
  • Leaflet: Frontend mapping library
  • Rails 6+: Backend framework

Optional

  • Redis: For caching (future enhancement)
  • Sidekiq: For background processing (future enhancement)

License and Credits

This implementation uses:

  • PostGIS for spatial calculations
  • Leaflet for map visualization
  • Ruby on Rails for API backend

The hexagon grid generation leverages PostGIS's built-in ST_HexagonGrid function for optimal performance and accuracy.