深圳街道级地图数据处理:SVG解析、JSON转换与可视化应用
2026/9/16 9:07:40 网站建设 项目流程

简介:深圳街道级行政区划地图数据包,覆盖全市十个区及大鹏新区,适合城市规划、地理信息研究、地图应用开发等场景使用。包内提供SVG矢量地图、JSON地理坐标数据及PNG预览图三类文件,SVG可无损缩放用于精细制图与交互设计,JSON适合导入GIS软件进行空间查询与密度分析,PNG便于快速查看整体布局。文件总数42个,压缩包大小1.23MB,结构按区分文件夹存放,检索方便。已有3424人学习下载。通过这组数据,读者可快速获取各区街道边界轮廓,辅助完成人口分布、商业选址、交通流量等专题研究,也可直接用于教学演示或地图产品开发。

1. 为什么街道级区划地图要同时给出 SVG、JSON 和 PNG

深圳十个辖区的街道级边界,平时散落在不同平台的数据集里,不是缺这个区就是少那个区,想凑齐一套矢量轮廓和坐标数据往往得自己做一轮清洗。这份《深圳各区区划地图(街道级).rar》把南山区、福田区、罗湖区、宝安区、龙岗区、盐田区、龙华区、坪山区、光明区和大鹏新区的 SVG 矢量轮廓、JSON 坐标数据以及 PNG 预览图全部按区编排,解压出来就能直接用,省去跨平台凑素材的功夫。

对两类人价值最大:一类是做可视化大屏和 Web 地图的前端开发者,拿 SVG 当渲染底图;另一类是搞地理数据分析和城市规划的数据工程师,拿 JSON 做空间统计。SVG 把轮廓画得足够清楚,JSON 把街道边界的坐标暴露成可计算的字段,两者互相补充。接下来直接看文件内部结构,把解析、转换和验证这两套数据的完整流程走一遍。

2. 解包先做结构分析:区划目录、SVG path 与 JSON geometry

2.1 压缩包的目录组织与命名规律

解压后,整体目录结构如下:

深圳各区区划地图(街道级)/ ├── 1-南山区/ │ ├── 南山区.svg │ ├── 南山区.json │ ├── 南山区.png │ └── 南山区-缩.png ├── 2-福田区/ │ ├── 福田区.svg │ ├── 福田区.json │ ├── 福田区.png │ └── 福田区-缩.png ├── 3-罗湖区/ ├── 4-盐田区/ ├── 5-宝安区/ ├── 6-龙岗区/ ├── 7-坪山区/ ├── 8-光明区/ ├── 9-龙华区/ ├── 10-大鹏新区/ ├── 全深圳.png └── shenzheng.svg

目录命名由“序号 + 区名”组成,序号从 1 到 10,大鹏新区排在最后。每个区目录下固定有四种文件:SVG、JSON、PNG 和-缩.png。PNG 适合做列表缩略图,-缩.png是更低分辨率的版本,用在地图加载占位。根目录的shenzheng.svg全深圳.png是十个区拼好的全域视图,适合需要整体展示深圳轮廓的场景。

在实际项目里,建议先扫一遍目录,生成区名到文件路径的映射表,后续切图、着色、数据关联都从这张表取路径,避免硬编码。这套包没有自带说明文件,数据来源、坐标系和更新时间需要自己通过内容判断。

2.2 SVG 文件内部:path 节点、viewBox 与街道命名

用文本编辑器打开任意一个区的 SVG,结构大致如下:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 800" width="100%" height="100%"> <g id="南山区"> <path id="南头街道" d="M123.4,56.7 L145.6,78.9 L167.8,100.2 Z" fill="#f7f7f7" stroke="#999999" stroke-width="0.5"/> <path id="粤海街道" d="M167.8,100.2 L190.1,122.5 L210.3,140.1 Z" fill="#f7f7f7" stroke="#999999" stroke-width="0.5"/> </g> </svg>

viewBox是绘制坐标系的范围,四位数字分别表示左上角 x、左上角 y、逻辑宽度和高度。d属性中M是起点,L是经过的点,Z闭合路径。这里要特别注意:这类导出地图的 SVG,path坐标是画布像素坐标,不是经纬度,直接贴到 Leaflet 或 Mapbox 上会错位,必须做坐标换算。

命令含义参数
M移动到起点x y
L画直线到指定点x y
H水平直线x
V垂直直线y
Z闭合路径

id与街道名的对应关系也不完全可靠。部分工具导出时会把id写成内部序号,街道名放在>{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "name": "南头街道" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [113.929, 22.533], [113.931, 22.534], [113.933, 22.531] ] ] ] } } ] }

properties里保存的是街道名称和可能的行政编码;geometry.type决定coordinates的嵌套层数。判断规则很直接:Polygon是三级嵌套,MultiPolygon是四级嵌套。解析时先读geometry.type再决定取第几层,否则碰到 MultiPolygon 直接按 Polygon 取索引会越界。

