ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

山东省17地市矢量图:格式、坐标系与QGIS处理全攻略

山东省17地市矢量图:格式、坐标系与QGIS处理全攻略

简介:《山东省17地市矢量图》是一份面向GIS教学、科研与地图可视化场景的矢量数据包,覆盖山东省全部17个地级市的行政边界与基础地理要素。资源共12个文件,包含shp(几何图形)、dbf(属性表)、shx(索引)、prj(投影坐标)等常用格式,以及lyr和mxd图层/工程文件,压缩包仅32KB,便于直接导入ArcGIS或QGIS等平台开展实验。已有1414人学习下载,常被用于空间查询、叠加分析、城市规划模拟等教学练习。借助这套数据,读者可快速掌握矢量数据处理流程,如投影转换、属性编辑、地图符号化与专题图制作,也可作为区域研究的底图数据,有效支撑从基础操作到综合分析的GIS实践。

1. 山东省17地市矢量图:还在用“过去式”行政边界的人,需要这份底图

很多人第一反应是:山东省现在不是16个地级市吗,怎么还在找“17地市矢量图”?但实际工作里,17地市版本一直没退役。存量业务系统、地方志、统计年鉴、画册和课程教材仍在使用旧版边界,行政区划调整前发布的专题地图、普查资料和项目档案里也都是17地市的空间数据。作为GIS数据处理或前端可视化工程师,你迟早会接到“用山东省17地市矢量图出图”这类需求。这篇笔记直接讲清楚:这份数据是什么、怎么选格式、用什么工具处理、坐标系怎么设,以及最容易让你翻车的那几个坑。目标是把一份历史版本行政边界图,变成你能稳定复用的出图底料。

2. 拿到“山东省17地市矢量图”前,先弄懂四件事:边界版本、数据格式、图层内容和坐标系

2.1 “17地市”是哪一年版本的边界:一次撤并带来的存量问题

山东省曾经有17个地级市,直到2019年年初,原莱芜市并入济南市,此后才变成16个。也就是说,标题里的“17地市矢量图”本质上描述的是2019年之前的行政区划状态。

这份数据到今天仍然有真实使用场景:地方志编纂需要用历史口径,年报数据要匹配当年统计年鉴的行政区划,展览馆和历史地图册要按老界线绘制,一些业务系统里存了十几年的地理编码还在引用17地市的代码表。所以,“17地市矢量图”不是错误数据,而是历史版本数据。问题在于,绝大多数下载渠道不标注边界年份,拿到手直接当现状图用,就会在涉及原莱芜市范围的图面处理上出问题。

我一般拿到数据后的第一件事,不是看图形,而是打开属性表确认边界口径。建议你在做任何处理之前,先回答三个问题:这份数据的边界是哪一年切割的?县城图层和地市图层是否来自同一套数据源?要素字段里是否带行政区划代码?回答完这三个问题,再决定后面怎么清洗和出图。

2.2 shp、GeoJSON、SVG 三种格式:用途分得清,坑才少

“矢量图”三个字涵盖的格式很广,实际项目里最常见的三种选择是 shapefile、GeoJSON 和 SVG。它们完全不互斥,但用错场合会非常难受。

格式最典型用途优势常见坑
shapefile(.shp)GIS 分析、QGIS/ArcGIS 工程、制图属性表结构完整,可记录代码与名称字段文件由多个同名后缀文件组成,漏拷一个就打不开
GeoJSONWeb 可视化、地图前端渲染文本格式可读、浏览器原生支持省界细碎点多,一个省级文件可能几十 MB,直接加载卡死
SVG平面设计、印刷出版、PPT缩放无损,可编辑节点丢失属性信息,无法挂接业务数据

这里有个带玄学色彩的教训:开发人员往往默认 GeoJSON 是万能格式,把全省17地市的面数据全部打进前端,结果页面首屏白屏十几秒。出印刷图的设计师则习惯要 SVG,但 SVG 是图形文件,不是地理数据,后续要改一个地市边界只能重新导出。

我的选择逻辑是:做空间分析、行政区划代码匹配、属性筛选,用 shapefile 或 GeoJSON;做网页端展示,用简化过的 GeoJSON;做印刷或视觉稿,才转 SVG。多个用途同时存在时,保留一份原始 shp 作为母版,其他格式都从它派生,不要反过来。

2.3 图层文件里到底有什么:字段、范围与完整性检查清单

下载一份“山东省17地市矢量图”,文件夹里通常不止一个 shp。常见情况是压缩包里同时包含省界、地市界、县区界,甚至还有乡镇界。先做完整性检查,再动手处理。

