
简介这份资源面向GIS开发者、数据可视化工程师及区域规划研究人员提供广东省地级市到县、镇级的SVG矢量地图解决Web端行政区域展示与交互地图开发中的素材需求。压缩包共49个文件以23个HTML页面和23个JavaScript脚本为主另含3张JPG预览图整体约1.64MBHTML负责地图布局与工具页面JS封装各市区域数据与交互逻辑轻量易集成。目前已有1871人学习下载。资源覆盖广州、深圳、佛山、东莞等21个地级市可借助D3.js或Leaflet.js实现点击弹窗、缩放平移等效果适用于区域规划、市场分析、灾害应急等场景为地理数据分析和决策支持提供直观且精确的支撑。1. 广东省地级市县镇级 SVG 地图一份能直接改色、能拆到镇街的矢量底图做数据可视化或者大屏项目的人多半都遇到过这种尴尬想画一张广东省地图找来的底图要么是低分辨率 PNG放大就糊要么是某个在线地图的截图改个颜色得重新导一遍更麻烦的是领导临时说「把佛山下辖的镇街也标出来」手里那份只有地级市边界的数据直接歇菜。这份「广东省地级市地图svg.zip」解决的正是这类问题——它把广东省的行政区划做成了 SVG 矢量格式覆盖省、地级市并且往下延伸到县、镇级边界。SVG 的好处在于它是纯文本的 XML路径数据写在path的d属性里改填充色、描边、加交互事件全在代码层面完成不用打开设计软件。适合谁用做政务大屏、疫情/人口热力分布、物流网点覆盖分析的前端和数据分析同学以及需要把地图嵌进 Web 页面或移动端 H5 的开发者。如果你只是想要一张静态配图发公众号那用在线工具生成 PNG 更省事但只要涉及「改颜色、加点击、按数据驱动渲染」SVG 就是绕不开的选择。2. SVG 底图的坐标系与路径结构先看懂 d 属性再动手2.1 为什么是 SVG 而不是 GeoJSON 或 Shapefile先把这个选型问题说清楚不然后面改起来容易走弯路。GeoJSON 和 Shapefile 是 GIS 领域的标准格式优势在于带地理坐标经纬度能直接叠加到 Leaflet、Mapbox、Cesium 这类地图引擎上做空间分析。但它们有个共同问题文件体积大且浏览器原生不认必须靠地图库解析。SVG 走的是另一条路——它本质是「画布上的路径」坐标是屏幕坐标或 viewBox 坐标不携带真实经纬度。这意味着两件事第一你不能拿它做缓冲区分析、距离计算这类 GIS 运算第二你也不需要引入任何地图库一个svg标签丢进 HTML 就能渲染配合 CSS 和 JavaScript 就能做交互。那什么时候该用 SVG我的判断标准是如果你的需求是「展示 交互」比如点击某个市高亮、按数值给不同地级市上色、鼠标悬停显示 tooltipSVG 是最轻的方案。反过来如果要做「空间查询」比如找出距离某点 50 公里内的所有镇街那必须回到 GeoJSON Turf.js 那条路。这份资源给的是 SVG所以它的主战场是可视化展示层不是空间分析层。常见做法是用 GeoJSON 做后台的空间计算把结果以数值形式传给前端前端再用这份 SVG 做渲染。两者不冲突各干各的活。2.2 拆开一个 pathd 属性里的 M、L、Z 到底在说什么拿到 SVG 文件后用文本编辑器打开你会看到大量这样的片段path id440600 name佛山市 dM 512.34 388.21 L 515.67 390.02 L 518.90 387.45 ... Z fill#e0e0e0 stroke#999 stroke-width0.5/这里的关键是d属性。它是一串路径指令常见的有M x yMove to把画笔移动到 (x, y)不画线。通常是一个子路径的起点。L x yLine to从当前点画直线到 (x, y)。ZClose path把当前子路径闭合自动连回起点。一个地级市的轮廓往往由成百上千个L指令组成因为边界是折线逼近的。id440600是行政区划代码440600 正是佛山的国标码这个设计很贴心——你可以直接用代码匹配数据不用手动维护名称映射。name属性是中文名方便调试时肉眼识别。提示不同来源的 SVG 可能用M/L/Z绝对坐标也可能用m/l/z相对坐标。相对坐标的路径在缩放时更容易出问题如果你发现某个市放大后位置偏移先检查是不是相对指令没处理好。2.3 用 Python 批量提取行政区划代码与名称在把地图接进项目之前我一般会先跑一遍解析脚本把每个 path 的 id、name、以及路径点数量导出来做成一张对照表。这样后面写交互逻辑时不用反复翻 SVG 源文件。import re from xml.etree import ElementTree as ET # 读取 SVG 文件注意命名空间 tree ET.parse(guangdong.svg) root tree.getroot() # SVG 的命名空间ElementTree 查找时需要带上 ns {svg: http://www.w3.org/2000/svg} rows [] for path in root.findall(.//svg:path, ns): pid path.get(id) # 行政区划代码 name path.get(name) # 中文名称 d path.get(d, ) # 统计路径指令数量粗略反映边界复杂度 cmd_count len(re.findall(r[MLZmlz], d)) rows.append((pid, name, cmd_count)) # 按代码排序输出 for pid, name, cnt in sorted(rows, keylambda x: x[0] or ): print(f{pid}\t{name}\t{cnt})这段脚本的逻辑很直白用ElementTree解析 XML遍历所有path元素把id、name和d属性里的指令数提取出来。cmd_count这个指标有用——如果一个镇的路径指令数只有个位数说明边界被极度简化了放大后会是明显的多边形不适合做精细展示。参数方面ns字典里的命名空间 URI 必须和 SVG 文件头部的xmlns一致否则findall会返回空列表这是新手最容易翻车的地方。跑完这张表你就能判断这份数据在你的场景下够不够用。3. 把 SVG 接进 Web 项目三种嵌入方式与交互绑定3.1 inline SVG、img 引用、CSS background 的取舍SVG 放进网页有三种常见方式选错了后面交互会很难受。第一种是 inline SVG直接把svg.../svg整段贴进 HTML。优点是 SVG 里的每个path都是 DOM 节点可以用document.getElementById拿到绑点击、改颜色、加动画都最方便。缺点是 HTML 文件会变大如果地图路径数据有几百 KB首屏加载会受影响。第二种是用img srcmap.svg。优点是浏览器可以缓存这个文件HTML 保持干净。缺点是 img 里的 SVG 是「死」的外部 JavaScript 拿不到内部的 path 节点没法做点击高亮。如果你只需要静态展示这种方式最省事。第三种是 CSSbackground-image: url(map.svg)。适合做背景装饰同样无法交互而且缩放控制不如前两种灵活。我的建议是要做交互就用 inline SVG纯展示用 img。这份资源里的文件如果体积不大地级市级别通常几十到几百 KBinline 是首选。3.2 用 JavaScript 给每个地级市绑定点击事件假设你已经把 SVG 内联到了页面里接下来给它加上点击高亮和数据展示。下面这段代码演示了核心逻辑// 假设 SVG 已经内联在 idgd-map 的容器里 const svg document.querySelector(#gd-map svg); const paths svg.querySelectorAll(path); // 保存当前选中的元素用于取消上一次高亮 let currentSelected null; paths.forEach(path { path.addEventListener(click, function () { // 取消上一次高亮 if (currentSelected) { currentSelected.style.fill #e0e0e0; } // 高亮当前点击的市 this.style.fill #ff7f50; currentSelected this; // 读取行政区划代码和名称 const code this.getAttribute(id); const name this.getAttribute(name); console.log(选中${name}${code}); // 这里可以触发数据加载比如根据 code 请求该市的统计数据 // loadCityData(code); }); // 鼠标悬停时改变光标样式提示可点击 path.addEventListener(mouseenter, function () { this.style.cursor pointer; this.style.strokeWidth 1.5; }); path.addEventListener(mouseleave, function () { this.style.strokeWidth 0.5; }); });逻辑说明先拿到所有path给每个绑定click。点击时先把上一次选中的填充色恢复成默认灰再把当前点击的改成橙色。currentSelected这个变量是关键没有它就会出现「点一个亮一个最后全亮」的翻车现场。参数方面fill和strokeWidth直接改的是内联样式优先级高于 SVG 属性里的fill所以能覆盖原始颜色。id和name来自 SVG 源文件前提是这份资源确实带了这两个属性——如果只有id没有name你就得自己维护一张代码到名称的映射表。3.3 按数据给地级市上色从数值到颜色的映射大屏项目里最常见的需求是「按 GDP 或人口给各市上不同深浅的颜色」。核心是把数值归一化到 0 到 1 之间再映射到色带。// 假设 data 是 { 440600: 12000, 440100: 25000, ... } 这样的对象 const data { 440100: 25000, // 广州 440300: 28000, // 深圳 440600: 12000, // 佛山 // ... 其他市 }; const values Object.values(data); const maxVal Math.max(...values); const minVal Math.min(...values); // 简单的线性插值从浅蓝到深蓝 function getColor(value) { const ratio (value - minVal) / (maxVal - minVal || 1); // 起始色 #c6dbef结束色 #08306b const r Math.round(198 (8 - 198) * ratio); const g Math.round(219 (48 - 219) * ratio); const b Math.round(239 (107 - 239) * ratio); return rgb(${r},${g},${b}); } paths.forEach(path { const code path.getAttribute(id); if (data[code] ! undefined) { path.style.fill getColor(data[code]); } else { path.style.fill #f0f0f0; // 无数据的市用浅灰 } });这段代码里ratio的计算要注意分母为 0 的情况——如果所有数值一样maxVal - minVal就是 0除法会得到Infinity或NaN所以加了|| 1兜底。颜色插值用的是 RGB 线性过渡简单够用如果对色彩过渡要求高可以换成 HSL 插值视觉上更均匀。data对象的 key 必须和 SVG 里的id完全一致包括前导零这是实际项目里最容易踩的坑之一。4. 县镇级数据的处理文件拆分、按需加载与性能取舍4.1 为什么镇级地图不能一股脑全塞进一个文件广东省有 21 个地级市、一百多个县级区划、上千个镇街。如果把所有镇级边界都画在一个 SVG 里文件体积会轻松突破几 MB浏览器解析 XML 和渲染上千个 path 的开销也很可观。我实测过一个包含两千多个 path 的 SVG 在低端安卓机上首次渲染要两三秒滚动和缩放明显卡顿。合理的做法是按地级市拆分文件。比如guangdong-cities.svg只放 21 个地级市轮廓用于省级总览guangdong-foshan-towns.svg只放佛山的镇街用于下钻页面。用户点击某个市时再动态加载对应的镇级文件。这样首屏只加载省级文件体积小、渲染快下钻时才有额外请求。注意拆分文件时各文件的 viewBox 要保持一致否则下钻后地图会跳位。常见做法是统一用省级地图的 viewBox镇级文件里只保留对应市的 path但坐标系不变。4.2 动态加载镇级 SVG 并替换视图下面演示点击佛山后加载镇级文件并替换当前地图的逻辑async function drillDown(cityCode, cityName) { const container document.querySelector(#gd-map); // 根据行政区划代码拼出文件名比如 440600 - foshan const fileNameMap { 440600: foshan, 440100: guangzhou, 440300: shenzhen, // ... 其他市 }; const fileName fileNameMap[cityCode]; if (!fileName) { console.warn(暂无 ${cityName} 的镇级数据); return; } try { const resp await fetch(./towns/guangdong-${fileName}-towns.svg); const svgText await resp.text(); // 替换容器内容 container.innerHTML svgText; // 重新绑定交互因为旧的 DOM 已经被替换 bindTownEvents(container); } catch (err) { console.error(镇级地图加载失败, err); } }逻辑上fetch拿到 SVG 文本后直接innerHTML替换容器内容这是最简单粗暴的做法。fileNameMap把国标代码映射到英文文件名避免中文文件名在 URL 里编码出问题。bindTownEvents需要重新调用因为原来的 path 节点已经被销毁旧的事件监听器不会自动转移到新节点上——这是很多人下钻后「点击没反应」的根本原因。参数方面fetch的路径要根据你的部署目录调整本地开发时注意跨域限制建议起一个静态服务器而不是直接双击 HTML 文件。4.3 镇级边界的简化与精度平衡镇级边界的数据量比市级大一个数量级如果原始数据是从高精度 GIS 文件转来的单个镇的 path 可能有几千个点。这种精度在屏幕展示上完全是浪费——一个镇在屏幕上可能就占 200 像素宽几千个点里绝大多数落在同一个像素内。常见做法是用 Douglas-Peucker 算法做折线简化把容差设在 0.5 到 1 个像素对应的坐标单位。Python 的shapely库有现成的simplify方法如果你拿到的是 GeoJSON 再转 SVG可以在转换前先简化。如果只有 SVG可以用svgo这类工具做路径优化它会合并冗余指令、压缩坐标精度。但要注意简化过度会导致相邻镇的边界出现缝隙或重叠视觉上很难看。我的经验是省级和市级简化容差可以大一些镇级要保守宁可文件大一点也别让边界对不上。5. 避坑与排查坐标偏移、颜色不生效、文件打不开的常见原因5.1 地图整体偏移或变形现象SVG 在浏览器里显示时地图整体偏到一边或者被拉扁拉长。原因viewBox和width/height的比例不一致。SVG 默认会按preserveAspectRatio的规则缩放如果 viewBox 是正方形而容器是长方形地图就会被拉伸。解决检查 SVG 根元素的viewBox属性确保它的宽高比和你的容器一致。或者在 CSS 里给 svg 设置width: 100%; height: auto;让它按自身比例缩放。如果必须填满固定容器用preserveAspectRatioxMidYMid meet保持比例居中。5.2 改了 fill 但颜色没变化现象JavaScript 里设置了path.style.fill red但地图上那个市还是原来的灰色。原因SVG 源文件里可能用了fill...属性而 CSS 或内联样式的优先级虽然更高但如果你的选择器没选中正确的元素或者路径被g包裹且g上有fill继承就会出问题。另一种可能是路径的fill被设成了none或者用了fill-opacity0。解决先用console.log(path)确认拿到的是正确的 DOM 节点再检查path.getAttribute(fill)和path.style.fill的实际值。如果是g继承的问题把fill设在path上而不是g上。用!important是下策优先排查优先级链。5.3 镇级文件加载后点击无响应现象省级地图点击正常下钻到镇级后点击任何镇都没反应。原因替换innerHTML后新的 path 节点没有绑定事件监听器。旧节点上的监听器随节点一起被销毁了。解决在替换内容之后重新执行绑定函数。更好的做法是用事件委托——把监听器绑在容器上利用事件冒泡捕获点击这样即使子节点被替换监听器依然有效。// 事件委托写法绑在容器上一劳永逸 document.querySelector(#gd-map).addEventListener(click, function (e) { if (e.target.tagName path) { const code e.target.getAttribute(id); const name e.target.getAttribute(name); console.log(点击了 ${name}${code}); // 处理高亮和业务逻辑 } });5.4 用设计工具打开 SVG 后文字或路径丢失现象把 SVG 拖进某些设计工具或在线编辑器发现部分路径不见了或者位置错乱。原因不同工具对 SVG 规范的支持程度不同。有些工具不认viewBox有些对style标签内的 CSS 支持不好还有些对transform属性的解析有差异。解决如果需要在设计工具里编辑先把 SVG 里的 CSS 样式内联到各元素的style属性上去掉style块。用transform的地方尽量用绝对坐标替代。导出时选择 SVG 1.1 兼容模式。这份资源如果是标准路径数据通常问题不大但经过工具二次编辑后容易引入兼容性问题。5.5 文件体积过大导致加载缓慢现象SVG 文件几 MB浏览器打开要等好几秒移动端更明显。原因路径坐标精度过高保留了太多小数点后位数或者包含了大量不可见的辅助元素。解决用svgo做一轮优化它会自动压缩坐标精度、合并路径、删除冗余属性。命令行执行npx svgo input.svg -o output.svg即可。如果优化后还是大考虑按需拆分只加载当前视图需要的部分。另外开启服务器的 gzip 压缩SVG 是文本格式压缩率通常能到 70% 以上。6. 进阶技巧用 CSS 变量做主题切换与 SVG 导出为图片6.1 用 CSS 自定义属性统一管理地图配色项目做多了会发现地图配色经常要改——白天模式一套、暗色模式一套、打印又一套。如果颜色散落在 JavaScript 和 SVG 属性里改起来就是灾难。我的习惯是全部收拢到 CSS 变量里。:root { --map-bg: #e0e0e0; --map-stroke: #999999; --map-hover: #ff7f50; --map-selected: #d94e2b; --map-nodata: #f0f0f0; } /* 暗色主题 */ [data-themedark] { --map-bg: #2a2a2a; --map-stroke: #555555; --map-hover: #4a9eff; --map-selected: #1e6fd9; --map-nodata: #333333; } #gd-map svg path { fill: var(--map-bg); stroke: var(--map-stroke); transition: fill 0.2s ease; } #gd-map svg path:hover { fill: var(--map-hover); }这样切换主题只需要改>async function exportMapAsPng(containerId, fileName map.png) { const svgEl document.querySelector(#${containerId} svg); if (!svgEl) return; // 克隆一份避免影响页面上的实时地图 const clone svgEl.cloneNode(true); // 设置明确的宽高否则某些浏览器导出为空白 const bbox svgEl.getBoundingClientRect(); clone.setAttribute(width, bbox.width); clone.setAttribute(height, bbox.height); const svgData new XMLSerializer().serializeToString(clone); const svgBlob new Blob([svgData], { type: image/svgxml;charsetutf-8 }); const url URL.createObjectURL(svgBlob); const img new Image(); img.onload function () { const canvas document.createElement(canvas); // 放大两倍导出更清晰 const scale 2; canvas.width bbox.width * scale; canvas.height bbox.height * scale; const ctx canvas.getContext(2d); ctx.scale(scale, scale); ctx.drawImage(img, 0, 0); // 触发下载 const a document.createElement(a); a.download fileName; a.href canvas.toDataURL(image/png); a.click(); URL.revokeObjectURL(url); }; img.src url; }这段代码有几个关键点。第一克隆节点是为了不破坏页面上正在交互的地图。第二必须显式设置width和height因为很多 SVG 只定义了viewBox在 canvas 绘制时会被当成 0 尺寸导出就是一张白图——这个坑我踩过不止一次。第三scale设为 2 是为了高清导出代价是内存占用翻倍如果地图很大要适当降低。第四XMLSerializer序列化时外部 CSS 不会被包含进去所以导出前要确保所有样式都是内联的或者手动把 CSS 变量替换成实际颜色值。6.3 一个我坚持了很久的习惯从那以后我每次拿到一份新的 SVG 地图资源第一件事不是急着往项目里塞而是先跑一遍解析脚本把 path 数量、id 命名规则、viewBox 范围、是否有 name 属性这四项确认清楚。这四样东西决定了后面所有交互逻辑怎么写、数据怎么匹配、性能怎么优化。很多看起来是「玄学」的显示问题根源都在这一步没做扎实。希望这份广东省 SVG 地图资源能帮你省下找底图、调坐标、对代码的时间把精力留给真正重要的数据逻辑和交互体验上。希望帮到你。本文还有配套的精品资源点击获取