# CLAUDE.md - Mapbox Globe Iteration 8

## Project Context

This is iteration 8 in a progressive web-enhanced learning series for Mapbox GL JS globe visualizations. This iteration focuses on **advanced point clustering** for large datasets, demonstrating educational infrastructure distribution globally.

## Running the Application

### Local Development Server

```bash
# From the mapbox_globe_8/ directory

# Python 3
python3 -m http.server 8000

# Python 2
python -m SimpleHTTPServer 8000

# Node.js
npx http-server -p 8000

# PHP
php -S localhost:8000
```

Then open: `http://localhost:8000`

## Code Architecture

### File Structure
```
mapbox_globe_8/
├── index.html          # UI, overlays, styling
├── src/
│   ├── index.js       # Map initialization, clustering logic
│   └── data/
│       └── data.js    # 650-school GeoJSON dataset
├── README.md
└── CLAUDE.md          # This file
```

### Key Components

1. **Clustering Configuration** (`src/index.js`)
   - Source setup with cluster parameters
   - `clusterMaxZoom: 14` - stops clustering at zoom 14
   - `clusterRadius: 50` - 50px aggregation radius

2. **Layer System**
   - `clusters` - Aggregated cluster circles
   - `cluster-count` - Point count labels
   - `unclustered-point` - Individual schools
   - `resource-rings` - Resource level indicators

3. **Data Structure** (`src/data/data.js`)
   - GeoJSON FeatureCollection
   - 311 educational facilities
   - Properties: name, type, students, teachers, ratio, resources, internet

## Web Research Integration

### Source URL
https://docs.mapbox.com/mapbox-gl-js/example/cluster/

### Techniques Learned & Applied

1. **Cluster Source Configuration**
   ```javascript
   {
       cluster: true,
       clusterMaxZoom: 14,
       clusterRadius: 50,
       generateId: true
   }
   ```

2. **Step-Based Cluster Styling**
   ```javascript
   'circle-color': [
       'step',
       ['get', 'point_count'],
       '#51bbd6', 50,
       '#f1f075', 150,
       '#f28cb1'
   ]
   ```

3. **Cluster Expansion Interaction**
   ```javascript
   map.getSource('schools').getClusterExpansionZoom(
       clusterId,
       (err, zoom) => {
           map.easeTo({ center: coords, zoom: zoom + 0.5 });
       }
   );
   ```

## Mapbox Best Practices Applied

### Performance Optimization
- **Clustering**: Reduces 311 points to manageable clusters
- **Layer Filtering**: Separate clustered/unclustered layers
- **Generate ID**: Enables efficient feature queries
- **Zoom-based Styling**: Adapts circle sizes to zoom level

### Visual Hierarchy
- Cluster size indicates density
- Color indicates cluster magnitude
- Individual schools show type and resource level
- Progressive disclosure through zoom

### Interactivity
- Click clusters to expand (learned from web research)
- Click schools for detailed popups
- Hover cursor changes
- Keyboard navigation support

## Code Patterns

### Mapbox Expression Syntax

**Step Expression** (thresholds):
```javascript
['step', ['get', 'property'],
    value1, threshold1,
    value2, threshold2,
    value3
]
```

**Match Expression** (categories):
```javascript
['match', ['get', 'type'],
    'Primary', '#667eea',
    'Secondary', '#764ba2',
    'University', '#f093fb',
    '#667eea'  // default
]
```

**Interpolate Expression** (continuous):
```javascript
['interpolate', ['linear'], ['zoom'],
    1, 3,    // at zoom 1, value 3
    10, 8    // at zoom 10, value 8
]
```

### Layer Filtering

**Show only clusters**:
```javascript
filter: ['has', 'point_count']
```

**Show only unclustered points**:
```javascript
filter: ['!', ['has', 'point_count']]
```

## Data Guidelines

### GeoJSON Structure
```javascript
{
    "type": "Feature",
    "geometry": {
        "type": "Point",
        "coordinates": [longitude, latitude]  // Note: lng first!
    },
    "properties": {
        "name": "School Name",
        "type": "Primary|Secondary|University",
        "students": 500,
        "teachers": 30,
        "ratio": 16.7,
        "resources": 75,  // percentage
        "internet": true,
        "country": "Country",
        "city": "City"
    }
}
```