把两份文件放在一起看,分工就很明确了:SVG 负责把轮廓呈现到页面上,JSON 负责给程序提供可计算的坐标序列。用街道名当主键,两套数据就能关联起来。

3. 解析 SVG path 并生成 GeoJSON:街道轮廓的提取与坐标转换

3.1 用 Python 批量提取十个区的街道 path

压缩包里没有现成的汇总 GeoJSON,第一步就是把十个区的 SVG 批量读一遍。用标准库xml.etree.ElementTree解析即可,不依赖额外库:

import os import xml.etree.ElementTree as ET BASE_DIR = "深圳各区区划地图(街道级)" NS = {"svg": "http://www.w3.org/2000/svg"} result = {} for entry in sorted(os.listdir(BASE_DIR)): district_path = os.path.join(BASE_DIR, entry) if not os.path.isdir(district_path): continue svg_file = next( (f for f in os.listdir(district_path) if f.endswith(".svg")), None ) if svg_file is None: continue tree = ET.parse(os.path.join(district_path, svg_file)) root = tree.getroot() streets = [] for node in root.findall(".//svg:path", NS): name = node.get("id") or node.get("data-name") or f"未命名_{len(streets)}" d = node.get("d", "") if d: streets.append({"name": name, "d": d}) result[entry] = streets for district, streets in result.items(): print(f"{district}: {len(streets)} 个街道")

代码逻辑说明:先遍历BASE_DIR下的子目录,目录名即区名;用next取目录内第一个.svg文件,避免路径写死;findall(".//svg:path", NS)查找全部 path 节点,NS是 SVG 命名空间字典,不传这个参数,path 节点会匹配不到。街道名优先取id,缺失时退到>import re def parse_d_to_points(d: str): tokens = re.findall(r"[MLHVQCZ]|[-+]?\d*\.?\d+", d) points = [] i = 0 while i < len(tokens): cmd = tokens[i] if cmd in ("M", "L"): x = float(tokens[i + 1]) y = float(tokens[i + 2]) points.append((x, y)) i += 3 elif cmd == "Z": break else: i += 1 return points

处理流程是:每遇到ML就取后面两个数字作为坐标点;遇到Z代表路径闭合,停止解析。tokens[i]是命令字符,tokens[i+1]tokens[i+2]是坐标数值。若碰上CQ这类曲线指令,简版会跳过控制点,对整体面积和相对形状影响不大;如果需要精确重建,再用svgpathtools做完整解析。

3.3 坐标映射:从 viewBox 像素坐标到经纬度

SVG 的像素坐标系和经纬度坐标系不直接对应,要做一个线性映射。思路是先把像素坐标按 viewBox 范围归一化,再用同一比例映射到该区的经纬度包围盒 bbox 里:

function svgToLngLat(px, py, viewBox, bbox) { const [vx0, vy0, vw, vh] = viewBox; const [lng0, lat0, lng1, lat1] = bbox; const ratioX = (px - vx0) / vw; const ratioY = (py - vy0) / vh; const lng = lng0 + ratioX * (lng1 - lng0); const lat = lat1 - ratioY * (lat1 - lat0); return [lng, lat]; }

viewBox取 SVG 根节点的四个值,bbox是目标区的经纬度范围,可以从随包的 JSON 坐标里算出。最需要注意的是纬度计算:SVG 的 y 轴向下递增,纬度从南到北递增,因此ratioY必须用lat1 - ratioY * (lat1 - lat0),否则映射出来的图形上下颠倒。这个线性变换依赖“源 SVG 为正交投影”的假设,对绝大多数从地图平台导出的行政区划素材成立;如果换成墨卡托投影的经纬度数据,要改成投影反算。

3.4 输出可复用的 GeoJSON 文件

解析完成后把坐标写进标准 GeoJSON,后面无论是 QGIS、GeoPandas 还是 Leaflet 都能直接读取:

import json geojson = { "type": "FeatureCollection", "features": [] } for district, streets in result.items(): for s in streets: points = parse_d_to_points(s["d"]) if len(points) < 3: continue coordinates = [ [svgToLngLat(x, y, view_box, bbox) for x, y in points] ] geojson["features"].append({ "type": "Feature", "properties": { "district": district, "name": s["name"] }, "geometry": { "type": "Polygon", "coordinates": coordinates } }) with open("shenzhen_streets_from_svg.geojson", "w", encoding="utf-8") as f: json.dump(geojson, f, ensure_ascii=False)

生成 GeoJSON 的关键点:少于三个点的 path 直接跳过,因为无法构成有效面;坐标对的顺序要保持一致,否则某些 GIS 工具会报拓扑错误;ensure_ascii=False保证中文字段以 UTF-8 写出。这段代码里的view_boxbbox是示意变量,实际建议每个区单独配置一份元数据字典,在循环里按district读取,避免共用包围盒导致边缘街道偏移。

4. JSON 坐标数据的空间分析:坐标系识别、面积计算与完整性校验

4.1 坐标系判断:从经纬度范围到偏移量比对

