A comprehensive TypeScript project for rendering multiple types of interactive map visualizations based on location coordinates using Leaflet and modern web technologies.
- πΊοΈ Interactive Maps: Built with Leaflet for smooth pan, zoom, and navigation
- π₯ Dynamic Heatmaps: Real-time heatmap rendering based on location data
- π Density Grid: Grid-based density analysis with configurable cell size
- π¨ Alert Markers: Interactive alert markers with custom icons and severity levels
- π¨ Customizable: Configurable colors, intensity, radius, and opacity for all visualization types
- π± Responsive: Works on desktop and mobile devices
- π Dark/Light Theme: Automatic theme detection and styling
- β‘ Fast: Built with Vite for optimal performance
- π File Loading: Support for JSON, CSV, TSV, and GeoJSON data files
- π URL Loading: Load data directly from web URLs
- π Drag & Drop: Easy file upload with drag and drop interface
- ποΈ Interactive Controls: Real-time configuration for each visualization type
- π§ Robust Tab Switching: Intelligent map rendering with container validation and automatic resizing
Classic intensity-based heatmap showing data distribution with smooth color gradients.
Grid-based visualization showing point density within configurable cells. Features:
- Adjustable grid size (10-50 cells)
- Color-coded density representation
- Cell-specific statistics (point count, density, area)
- Intelligent container sizing and rendering
- Automatic map invalidation on tab switching
Interactive markers with custom icons for all location data visualization. Features:
- Multiple alert types (critical, warning, info, danger, success)
- Severity levels (1-5) with visual indicators
- Rich popups with location details
- Optional pulse animations
- Customizable icon sizes and styles
- Works with any uploaded location data
The application uses separate renderer instances for each visualization type to prevent cross-contamination and ensure proper map rendering:
- Isolated Renderers: Each visualization type (heatmap, density, markers) maintains its own dedicated renderer instance
- Container Validation: Before rendering, the system checks if containers are visible and have proper dimensions
- Automatic Retry: If a container isn't ready, the system automatically retries rendering after a short delay
- Map Invalidation: After tab switches, maps are automatically resized to fit their containers correctly
- Error Handling: Comprehensive error handling prevents crashes from rendering issues
- Node.js 16+ and npm
- Install dependencies:
npm install- Start the development server:
npm run dev- Open your browser to
http://localhost:5173
The application now supports multiple visualization types through a unified interface:
import { MapVisualizationRenderer } from './components/MapVisualizationRenderer'
import type { Location, VisualizationType } from './types'
// Create location data
const locations: Location[] = [
{
id: 1,
name: 'New York',
coordinates: { lat: 40.7128, lng: -74.0060 },
intensity: 0.9
},
{
id: 2,
name: 'Los Angeles',
coordinates: { lat: 34.0522, lng: -118.2437 },
intensity: 0.7
}
]
// Initialize unified renderer
const renderer = new MapVisualizationRenderer({
container: 'map-container',
center: [39.8283, -98.5795],
zoom: 4,
visualizationType: 'heatmap'
})
// Switch between visualization types
renderer.setVisualizationType('density', {
density: {
gridSize: 20,
colorScale: ['#ffffcc', '#fd8d3c', '#800026'],
aggregationMethod: 'count'
}
})
renderer.render(locations)import type { AlertMarker } from './types'
const alertData: AlertMarker[] = [
{
id: 'alert-001',
name: 'Critical System Failure',
coordinates: { lat: 40.7580, lng: -73.9855 },
alertType: 'critical',
severity: 5,
status: 'active',
description: 'Multiple server failures detected',
timestamp: new Date()
}
]
// Render alert markers
renderer.setVisualizationType('markers', {
markers: {
iconSize: [32, 32],
pulseAnimation: true,
showPopups: true
}
})
renderer.render(alertData)The application supports loading location data from various file formats:
- JSON: Array of location objects or nested structure
- CSV: Comma-separated values with customizable column mapping
- TSV: Tab-separated values
- GeoJSON: Standard geographic data format
import { FileLoader } from './utils/fileLoader'
// Load from file
const file = document.getElementById('fileInput').files[0]
const result = await FileLoader.loadFromFile(file, {
format: 'csv',
hasHeader: true,
columnMapping: {
lat: 'latitude',
lng: 'longitude',
intensity: 'weight',
name: 'city_name'
}
})
// Load from URL
const urlResult = await FileLoader.loadFromUrl('https://example.com/data.json')
// Use the loaded data
renderer.updateData(result.locations)JSON Format:
[
{
"id": 1,
"name": "New York",
"lat": 40.7128,
"lng": -74.0060,
"intensity": 0.9
}
]CSV Format:
name,lat,lng,intensity
New York,40.7128,-74.0060,0.9
Los Angeles,34.0522,-118.2437,0.7JSON Format:
[
{
"id": 1,
"name": "New York",
"lat": 40.7128,
"lng": -74.0060,
"intensity": 0.9
}
]CSV Format:
name,lat,lng,intensity
New York,40.7128,-74.0060,0.9
Los Angeles,34.0522,-118.2437,0.7GeoJSON Format with Custom Properties:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [-74.0060, 40.7128]
},
"properties": {
"name": "New York",
"intensity": 0.9,
"address": "Π.Π₯Π°ΡΡΠΊΡΠ²ΡΡΠΊΠ°,2, ΠΠΈΡΠ², Π£ΠΊΡΠ°ΡΠ½Π°",
"alert_type": "2π΅ Π²ΠΈΠΉΡΠ»ΠΈ Π· ΠΏΠ°ΡΡΡΠ»ΡΠΊΠΈ Ρ ΡΠΏΡΡΡΠΈΠ»ΠΈΡΡ Π² ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄",
"operator": "Patrol Unit Alpha",
"priority": "medium",
"custom_field": "Any custom data"
}
}
]
}π Custom Properties Support: All properties from GeoJSON features are automatically displayed in marker popups. The application intelligently extracts common fields like
address,alert_type,operator, etc., and displays them in an organized format within the marker details.
The application provides rich support for displaying custom properties from GeoJSON data:
- Smart Field Mapping: Automatically detects common fields like
address,alert_type,operator,priority - Flexible Naming: Supports various naming conventions (
alert_type,alertType,type,category) - Unicode Support: Properly displays emoji and special characters in property values
- Multilingual: Full support for international text and addresses
- Organized Layout: Properties are displayed in a clean, organized format within marker popups
- Key Formatting: Property names are automatically formatted for better readability
- Value Processing: Handles different data types (strings, numbers, booleans, objects)
- Visual Hierarchy: Important properties are highlighted and organized logically
The system automatically categorizes and displays properties:
- Core Information: Name, description, coordinates
- Alert Details: Alert type, severity, status, timestamp
- Location Data: Address, geographical information
- Custom Properties: Any additional fields from your GeoJSON data
- Operational Data: Operator, priority, response times, etc.
For the GeoJSON example above, the marker popup will show:
π Location Details
Type: INFO
Severity: β
β
β
ββ
Time: [timestamp]
Coordinates: 40.712800, -74.006000
π Additional Information
Address: Π.Π₯Π°ΡΡΠΊΡΠ²ΡΡΠΊΠ°,2, ΠΠΈΡΠ², Π£ΠΊΡΠ°ΡΠ½Π°
Alert Type: 2π΅ Π²ΠΈΠΉΡΠ»ΠΈ Π· ΠΏΠ°ΡΡΡΠ»ΡΠΊΠΈ Ρ ΡΠΏΡΡΡΠΈΠ»ΠΈΡΡ Π² ΠΏΠ΅ΡΠ΅Ρ
ΠΎΠ΄
Operator: Patrol Unit Alpha
Priority: medium
interface Location {
id?: string | number; // Optional unique identifier
name?: string; // Optional location name
coordinates: { // Required coordinates
lat: number; // Latitude (-90 to 90)
lng: number; // Longitude (-180 to 180)
};
intensity?: number; // Heat intensity (0-1)
metadata?: Record<string, any>; // Optional additional data
}interface AlertMarker extends Location {
alertType?: 'critical' | 'warning' | 'info' | 'resolved' | 'danger' | 'success';
timestamp?: Date; // Alert timestamp
description?: string; // Alert description
severity?: number; // Severity level (1-5)
status?: 'active' | 'acknowledged' | 'resolved';
}// Density visualization config
interface DensityConfig {
gridSize?: number; // Grid cell count (default: 20)
colorScale?: string[]; // Color gradient array
opacity?: number; // Cell opacity
aggregationMethod?: 'count' | 'sum' | 'average';
showGrid?: boolean; // Show grid lines
}
// Marker visualization config
interface MarkerConfig {
iconType?: 'alert' | 'warning' | 'info' | 'danger' | 'success' | 'critical';
iconSize?: [number, number]; // Icon dimensions
clustering?: boolean; // Enable marker clustering
showPopups?: boolean; // Show popup on click
pulseAnimation?: boolean; // Enable pulse animation
}
// Point visualization config
interface PointConfig {
radius?: number; // Circle radius
fillColor?: string; // Fill color
strokeColor?: string; // Border color
strokeWidth?: number; // Border width
fillOpacity?: number; // Fill transparency
strokeOpacity?: number; // Border transparency
}new MapVisualizationRenderer({
container: string | HTMLElement, // Map container
center?: [number, number], // Initial center coordinates
zoom?: number, // Initial zoom level
visualizationType?: VisualizationType,
config?: VisualizationTypeConfigs
})-
render(data: Location[]): void- Render data with current visualization type
-
setVisualizationType(type: VisualizationType, config?: VisualizationTypeConfigs): void- Change visualization type and optionally update config
-
updateConfig(config: VisualizationTypeConfigs): void- Update visualization configuration
-
getStats(): object- Get current visualization statistics
-
destroy(): void- Clean up and remove map instance
-
getMap(): L.Map- Get the underlying Leaflet map instance
-
initialize(config: HeatmapVisualization): Promise<void>- Initialize map and heatmap with configuration
-
updateData(locations: Location[], config?: HeatmapConfig): void- Update heatmap with new location data
-
destroy(): void- Clean up and remove map instance
-
getMap(): L.Map | null- Get the underlying Leaflet map instance
-
calculateDensityGrid(locations: Location[], bounds: Bounds, config: DensityConfig): DensityGridCell[]- Calculate density grid from location data
-
getColorForDensity(value: number, maxValue: number, colorScale: string[]): string- Get color for specific density value
-
calculateOptimalGridSize(locations: Location[]): number- Calculate optimal grid size based on data density
-
createAlertIcon(alertType: string, severity: number, config: MarkerConfig): L.DivIcon- Generate custom alert icon
-
createAlertPopup(alert: AlertMarker): string- Create popup content for alert markers
-
getAlertMarkerCSS(): string- Get CSS styles for alert markers
-
validateCoordinates(coord: Coordinate): boolean- Validate geographic coordinates
-
calculateDistance(coord1: Coordinate, coord2: Coordinate): number- Calculate distance between two points in kilometers
-
convertToHeatmapData(locations: Location[]): HeatmapDataPoint[]- Convert Location objects to heatmap format
-
calculateCenter(locations: Location[]): Coordinate- Calculate geographic center of locations
-
normalizeIntensities(locations: Location[]): Location[]- Normalize intensity values to 0-1 range
-
loadFromFile(file: File, options?: FileLoadOptions): Promise<FileLoadResult>- Load location data from uploaded file
-
loadFromUrl(url: string, options?: FileLoadOptions): Promise<FileLoadResult>- Load location data from web URL
-
generateSampleFiles(): Record<string, string>- Generate sample data files for testing
interface FileLoadOptions {
format?: 'json' | 'csv' | 'tsv' | 'geojson';
columnMapping?: {
lat: string;
lng: string;
intensity?: string;
name?: string;
id?: string;
};
delimiter?: string;
hasHeader?: boolean;
}- TypeScript - Type-safe JavaScript
- Vite - Fast build tool and dev server
- Leaflet - Interactive map library
- heatmap.js - Heatmap visualization
- Chrome 87+
- Firefox 78+
- Safari 14+
- Edge 88+
npm run dev- Start development servernpm run build- Build for productionnpm run preview- Preview production buildnpm run type-check- Run TypeScript type checking
src/
βββ components/ # Reusable components
β βββ HeatmapRenderer.ts # Legacy heatmap renderer
β βββ MapVisualizationRenderer.ts # Unified multi-type renderer
β βββ FileUploadComponent.ts # File upload interface
βββ data/ # Sample data and generators
β βββ sampleData.ts # Original sample data
β βββ sampleAlertData.ts # Alert and visualization samples
βββ utils/ # Utility functions
β βββ geoUtils.ts # Geographic calculations
β βββ dataProcessor.ts # Data processing utilities
β βββ fileLoader.ts # File loading utilities
β βββ densityCalculator.ts # Density grid calculations
β βββ alertIconGenerator.ts # Alert icon generation
βββ types/ # TypeScript interfaces
β βββ index.ts # Type definitions
βββ main.ts # Application entry point
βββ style.css # Global styles and theming
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit changes (
git commit -m 'Add amazing feature') - Push to branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License.
- OpenStreetMap contributors for map tiles
- Leaflet team for the excellent mapping library
- Patrick Wied for heatmap.js