正常一份合格的数据至少包含这样几类文件:以 .shp 结尾的几何文件、.dbf 属性表、.shx 索引文件,以及可选的 .prj 坐标投影信息文件。缺 .prj 时 GIS 软件无法判断坐标系统,这是后面所有错位问题的源头。

检查步骤我按顺序做:

  1. 用 QGIS 打开 shp,查看图层属性里的“信息”面板,确认要素数量是否为17个(不含省界图层时)。
  2. 打开属性表,看有没有“地市名称”“行政区划码”“面积”字段。名称字段常是拼音缩写(如 SDQD),需要人工映射。
  3. 对地市要素做“几何检查”,跑一遍后确认没有缝隙和重叠。
  4. 在图层属性里看坐标系统。如果是“未知”或“GCS_WGS_1984”之外的奇怪名字,先记录并统一处理。
  5. 把当前矢量叠加到在线底图或已知正确的最新版边界上做目视抽检,重点关注原莱芜市周边和胶东半岛沿海区域。

整个检查过程不需要写代码,但没有做这一轮就直接进入出图,大概率会在最后交付时被项目的业务方指出边界错误。至此,数据层的基础认知已经铺好,下面进入实际动手环节。

3. 用 QGIS 处理山东省17地市矢量图:从加载 shp 到合并出图

3.1 在 QGIS 里加载17地市 shp 并确认坐标系

QGIS 是目前处理这类行政边界数据最顺手的免费工具。打开软件后,直接把 shp 文件拖进图层面板即可,不需要走“新建项目”向导。更可控的方式是通过菜单添加:图层 → 添加图层 → 添加矢量图层,然后选择文件。

加载完成后不要急着看地图,先确认坐标系。如果文件带有 .prj,QGIS 会自动识别并显示在右下角;如果不带,状态栏会显示“未知 CRS”。对17地市边界这类中国区域数据,高概率是 WGS84 经纬度坐标(EPSG:4326),也有部分来源使用 CGCS2000 地理坐标系。在 QGIS 中可以通过 Python 控制台快速确认:

layer = iface.activeLayer() crs = layer.crs() print(crs.authid(), crs.description())

这段代码会打印当前活动图层的坐标系 ID 和描述。如果输出为“EPSG:4326 - WGS 84”或“EPSG:4490 - CGCS 2000”,说明是地理坐标系,单位是度;如果看到“EPSG:3857”,说明是 Web 墨卡托投影,单位是米。这一区分直接影响后续面积计算和打印比例设置。

3.2 清洗属性表:字段名、编码与名称映射

17地市数据的属性表质量参差不齐。常见问题是字段名是拼音缩写而不是中文名称、名称字段值有前后空格、个别地市存在多条记录(通常是某个地市被拆成多块面)。这些都会在联动图表、行政区划代码匹配时产生莫名其妙的失败。

我的做法是建立一张映射表,把拼音缩写转成标准中文名,然后通过字段计算器新生成一列“标准名称”。如果原始数据根本没有名称字段,只有行政区划代码,就需要用代码表去匹配。行政区划代码是唯一的,比名称更可靠,推荐优先以代码的主键做关联。

清洗完成之后,把属性表合并成一张干净的表。具体操作:在图层属性 → 字段选项卡里可以重命名字段;右键图层打开属性表后在编辑器模式下修改值。全部改完记得提交编辑并保存。如果你需要批量处理多个来源的数据,建议把映射表导出为 CSV,用 QGIS 的“连接”功能反复复用,而不是每次手动改。

3.3 把17个地市合并成省级边界,并拆分你需要的子集

很多出图场景需要先得到山东省全省轮廓,再把17个地市叠加上去。QGIS 里做这一步叫“溶解”(Dissolve)。菜单路径是:处理工具箱 → 矢量几何 → 合并。源图层选择17地市图层,合并字段选择空值(即全部合并),运行后得到全省一个整体面。

反过来,如果只需要某几个地市的数据,用属性筛选导出更快:

from qgis.core import QgsFeatureRequest layer = iface.activeLayer() request = QgsFeatureRequest().setFilterExpression("标准名称 IN ('胶东某市', '内陆某市')") selected = layer.getFeatures(request)

这个 Python 片段在 QGIS 控制台里运行后,会把符合条件的要素选出来,之后右键图层 → 导出 → 保存所选要素为,即可得到一份子集数据。setFilterExpression里支持 SQL 风格表达式,“IN”比一串“OR”条件更简洁。

