# Beacon System Documentation

## 📍 Local Safety & Social Awareness Platform

**Version**: 3.0  
**Status**: ✅ **COMPLETED**  
**Last Updated**: February 17, 2026

---

## 🎯 Overview

The Beacon system transforms local safety and community awareness into an engaging, positive platform that connects neighbors and promotes mutual aid rather than fear-mongering. It combines real-time mapping, categorized alerts, and actionable help items to create a safer, more connected community.

## 🏗️ Architecture

### System Components

```
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Beacon API      │    │   Map Service     │    │   Notification   │
│                 │◄──►│                 │◄──►│                 │
│ • CRUD Operations│    │ • Geospatial     │    │ • Push Alerts   │
│ • Validation     │    │ • Clustering     │    │ • Email Alerts   │
│ • Scoring        │    │ • Filtering     │    │ • SMS Alerts   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Database        │    │   External APIs   │    │   User Interface │
│                 │    │                 │    │                 │
│ • Beacon Data     │    │ • Geocoding     │    │ • Map View     │
│ • Categories     │    │ • Reverse Geocoding│    │ • Beacon Feed   │
│ • Relationships  │    │ • Weather API    │    │ • Detail View   │
│ • Analytics      │    │ • Address API    │    │ • Create/Edit  │
└─────────────────    └─────────────────    └─────────────────┘
```

---

## 🎨 Core Features

### 📍 Map-Based Discovery
- **Interactive Map**: Flutter Map with clustered pin visualization
- **Real-time Updates**: Live beacon updates without page refresh
- **Geospatial Search**: Find beacons by location or radius
- **Neighborhood Filtering**: Filter by specific neighborhoods or areas
- **Layer Control**: Toggle different beacon categories on map

### 🏷️ Beacon Categories
- **Safety Alert**: Emergency situations, public safety concerns
- **Community Need**: Requests for help, volunteer opportunities
- **Lost & Found**: Missing persons, pets, or items
- **Events**: Community events, meetings, gatherings
- **Mutual Aid**: Resource sharing, community support initiatives

### ✅ Verification System
- **Official Badges**: Verified badges for government and official organizations
- **Trust Indicators**: Visual indicators for source reliability
- **Confidence Scoring**: Community-driven trust metrics
- **Source Validation**: API integration for official verification

### 🤝 Action-Oriented Help
- **How to Help**: Specific, actionable help items for each beacon
- **Volunteer Opportunities**: Sign-up forms and contact information
- **Resource Sharing**: Links to needed resources or donations
- **Community Coordination**: Tools for organizing community response

### 📊 Analytics & Insights
- **Engagement Metrics**: Track vouch/report ratios and community response
- **Resolution Tracking**: Monitor beacon lifecycle from active to resolved
- **Impact Assessment**: Measure community impact of beacon activities
- **Trend Analysis**: Identify patterns in local safety and community needs

---

## 📱 Implementation Details

### Backend Services

#### Beacon Handler
**File**: `go-backend/internal/handlers/beacon_handler.go`

```go
type BeaconHandler struct {
    db          *pgxpool.Pool
    geoService  *GeoService
    notifier    *NotificationService
    validator   *BeaconValidator
}

// Create new beacon
func (h *BeaconHandler) CreateBeacon(c *gin.Context) {
    var req CreateBeaconRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // Validate beacon data
    if err := h.validator.ValidateBeacon(req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // Create beacon with geocoding
    beacon, err := h.geoService.GeocodeLocation(req.Latitude, req.Longitude)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to geocode location"})
        return
    }
    
    // Save to database
    id, err := h.db.Exec(
        `INSERT INTO beacons (title, description, category, latitude, longitude, 
                           author_id, is_official, created_at) 
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
        req.Title, req.Description, req.Category, beacon.Latitude, 
        beacon.Longitude, req.AuthorID, req.IsOfficial, time.Now(),
    )
    
    c.JSON(201, gin.H{"id": id, "status": "created"})
}

// Get beacons with clustering
func (h *BeaconHandler) GetBeacons(c *gin.Context) {
    var filters BeaconFilters
    if err := c.ShouldBindQuery(&filters); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // Get beacons with clustering
    beacons, err := h.geoService.GetClusteredBeacons(filters)
    if err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }
    
    c.JSON(200, beacons)
}
```

#### Geospatial Service
**File**: `go-backend/internal/services/geo_service.go`

```go
type GeoService struct {
    db *pgxpool.Pool
}

