Spaces:
Running
Running
| import { useRef, useEffect, useState } from 'react' | |
| import * as Plot from '@observablehq/plot' | |
| const smoothProgressBar = fraction => { | |
| const blocks = ['β', 'β', 'β', 'β', 'β', 'β', 'β', 'β'] | |
| const width = 10 | |
| const totalUnits = width * 8 | |
| const filledUnits = Math.round(fraction * totalUnits) | |
| const fullBlocks = Math.floor(filledUnits / 8) | |
| const remainder = filledUnits % 8 | |
| return ( | |
| 'β'.repeat(fullBlocks) + (remainder > 0 ? blocks[remainder - 1] : '') || 'β' | |
| ) | |
| } | |
| const makeTitle = data => d => { | |
| const cData = data[d.properties?.ISO_A2_EH] | |
| const languages = cData?.languages.toSorted( | |
| (a, b) => b.population - a.population | |
| ) | |
| const pop = languages?.map(a => a.population).reduce((prev, a) => prev + a, 0) | |
| const langstring = | |
| languages | |
| ?.slice(0, 10) | |
| .map( | |
| a => | |
| `${smoothProgressBar(a.population / pop)} ${ | |
| a.name | |
| } β ${a.score.toFixed(2)}` | |
| ) | |
| .join('\n\n') + (languages?.length > 10 ? `\n\n...` : '') | |
| return `${d.properties.ADMIN} β ${cData?.score.toFixed(2)}\n\n${langstring}` | |
| } | |
| const WorldMap = ({ data }) => { | |
| const containerRef = useRef() | |
| const [mapData, setMapData] = useState() | |
| useEffect(() => { | |
| fetch('/world.geo.json') | |
| .then(res => res.json()) | |
| .then(setMapData) | |
| }, []) | |
| useEffect(() => { | |
| console.log('countries', data) | |
| if (mapData === undefined || data === undefined) return | |
| const countriesDict = data.reduce((acc, country) => { | |
| acc[country.iso2] = country | |
| return acc | |
| }, {}) | |
| const plot = Plot.plot({ | |
| subtitle: 'Language Proficiency Score by Country', | |
| width: 750, | |
| height: 500, | |
| projection: 'equal-earth', | |
| marks: [ | |
| Plot.geo(mapData, { | |
| fill: d => countriesDict[d.properties?.ISO_A2_EH]?.score, | |
| title: makeTitle(countriesDict), | |
| tip: true | |
| }) | |
| ], | |
| color: { | |
| scheme: 'Greens', | |
| unknown: 'gray', | |
| label: 'Score', | |
| legend: true, | |
| domain: [0, 0.7] | |
| }, | |
| style: { | |
| fontFamily: 'monospace' | |
| } | |
| }) | |
| containerRef.current.append(plot) | |
| return () => plot.remove() | |
| }, [mapData, data]) | |
| return ( | |
| <div | |
| ref={containerRef} | |
| style={{ | |
| width: '100%', | |
| height: '100%', | |
| display: 'flex', | |
| alignItems: 'center', | |
| justifyContent: 'center' | |
| }} | |
| /> | |
| ) | |
| } | |
| export default WorldMap | |