合并出来的省级边界会有两个风险点:一是边界沿线出现极细小的缝隙,原因是17个面之间原本存在拓扑不一致;二是合并后保留了多少个内部节点,这会直接影响 shp 文件大小。缝隙问题依靠“修复几何”工具做一步容差处理,节点数量问题在导出时可以设置简化阈值,后面第6章会给出具体参数。

4. 山东省17地市矢量图的坐标系选择:投影不对,位置和面积全不对

4.1 WGS84、CGCS2000 与 Web 墨卡托:三个坐标系的选择逻辑

坐标系的坑在行政边界数据里排名第一。下面这张表想把最常见的三个坐标系彻底说清。

坐标系名称EPSG单位适用场景主要问题
WGS84 经纬度4326度数据交换、GPS 原始输出长度和面积不能直接在图上量测
CGCS2000 地理坐标系4490度国内国土、规划领域标准数据与 WGS84 差异在米级以内,一般业务可忽略
Web 墨卡托3857米在线地图切片、前端底图越往高纬度面积变形越严重

如果你在 QGIS 里看到 .prj 标注 CGCS2000,不要强行把它改判为 WGS84。两者差值不大,但在1:5万以上比例尺的打印图上,地市界线的位置偏差肉眼可见。同时不要忽略一种情况:下载的 shp 没有 .prj 文件,软件默认按 WGS84 读取,但数据真实坐标可能是 CGCS2000,这种情况下叠加其他数据时会出现整体偏移,且偏移量随区域变化。

4.2 面积计算为什么不能直接在图面上量测:投影转换与椭球面积

经纬度坐标系下的地图单位是度,直接使用字段计算器里的$area函数,算出的面积结果不是平方千米,而是度数下的椭球面积,数值上不可读。处理思路有两种:一是临时把图层投影到一个适合中国区域的等积投影再做计算;二是使用 QGIS 的椭球面积计算接口。

这里给一段可以在属性表字段计算器里直接使用的表达式:

area($geometry) / 1000000

注意,这个表达式得到的结果是否可靠,取决于图层本身有没有设置正确的投影。如果图层仍是4326,计算出来的面积是“平方度”,需要先执行“重投影图层”工具,选择适合的参数再做面积计算。专门做统计出图的场景,我一般先重投影到 Albers 等积圆锥投影,再导出属性表。

更稳的做法是不要手工在属性表里算面积,而是用“测量”工具或“字段计算器”里勾选“使用椭球面积计算”选项,这样即便图层保持经纬度坐标,QGIS 也会按椭球模型算出近似真实的面积。

4.3 打印出图与 Web 发布的不同坐标设置

打印地图要求坐标系的变形尽可能均匀,常用做法是选择一个适合山东区域的投影带;而 Web 发布则基本固定是 Web 墨卡托。同一份17地市数据在两个场景下可能处理成两套成果,不必谋求一套数据通吃。

打印场景下,我推荐在 QGIS 工程里直接设置“项目 CRS”为以山东为中心的高斯投影或兰伯特等积投影,页面设置里选择 A3 横向,比例尺按实际图幅调整为1:150万或1:200万。出图时勾选“使用投影比例尺”,保证图面比例尺标注与实际一致。

Web 发布场景下,前端地图库通常基于 EPSG:3857,但这不意味着后端数据必须存成3857。更好的习惯是后端保留4326的原始GeoJSON,前端在加载时通过地图库的投影功能动态转换,避免直接存储3857导致边界节点坐标膨胀。真要用3857出切片,也建议先简化边界,再转投影。

5. 处理“山东省17地市矢量图”最常见的五个坑:现象、原因与解决方案

5.1 相邻地市之间出现缝隙或重叠:边界不闭合

现象:在两个地市的接边处存在一条极细的白缝,或者放大后两个面互相侵入。打印图不明显,但在透明度较高的专题图上非常显眼。

原因:17个地市的面不是来自同一套数据切割,而是由多个县界拼接后再合并,拼接处存在拓扑容差差异。

解决:先用“修复几何”工具将所有要素过一遍,再用“消除缝隙”步骤设置一个0.001度的容差进行微调。QGIS 里可以用 v.clean(GRASS 组件)里的“break”和“snap”步骤,参数建议为阈值0.001。处理完后重新检查几何有效性。

5.2 叠加最新数据时,原来莱芜市的区域对不上

现象:最新版在线底图上,原莱芜市区域已经显示为地级市的一部分,但17地市矢量图里还有独立的边界,导致引用的图表口径混乱。