// Cluster nearby beacons
func (s *GeoService) GetClusteredBeacons(filters BeaconFilters) ([]BeaconCluster, error) {
    // Get all beacons within radius
    query := `
        SELECT id, title, category, latitude, longitude, author_id, is_official, created_at
        FROM beacons 
        WHERE ST_DWithin(
            ST_MakePoint(longitude, latitude, 4326),
            ST_MakePoint($1, $2, $3),
            $4
        )
        ORDER BY created_at DESC
    `
    
    rows, err := s.db.Query(context.Background(), query, filters.CenterLat, filters.CenterLng, filters.RadiusKm * 1000)
    if err != nil {
        return nil, err
    }
    defer rows.Close()
    
    var beacons []EnhancedBeacon
    for rows.Next() {
        var beacon EnhancedBeacon
        if err := rows.Scan(&beacon.ID, &beacon.Title, &beacon.Category, 
            &beacon.Latitude, &beacon.Longitude, &beacon.AuthorID, 
            &beacon.IsOfficial, &beacon.CreatedAt); err != nil {
            return nil, err
        }
        beacons = append(beacons, beacon)
    }
    
    // Cluster beacons
    return s.clusterBeacons(beacons), nil
}

// Geocode address to coordinates
func (s *GeoService) GeocodeLocation(lat, lng float64) (*Location, error) {
    // Use reverse geocoding service
    // This would integrate with Google Geocoding API or similar
    return &Location{
        Latitude:  lat,
        Longitude: lng,
        Address:   "Reverse geocoded address",
        City:      "City name",
        Country:   "Country name",
    }, nil
}
```

### Frontend Components

#### Enhanced Beacon Map Widget
**File**: `sojorn_app/lib/widgets/beacon/enhanced_beacon_map.dart`

```dart
class EnhancedBeaconMap extends ConsumerWidget {
  final List<EnhancedBeacon> beacons;
  final Function(EnhancedBeacon)? onBeaconTap;
  final Function(LatLng)? onMapTap;
  final BeaconFilter? filter;
  final bool showUserLocation;
  final bool enableClustering;
  
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return FlutterMap(
      mapController: _mapController,
      options: MapOptions(
        initialCenter: widget.initialCenter ?? _userLocation,
        initialZoom: _currentZoom,
        onMapEvent: (event) => _handleMapEvent(event),
        onTap: (tapPosition, point) => widget.onMapTap?.call(point),
      ),
      children: [
        TileLayer(
          urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
          userAgentPackageName: 'com.example.sojorn',
        ),
        MarkerLayer(
          markers: _buildMapMarkers(),
        ),
        if (widget.showUserLocation)
          MarkerLayer(
            markers: _buildUserLocationMarker(),
          ),
      ],
    );
  }
}
```

#### Beacon Detail Screen
**File**: `sojorn_app/lib/screens/beacon/enhanced_beacon_detail_screen.dart`

```dart
class EnhancedBeaconDetailScreen extends StatelessWidget {
  final EnhancedBeacon beacon;
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: Text(beacon.title),
        iconTheme: const IconThemeData(color: Colors.white),
      ),
      body: CustomScrollView(
        slivers: [
          // Map view with beacon location
          SliverAppBar(
            expandedHeight: 250,
            pinned: true,
            flexibleSpace: FlexibleSpaceBar(
              background: Container(
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      Colors.transparent,
                      Colors.black.withOpacity(0.7),
                      Colors.black,
                    ],
                  ),
                ),
              ),
              ),
              child: FlutterMap(
                mapController: _mapController,
                options: MapOptions(
                  initialCenter: LatLng(beacon.lat, beacon.lng),
                  initialZoom: 15.0,
                ),
                children: [
                  TileLayer(
                    urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                  ),
                  MarkerLayer(
                    markers: [
                      Marker(
                        point: LatLng(beacon.lat, beacon.lng),
                        child: _buildBeaconMarker(beacon),
                      ),
                    ],
                  ),
                ],
              ),
            ),
            
            // Beacon content
            SliverToBoxAdapter(
              child: Padding(
                padding: const EdgeInsets.all(16),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    // Title and metadata
                    _buildBeaconHeader(beacon),
                    
                    // Description
                    _buildBeaconDescription(beacon),
                    
                    // Image if available
                    if (beacon.imageUrl != null)
                      _buildBeaconImage(beacon.imageUrl),
                    
                    // Help actions
                    _buildHelpActions(beacon),
                    
                    // Engagement stats
                    _buildEngagementStats(beacon),
                  ],
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
```

---

## 🗂️ Data Models

### Enhanced Beacon Model
```dart
class EnhancedBeacon {
  final String id;
  final String title;
  final String description;
  final BeaconCategory category;
  final BeaconStatus status;
  final double lat;
  final double lng;
  final String authorId;
  final String authorHandle;
  final String? authorAvatar;
  final bool isVerified;
  final bool isOfficialSource;
  final String? organizationName;
  final DateTime createdAt;
  final DateTime? expiresAt;
  final int vouchCount;
  final int reportCount;
  final double confidenceScore;
  final String? imageUrl;
  final List<String> actionItems;
  final String? neighborhood;
  final double? radiusMeters;
}
```

### Beacon Cluster Model
```dart
class BeaconCluster {
  final List<EnhancedBeacon> beacons;
  final double lat;
  final double lng;
  final int count;
  
  BeaconCluster({
    required this.beacons,
    required this.lat,
    required this.lng,
  }) : count = beacons.length;
  
  BeaconCategory get dominantCategory {
    // Find most common category in cluster
    final categoryCount = <BeaconCategory, int>{};
    for (final beacon in beacons) {
      categoryCount[beacon.category] = (categoryCount[beacon.category] ?? 0) + 1;
    }
    
    return categoryCount.entries.reduce((a, b) => 
      a.value > b.value ? a : b
    ).key;
  }
  
  bool get hasOfficialSource {
    return beacons.any((b) => b.isOfficialSource);
  }
  
  EnhancedBeacon get priorityBeacon {
    // Return highest priority beacon
    final officialBeacons = beacons.where((b) => b.isOfficialSource).toList();
    if (officialBeacons.isNotEmpty) {
      return officialBeacons.reduce((a, b) => 
        a.createdAt.isAfter(b.createdAt) ? a : b
      );
    }
    
    final highConfidenceBeacons = beacons.where((b) => b.isHighConfidence).toList();
    if (highConfidenceBeacons.isNotEmpty) {
      return highConfidenceBeacons.reduce((a, b) => 
        a.createdAt.isAfter(b.createdAt) ? a : b
      );
    }
    
    return beacons.reduce((a, b) => 
      a.createdAt.isAfter(b.createdAt) ? a : b
    );
  }
}
```

### Beacon Filter Model
```dart
class BeaconFilter {
  final Set<BeaconCategory> categories;
  final Set<BeaconStatus> statuses;
  final bool onlyOfficial;
  final double? radiusKm;
  final String? neighborhood;
  
  bool matches(EnhancedBeacon beacon) {
    // Category filter
    if (categories.isNotEmpty && !categories.contains(beacon.category)) {
      return false;
    }
    
    // Status filter
    if (statuses.isNotEmpty && !statuses.contains(beacon.status)) {
      return false;
    }
    
    // Official filter
    if (onlyOfficial && !beacon.isOfficialSource) {
      return false;
    }
    
    // Neighborhood filter
    if (neighborhood != null && beacon.neighborhood != neighborhood) {
      return false;
    }
    
    return true;
  }
}
```

---

## 🔧 Technical Implementation

### Geospatial Database Schema
```sql
-- Beacon table with geospatial capabilities
CREATE TABLE IF NOT EXISTS beacons (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    title VARCHAR(100) NOT NULL,
    description TEXT,
    category VARCHAR(50) NOT NULL CHECK (category IN ('safety_alert', 'community_need', 'lost_found', 'event', 'mutual_aid')),
    status VARCHAR(20) NOT NULL DEFAULT 'active',
    latitude DECIMAL(10, 8) NOT NULL,
    longitude DECIMAL(10,8) NOT NULL,
    author_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    is_official BOOLEAN DEFAULT FALSE,
    is_verified BOOLEAN DEFAULT FALSE,
    organization_name VARCHAR(100),
    image_url TEXT,
    neighborhood VARCHAR(50),
    radius_meters DECIMAL(8,2),
    vouch_count INTEGER DEFAULT 0,
    report_count INTEGER DEFAULT 0,
    confidence_score DECIMAL(5,2) DEFAULT 0.0,
    action_items TEXT[],
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW(),
    expires_at TIMESTAMP,
    
    -- Geospatial index for location queries
    INDEX idx_beacon_location ON beacons USING GIST (
      geography(Point(longitude, latitude), 
      Circle(radius_meters)
    );
    
    -- Category index for filtering
    INDEX idx_beacon_category ON beacons(category);
    
    -- Status index for filtering
    INDEX idx_beacon_status ON beacons(status);
    
    -- Created at index for sorting
    INDEX idx_beacon_created_at ON beacons(created_at DESC);
    
    -- Author index for user-specific queries
    INDEX idx_beacon_author_id ON beacons(author_id);
);

-- Beacon relationships
CREATE TABLE IF NOT EXISTS beacon_relationships (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    beacon_id UUID NOT NULL REFERENCES beacons(id) ON DELETE CASCADE,
    related_beacon_id UUID NOT NULL REFERENCES beacons(id) ON DELETE CASCADE,
    relationship_type VARCHAR(20) NOT NULL,
    created_at TIMESTAMP DEFAULT NOW(),
    
    UNIQUE (beacon_id, related_beacon_id, relationship_type)
);
```

### Clustering Algorithm
```go
// Cluster nearby beacons based on distance and zoom level
func (s *GeoService) clusterBeacons(beacons []EnhancedBeacon) []BeaconCluster {
    if (!s.enableClustering || _currentZoom >= 15.0) {
        // Show individual beacons at high zoom
        return beacons.map((beacon) => BeaconCluster{
            beacons: []EnhancedBeacon{beacon},
            lat: beacon.lat,
            lng: beacon.lng,
        });
    }
    
    // Calculate cluster radius based on zoom level
    clusterRadius := 0.01 * (16.0 - _currentZoom)
    
    var clusters []BeaconCluster
    processedBeacons := make(map[string]bool)
    
    for _, beacon := range beacons {
        if processedBeacons[beacon.ID] {
            continue
        }
        
        var nearbyBeacons []EnhancedBeacon
        for _, otherBeacon := range beacons {
            if processedBeacons[otherBeacon.ID] {
                continue
            }
            
            distance := calculateDistance(beacon, otherBeacon)
            if distance <= clusterRadius {
                nearbyBeacons = append(nearbyBeacons, otherBeacon)
                processedBeacons[otherBeacon.ID] = true
            }
        }
        
        if len(nearbyBeacons) > 0 {
            // Calculate cluster center (average position)
            avgLat := nearbyBeacons.reduce((sum, beacon) => 
                sum.lat + beacon.lat, 0
            ) / float64(len(nearbyBeacons))
            avgLng := nearbyBeacons.reduce((sum, beacon) => 
                sum.lng + beacon.lng, 0
            ) / float64(nearbyBeacons))
            
            clusters = append(clusters, BeaconCluster{
                beacons: nearbyBeacons,
                lat: avgLat,
                lng: avgLng,
            })
            
            // Mark all beacons in this cluster as processed
            for _, beacon := range nearbyBeacons {
                processedBeacons[beacon.ID] = true
            }
        }
    }
    
    return clusters
}

func calculateDistance(beacon1, beacon2 EnhancedBeacon) float64 {
    // Haversine distance calculation
    lat1 := beacon1.lat * math.Pi / 180
    lng1 := beacon1.lng * math.Pi / 180
    lat2 := beacon2.lat * math.Pi / 180
    lng2 := beacon2.lng * math.Pi / 180
    
    dlat := lat2 - lat1
    dlng := lng2 - lng1
    
    a := math.Sin(dlat/2) * math.Sin(dlng/2)
    c := math.Cos(lat1) * math.Cos(lat2)
    
    return 6371 * 2 * math.Asin(math.Sqrt(a*a + c*c*math.Cos(dlng/2)*math.Cos(dlng/2)))
}
```

---

## 📱 User Interface

### Map View
- **Interactive Controls**: Zoom, pan, and tap interactions
- **Cluster Visualization**: Visual clustering of nearby beacons
- **Filter Controls**: Category, status, and official source filters
- **User Location**: Current location indicator on map
- **Legend**: Clear visual indicators for different beacon types

### Beacon Feed
- **Card Layout**: Clean, scannable card design
- **Category Badges**: Visual category indicators
- **Trust Indicators**: Verification and confidence scores
- **Action Buttons**: Quick access to help actions
- **Preview Images**: Thumbnail previews when available

### Detail View
- **Full Context**: Complete beacon information and description
- **Map Integration**: Embedded map showing beacon location
- **Help Actions**: Detailed help items with contact information
- **Engagement**: Vouch, report, and share functionality
- **Author Info**: Beacon creator information and verification status

### Creation Flow
- **Location Selection**: Map-based location selection or current location
- **Category Selection**: Choose appropriate beacon category
- **Information Entry**: Title, description, and details
- **Image Upload**: Optional image for visual context
- **Review & Post**: Preview and publish beacon

---

## 🔒 Security & Privacy

### Location Privacy
- **Approximate Location**: Beacon locations are approximate to protect exact addresses
- **User Control**: Users control location sharing preferences
- **Data Minimization**: Only necessary location data is stored
- **Retention Policy**: Location data retained according to user preferences

### Content Moderation
- **AI Analysis**: AI-powered content analysis for safety
- **Community Flagging**: User-driven content reporting system
- **Automated Filtering**: Automatic detection of inappropriate content
- **Appeal Process**: Fair and transparent appeal system

### User Safety
- **Anonymous Reporting**: Option to report anonymously
- **Block System**: Block problematic users from seeing beacons
- **Reporting History**: Track and manage reporting history
- **Emergency Contacts**: Quick access to emergency services

---

## 📊 Analytics & Metrics

### Engagement Metrics
- **Vouch/Report Ratio**: Community trust indicators
- **Response Time**: Average time to first response
- **Resolution Rate**: Percentage of beacons marked as resolved
- **Participation Rate**: Community engagement levels

### Geographic Insights
- **Hotspot Analysis**: Areas with high beacon activity
- **Coverage Maps**: Geographic coverage visualization
- **Trend Analysis**: Patterns in local needs and issues
- **Resource Distribution**: Analysis of help requests and offers

### Performance Metrics
- **Map Performance**: Map rendering and interaction performance
- **API Response**: Beacon API response times
- **Database Queries**: Database query optimization metrics
- **User Experience**: Page load and interaction performance

---

## 🚀 Deployment

### Environment Configuration
```bash
# Geospatial database setup
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_topology;

# Beacon service configuration
BEACON_SERVICE_URL=http://localhost:8080
BEACON_SERVICE_TIMEOUT=30s
BEACON_MAX_RADIUS_KM=50

# Notification settings
BEACON_NOTIFICATION_ENABLED=true
BEACON_PUSH_NOTIFICATIONS=true
BEACON_EMAIL_NOTIFICATIONS=true
BEACON_SMS_NOTIFICATIONS=false
```

### Health Checks
```go
// Beacon service health check
func (s *BeaconService) HealthCheck() HealthStatus {
    // Check database connectivity
    if err := s.db.Ping(context.Background()); err != nil {
        return HealthStatus{
            Status: "unhealthy",
            Message: "Database connection failed",
        }
    }
    
    // Check geospatial extensions
    var result string
    err := s.db.QueryRow(
        context.Background(),
        "SELECT 1 FROM postgis_version",
        &result,
    )
    if err != nil {
        return HealthStatus{
            Status: "degraded",
            Message: "PostGIS extensions not available",
        }
    }
    
    return HealthStatus{
        Status: "healthy",
        Message: "Beacon service ready",
    }
}
```

---

## 📚 Troubleshooting

### Common Issues

#### Map Not Loading
```dart
// Check map initialization
if (_mapController == null) {
  _initMap();
}

// Check network connectivity
final connectivity = await Connectivity().checkConnectivity();
if (!connectivity) {
  _showError('No internet connection');
  return;
}
```

#### Geocoding Errors
```go
// Check PostGIS extensions
_, err := db.QueryRow(
    context.Background(),
    "SELECT postgis_version",
    &result,
)
if err != nil {
    log.Error("PostGIS not available: %v", err)
    return nil, err
}
```

#### Clustering Issues
```dart
// Check clustering parameters
if (_currentZoom < 10.0 && _enableClustering) {
  // Clustering disabled at low zoom levels
  return _buildIndividualMarkers();
}

// Check cluster radius
if (clusterRadius < 0.001) {
  // Cluster radius too small
  return _buildIndividualMarkers();
}
```

---

## 📝 Future Enhancements

### Version 3.1 (Planned)
- **Real-time Updates**: WebSocket-based live beacon updates
- **Advanced Analytics**: More detailed engagement metrics
- **Mobile Optimization**: Improved mobile performance
- **Offline Support**: Offline map caching and sync

### Version 4.0 (Long-term)
- **3D Map View**: 3D visualization of beacon locations
- **AR Integration**: Augmented reality beacon discovery
- **Voice Commands**: Voice-controlled beacon creation
- **Machine Learning**: Predictive beacon suggestions

---

## 📞 Support & Documentation

### User Guides
- **Getting Started**: Quick start guide for beacon creation
- **Safety Guidelines**: Best practices for beacon usage
- **Community Building**: Guide to effective community engagement
- **Troubleshooting**: Common issues and solutions

### Developer Resources
- **API Documentation**: Complete API reference
- **Database Schema**: Database design and relationships
- **Integration Guide**: Third-party service integration
- **Code Examples**: Sample code and implementations

### Community Support
- **Discord**: Beacon development discussion channel
- **GitHub**: Issue tracking and feature requests
- **Documentation**: Regular updates and improvements
- **Training**: Educational resources and tutorials

---

**📍 The Beacon system transforms local safety into an engaging, positive community platform that connects neighbors and promotes mutual aid rather than fear-mongering. With intelligent clustering, verified sources, and actionable help items, it creates a safer, more connected community.**
