# 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.