/* global React */ const { useState: useStateMap, useMemo: useMemoMap, useEffect: useEffectMap, useRef: useRefMap } = React; // Real GeoJSON-based map of Centro-Sul cearense — adapted for MRTI function MapCS({ heatmap='talentos', onSelect, focused, onPersonaSelect }) { const [geo, setGeo] = useStateMap(null); const [hovered, setHovered] = useStateMap(null); const [tip, setTip] = useStateMap(null); useEffectMap(() => { fetch('mapdata.json').then(r => r.json()).then(setGeo); }, []); const W = 800, H = 600, PAD = 30; const project = useMemoMap(() => { if (!geo) return null; let minX=Infinity, minY=Infinity, maxX=-Infinity, maxY=-Infinity; for (const f of geo.centroSul.features) { walkCoordsMap(f.geometry, (lon, lat) => { if (lonmaxX) maxX=lon; if (lat>maxY) maxY=lat; }); } const dx = maxX - minX, dy = maxY - minY; const midLat = (minY + maxY) / 2; const aspectFix = Math.cos(midLat * Math.PI / 180); const scaleX = (W - PAD*2) / dx; const scaleY = (H - PAD*2) / (dy / aspectFix); const scale = Math.min(scaleX, scaleY); const offX = PAD + ((W - PAD*2) - dx * scale) / 2; const offY = PAD + ((H - PAD*2) - (dy / aspectFix) * scale) / 2; return (lon, lat) => [ offX + (lon - minX) * scale, offY + (maxY - lat) / aspectFix * scale, ]; }, [geo]); const polyToPath = (geometry) => { if (!project) return ''; const rings = geometry.type === 'Polygon' ? [geometry.coordinates] : geometry.coordinates; let d = ''; for (const poly of rings) { for (const ring of poly) { ring.forEach(([lon, lat], i) => { const [x, y] = project(lon, lat); d += (i === 0 ? 'M' : 'L') + x.toFixed(1) + ' ' + y.toFixed(1) + ' '; }); d += 'Z '; } } return d; }; const centroid = useMemoMap(() => { if (!geo || !project) return {}; const out = {}; for (const f of geo.centroSul.features) { const rings = f.geometry.type === 'Polygon' ? f.geometry.coordinates : f.geometry.coordinates[0]; let largest = rings[0], maxLen = rings[0].length; if (f.geometry.type === 'MultiPolygon') { for (const poly of f.geometry.coordinates) { if (poly[0].length > maxLen) { largest = poly[0]; maxLen = poly[0].length; } } } let sx = 0, sy = 0; for (const [lon, lat] of largest) { const [x, y] = project(lon, lat); sx += x; sy += y; } out[f.properties.name] = [sx / largest.length, sy / largest.length]; } return out; }, [geo, project]); const munByName = useMemoMap(() => { const map = {}; window.MUNICIPALITIES.forEach(m => { map[NAME_MAP_M[m.id] || m.nome] = m; }); return map; }, []); const heat = useMemoMap(() => window.densidadePorMunicipio(heatmap), [heatmap]); const maxHeat = Math.max(...Object.values(heat), 1); if (!geo) return
Carregando mapa…
; const mrColors = window.MR_COLORS; const total = window.MUNICIPALITIES; // Color scale based on heatmap const heatColor = (v) => { if (v === 0) return '#E5F0FC'; const t = v / maxHeat; // Light blue → deep blue const r = Math.round(229 + (11-229)*t); const g = Math.round(240 + (95-240)*t); const b = Math.round(252 + (190-252)*t); return `rgb(${r},${g},${b})`; }; return (
{ if (hovered) { const r = e.currentTarget.getBoundingClientRect(); setTip({x: e.clientX - r.left, y: e.clientY - r.top}); } }} onMouseLeave={() => { setHovered(null); setTip(null); }}> {/* surrounding ceará */} {geo.surrounding.features.map((f, i) => ( ))} {geo.excluded && ( {geo.excluded.features.map((f, i) => ( ))} )} {/* Centro-Sul municipalities */} {geo.centroSul.features.map(f => { const mun = munByName[f.properties.name]; if (!mun) return null; const isHover = hovered === mun.id; const isFocus = focused === mun.id; const v = heat[mun.id] || 0; const fill = heatmap === 'mr' ? mrColors[mun.mr] : heatColor(v); return ( setHovered(mun.id)} onMouseLeave={() => setHovered(null)} onClick={() => onSelect && onSelect(mun.id)} /> ); })} {/* labels */} {geo.centroSul.features.map(f => { const mun = munByName[f.properties.name]; if (!mun) return null; const c = centroid[f.properties.name]; if (!c) return null; const isPolo = mun.isPolo; const v = heat[mun.id] || 0; const fillT = heatmap !== 'mr' && (v > maxHeat*0.5) ? '#fff' : (heatmap==='mr' ? '#fff' : '#1E293B'); return ( {mun.nome.length > 16 ? mun.nome.split(' ')[0] : mun.nome} {v > 0 && ( {v} )} ); })} {tip && hovered && (() => { const mun = window.MUNICIPALITIES.find(m => m.id === hovered); if (!mun) return null; const v = heat[mun.id] || 0; const labels = {talentos:'Talentos', projetos:'Projetos', eventos:'Eventos', mr:'Microrregião'}; return (
{mun.nome}
{window.MR_NAMES[mun.mr]}
{labels[heatmap]||'Valor'} {v}
População {(mun.pop/1000).toFixed(1)}k
Clique para abrir o painel
); })()}
); } const NAME_MAP_M = { 'iguatu': 'Iguatu', 'piquet': 'Piquet Carneiro', 'irapuan': 'Deputado Irapuan Pinheiro', 'acopiara': 'Acopiara', 'quixelo': 'Quixelô', 'oros': 'Orós', 'ico': 'Icó', 'saboeiro': 'Saboeiro', 'jucas': 'Jucás', 'carius': 'Cariús', 'tarrafas': 'Tarrafas', 'cedro': 'Cedro', 'lavras': 'Lavras da Mangabeira', 'umari': 'Umari', 'baixio': 'Baixio', 'ipaumirim': 'Ipaumirim', }; function walkCoordsMap(geom, fn) { function walk(c) { if (typeof c[0] === 'number') fn(c[0], c[1]); else c.forEach(walk); } walk(geom.coordinates); } window.MapCS = MapCS;