# Mapbox Style Patterns

Quick reference for common style patterns, layer configurations, and data-driven styling.

## Layer Types Quick Reference

| Layer Type         | Use For                     | Key Properties                       |
| ------------------ | --------------------------- | ------------------------------------ |
| **fill**           | Polygons (countries, parks) | `fill-color`, `fill-opacity`         |
| **line**           | Roads, boundaries           | `line-color`, `line-width`           |
| **symbol**         | Labels, icons               | `text-field`, `icon-image`           |
| **circle**         | Points (markers, heatmap)   | `circle-radius`, `circle-color`      |
| **heatmap**        | Density visualization       | `heatmap-intensity`, `heatmap-color` |
| **fill-extrusion** | 3D buildings                | `fill-extrusion-height`              |
| **raster**         | Satellite, aerial imagery   | `raster-opacity`                     |

## Data-Driven Styling Patterns

### Based on Property Value

```javascript
// ✅ Color by category
'fill-color': [
  'match',
  ['get', 'type'],
  'park', '#90EE90',
  'water', '#87CEEB',
  'urban', '#D3D3D3',
  '#CCCCCC' // default
]

// ✅ Size by numeric value
'circle-radius': [
  'interpolate', ['linear'],
  ['get', 'population'],
  0, 5,
  1000000, 20
]
```

### Based on Zoom Level

```javascript
// ✅ Show/hide by zoom
'visibility': [
  'step',
  ['zoom'],
  'none',  // Hidden below zoom 10
  10, 'visible'
]

// ✅ Size by zoom
'text-size': [
  'interpolate', ['linear'],
  ['zoom'],
  8, 10,    // Small at zoom 8
  16, 18    // Large at zoom 16
]
```

## Common Patterns

### 1. Clustering

```javascript
map.addSource('points', {
  type: 'geojson',
  data: geojson,
  cluster: true,
  clusterRadius: 50,
  clusterMaxZoom: 14
});

// Cluster circles
map.addLayer({
  id: 'clusters',
  type: 'circle',
  source: 'points',
  filter: ['has', 'point_count'],
  paint: {
    'circle-color': ['step', ['get', 'point_count'], '#51bbd6', 100, '#f1f075', 750, '#f28cb1'],
    'circle-radius': ['step', ['get', 'point_count'], 20, 100, 30, 750, 40]
  }
});
```

### 2. Feature State (Hover/Selection)

```javascript
// ✅ Hover effect without modifying data
map.on('mousemove', 'layer', (e) => {
  if (hoveredId) {
    map.setFeatureState(
      { source: 'source', id: hoveredId },
      { hover: false }
    );
  }
  hoveredId = e.features[0].id;
  map.setFeatureState(
    { source: 'source', id: hoveredId },
    { hover: true }
  );
});

// Style based on state
'fill-color': [
  'case',
  ['boolean', ['feature-state', 'hover'], false],
  '#0080ff',  // Hover color
  '#3bb2d0'   // Default color
]
```

For binding the event with the newer `addInteraction` API instead of `map.on()`, and for `appearances` (a declarative alternative to the `case`/`feature-state` expression above, symbol layers only), see [references/interactions.md](references/interactions.md).

### 3. Filters

```javascript
// ✅ Filter by property
map.setFilter('layer', ['==', ['get', 'type'], 'restaurant']);

// ✅ Filter by multiple conditions
map.setFilter('layer', ['all', ['==', ['get', 'type'], 'restaurant'], ['>', ['get', 'rating'], 4]]);

// ✅ Filter by zoom
map.setFilter('layer', ['all', ['>=', ['zoom'], 10], ['<', ['zoom'], 14]]);
```

### 4. Expressions

```javascript
// ✅ Conditional styling
'circle-color': [
  'case',
  ['<', ['get', 'value'], 10], '#00ff00',  // Green if < 10
  ['<', ['get', 'value'], 20], '#ffff00',  // Yellow if < 20
  '#ff0000'  // Red otherwise
]

// ✅ Math operations
'circle-radius': [
  '*',
  ['sqrt', ['get', 'population']],
  0.01
]

// ✅ String concatenation
'text-field': ['concat', 'Population: ', ['get', 'pop']]
```

## Performance Patterns

### Vector Tiles vs GeoJSON

**Use vector tiles when:**

- Large datasets (>5MB)
- Need different zoom levels
- Want server-side updates

**Use GeoJSON when:**

- Small datasets (<5MB)
- Frequent client-side updates
- Simple implementation needed

### Layer Optimization

```javascript
// ✅ Set minzoom/maxzoom
map.addLayer({
  id: 'layer',
  minzoom: 10, // Only show zoom 10+
  maxzoom: 16 // Hide above zoom 16
});

// ✅ Use feature state instead of removing/re-adding
// Bad: map.removeLayer() / map.addLayer()
// Good: map.setFeatureState()

// ✅ Combine similar layers
// Bad: Separate layer for each category
// Good: One layer with data-driven styling
```

## Style Management

### Dynamic Style Updates

```javascript
// ✅ Update paint property
map.setPaintProperty('layer', 'fill-color', '#ff0000');

// ✅ Update layout property
map.setLayoutProperty('layer', 'visibility', 'none');

// ✅ Update source data
map.getSource('source').setData(newGeojson);

// ✅ Batch updates (better performance)
map.once('idle', () => {
  // Multiple style changes here
});
```

### Before Layers

```javascript
// ✅ Insert layer at specific position
map.addLayer(
  {
    id: 'new-layer',
    type: 'fill',
    source: 'source'
  },
  'existing-layer-id'
); // Insert before this layer
```

## Common Use Cases

### Choropleth Map

```javascript
map.addLayer({
  id: 'choropleth',
  type: 'fill',
  source: 'counties',
  paint: {
    'fill-color': ['interpolate', ['linear'], ['get', 'density'], 0, '#f7fbff', 100, '#08519c'],
    'fill-opacity': 0.7
  }
});
```

### Route Visualization

```javascript
map.addLayer({
  id: 'route',
  type: 'line',
  source: 'route',
  paint: {
    'line-color': '#0080ff',
    'line-width': 5,
    'line-opacity': 0.8
  }
});
```

### 3D Buildings

```javascript
map.addLayer({
  id: 'buildings',
  type: 'fill-extrusion',
  source: 'composite',
  'source-layer': 'building',
  paint: {
    'fill-extrusion-color': '#aaa',
    'fill-extrusion-height': ['get', 'height'],
    'fill-extrusion-base': ['get', 'min_height'],
    'fill-extrusion-opacity': 0.8
  }
});
```

## Quick Reference: Expression Types

| Type         | Function                 | Example            |
| ------------ | ------------------------ | ------------------ |
| **Decision** | match, case              | Color by category  |
| **Ramp**     | interpolate, step        | Size by value      |
| **Math**     | +, -, \*, /, %           | Calculate values   |
| **String**   | concat, upcase, downcase | Format labels      |
| **Lookup**   | get, has, at             | Access properties  |
| **Zoom**     | zoom                     | Zoom-based styling |

## Debugging Tips

```javascript
// ✅ Check if layer exists
if (map.getLayer('layer-id')) {
  map.removeLayer('layer-id');
}

// ✅ Check if source exists
if (map.getSource('source-id')) {
  map.removeSource('source-id');
}

// ✅ List all layers
console.log(map.getStyle().layers);

// ✅ Get layer paint properties
console.log(map.getPaintProperty('layer', 'fill-color'));
```
