简介这份资源是面向 ECharts 地图可视化开发者的湖南省行政区划 JSON 数据包覆盖全省各市及下辖区县适合需要在项目中快速接入湖南地图的前端与数据可视化人员使用。压缩包为 rar 格式体积约 631KB文件以 JSON 地图数据为主按市、县层级组织解压后可直接放入项目供 ECharts 调用无需自行绘制或转换边界数据。使用时配合 ECharts 官方地图示例即可实现省、市、区县多级地图的可视化展示例如需要长沙市区县地图时单独取出对应 JSON 引入即可。资源已有 2163 人学习下载说明其在同类地图数据中具备一定实用性与参考价值能帮助开发者省去地图数据搜集与格式整理环节把精力集中在图表配置与业务呈现上。1. 从一张“点不亮”的湖南地图说起前阵子帮一个做区域业务看板的朋友排查问题页面里用 echarts 画湖南省地图结果地图区域一片空白控制台只丢一句Map hunan not exists。他以为是 echarts 版本问题来回升降级折腾了一下午最后发现根因特别朴素项目里只引了echarts.min.js却没注册任何 GeoJSON 地图数据。echarts 从 5.x 起把内置地图全部剥离任何省份、城市、区县地图都得自己准备 JSON 并registerMap。这份「echarts 湖南省 json 地图包含各市、县地图」资源解决的正是这个高频卡点它把湖南省整体轮廓、下辖各地级市、以及市下面的区县边界数据打包成可直接喂给 echarts 的 JSON省掉你自己去公开地理数据源抓取、裁剪、坐标纠偏的环节。适合做区域数据大屏、业务分布热力图、下钻联动看板的前端和数据同学也适合只想快速验证一个地图 demo 的开发者。下面按“数据怎么组织、怎么注册、怎么下钻、坑在哪”一路拆。2. 地图 JSON 的结构与注册机制先搞懂 echarts 认什么2.1 GeoJSON 与 echarts 的 registerMap 契约echarts 不直接吃任意 JSON它认的是符合 GeoJSON 规范的地理数据核心是type: FeatureCollection加一组features每个 feature 带properties.name和geometry。registerMap(hunan, geoJson)做的事就是把这个名字和这份几何数据绑定到全局地图注册表里之后series.map: hunan才能找到它。所以拿到资源包后第一件事不是急着写 option而是打开一个 JSON 看三样东西顶层是不是 FeatureCollection、features 里每个properties.name是不是中文地名、geometry 的坐标是不是经纬度一般是[lng, lat]顺序。这三样任何一样不对地图要么不显示要么显示成一团乱线。常见做法是先用 Node 快速体检一遍避免把问题带进浏览器// check-geo.js Node 环境快速校验地图 JSON 结构 const fs require(fs); const path ./hunan.json; const geo JSON.parse(fs.readFileSync(path, utf8)); console.log(顶层类型:, geo.type); // 期望 FeatureCollection console.log(要素数量:, geo.features.length); // 湖南一般 14 个市州 const names geo.features.map(f f.properties f.properties.name); console.log(地名样例:, names.slice(0, 5)); // 抽查第一个要素的坐标维度判断是经纬度还是投影坐标 const firstCoord geo.features[0].geometry.coordinates[0][0][0]; console.log(坐标样例:, firstCoord); // 期望 [110~114, 24~30] 量级这段脚本的逻辑很直白type不对说明不是标准 GeoJSON需要转换features.length能反推这份文件是省级还是市级市级文件通常只有几个到十几个要素坐标样例如果出现几十万、几百万这种量级说明是投影坐标如 Web 墨卡托直接塞给 echarts 会跑到画布外得先做经纬度转换。参数上唯一要改的是path指向你实际解压出来的文件。我一般会把省、市、县三级文件都跑一遍确认命名一致——比如省级叫hunan.json市级可能叫changsha.json、zhuzhou.json县级再细一层命名不统一是后面下钻联动翻车的头号原因。2.2 省、市、县三级文件的组织方式这类资源包通常按行政层级分目录省级一个文件市级按市名各一个文件县级再按区县名拆分。理解这个层级关系决定了你后面下钻逻辑怎么写。省级地图用于首屏总览点击某个市后加载对应市级 JSON 重新registerMap并setOption再点区县继续下钻。关键点在于每次下钻都要用不同的 map 名字注册不能复用同一个名字反复覆盖否则 echarts 内部缓存会导致图形错乱。我一般用hunan、hunan-changsha、hunan-changsha-yuelu这种带层级前缀的命名既避免冲突也方便在代码里根据当前层级反推该加载哪个文件。// 下钻时按层级拼接 map 名避免同名覆盖 function mapNameOf(level, code) { // level: province | city | county return level province ? hunan : hunan-${code}; } // 注册前先判断是否已注册已注册则跳过减少重复解析开销 const registered new Set(); async function ensureMap(name, url) { if (registered.has(name)) return name; const geo await fetch(url).then(r r.json()); echarts.registerMap(name, geo); registered.add(name); return name; }mapNameOf用层级加编码拼名字ensureMap用Set做幂等注册——这是血泪经验下钻再返回上级时如果不做幂等判断每次返回都重新 fetch 并 register数据量大时会有明显卡顿。url参数指向资源包里对应层级的 JSON 路径name就是拼出来的 map 名。注意fetch在本地file://协议下会被浏览器拦截调试时务必起一个静态服务这是新手最常踩的第一个坑后面避坑章节还会展开。3. 从零跑通一张湖南地图option 配置与视觉映射3.1 最小可运行示例与关键参数拿到 JSON 并确认结构无误后先别急着做下钻用最小配置把省级地图点亮确认数据链路通了再往上叠功能。下面这段是能直接抄的骨架重点看geo和series两块的配合// 最小可运行注册湖南地图并渲染 import * as echarts from echarts; import hunanGeo from ./geo/hunan.json; echarts.registerMap(hunan, hunanGeo); const chart echarts.init(document.getElementById(map)); chart.setOption({ tooltip: { trigger: item, formatter: {b} }, // {b} 取地名 geo: { map: hunan, roam: true, // 允许缩放拖拽 zoom: 1.1, // 初始缩放避免贴边 label: { show: true, fontSize: 10 }, itemStyle: { areaColor: #e8f0fe, borderColor: #8ab4f8, borderWidth: 1 }, emphasis: { itemStyle: { areaColor: #fdd663 } } }, series: [{ type: map, map: hunan, geoIndex: 0, // 与上面的 geo 共用坐标系 data: [ { name: 长沙市, value: 120 }, { name: 株洲市, value: 80 } ] }] });逻辑说明geo负责底图渲染和交互缩放、标签、描边series负责数据着色。geoIndex: 0让 series 复用 geo 的坐标系这样数据热力才能准确贴到对应行政区上。参数上roam: true打开缩放拖拽大屏场景常关掉防止误操作zoom初始值建议 1.01.2太小地图缩在角落太大边缘被裁label.show在省级层面建议开到县级层面要素太密要关掉否则文字糊成一片。data里的name必须和 JSON 里properties.name完全一致差一个字比如“长沙市”写成“长沙”这块区域就不会着色这是最高频的“数据不生效”原因。3.2 visualMap 分段着色与数据对齐真实业务里数据值跨度大直接线性映射会让大部分区域颜色趋同看不出差异。用visualMap做分段piecewise比连续continuous更直观尤其适合业务分级场景比如按订单量分“高/中/低”三档。// 分段着色按数值区间给不同颜色 visualMap: { type: piecewise, min: 0, max: 200, splitNumber: 4, orient: horizontal, left: center, bottom: 10, inRange: { color: [#d4e8ff, #8ab4f8, #3b6fd4, #1a3d8f] }, text: [高, 低], calculable: true }splitNumber控制分几档inRange.color是从低到高的色带orient和left/bottom决定图例位置。这里有个容易忽略的点visualMap的min/max要覆盖你data里的实际值域否则超出范围的值会被截断成端点色看起来像“数据错了”。我一般会先算一遍数据的 min/max 再填或者干脆不写 min/max 让 echarts 自适应但自适应在数据全为 0 时会出现色带异常稳妥做法还是显式给范围。数据对齐方面建议在渲染前做一次名称校验把data里的 name 和 JSON 的 name 集合做交集缺的补 0、多的剔除避免“有数据但地图不亮”的玄学问题。4. 市级、县级下钻联动事件、异步加载与返回栈4.1 点击事件驱动的下钻流程下钻的本质是“点击某区域 → 拿到该区域编码或名称 → 加载对应 JSON → 重新注册 → 更新 option”。echarts 的chart.on(click, ...)能拿到点击项的名称但名称到文件名的映射需要你自己维护一张表因为中文名和拼音文件名不一定对得上。// 维护名称到文件编码的映射避免中文名直接拼路径 const cityCodeMap { 长沙市: changsha, 株洲市: zhuzhou, 湘潭市: xiangtan // ... 其余市州 }; chart.on(click, async (params) { const cityName params.name; const code cityCodeMap[cityName]; if (!code) return; // 点到非市级区域直接忽略 const mapName hunan-${code}; await ensureMap(mapName, ./geo/city/${code}.json); chart.setOption({ geo: { map: mapName }, series: [{ map: mapName, data: [] }] // 清空旧数据等待新数据填充 }); });cityCodeMap是下钻的“路由表”没有它你就得靠中文名猜文件名一旦资源包命名规则变了就全崩。ensureMap复用上一节的幂等注册。setOption里同时更新geo.map和series.map并清空data否则上一级的数据会残留到下一级出现“长沙的数据画在株洲地图上”的诡异现象。注意setOption默认是合并模式切换地图时建议加{ notMerge: true }彻底替换避免旧配置残留。4.2 返回上级与面包屑状态管理下钻容易返回难。用户点了几层之后要能退回去就得维护一个层级栈。我一般用一个数组记录访问路径每次下钻 push返回时 pop 并重新加载上一级。// 层级栈记录 [ {name, mapName, url} ] const stack [{ name: 湖南省, mapName: hunan, url: ./geo/hunan.json }]; async function drillDown(cityName, code) { const mapName hunan-${code}; await ensureMap(mapName, ./geo/city/${code}.json); stack.push({ name: cityName, mapName, url: ./geo/city/${code}.json }); renderMap(mapName); } async function goBack() { if (stack.length 1) return; // 已在省级不再返回 stack.pop(); const prev stack[stack.length - 1]; await ensureMap(prev.mapName, prev.url); renderMap(prev.mapName); }stack的每一项存了名称、map 名和 URL返回时直接取上一项重新注册渲染不用重新推导。renderMap是你封装的统一渲染函数负责setOption和更新面包屑。这里的关键是返回时也要走ensureMap因为用户可能从别的入口清过注册表幂等判断能兜住。面包屑 UI 直接读stack渲染即可点击某一级就连续pop到那一层。县级下钻同理只是 URL 换成./geo/county/xxx.json编码表再细一层。层级深了之后要注意内存注册过的地图不会自动释放页面停留很久的看板建议限制下钻深度或做手动清理。5. 避坑与排查地图不显示、错位、卡顿的常见根因5.1 地图一片空白控制台报 Map xxx not exists现象setOption后地图区域空白控制台明确提示地图未注册。原因通常有三一是只引了 echarts 没引 JSON二是registerMap的名字和series.map不一致大小写、连字符三是 JSON 加载失败但被try/catch吞掉了。解决先在registerMap后打印echarts.getMap(hunan)确认注册成功再核对 option 里的 map 名逐字符一致。异步加载场景要确保registerMap在setOption之前完成用await或回调串起来别让两个异步操作赛跑。5.2 地图显示但形状扭曲、跑到画布外现象地图能出来但被拉长、压扁或只露出一角。原因基本是坐标系不匹配——JSON 里是投影坐标数值几十万级echarts 按经纬度解析自然错位。解决用第 2 章的体检脚本看坐标量级若是投影坐标需要先转成 WGS84 经纬度再注册。另一种情况是geo.boundingCoords或center/zoom设错手动指定了不合适的中心点。稳妥做法是不写center让 echarts 自动适配只调zoom。5.3 数据着色不生效部分区域始终是默认色现象data里明明有某个市的数据地图上那块却不着色。原因九成是名称不匹配JSON 里是“长沙市”data 里写“长沙”或者 JSON 用了“湘西土家族苗族自治州”这种长名data 里简写了。解决渲染前打印 JSON 的 name 集合和 data 的 name 集合做 diff缺的补、多的删。这个校验我建议固化成工具函数每次数据更新都跑一遍比肉眼核对靠谱。5.4 下钻后返回地图错乱或数据串层现象下钻再返回地图显示成上一级的形状但数据是下一级的或者图形重叠。原因是没有用notMerge: truesetOption合并模式把新旧配置混在一起或者 map 名复用了同一个导致缓存冲突。解决切换层级时setOption(option, { notMerge: true })map 名按层级加前缀保证唯一返回时重新注册上一级地图。5.5 本地打开 HTML 地图不加载现象直接双击 HTML 文件地图空白控制台报跨域或 fetch 失败。原因file://协议下浏览器禁止fetch本地 JSON。解决起一个静态服务npx serve或python -m http.server都行通过http://localhost访问。这个坑几乎每个新手都会踩一次记住“地图 JSON 必须走 HTTP”就行。6. 进阶技巧用 geoJSON 裁剪与按需加载压住首屏体积资源包把省、市、县全量打包文件数量不少如果首屏就把所有 JSON 都请求一遍弱网环境下白屏时间会很难看。我的习惯是首屏只加载省级文件市级和县级按需懒加载配合ensureMap的幂等缓存用户不下钻就不请求。具体做法是把ensureMap的fetch包一层 loading 状态下钻时先显示骨架再渲染避免点击后长时间无反馈。另一个实用技巧是坐标精度裁剪。公开地理数据往往保留 6 位以上小数对屏幕渲染来说 4 位足够多出来的位数纯属浪费带宽。可以用脚本对 JSON 做一次精度压缩// 递归压缩坐标精度到 4 位小数减小文件体积 function roundCoords(node, digits 4) { if (Array.isArray(node)) { // 判断是否为坐标对 [lng, lat] if (typeof node[0] number) { return node.map(n n.toFixed(digits)); } return node.map(n roundCoords(n, digits)); } return node; } const compressed { ...geo, features: geo.features.map(f ({ ...f, geometry: { ...f.geometry, coordinates: roundCoords(f.geometry.coordinates) } })) };roundCoords递归遍历坐标数组遇到数字对就toFixed(4)再转回数字digits可按需调到 3 或 5。实测这类压缩能把文件体积降一截视觉上几乎无差别代价是极少数超小行政区边界可能略微简化做精细边界展示时要谨慎。压缩后的文件建议另存一份别覆盖原始数据方便出问题时回退。验证方面我一般会做两件事一是用echarts.getMap(name)确认注册表里有数据且 features 数量对得上二是在浏览器里逐个点击下钻再返回确认层级栈和地图切换都正常。这套流程走下来基本能覆盖 90% 的线上问题。从那以后我每次接地图需求都强制先跑一遍结构体检脚本再写 option省下的排查时间远比脚本本身多。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?