JSON 的坐标字段可以直接读进代码,但坐标系不确认,所有统计结果都不可信。深圳本地数据最常碰到三种坐标系:WGS-84、GCJ-02 和 BD-09,数值范围很接近,肉眼很难区分。下面的判断表可以作为第一步:

坐标系经度范围纬度范围坐标示例
WGS-84113.75 ~ 114.6522.43 ~ 22.85113.920000, 22.540000
GCJ-02113.75 ~ 114.6522.43 ~ 22.85113.923456, 22.541234
BD-09113.76 ~ 114.6622.44 ~ 22.86113.929876, 22.547654
EPSG:385712660000 ~ 127600002570000 ~ 263000012678384, 2568230

如果 JSON 坐标是千万元素级别的整数,直接按 EPSG:3857 处理;如果是 113 开头的小数,需要进一步判断偏移量。用一个街道边界上的已知锚点与底图对比,或者用pyproj把 WGS-84 转成 GCJ-02 后和文件坐标做差:

import json from pyproj import Transformer with open("南山区.json", encoding="utf-8") as f: data = json.load(f) coords = data["features"][0]["geometry"]["coordinates"] sample_lng = coords[0][0][0][0] sample_lat = coords[0][0][0][1] print("文件坐标:", sample_lng, sample_lat)

这段代码从第一条 feature 里取第一个坐标点并输出。拿到数值后,与在线底图上同一个街道的标志性位置比对,偏移几十米且方向规律,基本可认定为 GCJ-02。这一步决定了后续在做空间统计时,要不要先做投影转换。需要注意pyproj不是标准库,使用前先执行pip install pyproj

4.2 用 geopandas 做街道级面积与空间统计

坐标基准确认后,GeoPandas 读 JSON 成本很低。下面这段代码计算南山区每个街道的近似面积和代表性中心点:

import geopandas as gpd gdf = gpd.read_file("南山区.json") gdf["area_km2"] = gdf.geometry.area / 1_000_000 gdf["center"] = gdf.geometry.representative_point() print(gdf[["name", "area_km2"]].sort_values("area_km2", ascending=False))

geometry.area的计算单位取决于当前坐标系。如果gdf是经纬度,得到的是平方度,只能横向比较相对大小,不能直接当平方公里。要拿真实面积,需先将数据转换到深圳所在地区的 UTM 投影,即 EPSG:32649:

gdf = gdf.to_crs("EPSG:32649") gdf["area_km2"] = gdf.geometry.area / 1e6

to_crs负责投影转换,转换后area的单位变为平方米,除以 1e6 得到平方公里。representative_point()返回多边形内部的代表性点,比centroid()更适合画 label,因为后者在多边形是凹形时可能落到图形外部。

4.3 街道数量缺失检查与边界差异分析

拿到十个区的 JSON 后,最先做街道数量核对。把name字段打印出来,和正规划区名录比对,也可以用脚本检查明显缺失:

expected = { "南山区": ["南头", "南山", "沙河", "蛇口", "招商", "粤海", "西丽", "桃源"], "福田区": ["园岭", "南园", "福田", "沙头", "梅林", "华富", "香蜜湖", "莲花", "福保", "华强北"] } for district, names in expected.items(): gdf = gpd.read_file(f"{district}.json") actual = set(gdf["name"].str.replace("街道", "")) missing = set(names) - actual extra = actual - set(names) if missing or extra: print(district, "缺少:", missing, "多出:", extra)

expected需要根据当前行政区划版本手动维护,replace("街道", "")是为了去掉后缀再比对,避免“蛇口”和“蛇口街道”匹配不上。出现差异时,要判断是街道调整、文件缺块还是命名不一致导致,不能直接忽略。深圳近几年做过多次街道拆分,旧数据和现势数据对不上是常见情况。

5. 把 SVG 轮廓和 JSON 数据联动:街道级可视化与细节调优

5.1 用 JSON 驱动 SVG 着色的联动方案

SVG 和 JSON 的链路打通后,常见落地场景是街道级指标地图。实现思路是 SVG 负责呈现轮廓,JSON 负责存指标数值,用街道名关联:

<svg id="map" viewBox="0 0 1000 800"> <path>gdf_simple = gdf.copy() gdf_simple["geometry"] = gdf.geometry.simplify( 0.0001, preserve_topology=True )

simplify0.0001是最小允许误差,单位与当前坐标系一致。经纬度坐标系下 0.0001 度约合 11 米,街道轮廓整体形状不变,但顶点数会明显下降。preserve_topology设为True可以避免多边形之间出现重叠或空隙。化简后的数据既能用于 GeoJSON 输出,也能转成 SVG path 画到页面。

这套压缩包里的 SVG、JSON 和 PNG 到这里就全部串起来了:SVG 当渲染层,JSON 当数据层,PNG 做预览兜底。调试时如果发现某个街道的轮廓在放大后出现锯齿,别忘了给对应的path加上shape-rendering="geometricPrecision",矢量地图的边缘毛刺多半出在这个属性上。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询