React TypeScript中ECharts地图实现邻区异色的方法
德克萨斯州ECharts地图邻县颜色不重复实现方案
问题描述
我已基于React TypeScript实现了包含各县的德克萨斯州ECharts地图,鼠标悬停时会显示区域名称,但所有县均使用同一颜色。现需实现每个区域颜色不同且相邻县颜色不重复的效果,请问如何修改现有代码?
原代码
type MapProps = { state: string stateId: string mapColor?: string mapHoverColor?: string } export function MapLink(props: MapProps) { const option = { textStyle: { fontFamily: '"Roboto","Helvetica","Arial",sans-serif', }, tooltip: { trigger: 'item', formatter: (params: any) => { const wda = wdaCounties.wdas.filter((item) => item.counties.includes(params.name)) const counties = wda[0].wda return counties } }, series: [ { name: 'Selected', type: 'map', map: 'Texas', nameProperty: 'NAME', selectedMode: 'multiple', right: '10px', label: { show: false, }, emphasis: { label: { show: false, }, itemStyle: { areaColor: props.mapHoverColor||'#EADDCA', }, }, itemStyle: { areaColor: props.mapColor||'#5f8f2c', }, select: { label: { show: false, }, itemStyle: { areaColor: props.mapHoverColor||'#EADDCA', }, }, data: [], }, ], } const mapRef = useRef<EChartsInstance | null>(null) const [mapOptions] = useState({ ...option, series: [ { ...option.series[0], map: props.state, }, ], }) const texasCountyData = { type: usCountyDta.type, features: usCountyDta.features.filter((feature) => feature.properties.STATE === props.stateId), } // @ts-ignore echarts.registerMap(props.state, texasCountyData) const updateMap = (map: EChartsInstance, selection: string[]) => { try { map.dispatchAction({ type: EVENT_UNSELECT, name: texasCountyData.features.map((feature) => feature.properties.NAME), }) map.dispatchAction({ type: EVENT_SELECT, name: selection, }) } catch { } } if (mapRef.current) { let mapInstance = mapRef.current.getEchartsInstance() mapInstance.on(EVENT_CLICK, (params: any) => { const wda = wdaCounties.wdas.filter((item) => item.counties.includes(params.name)) const counties = wda[0].counties const url = wda[0].url window.open(url, "_self") }) mapInstance.on('mouseover', (params: any) => { const wda = wdaCounties.wdas.filter((item) => item.counties.includes(params.name)) const counties = wda[0].counties updateMap(mapInstance, counties) }) mapInstance.on('mouseout', () => { updateMap(mapInstance, []) }) } return ( <> <div style={{ display: 'flex', alignItems: 'center' }}> ...... </div> </> ) }
修改方案
核心思路
通过构建县邻接关系 + 贪心颜色分配算法,给每个县分配与相邻县不同的颜色,最终将颜色数据注入ECharts的series.data中。
具体代码修改
1. 添加颜色池与邻接关系工具函数
在组件内部添加以下代码,用于生成颜色池、判断县的邻接关系、构建邻接表:
// 定义高区分度颜色池,数量建议多于最大相邻县数 const COLOR_POOL = [ '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9', '#F8C471', '#82E0AA', '#F1948A', '#85C1E9', '#D7BDE2' ]; // 构建县的邻接关系表 const buildAdjacencyList = (features: any[]) => { const adjacencyList: Record<string, string[]> = {}; features.forEach(feature => { adjacencyList[feature.properties.NAME] = []; }); // 遍历所有县对,判断邻接关系 for (let i = 0; i < features.length; i++) { const featureA = features[i]; for (let j = i + 1; j < features.length; j++) { const featureB = features[j]; if (isAdjacent(featureA.geometry, featureB.geometry)) { adjacencyList[featureA.properties.NAME].push(featureB.properties.NAME); adjacencyList[featureB.properties.NAME].push(featureA.properties.NAME); } } } return adjacencyList; }; // 邻接判断(示例简化版,实际推荐用turf.js的booleanIntersects提高准确度) const isAdjacent = (geoA: any, geoB: any) => { // 粗略判断坐标是否有重叠,生产环境建议替换为专业空间库实现 const coordsA = JSON.stringify(geoA.coordinates); const coordsB = JSON.stringify(geoB.coordinates); return coordsA.includes(coordsB.substring(0, 50)) || coordsB.includes(coordsA.substring(0, 50)); };
2. 生成带颜色的县数据
添加函数生成每个县的颜色配置:
// 为每个县分配不与邻县重复的颜色 const generateColoredCountyData = () => { const adjacencyList = buildAdjacencyList(texasCountyData.features); const coloredData: any[] = []; const usedColors: Record<string, string> = {}; texasCountyData.features.forEach(feature => { const countyName = feature.properties.NAME; // 获取相邻县已使用的颜色 const neighborColors = adjacencyList[countyName] .map(neighbor => usedColors[neighbor]) .filter(Boolean); // 从颜色池选第一个未被邻县使用的颜色 const selectedColor = COLOR_POOL.find(color => !neighborColors.includes(color)) || COLOR_POOL[0]; usedColors[countyName] = selectedColor; coloredData.push({ name: countyName, itemStyle: { areaColor: selectedColor } }); }); return coloredData; };
3. 修改ECharts配置
- 移除series中统一的
itemStyle.areaColor(改为由data单独控制) - 将series的data替换为生成的带颜色数据
修改后的option和mapOptions部分:
const option = { textStyle: { fontFamily: '"Roboto","Helvetica","Arial",sans-serif', }, tooltip: { trigger: 'item', formatter: (params: any) => { const wda = wdaCounties.wdas.filter((item) => item.counties.includes(params.name)) const counties = wda[0].wda return counties } }, series: [ { name: 'Selected', type: 'map', map: 'Texas', nameProperty: 'NAME', selectedMode: 'multiple', right: '10px', label: { show: false, }, emphasis: { label: { show: false, }, itemStyle: { areaColor: props.mapHoverColor||'#EADDCA', }, }, // 移除统一的areaColor配置 select: { label: { show: false, }, itemStyle: { areaColor: props.mapHoverColor||'#EADDCA', }, }, data: [], // 后续会替换为带颜色的数据 }, ], } // 生成带颜色的数据并更新mapOptions const coloredCountyData = generateColoredCountyData(); const [mapOptions] = useState({ ...option, series: [ { ...option.series[0], map: props.state, data: coloredCountyData // 注入带颜色的数据 }, ], })
优化建议
- 邻接判断准确性:示例中的
isAdjacent是简化实现,生产环境建议引入turf.js库,使用turf.booleanIntersects函数准确判断两个GeoJSON区域是否相邻。 - 颜色池扩展:如果德克萨斯州的县数量较多,可扩展
COLOR_POOL的颜色数量,确保有足够多的颜色供分配。 - 逻辑复用:可将颜色分配、邻接关系构建逻辑抽成自定义Hook,方便在其他地图组件中复用。
内容的提问来源于stack exchange,提问作者Chinnadurai Mani
相关产品推荐
相关产品推荐

