- Add hotspot calculation methods to analytics service - Implement MySQL spatial queries for clustering (5+ reports within 500m) - Calculate severity scores based on report count and frequency - Create GET /api/analytics/hotspots endpoint - Create GET /api/analytics/hotspots/:id endpoint for details - Create POST /api/analytics/hotspots/calculate for manual trigger - Implement scheduled job with node-cron (runs every 5 minutes) - Add comprehensive test scripts for hotspot functionality - Create detailed implementation documentation Requirements addressed: - 3.2: Identify areas with 5+ reports within 500m radius - 3.3: Rank hotspots by severity score - 3.4: Update calculations within 5 minutes - 3.5: Provide data for map visualization with severity indicators
8.5 KiB
Hotspot Calculation System Implementation
Overview
The hotspot calculation system identifies and tracks areas with high concentrations of litter reports. It automatically calculates hotspots every 5 minutes using MySQL spatial queries and stores the results for efficient retrieval.
Requirements Addressed
- Requirement 3.2: Identify areas with at least 5 reports within a 500-meter radius over the past 30 days
- Requirement 3.3: Rank Litter Hotspots by severity score calculated from report quantity and frequency
- Requirement 3.4: Update Litter Hotspot calculations within 5 minutes of new report submissions
- Requirement 3.5: Display Litter Hotspot boundaries on the Map Interface with color-coded severity indicators
Architecture
Components
-
Spatial Utilities (
server/src/utils/spatial.ts)calculateHotspots(): Performs MySQL spatial clustering using ST_GeoHashupdateHotspotsTable(): Clears old hotspots and inserts new calculations
-
Analytics Service (
server/src/services/analyticsService.ts)getHotspots(): Retrieves current hotspots from databasecalculateAndUpdateHotspots(): Triggers hotspot calculationgetHotspotDetails(id): Gets detailed information about a specific hotspot
-
Analytics Controller (
server/src/controllers/analyticsController.ts)getHotspots(): GET endpoint handlergetHotspotDetails(): GET endpoint handler for specific hotspotcalculateHotspots(): POST endpoint handler for manual calculation
-
Scheduled Jobs (
server/src/services/scheduledJobs.ts)- Runs hotspot calculation every 5 minutes using node-cron
- Initialized on server startup
API Endpoints
GET /api/analytics/hotspots
Retrieves all current hotspots (calculated within the last hour).
Response:
{
"success": true,
"data": [
{
"id": "uuid",
"latitude": 53.3498,
"longitude": -6.2603,
"radius": 500,
"reportCount": 12,
"severityScore": 28.5,
"lastReportDate": "2025-11-17T00:00:00.000Z",
"calculatedAt": "2025-11-17T00:15:00.000Z"
}
]
}
GET /api/analytics/hotspots/:id
Retrieves detailed information about a specific hotspot, including all reports within its radius.
Response:
{
"success": true,
"data": {
"hotspot": {
"id": "uuid",
"latitude": 53.3498,
"longitude": -6.2603,
"radius": 500,
"reportCount": 12,
"severityScore": 28.5,
"lastReportDate": "2025-11-17T00:00:00.000Z",
"calculatedAt": "2025-11-17T00:15:00.000Z"
},
"reports": [
{
"id": "uuid",
"latitude": 53.3500,
"longitude": -6.2600,
"litterType": "plastic",
"quantity": "significant",
"createdAt": "2025-11-16T12:00:00.000Z",
"distance": 45.2
}
]
}
}
POST /api/analytics/hotspots/calculate
Manually triggers hotspot calculation (useful for testing or immediate updates).
Response:
{
"success": true,
"data": {
"success": true,
"hotspotsCalculated": 3,
"timestamp": "2025-11-17T00:15:00.000Z"
}
}
Calculation Algorithm
Step 1: Spatial Clustering
The system uses MySQL's ST_GeoHash() function to group nearby reports:
SELECT
AVG(latitude) as center_lat,
AVG(longitude) as center_lng,
COUNT(*) as report_count,
AVG(severity_value) as avg_severity,
MAX(created_at) as last_report_date
FROM reports
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY ST_GeoHash(location, 7)
HAVING COUNT(*) >= 5
Step 2: Severity Score Calculation
Severity score is calculated as:
severity_score = report_count × avg_severity
Where severity values are:
- minimal: 1
- moderate: 2
- significant: 3
- severe: 4
Step 3: Storage
Hotspots are stored in the hotspots table with:
- Geographic center point (POINT spatial type)
- 500m radius
- Report count
- Severity score
- Last report date
- Calculation timestamp
Step 4: Cleanup
Old hotspots (calculated more than 1 hour ago) are automatically removed before new calculations.
Scheduled Job
The hotspot calculation runs automatically every 5 minutes:
cron.schedule('*/5 * * * *', async () => {
await analyticsService.calculateAndUpdateHotspots();
});
This ensures hotspots are updated within 5 minutes of new report submissions (Requirement 3.4).
Database Schema
CREATE TABLE hotspots (
id VARCHAR(36) PRIMARY KEY DEFAULT (UUID()),
location POINT NOT NULL SRID 4326,
latitude DECIMAL(10, 8) NOT NULL,
longitude DECIMAL(11, 8) NOT NULL,
radius DECIMAL(10, 2) NOT NULL COMMENT 'Radius in meters',
report_count INT NOT NULL,
severity_score DECIMAL(5, 2) NOT NULL,
last_report_date TIMESTAMP NOT NULL,
calculated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
SPATIAL INDEX idx_location (location),
INDEX idx_severity_score (severity_score)
) ENGINE=InnoDB;
Testing
Unit Tests
Run the hotspot calculation test:
cd server
npx tsx src/scripts/testHotspots.ts
API Endpoint Tests
- Start the server:
cd server
npm run dev
- Run endpoint tests:
npx tsx src/scripts/testHotspotEndpoints.ts
Manual Testing
# Get all hotspots
curl http://localhost:3005/api/analytics/hotspots
# Trigger manual calculation
curl -X POST http://localhost:3005/api/analytics/hotspots/calculate
# Get specific hotspot details
curl http://localhost:3005/api/analytics/hotspots/{hotspot-id}
Frontend Integration
To display hotspots on the map:
import { useQuery } from '@tanstack/react-query';
function MapView() {
const { data: hotspots } = useQuery({
queryKey: ['hotspots'],
queryFn: async () => {
const response = await fetch('/api/analytics/hotspots');
return response.json();
},
refetchInterval: 5 * 60 * 1000, // Refetch every 5 minutes
});
// Render hotspots as circles on the map
hotspots?.data.forEach(hotspot => {
L.circle([hotspot.latitude, hotspot.longitude], {
radius: hotspot.radius,
color: getSeverityColor(hotspot.severityScore),
fillOpacity: 0.3,
}).addTo(map);
});
}
function getSeverityColor(score: number): string {
if (score >= 50) return '#dc2626'; // red - critical
if (score >= 30) return '#ea580c'; // orange - high
if (score >= 15) return '#f59e0b'; // yellow - medium
return '#84cc16'; // green - low
}
Performance Considerations
- Spatial Indexes: The
hotspotstable uses spatial indexes for efficient location queries - Caching: Hotspots are cached for 1 hour to reduce calculation frequency
- Scheduled Updates: 5-minute intervals balance freshness with server load
- Query Optimization: Uses MySQL spatial functions for efficient clustering
Monitoring
The scheduled job logs its activity:
[Scheduled Jobs] Initializing scheduled jobs...
[Scheduled Jobs] ✓ Hotspot calculation job scheduled (every 5 minutes)
[Scheduled Jobs] Running hotspot calculation...
[Hotspot Calculation] Starting hotspot calculation...
[Hotspot Calculation] Successfully calculated 3 hotspots
[Scheduled Jobs] Hotspot calculation completed: 3 hotspots calculated
Future Enhancements
- Historical Tracking: Store hotspot history for trend analysis
- Notification System: Alert users when new hotspots appear in their areas of interest
- Predictive Analysis: Use ML to predict future hotspot locations
- Custom Thresholds: Allow admins to configure minimum report count and radius
- Hotspot Decay: Reduce severity scores for hotspots with no recent reports
Troubleshooting
No Hotspots Calculated
- Verify there are at least 5 reports within 500m in the last 30 days
- Check that reports have valid spatial data
- Review logs for calculation errors
Scheduled Job Not Running
- Verify node-cron is installed:
npm list node-cron - Check server logs for initialization messages
- Ensure server is running continuously
Spatial Query Errors
- Verify MySQL version is 8.0+ with spatial support
- Check that POINT columns use SRID 4326
- Ensure spatial indexes are created
Related Files
server/src/services/analyticsService.ts- Hotspot business logicserver/src/controllers/analyticsController.ts- API endpointsserver/src/routes/analyticsRoutes.ts- Route definitionsserver/src/services/scheduledJobs.ts- Cron job configurationserver/src/utils/spatial.ts- Spatial utility functionsserver/prisma/schema.prisma- Database schema