
简介一份涵盖河北省58个地表水水质国控断面的地理信息数据包面向环境监测、GIS应用及水资源管理研究者用于断面分布可视化、水质空间分析与污染源排查。包内包含完整Shapefile文件体系.shp记录断面几何位置.dbf提供所属省份、城市、流域、河流及经纬度等属性.prj定义坐标系另有.sbn、.sbx、.shx索引、.cpg编码和.xml元数据共8个文件RAR压缩后仅7KB可在ArcGIS等软件中直接加载。每个断面都是国家环保部门设定的固定监测点能支撑长期水质变化追踪、国控点位管理和环境决策已有1020人学习下载作为基础底图可快速导入科研或业务系统减少数据采集与坐标校正环节直接聚焦断面监测与治理。虽然包体很小但空间信息完整可帮助用户快速掌握河北省国控水质监测网络的整体布局。1. 58个国控断面坐标数据背后是完整的空间这一层拿到一份《河北省地表水水质国控断面坐标数据》里面是58个水质断面的名称、编码、经纬度普通直觉是“往地图上撒点就行”。实际工作中这58个坐标点是一整套水质评价业务的空间骨架每个月公开发布的水质类别、超标因子、上下游对比、重点湖泊富营养化状态都要先落到这58个点上才能分析。坐标数据本身只有几十行但坐标系是否统一、断面编码是否稳定、点位是否与河流实际走向吻合直接决定后续数据关联和可视化的成败。这篇文章按“坐标系基线 → 数据清洗校验 → 关联水质数据 → 可视化核查 → 跨期更新”的顺序把一套可直接复用的处理流程讲清楚适合做环境数据分析、GIS 可视化以及环保信息化系统的工程师参考。2. 国控断面坐标数据的结构与坐标系基线2.1 58个断面在监测网里的角色为什么这58个点就够了国家地表水环境质量监测网在主要河流干流、重要支流、湖库和跨行政区交界处布设断面用固定的采样点位代表所在水体的水质状况。河北省的58个国控断面覆盖滹沱河、滦河、潮白河、永定河、大清河、子牙河、漳卫南运河等主要水系以及若干重要湖库和饮用水源地。每个断面有唯一的断面编码和稳定的采样垂线月度水质数据都挂在该编码下。这58个点的空间意义大于普通标注点。断面的上下游关系决定了污染责任判定顺序断面所在位置与入河排污口、支流汇入口的相对关系影响超标因子分析。因此坐标数据不是只用于画点而是需要支撑距离计算、流向判断和空间关联。处理这58个断面坐标时建议始终保留断面编码、断面名称、经度、纬度、所在水体五个核心字段后续所有分析都以断面编码为主键。2.2 坐标系基线WGS84、CGCS2000 与 GCJ02 的判断方法处理坐标数据的第一件事是确认坐标系而不是先画图。地表水国控断面数据常用的坐标系有两种WGS84EPSG:4326和 CGCS2000EPSG:4490。WGS84 是 GPS 设备的原生坐标系CGCS2000 是中国官方测绘基准两者在平面位置上的差异通常在厘米到分米级对断面呈现几乎没有影响。真正的坑是 GCJ02火星坐标系国内地图厂商普遍使用它对坐标做了加密偏移偏移量达到几十米到几百米。如果用手持 GPS 或地图拾取器补充点位拿到的坐标很可能是 GCJ02直接放进 WGS84 的底图断面会落在河流岸上甚至路中央。判断坐标系可以看数据来源官方发布或测绘单位提供的坐标通常是 WGS84 或 CGCS2000从百度、高德地图拾取的坐标是 GCJ02 或 BD09。也可以用已知河流走向对照把坐标叠加到卫星影像上若所有点位都整体偏向水体一侧几十米那八成是坐标系没对齐。坐标系EPSG 代码常见来源与 WGS84 差异量级WGS844326GPS 设备、国际通用数据基准本身CGCS20004490国内测绘成果、官方发布厘米级GCJ02无公开 EPSG 标准高德、腾讯等国内地图几十至几百米BD09无公开 EPSG 标准百度地图在 GCJ02 基础上再偏拿到一份坐标数据后先抽样检查几个点与已知水体位置的关系。比如找到一个断面名称确认它对应哪条河哪段再用影像底图核对。如果点位沿河流方向整体平移优先怀疑坐标系问题而不是点位本身录错。2.3 字段结构与断面编码坐标数据行的最小可用格式实际交付的坐标数据经常带很多附加列比如考核城市、水功能区名称、经度分秒、纬度分秒等也会出现同一列叫“东经”“经度”“Lon”的不同命名。处理时先把字段统一为 ASCII 小写加下划线风格再检查断面编码的完整性。断面编码是官方监测数据里的稳定标识月度水质表、年度评价表都依赖它关联如果编码有空格、全半角差异或前后版本改动后续 join 会静默产生大量缺失行。我一般会把原始表规约成固定结构site_code、site_name、water_body、province、city、lon、lat、source_crs。其中 source_crs 记录这批坐标来自哪个坐标系这是最容易忽略也最关键的元数据。坐标数据在交付时经常不带坐标系说明收到后第一件事不是清洗而是给每个字段补上来源和坐标系信息这能避免两三个月后用数据时产生歧义。3. 用 Python 清洗与校验 58 个断面坐标数据3.1 读取与字段规范化从混杂 Excel 到干净的 DataFrame国控断面坐标数据多以 Excel 或 CSV 交付列名不统一、存在合并单元格、经纬度带度分秒格式等情况都很常见。先用 pandas 读入并查看结构再做规范化。import pandas as pd # 读取断面坐标原始表 raw pd.read_excel(hebei_sites_2024.xlsx, sheet_name断面信息表) print(raw.columns.tolist()) print(raw.head(5).to_string())读取后先处理列名映射。原始表里的“序号”“断面名称”“所在河流”“经度(°)”“纬度(°)”等中文列名统一映射为英文小写字段避免后续代码里反复写中文键名。经纬度如果存在“115°30′25″”这类度分秒字符串需要用正则拆成十进制度数。import re def dms_to_decimal(s): s str(s).strip() if not s or s.lower() nan: return None # 匹配 度分秒 或 度分 格式支持正负号和东西南北缀 m re.search(r([-]?\d(?:\.\d)?)°?\s*(\d(?:\.\d)?)?′?\s*(\d(?:\.\d)?)?″?, s) if not m: return float(s) deg float(m.group(1)) minute float(m.group(2) or 0) second float(m.group(3) or 0) if deg 0: return deg - minute / 60 - second / 3600 return deg minute / 60 second / 3600 raw[经度_清洗] raw[经度].map(dms_to_decimal) raw[纬度_清洗] raw[纬度].map(dms_to_decimal)度分秒转十进制度的关键点有两个负度数的符号要提出来不能把-115°30′算成-115 30/60北纬东经直接转换南纬西经要保留符号。转换后抽样检查几条边界河流的坐标比如滦河源头段和漳卫南运河出境段确认数值处于合理范围。3.2 范围校验与重复断面检查先看经纬度有没有硬伤河北省大致位于东经113.0°至120.2°、北纬35.0°至43.0°之间。用包围盒做一次粗筛能把经纬度填反、小数点错位、坐标落在省外的问题快速过滤出来。LON_RANGE (113.0, 120.2) LAT_RANGE (35.0, 43.0) def in_hebei(lon, lat): return LON_RANGE[0] lon LON_RANGE[1] and LAT_RANGE[0] lat LAT_RANGE[1] df[范围合格] df.apply(lambda r: in_hebei(r[lon], r[lat]), axis1) bad_range df[~df[范围合格]] print(f范围外记录数: {len(bad_range)}) print(bad_range[[site_code, site_name, lon, lat]].to_string())范围校验本身不能证明点位正确只能排除明显错误。接下来检查断面编码唯一性和经纬度重复值。正常情况下58个断面编码不应重复经纬度也不应出现完全一致的两行除非某个点位在同一条河上被重复录入。发现编码有重复时要回到原始监测表单里确认是否跨年份断面调整导致同一编码出现两个版本这类问题在坐标数据清洗里很常见。3.3 用 pyproj 完成 WGS84/CGCS2000/Web Mercator 坐标转换确认坐标系后统一转成 WGS84 便于和 OpenStreetMap、卫星影像以及常见开源可视化库对接。pyproj 是常用工具转换 CGCS2000 到 WGS84 的代码如下。from pyproj import Transformer # always_xyTrue 表示输入输出顺序固定为 (经度, 纬度) transformer Transformer.from_crs(EPSG:4490, EPSG:4326, always_xyTrue) df[lon_wgs84], df[lat_wgs84] zip(*df.apply( lambda r: transformer.transform(r[lon], r[lat]), axis1 ))这里的always_xyTrue参数需要特别说明pyproj 默认的坐标顺序在某些 CRS 定义下会变成 (纬度, 经度)对习惯 (lon, lat) 的开发者很容易踩坑。显式声明always_xyTrue后输入和输出都固定为 (经度, 纬度)代码行为稳定可预期。CGCS2000 和 WGS84 之间的坐标差异在厘米量级对断面数据的呈现影响可以忽略如果交付方要求必须用 CGCS2000 出图用同样的方式反向转换即可。如果要加载到 Web 瓦片地图做可视化还需要用 Web MercatorEPSG:3857坐标转换方式相同。需要注意Web Mercator 不是标准的经纬度坐标系它会把纬度压缩变形只适合屏幕显示和瓦片计算不适合做距离和面积量算。to_mercator Transformer.from_crs(EPSG:4326, EPSG:3857, always_xyTrue) df[x_mercator], df[y_mercator] zip(*df.apply( lambda r: to_mercator.transform(r[lon_wgs84], r[lat_wgs84]), axis1 ))3.4 空间一致性校验用河网缓冲确认断面没有漂移坐标格式正确不代表空间位置正确。断面应该落在河流主河道上或紧邻水边如果落在山脊、农田或明显偏离河道的城镇建成区通常是原始记录错误或者坐标系混用。用一条河网线数据做缓冲再与断面点做空间连接可以批量判断点位是否可信。import geopandas as gpd from shapely.geometry import Point site_gdf gpd.GeoDataFrame( df, geometrygpd.points_from_xy(df[lon_wgs84], df[lat_wgs84]), crsEPSG:4326, ) river_gdf gpd.read_file(hebei_rivers.geojson) river_gdf river_gdf.to_crs(EPSG:3857) # 河流线生成 500 米缓冲区检查断面是否在缓冲区范围内 river_buf river_gdf.geometry.buffer(500) site_gdf_merc site_gdf.to_crs(EPSG:3857) site_gdf_merc[距河流最近距离_m] site_gdf_merc.geometry.distance(river_buf.unary_union) outliers site_gdf_merc[site_gdf_merc[距河流最近距离_m] 500] print(f偏离河网超 500 米的断面数: {len(outliers)})这里的 500 米阈值不是固定标准。山区河流峡谷段河道较窄平原区河流有堤防和滩地断面在河岸一侧几十米都算正常当点位落在离河1公里以上的位置时才需要人工复核。河网数据建议从国家地理信息公共服务平台或 OpenStreetMap 的河流水系层获取导入前要统一坐标系否则缓冲距离白算。空间一致性校验对坐标数据的意义在于它能发现数值上正确、空间上错误的数据这类错误靠人眼看58个点很难全部发现。4. 坐标数据落地关联水质监测数据与可视化核查4.1 用断面编码关联逐月水质数据坐标只是起点坐标数据整理好之后下一步通常是关联逐月水质监测数据。监测总站发布的月度数据中断面编码是主键包含监测月份、水温、pH、溶解氧、高锰酸盐指数、氨氮、总磷等指标以及当月水质类别。先核对编码在两边的唯一性再执行关联。water pd.read_csv(month_water_quality_2024.csv, encodingutf-8-sig) print(断面编码唯一性:, water[断面编码].is_unique) print(坐标表行数:, len(df), 水质表断面数:, water[断面编码].nunique()) merged df.merge( water, left_onsite_code, right_on断面编码, howinner, suffixes(_site, _mon), ) print(f关联后记录数: {len(merged)})关联前先检查唯一性避免一对多导致行数膨胀。如果水质表里同一个断面编码在一个月内出现多条记录需要确认是否包含采样垂线分层数据一般国控断面评价用的是表层混合样明细数据里出现多条时要做聚合再关联。关联后要核对落空的断面编码把坐标表里有、水质表里没有的编码单独输出这类缺失往往是断面在当年新增或撤销而不是关联代码写错。4.2 坐标点与水质数据的空间关联备选按最近距离回落如果水质表里没有稳定的断面编码只有断面名称或者名称在不同年份有微小差异可以用空间距离做二次匹配。把坐标表和水质表里的断面名称归一化后尝试精确匹配剩余未匹配的记录再转成点计算相互距离取距离最近的断面作为候选对应关系。from scipy.spatial import cKDTree water_sites gpd.GeoDataFrame( water, geometrygpd.points_from_xy(water[经度], water[纬度]), crsEPSG:4326, ) tree cKDTree(water_sites[[经度, 纬度]].values) df[[匹配水质断面, 最近距离km]] df.apply( lambda r: water_sites.iloc[tree.query([r[lon], r[lat]])[1]][断面名称] if tree.query([r[lon], r[lat]])[0] 5 else (, 999), axis1, )按最近距离匹配只适合做辅助确认不能代替编码关联。两个断面在同一条河的上下游相距不足1公里时单纯用距离会串位必须再核对断面名称和水体名称的语义。最近距离超过5公里的匹配结果默认不可信直接置空交给人工处理。4.3 可视化核查QGIS 叠卫星底图与 folium 交互页坐标数据做完清洗后最后一道核查是可视化。QGIS 加载 WGS84 坐标的 CSV 文件叠加卫星影像底图逐个断面核对其是否落在河道水面上。核查时要按断面所在水体分批看不要一次把58个点都堆在地图上点位密集区域如白洋淀周边、子牙河流域容易互相压盖看不清单个点是否偏离。生成一份可交互的 HTML 地图用于团队内部复核用 folium 可以在浏览器里查看点位的名称和当前水质类别。import folium # 以河北省中部为中心缩放级别覆盖全省 m folium.Map(location[38.2, 115.5], zoom_start7, tilesOpenStreetMap) for _, r in merged.iterrows(): color red if str(r[水质类别]).startswith(劣) else blue folium.Marker( location[r[lat], r[lon]], popupf{r[site_name]} {r[水质类别]}, iconfolium.Icon(colorcolor), ).add_to(m) m.save(hebei_sites_water_quality.html)这个脚本里 location 参数是[纬度, 经度]与地图上常见的经纬度书写顺序相反写成[经度, 纬度]时点会直接跑到海里。folium 的 popup 只接受字符串拼接时要显式转类型否则某些 Python 版本下会静默丢弹窗内容。核查时除了看点位本身还要叠加河网图层确认点与河道重合重点看水质类别异常的断面是否出现了坐标偏移因为点位偏了几百米水质数据可能对应到完全不同的一段河。5. 断面坐标的版本化管理与跨期漂移检查国控断面名单不是永久不变的。每年可能因为河流治理工程、断面调整、行政区划变更出现小幅调整58个断面的数量是某一时间点的静态快照。坐标数据用 Git 做版本管理是最直接的做法把 CSV 文件纳入仓库每次更新记录数据来源、获取日期、坐标系说明和变更说明diff 时能清楚看到哪些断面的经纬度变化了。跨期漂移检查要对比新老两个版本的坐标而不是靠肉眼看。同一个断面编码在两期数据中的距离如果超过一定阈值就需要重新确认坐标来源。河流断面实际位置一般不会在一年内有明显变化出现几十米以上的偏移大概率是坐标系混用或点位重新采集造成的不一致。def haversine_m(lon1, lat1, lon2, lat2): from math import radians, sin, cos, asin, sqrt R 6371000.0 lon1, lat1, lon2, lat2 map(radians, [lon1, lat1, lon2, lat2]) dlon lon2 - lon1 dlat lat2 - lat1 a sin(dlat / 2) ** 2 cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2 return 2 * R * asin(sqrt(a)) old pd.read_csv(sites_2024.csv) new pd.read_csv(sites_2025.csv) compare old.merge(new, onsite_code, suffixes(_2024, _2025)) compare[偏移_m] compare.apply( lambda r: haversine_m(r[lon_2024], r[lat_2024], r[lon_2025], r[lat_2025]), axis1, ) print(compare.sort_values(偏移_m, ascendingFalse)[[site_code, site_name, 偏移_m]].head(10))跑出结果后偏移超过100米的断面单独拉出来人工确认是河流改道、断面位置正式调整还是坐标系填错了。可以把每年的坐标数据保持在同一个仓库里用 Git tag 标记年度版本后续接月度水质数据时固定用当年的断面编码文件避免跨年版本混用产生错误关联。日常用 58 个断面坐标时把这个版本化的校验脚本挂在数据更新流程里比每次手动比对经纬度可靠得多后续也能直接基于这套版本输出的坐标数据做空间插值和水质演变分析。本文还有配套的精品资源点击获取