ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

成都市矢量数据三年变化检测:坐标系、字段与拓扑验收实战

成都市矢量数据三年变化检测:坐标系、字段与拓扑验收实战 简介这份2020—2022年成都市矢量数据资源面向GIS从业者、城市规划研究者、交通与水文分析人员及地图应用开发者提供覆盖成都全域的多要素空间数据集可用于空间分析、制图与决策支持。压缩包共229个文件约95.58MB以shp、shx、dbf、prj等Shapefile核心格式为主辅以sbx、sbn空间索引与cpg编码文件另含xlsx统计表、xml元数据及tif栅格影像兼顾矢量与栅格处理需求。内容涵盖路网城市道路、铁路、高速、国道及各级公路、河流水系、建筑轮廓、省市县乡镇行政区划以及包含医疗保健、住宿、商务住宅、风景名胜、科教文化、金融保险、交通设施等分类的十万级POI数据并附DEM数字高程模型。目前已有1673人学习下载适合需要完整成都空间底图、开展城市研究与智慧城市系统搭建的读者参考使用。1. 成都市矢量数据这三年谁在找、拿来干什么、值不值得跟2020 到 2022 这三年成都的城市骨架变化比很多人想象得快——天府新区路网加密、东部新区从图纸落到地面、绕城内外大量地块性质调整。做城市分析、选址评估、物流路径规划的人绕不开一套能覆盖这三年的成都市矢量数据。它通常指行政区划边界、道路路网、水系、建筑轮廓、POI 点、土地利用这几类图层格式以 Shapefile、GeoJSON、FileGDB 为主坐标系多为 WGS84 或 CGCS2000。找这套数据的人诉求很集中要么想做三年对比看城市扩张方向要么要给自己的模型喂一份本地底图要么在做一个需要行政边界做裁剪的活儿。这篇不聊虚的把数据拿到手之后怎么验、怎么用、参数怎么设、哪里容易翻车一条条讲清楚。适合已经有一份数据但不确定能不能直接用的人也适合正准备去找这套数据的人。2. 拿到成都市矢量数据先别急着画图坐标系与字段的验收流程一套矢量数据能不能用不取决于它画出来好不好看而取决于坐标系对不对、字段完不完整、拓扑有没有断裂。我见过太多人拿到 Shapefile 直接拖进 QGIS 就开始出图结果面积算出来差了几十倍或者两个图层叠不上回头排查半天发现是坐标系问题。这一章把验收流程拆成可执行的步骤每一步都有对应的命令和判断标准。2.1 坐标系判定WGS84 还是 CGCS2000差在哪成都市矢量数据常见的坐标系有三种WGS84EPSG:4326、CGCS2000EPSG:4490、以及投影后的 UTM 或高斯克吕格如 EPSG:4547 CGCS2000 / 3-degree Gauss-Kruger CM 105E。WGS84 和 CGCS2000 在成都这个纬度上经纬度数值差异通常在小数点后五六位肉眼看不出但做面积和距离计算时会有米级偏差。如果你要做的是展示类地图两者互换问题不大如果要做缓冲区分析、面积统计、路径长度计算必须统一到投影坐标系。判断当前数据坐标系最直接的办法是看 Shapefile 的 .prj 文件或者用 Python 读一遍import geopandas as gpd # 读取矢量数据查看坐标系 gdf gpd.read_file(chengdu_roads_2020.shp) print(当前坐标系:, gdf.crs) print(要素数量:, len(gdf)) print(几何类型:, gdf.geom_type.unique()) print(字段列表:, list(gdf.columns))这段代码做了四件事读文件、打印 CRS、统计要素数、列出字段。如果gdf.crs返回None说明 .prj 文件丢失这份数据就是“黑匣子”——你不知道它是什么坐标系后续所有空间计算都不可信。遇到这种情况常见做法是找同批数据的其他图层交叉比对或者用已知地标如天府广场的经纬度反推。参数说明gpd.read_file()默认读取第一个图层如果是 FileGDB 多图层需要加layer参数指定。gdf.crs返回的是 pyproj 的 CRS 对象可以直接用于后续的to_crs()转换。2.2 字段完整性检查哪些字段缺了会让分析断腿成都市矢量数据的字段设计不同来源差异很大。道路数据至少要有名称、等级、长度行政区划要有名称、编码、层级POI 要有类别、名称、经纬度。验收时重点看三件事字段名是否可读有些数据用拼音缩写或英文缩写、字段值是否为空、分类字段的枚举值是否一致。# 检查关键字段的空值率和唯一值 for col in [name, type, level]: if col in gdf.columns: null_rate gdf[col].isna().mean() print(f{col} 空值率: {null_rate:.2%}) if gdf[col].dtype object: print(f{col} 唯一值示例: {gdf[col].unique()[:10]}) else: print(f警告: 缺少字段 {col})逻辑很直白空值率超过 30% 的字段基本不能作为分析主键分类字段的唯一值如果出现“道路”“road”“RD”混用后续分组统计会碎成一地。我一般会先把分类字段做一次标准化映射再进入分析环节。2.3 拓扑检查路网断头、面重叠、悬挂点怎么查拓扑问题是最容易被忽略、又最影响结果的。路网断头会导致路径分析失败面重叠会导致面积统计重复计算悬挂点会让缓冲区分析出现毛刺。用 GeoPandas 加 Shapely 可以做基础检查from shapely.validation import explain_validity # 检查几何有效性 invalid gdf[~gdf.geometry.is_valid] print(f无效几何数量: {len(invalid)}) if len(invalid) 0: for idx, row in invalid.head(5).iterrows(): print(f索引 {idx}: {explain_validity(row.geometry)}) # 路网断头检查找出度为1的端点简化逻辑 # 实际项目中通常用 networkx 构建图后统计explain_validity会告诉你几何哪里有问题常见的是“Self-intersection”和“Ring Self-intersection”。修复方式通常是buffer(0)但这个操作会改变几何形状对面积敏感的分析要慎用。路网断头检查更可靠的做法是把线要素转成图结构统计每个节点的度度为 1 的就是断头点。提示拓扑修复没有后悔药改之前一定先备份原始文件。我习惯在文件名里加_raw后缀保留原始版本所有修复操作在新文件上做。3. 三年数据怎么对齐时间维度上的裁剪、匹配与变化检测2020、2021、2022 三年的数据放在一起最大的问题不是格式而是“对不上”——边界调整了、路网延伸了、地块合并了。这一章讲怎么把三年数据对齐到同一套空间基准上以及怎么做变化检测。3.1 统一边界用哪一年的行政区划做底三年间成都的行政区划有过调整如果直接用 2020 年的边界去裁剪 2022 年的数据边缘部分会丢。常见做法是取三年边界的并集作为分析范围或者以最新一年的边界为准把前两年的数据裁剪到同一范围。# 读取三年行政区划边界 boundary_2020 gpd.read_file(boundary_2020.shp).to_crs(EPSG:4547) boundary_2021 gpd.read_file(boundary_2021.shp).to_crs(EPSG:4547) boundary_2022 gpd.read_file(boundary_2022.shp).to_crs(EPSG:4547) # 取并集作为统一分析范围 from shapely.ops import unary_union unified unary_union([ boundary_2020.geometry.unary_union, boundary_2021.geometry.unary_union, boundary_2022.geometry.unary_union ]) # 裁剪各年数据到统一范围 roads_2020 gpd.clip(gpd.read_file(roads_2020.shp).to_crs(EPSG:4547), unified) roads_2022 gpd.clip(gpd.read_file(roads_2022.shp).to_crs(EPSG:4547), unified)这里的关键参数是to_crs(EPSG:4547)把经纬度转成投影坐标后再做裁剪和面积计算。unary_union把三个多边形合并成一个gpd.clip按这个范围裁。注意clip会保留落在边界内的部分跨界的线会被切断这是符合预期的。3.2 变化检测新增道路、消失地块、面积增减怎么算变化检测的核心是“差集”操作。新增道路 2022 年路网减去 2020 年路网消失地块 2020 年地块减去 2022 年地块。用 GeoPandas 的overlay可以做# 新增道路2022 减去 2020 new_roads gpd.overlay(roads_2022, roads_2020, howdifference) print(f新增道路长度: {new_roads.geometry.length.sum() / 1000:.2f} 公里) # 面积变化按行政区统计 landuse_2020[area_ha] landuse_2020.geometry.area / 10000 landuse_2022[area_ha] landuse_2022.geometry.area / 10000 # 按区县汇总 summary_2020 landuse_2020.groupby(district)[area_ha].sum() summary_2022 landuse_2022.groupby(district)[area_ha].sum() change (summary_2022 - summary_2020).sort_values(ascendingFalse) print(change.head(10))overlay的howdifference返回的是在第一个图层但不在第二个图层中的部分。面积单位换算成公顷除以 10000更符合国内规划习惯。按区县汇总时groupby的字段名要和数据里的实际字段一致如果字段是英文缩写先做一次重命名。3.3 时间戳对齐属性表里的年份字段怎么用有些数据集在属性表里带了年份字段比如year或update_time。如果三年数据是合并在一个文件里的可以直接按字段筛选如果是分开的文件建议合并后加一个source_year字段再统一处理。# 合并三年数据并标记来源年份 roads_2020[source_year] 2020 roads_2021[source_year] 2021 roads_2022[source_year] 2022 import pandas as pd roads_all pd.concat([roads_2020, roads_2021, roads_2022], ignore_indexTrue) roads_all gpd.GeoDataFrame(roads_all, crsEPSG:4547) # 按年份统计道路总长度 length_by_year roads_all.groupby(source_year).apply( lambda x: x.geometry.length.sum() / 1000 ) print(length_by_year)pd.concat合并时要注意列名一致如果某一年多了或少了一个字段合并后会出现 NaN。ignore_indexTrue重置索引避免索引重复。gpd.GeoDataFrame重新包装确保几何列被正确识别。4. 避坑与排查成都市矢量数据落地时最容易翻车的五个地方这一章是我自己踩过的坑也是周围同事问得最多的。每条按“现象 → 原因 → 解决”写不绕弯子。4.1 面积算出来差了几十倍现象用 GeoPandas 算地块面积结果和 ArcGIS 里对不上差几十倍甚至上百倍。原因数据还在 EPSG:4326经纬度下就直接调.area返回的是“平方度”而不是平方米。成都纬度约 30.6 度1 度经度约 96 公里1 度纬度约 111 公里平方度换算成平方米的系数在千万级别。解决所有面积和距离计算前先to_crs()转到投影坐标系。成都常用 EPSG:4547CGCS2000 3 度带 105E或 EPSG:32648WGS84 UTM 48N。转完再算结果和 ArcGIS 一致。4.2 两个图层叠不上偏移几百米现象道路图层和行政区划图层叠加后道路整体偏移不在正确位置。原因两个图层坐标系不一致一个是 WGS84一个是 CGCS2000或者一个是地理坐标一个是投影坐标。虽然 WGS84 和 CGCS2000 在成都差异不大但投影和地理坐标混用会导致严重偏移。解决用gdf.crs检查每个图层的坐标系统一to_crs()到同一个投影坐标系后再叠加。如果 .prj 丢失用已知控制点反推坐标系或者找同源数据比对。4.3 Shapefile 字段名被截断成 10 个字符现象字段名district_name变成district_npopulation_2020变成populati_1。原因Shapefile 格式本身限制字段名最长 10 个字符这是 1990 年代的设计遗留。用 GeoPandas 或 QGIS 导出 Shapefile 时超长字段名会被自动截断。解决如果字段名很重要改用 GeoPackage.gpkg或 FileGDB 格式这两种没有 10 字符限制。如果必须用 Shapefile在导出前把字段名改成 10 字符以内的缩写并保留一份字段映射表。4.4 中文属性乱码现象属性表里的中文名称显示为乱码如“æ¦ä¾¯å”。原因Shapefile 的 .dbf 文件默认编码是系统编码Windows 下是 GBKLinux/Mac 下可能是 UTF-8。跨平台打开时编码不匹配就乱码。解决用 GeoPandas 读取时指定encoding参数如gpd.read_file(data.shp, encodinggbk)或encodingutf-8。如果已经乱码用gdf[name] gdf[name].str.encode(latin1).str.decode(gbk)尝试修复。根治办法是转成 GeoPackage 或 GeoJSON这两种格式默认 UTF-8。4.5 裁剪后要素数量对不上现象用行政区划裁剪路网裁剪后要素数量比预期少很多或者多了很多碎线。原因gpd.clip会把跨界的线切断一条路如果跨了两个区会被切成两段。如果原数据有 1000 条路裁剪后可能变成 1500 条。反过来如果边界本身有拓扑问题如自相交裁剪结果可能丢失要素。解决裁剪前先检查边界几何有效性用boundary.geometry.is_valid.all()确认。裁剪后如果要保持原要素数量用gpd.sjoin做空间连接而不是clip这样跨界的要素会保留完整几何只是属性上归属一个区。5. 从三年数据里挖出趋势一个可复用的变化检测脚本与验证方法前面讲了验收、对齐、避坑这一章落到一个具体技巧怎么用一套脚本把三年数据的变化趋势跑出来并且验证结果是否可信。这套脚本我用了两年多改过几版现在这版比较稳。核心思路是统一坐标系 → 统一边界 → 按年分组 → 差集检测 → 可视化验证。先看完整脚本import geopandas as gpd import pandas as pd from shapely.ops import unary_union # 配置区 CRS EPSG:4547 YEARS [2020, 2021, 2022] BOUNDARY_FILE boundary_{year}.shp ROADS_FILE roads_{year}.shp # 1. 读取并统一边界 boundaries {} for y in YEARS: b gpd.read_file(BOUNDARY_FILE.format(yeary)).to_crs(CRS) boundaries[y] b assert b.geometry.is_valid.all(), f{y} 年边界存在无效几何 unified_boundary unary_union([b.geometry.unary_union for b in boundaries.values()]) # 2. 读取路网并裁剪 roads {} for y in YEARS: r gpd.read_file(ROADS_FILE.format(yeary)).to_crs(CRS) r gpd.clip(r, unified_boundary) r[source_year] y roads[y] r # 3. 逐年变化检测 for i in range(len(YEARS) - 1): y1, y2 YEARS[i], YEARS[i 1] new_roads gpd.overlay(roads[y2], roads[y1], howdifference) new_length new_roads.geometry.length.sum() / 1000 print(f{y1} → {y2} 新增道路: {new_length:.2f} 公里) # 4. 合并全量数据用于趋势分析 all_roads pd.concat(roads.values(), ignore_indexTrue) all_roads gpd.GeoDataFrame(all_roads, crsCRS) # 5. 按年统计总长度 trend all_roads.groupby(source_year).apply( lambda x: pd.Series({ total_km: x.geometry.length.sum() / 1000, count: len(x) }) ) print(trend)这段脚本的关键点assert语句做边界有效性检查不通过直接报错避免带着问题数据往下跑gpd.clip统一裁剪范围保证三年数据可比overlay的difference做差集得到新增部分最后按年汇总输出趋势表。验证方法有三层。第一层是数值验证新增道路长度不应该超过当年总长度的某个比例如果 2020→2021 新增了 80% 的路大概率是数据问题而不是真实增长。第二层是空间验证把新增道路叠加到卫星影像上抽查几个区域看是否真的对应新建道路。第三层是交叉验证如果手头有统计年鉴的道路里程数据和脚本算出来的对比偏差在 10% 以内算合理。我一般会在脚本最后加一段导出把变化检测结果存成 GeoPackage方便在 QGIS 里做可视化检查# 导出变化检测结果 new_roads.to_file(new_roads_2021_2022.gpkg, driverGPKG) trend.to_csv(road_trend_2020_2022.csv, encodingutf-8-sig)encodingutf-8-sig是为了 Excel 打开 CSV 时中文不乱码这个小细节坑过我一次后来就养成习惯了。这套脚本的边界在于它假设三年数据的字段结构基本一致如果某一年多了或少了关键字段concat后会出现大量 NaN需要在合并前做字段对齐。另外overlay在数据量大时比较慢成都全市路网三年数据跑一次大概几分钟如果要素超过百万级建议先按区县拆分再跑。三年成都市矢量数据的价值不在于数据本身有多全而在于你能不能把它对齐、验证、跑出可信的结论。我自己的习惯是拿到任何一套矢量数据先跑坐标系和字段检查再做拓扑验证最后才进入分析。这个顺序反过来后面全是返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表