### Data Quality Standards
- Accurate geographic coordinates (longitude, latitude order)
- Realistic enrollment and ratio figures
- Resource percentages (0-100)
- Boolean internet access flag
- Comprehensive location metadata

## Styling Guidelines

### Color Palette

**Clusters** (by density):
- `#51bbd6` - Blue (small clusters)
- `#f1f075` - Yellow (medium clusters)
- `#f28cb1` - Pink (large clusters)

**School Types**:
- `#667eea` - Primary (purple)
- `#764ba2` - Secondary (violet)
- `#f093fb` - University (pink)

**Resource Levels**:
- `#48bb78` - Well resourced (green)
- `#ecc94b` - Moderate (yellow)
- `#f56565` - Under-resourced (red)

### Globe Atmosphere
```javascript
map.setFog({
    color: 'rgba(5, 10, 20, 0.9)',
    'high-color': 'rgba(36, 92, 223, 0.35)',
    'horizon-blend': 0.3,
    'space-color': '#000814',
    'star-intensity': 0.7
});
```

## Debugging Tips

### Console Logging
```javascript
// Check cluster state
map.on('click', 'clusters', (e) => {
    console.log('Cluster clicked:', e.features[0].properties);
});

// Monitor data loading
map.on('sourcedata', (e) => {
    if (e.sourceId === 'schools' && e.isSourceLoaded) {
        console.log('Schools data loaded');
    }
});
```

### Layer Inspection
```javascript
// Query visible features
const features = map.queryRenderedFeatures({ layers: ['clusters'] });
console.log('Visible clusters:', features.length);

// Check source data
const sourceData = map.getSource('schools')._data;
console.log('Total features:', sourceData.features.length);
```

## Performance Monitoring

### FPS Check
```javascript
map.on('render', () => {
    const fps = 1000 / map._frameTime;
    console.log('FPS:', fps.toFixed(2));
});
```

### Cluster Statistics
```javascript
map.on('load', () => {
    const allFeatures = map.querySourceFeatures('schools');
    const clusters = allFeatures.filter(f => f.properties.cluster);
    console.log('Clusters rendered:', clusters.length);
});
```

## Accessibility Considerations

- Keyboard navigation implemented (arrow keys)
- High contrast color scheme
- Clear visual hierarchy
- Descriptive popup content
- Focus states for interactive elements

## Common Issues & Solutions

### Issue: Clusters not appearing
**Solution**: Check `clusterMaxZoom` - ensure it's lower than current zoom level

### Issue: Expansion zoom not working
**Solution**: Verify cluster source has `generateId: true`

### Issue: Poor performance with large datasets
**Solution**: Reduce `clusterRadius` or lower `clusterMaxZoom`

### Issue: Popup coordinates wrapping
**Solution**: Use coordinate normalization:
```javascript
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
    coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
```

## Extension Ideas

### Easy Additions
- Add school type filters (show only universities)
- Implement resource level filtering
- Add search by country/city
- Export visible schools as CSV

### Medium Complexity
- Time-series data (enrollment over years)
- Heat map overlay for density
- Statistical dashboard panel
- Comparative country analysis

### Advanced Features
- 3D building extrusions for schools
- Custom cluster markers
- Real-time enrollment updates
- WebGL custom layers

## Mapbox Token Notes

Current token is public example - suitable for:
- Development
- Testing
- Learning
- Low-traffic demos

For production:
1. Create Mapbox account
2. Generate new token
3. Set appropriate URL restrictions
4. Monitor usage limits

## Version Information

- **Mapbox GL JS**: v3.0.1
- **Projection**: Globe
- **Style**: dark-v11
- **Clustering**: Enabled (radius 50, maxZoom 14)
- **Dataset**: 311 schools, 142 countries

## Learning Outcomes

This iteration demonstrates:
1. Large dataset clustering configuration
2. Step-based expression styling
3. Interactive cluster expansion
4. Performance optimization techniques
5. Progressive detail disclosure UX
6. Multi-layer composition with filtering

## Next Steps

For iteration 9, consider:
- 3D extrusions based on enrollment
- Animated transitions between states
- Advanced filtering/search UI
- Time-based data animations
- Custom WebGL rendering

---

**Remember**: Always test clustering parameters with your dataset size. Adjust `clusterRadius` and `clusterMaxZoom` based on data density and UX requirements.
