You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 21:07:47