my eye

author Angelo Gladding

name Districts

published 2025-12-25T15:29:04.136115-08:00

type entry

updated 2025-12-25T16:29:51.432839-08:00

url /south-pasadena/districts, /2025/12/25/xq

visibility public

Content

<div id="map" style="width: 100%; height: 70vh"></div>

<link
  href="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.css"
  rel="stylesheet"
/>
<script src="https://api.mapbox.com/mapbox-gl-js/v2.15.0/mapbox-gl.js"></script>

<script>
  mapboxgl.accessToken =
    'pk.eyJ1IjoicmFndC1hZyIsImEiOiJjbWR3ZjZ5cjkxMmRhMmtwbHVlcDBrN240In0.nCEdq0_XYbjl8jq6YV4p5g'

  const map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox/satellite-streets-v12',
    center: [-118.15, 34.113],
    zoom: 13
  })

  map.addControl(new mapboxgl.NavigationControl())

  const DISTRICT_COLORS = {
    1: '#b58900',
    2: '#cb4b16',
    3: '#d33682',
    4: '#6c71c4',
    5: '#2aa198'
  }

  function mercatorToLngLat(x, y) {
    const originShift = 20037508.342789244
    const lon = (x / originShift) * 180.0
    let lat = (y / originShift) * 180.0
    lat = 180.0 / Math.PI * (2.0 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0)
    return [lon, lat]
  }

  function esriRingsToGeoJSONPolygon(rings) {
    const coords = rings.map(function (ring) {
      return ring.map(function (pt) {
        return mercatorToLngLat(pt[0], pt[1])
      })
    })
    return { type: 'Polygon', coordinates: coords }
  }

  function arcgisFeatureCollectionToGeoJSON(fc) {
    const layer = fc.layers[0]
    const features = layer.featureSet && layer.featureSet.features ? layer.featureSet.features : []

    return {
      type: 'FeatureCollection',
      features: features.map(function (f) {
        const attrs = f.attributes || {}
        const district = attrs.District
        const color = DISTRICT_COLORS[district] || '#6c71c4'

        return {
          type: 'Feature',
          properties: {
            District: district,
            _color: color
          },
          geometry: esriRingsToGeoJSONPolygon(f.geometry && f.geometry.rings ? f.geometry.rings : [])
        }
      })
    }
  }

  function bboxOfFeatureCollection(gj) {
    let minX = 180,
      minY = 90,
      maxX = -180,
      maxY = -90

    gj.features.forEach(function (feat) {
      const geom = feat.geometry
      if (!geom || geom.type !== 'Polygon') return
      geom.coordinates.forEach(function (ring) {
        ring.forEach(function (pt) {
          const x = pt[0]
          const y = pt[1]
          if (x < minX) minX = x
          if (y < minY) minY = y
          if (x > maxX) maxX = x
          if (y > maxY) maxY = y
        })
      })
    })

    return [[minX, minY], [maxX, maxY]]
  }

  async function loadDistricts() {
    const r = await fetch('/media/kYMm.json')
    if (!r.ok) throw new Error('Failed to fetch /media/kYMm.json')
    return await r.json()
  }

  map.on('load', async function () {
    const raw = await loadDistricts()

    // Your file is an object with a "featureCollection" property
    const gj = arcgisFeatureCollectionToGeoJSON(raw.featureCollection)

    map.addSource('districts', { type: 'geojson', data: gj })

    map.addLayer({
      id: 'districts-fill',
      type: 'fill',
      source: 'districts',
      paint: {
        'fill-color': ['get', '_color'],
        'fill-opacity': 0.18
      }
    })

    map.addLayer({
      id: 'districts-outline',
      type: 'line',
      source: 'districts',
      paint: {
        'line-color': ['get', '_color'],
        'line-width': 6
      }
    })

    map.fitBounds(bboxOfFeatureCollection(gj), { padding: 30, duration: 0 })
  })
</script>