原因:不是数据坏了,而是两个版本本来就并存。数据源未标注边界年份,使用方误把它当作当前行政区划。

解决:在图层命名和项目文档里强制标注“边界截止日期:2019年1月前”。如果需要同时体现旧地市界和当前边界,做成两个图层并添加说明文字,不要用属性筛选强行“融合”。部分业务方坚持要最新边界时,直接从当前的16地市数据替换,不要在这份老版数据上修修补补。

5.3 GeoJSON 文件太大,网页首屏加载卡死

现象:把17地市 shp 转成 GeoJSON 后文件动辄几十 MB,地图初始化阶段白屏很长时间;移动端浏览器还会出现崩溃。

原因:行政边界的高精度节点全部保留,用 QGIS 默认导出 GeoJSON 时没有做简化,层级细节远超 Web 展示需求。

解决:导出时设置简化容差。建议省级边界图控制在0.001度到0.005度之间,既能保持地市边界形状基本不变,又能让文件大小下降60%以上。更精细的做法是对不同缩放层级做多套简化数据,这个在最后一章给出命令。

5.4 属性表里的中文名称变成乱码

现象:用 QGIS 打开后,地市名称显示为“?????”或一堆乱码符号,无法正常匹配数据。

原因:shapefile 的 .dbf 文件存储编码不是 QGIS 默认的 UTF-8,很多国内来源的旧 shp 用的是 GBK 或 GB2312 编码。

解决:加载文件时在“数据源管理器”里手动指定编码为 GBK,或者在 QGIS 里用“另存为”功能,输出编码选择 UTF-8,形成一份新文件。这里有个经验:与其每次加载时手动选择,不如直接清洗后另存一份 UTF-8 版本,后面所有项目都用这个新文件,不再动原始文件。

5.5 在属性表里算出来的面积数字离谱:单位是度²

现象:某个地市算出来面积值是几万,但实际应该是几千平方千米。反复检查字段和图层都没发现问题。

原因:没有区分“椭球面积”和“投影面积”,直接用经纬度坐标计算默认面积,得到的是角度平方单位。

解决:属性表字段计算器里勾选“使用椭球面积计算”,或者先把图层重投影到适合山东区域的等积圆锥投影,再计算面积字段。做面积统计时统一用同一个投影参数,避免17个地市之间面积口径不一致。

6. 从原始数据到可发布底图:一套通用处理命令链和自检习惯

6.1 用 ogr2ogr 完成转码、简化与按地市拆分

QGIS 交互操作适合单次出图,但批量处理或定时更新时,命令行的效率优势非常明显。系统里装好 GDAL 后,可以用ogr2ogr一条命令完成格式转换、编码处理和数据简化。下面是一套我在项目里重复使用的命令链。

# 第一步:把 shapefile 转为 UTF-8 编码的 GeoJSON,并做适度简化 ogr2ogr -f GeoJSON -lco ENCODING=UTF-8 \ -simplify 0.002 \ shandong17_simplified.geojson \ 山东省17地市.shp # 第二步:按属性字段“标准名称”把单个地市拆分成独立文件 for city in 某沿海市 某内陆市 某北部市; do ogr2ogr -f GeoJSON \ -where "标准名称 = '${city}'" \ "./output/${city}.geojson" \ shandong17_simplified.geojson done

第一条命令中的-simplify 0.002是简化容差参数,单位是度,数值越小保留细节越多。0.002 在省级边界图上能保留较完整的海岸线轮廓,同时能把文件控制在适合 Web 加载的大小。第二条命令用-where做属性过滤,等同于 QGIS 里的按字段选择导出。

ogr2ogr是 GDAL 自带的矢量数据转换工具,跨平台可用,参数风格统一。用命令链替代手工操作后,后台的定时更新和数据管线可以自动完成同一步骤,避免人力反复点击。

6.2 交付前最后跑一次几何校验,再确认坐标系

数据处理完成后,我有两个必做动作。第一个是几何拓扑校验,直接用 ogrinfo 快速查看图层是否可正常读取,并统计要素数量:

ogrinfo -al -so shandong17_simplified.geojson

这条命令会输出要素个数、图层范围、字段结构。如果要素个数不是17,或者范围里出现明显超出山东纬度的数值,说明数据有问题。第二个动作是回到 QGIS,把处理后的结果叠在一张在线底图上,随机抽取两个地市边界做目视对照。自动化校验只能发现格式层面问题,行政边界的细微错位最终还是靠人眼抽查。我一直保留这个习惯,它救过很多次快出问题的交付。希望帮到你。

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

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进