资讯动态

中国人口密度公里格网栅格数据:从读取、分区统计到暴露度分析的完整避坑指南

发布时间:2026/10/27 17:35:02 来源:尧图企业网站定制
简介这份《中国人口密度公里格网栅格数据》面向地理信息系统从业者、城市规划人员、社会科学研究者及高校师生用于开展人口分布的空间分析与可视化研究。资源以1平方公里格网为单位组织人口密度信息可直接导入ArcGIS、QGIS等软件支撑城市规划、公共服务布局、生态承载力评估及人口迁移与城市化趋势研究等场景。压缩包共240个文件约60.58MB以shp、dbf、prj、shx、cpg等Shapefile配套文件为主辅以adf栅格文件与xml元数据覆盖矢量边界、属性表、投影信息与栅格像元值便于分层管理与空间叠加分析。目前已有2732人学习下载。数据经过清洗、标准化与格式转换读者可据此快速完成人口密集区与稀疏区的识别、专题地图渲染及空间查询建模为政策制定与学术研究提供可靠的数据支撑。1. 中国人口密度公里格网栅格数据从压缩包到可分析图层的完整路径拿到一个名为「中国人口密度公里格网栅格数据.zip」的压缩包很多人第一反应是解压、拖进 GIS 软件、出图然后发现坐标系对不上、值域全是 0 到几万、沿海亮成一片而西部几乎全黑根本没法直接做城乡对比或选址分析。这个数据集本质上是一套 1km×1km 分辨率的全国人口空间分布栅格每个像元的值代表该平方公里内的人口数或人口密度常见来源包括 WorldPop、GPW、LandScan 以及国内一些课题组发布的格网化成果。它能解决的问题很具体把行政区级别的人口统计拆到公里尺度用于站点选址、灾害影响人口估算、商业热力分析、遥感与人口耦合建模。适合谁做空间分析、城市规划、应急评估、遥感应用的一线从业者以及需要把人口变量落到栅格上跑模型的研究生和工程师。这一章不展开操作先把「这个包到底是什么、能干什么、边界在哪」讲清楚后面几章再一步步拆解怎么用、怎么避坑。2. 先搞懂公里格网人口数据的结构与选型逻辑2.1 公里格网栅格的像元值到底代表什么「公里格网」通常指 1km 空间分辨率的规则栅格地理坐标系多为 WGS84EPSG:4326投影坐标系常见 Albers 等积投影或 Web Mercator。像元值有两种主流表达一种是人口总数每个像元内有多少人另一种是人口密度人/平方公里。这两者在 1km 格网下数值恰好接近但含义不同。如果你拿到的数据像元值范围在 0 到几万之间大概率是人口总数如果范围在 0 到几千甚至上万且沿海城市核心区特别高可能是密度。判断方法很简单用统计工具看最大值和直方图再对照已知城市的人口量级。比如上海某核心区 1km 格网人口总数可能在 3 万到 5 万密度也是同一量级但如果你要做区域汇总必须确认单位否则汇总结果会差几个数量级。常见数据源各有侧重WorldPop 提供逐年、多年龄性别分组适合做动态分析GPW 版本稳定、全球覆盖适合做长时序对比LandScan 利用遥感与夜间灯光等辅助数据对城市内部异质性刻画更细。国内也有基于普查数据反演的公里格网产品精度在东部较高、西部较低。选型时先问自己要的是总数还是密度要单年还是多年要全国还是分省答案不同选的包也不同。压缩包名字里只写了「中国人口密度公里格网栅格数据」没有版本和年份解压后第一件事就是看元数据文件或文件名里的年份标识。2.2 坐标系与投影为什么直接叠加会错位很多新手把栅格拖进 QGIS 或 ArcGIS再叠加一个行政区边界发现边界和栅格对不上偏移几百米甚至几公里。原因通常是坐标系不一致。WGS84 地理坐标系下1km 格网在经度方向的实际地面距离随纬度变化直接量算面积会失真。正确做法是如果做全国尺度统计先投影到等积投影如 Albers Equal Area中央经线 105°E双标准纬线 25°N 和 47°N再按格网汇总如果只是出图展示用 Web Mercator 也可以但不要用它做面积计算。另一个常见问题是栅格原点左上角坐标和行政区边界的对齐方式不同数据源可能采用角点对齐或中心点对齐叠加时会出现半个像元的偏移。解决办法是统一重采样到同一网格或者用「按掩膜提取」时设置对齐参数。提示拿到任何格网数据先看.prj文件或配套的.xml元数据确认坐标系、像元大小、左上角坐标和无数据值再动手分析。2.3 用 Python 读取并快速体检数据下面这段代码用rasterio读取栅格输出基本信息并画直方图。这是每次拿到新数据的第一步能帮你判断像元值含义、有无异常值、无数据值设成了多少。import rasterio import numpy as np import matplotlib.pyplot as plt # 打开栅格文件替换为你的实际路径 path china_pop_1km.tif with rasterio.open(path) as src: print(坐标系:, src.crs) print(像元大小:, src.res) print(行列数:, src.height, src.width) print(无数据值:, src.nodata) data src.read(1) # 读第一波段 # 把无数据值替换为 nan if src.nodata is not None: data data.astype(float) data[data src.nodata] np.nan print(最小值:, np.nanmin(data)) print(最大值:, np.nanmax(data)) print(均值:, np.nanmean(data)) # 画直方图去掉 nan valid data[~np.isnan(data)] plt.hist(valid, bins100) plt.xlabel(像元值) plt.ylabel(频数) plt.title(人口格网值分布) plt.show()逻辑说明rasterio.open读取元数据src.read(1)读第一波段。无数据值必须显式替换为nan否则统计会被拉偏。直方图能直观看出分布形态如果绝大多数像元集中在 0 附近少数极高值说明是典型的人口稀疏分布后续做回归或聚类时可能需要对数变换。参数方面bins100可根据数据量调整数据量大时用 200 到 500 更细。如果最大值异常大比如超过 100 万检查是否把无数据值设成了 0 或 -9999 但没处理。2.4 重采样与裁剪把全国数据切到研究区全国 1km 栅格大约有几十万行乘几十万列直接全图加载会吃满内存。常见做法是先按研究区边界裁剪再重采样到目标分辨率。裁剪用rasterio.mask重采样用rasterio.warp或gdalwarp。下面给出裁剪的代码示例。import geopandas as gpd import rasterio from rasterio.mask import mask # 读取研究区边界例如某个市的 shp gdf gpd.read_file(study_area.shp) # 确保边界和栅格坐标系一致不一致先 to_crs with rasterio.open(china_pop_1km.tif) as src: gdf gdf.to_crs(src.crs) geoms [geom for geom in gdf.geometry] out_image, out_transform mask(src, geoms, cropTrue) out_meta src.meta.copy() out_meta.update({ height: out_image.shape[1], width: out_image.shape[2], transform: out_transform }) with rasterio.open(study_area_pop.tif, w, **out_meta) as dest: dest.write(out_image, 1)逻辑说明mask函数按几何边界裁剪cropTrue会缩小输出范围。out_meta继承原元数据并更新行列数和变换参数。注意边界 shp 的坐标系必须和栅格一致否则to_crs转换后再裁剪。如果研究区跨多个投影带建议先统一到等积投影再裁剪。参数上cropTrue是必须的否则输出还是全国范围。裁剪后文件大小会大幅下降后续分析更快。3. 从像元值到业务指标统计、分区与可视化3.1 按行政区汇总人口分区统计的三种实现拿到公里格网后最常见的需求是「按省/市/县汇总人口」。栅格转矢量再汇总是一种方式但更高效的是用分区统计。QGIS 里有「分区统计」工具Python 里可以用rasterstats。下面用rasterstats做按行政区求和。import geopandas as gpd from rasterstats import zonal_stats # 读取行政区边界 gdf gpd.read_file(counties.shp) # 分区统计categoricalFalse 表示数值型求和 stats zonal_stats(gdf, china_pop_1km.tif, stats[sum, mean, max], nodata-9999) # 把结果写回 GeoDataFrame gdf[pop_sum] [s[sum] for s in stats] gdf[pop_mean] [s[mean] for s in stats] gdf.to_file(counties_pop.shp)逻辑说明zonal_stats对每个几何对象内的像元做统计stats指定需要的指标。nodata参数要和栅格的无数据值一致否则会把无数据当 0 参与求和。如果行政区边界和栅格坐标系不一致zonal_stats内部会做投影转换但最好提前统一。参数上sum得到总人口mean得到平均密度max得到最密集的 1km 格网人口。注意如果栅格是密度而非总数sum得到的是「密度之和」没有直接意义需要乘以像元面积1km²才近似总数。3.2 人口密度分级与制图让地图会说话直接展示原始像元值地图会一片糊。常见做法是分级设色分级方法有等间距、分位数、自然断点Jenks。人口数据强烈右偏等间距会让绝大多数区域落在最低一级看不出差异。推荐用分位数或自然断点。下面用mapclassify做自然断点分级并出图。import rasterio import numpy as np import matplotlib.pyplot as plt from mapclassify import NaturalBreaks with rasterio.open(china_pop_1km.tif) as src: data src.read(1).astype(float) data[data src.nodata] np.nan valid data[~np.isnan(data)] # 自然断点分成 7 级 nb NaturalBreaks(valid, k7) print(分级边界:, nb.bins) # 按分级边界离散化 classified np.digitize(data, nb.bins) classified[np.isnan(data)] 0 plt.imshow(classified, cmapYlOrRd) plt.colorbar(label人口密度等级) plt.title(中国人口密度公里格网分级图) plt.show()逻辑说明NaturalBreaks根据数据分布找最优分割点k7是常用级数。np.digitize把连续值映射到等级。出图时用YlOrRd色带低值浅黄、高值深红符合人口密度直觉。参数上k可以根据需要调整5 到 9 都常见。如果数据量太大先抽样再算断点否则计算慢。注意classified中 0 代表无数据或最低级出图时把 0 设为透明或白色。3.3 与遥感、POI 数据叠加的注意事项人口格网常和夜间灯光、NDVI、POI 密度做叠加分析。这里最大的坑是空间分辨率不匹配。夜间灯光常见 500m 或 1kmNDVI 可能 250m 或 30mPOI 是点数据。叠加前统一到同一网格用最近邻或双线性重采样。人口数据不适合双线性因为会平滑掉城市内部差异最近邻更保真。POI 点转栅格时用核密度估计再重采样到 1km比直接计数更合理。另外时间年份要对齐人口数据是 2020 年灯光是 2013 年叠加分析结论会不可靠。注意不同来源的人口格网对同一地区的估计可能差 20% 以上尤其是西部县域。做跨源对比时先在同一组行政区上汇总看总量差异再决定是否融合。4. 避坑与排查公里格网人口数据最常见的五个翻车现场4.1 像元值全是 0 或异常大现象打开栅格大片区域值为 0或者最大值达到几百万。原因无数据值被设成了 0或者单位是「人/平方公里」但误当总数汇总。解决先看元数据里的nodata用rasterio读取时替换为nan再看直方图如果 0 值占比超过 60% 且集中在西部可能是真实稀疏也可能是无数据。对照行政区边界如果边界内也全是 0基本是无数据。最大值异常时检查是否把无数据值 -9999 当有效值统计了。4.2 坐标系不一致导致面积算错现象按行政区汇总的人口和统计年鉴对不上偏差超过 30%。原因栅格是地理坐标系直接算面积时用了度数而非米或者投影不适合等积。解决先投影到 Albers 等积投影再算面积和汇总。QGIS 里用「重新投影图层」Python 里用gdalwarp -t_srs EPSG:...。确认投影参数中央经线 105°E标准纬线 25°N 和 47°N这是全国尺度常用参数。4.3 裁剪后像元值变了现象裁剪前后同一位置的值不一致。原因裁剪时用了双线性重采样改变了像元值。解决裁剪用最近邻保持原始值不变。rasterio.mask默认不重采样但如果你先重采样再裁剪值就变了。正确顺序是先裁剪再按需重采样且重采样方法选最近邻。4.4 分区统计把无数据当 0 求和现象某些行政区人口汇总明显偏低。原因zonal_stats的nodata参数没设或者栅格无数据值是 0 但实际是缺失。解决显式设置nodata并在统计前把无数据替换为nan。如果无数据值就是 0需要结合边界判断边界内 0 值像元是否合理不合理则视为缺失。4.5 出图时高值被低值淹没现象地图上只有沿海几个红点内陆全黑。原因用了等间距分级人口右偏分布导致高值拉大区间。解决改用分位数或自然断点分级或者对数据做对数变换后再出图。对数变换后1 到 10 和 1000 到 10000 的差异都能显示出来。出图时加图例说明分级方法避免误读。5. 进阶技巧用人口格网做可达性与暴露度分析5.1 基于格网的人口加权距离一个典型进阶用法是计算「某设施的服务人口」。比如一个医院服务半径 5km要算覆盖多少人口。做法是以医院为中心生成 5km 缓冲区和人口栅格做分区统计求和。但更精细的是做成本距离考虑路网和地形。如果只用直线距离用rasterio和scipy就能实现。下面给出缓冲区统计的代码。import geopandas as gpd from shapely.geometry import Point from rasterstats import zonal_stats # 医院位置 hospital Point(116.4, 39.9) # 生成 5km 缓冲区注意要投影到米制坐标系 gdf gpd.GeoDataFrame([{geometry: hospital}], crsEPSG:4326) gdf gdf.to_crs(EPSG:3857) # Web Mercator 近似米 gdf[geometry] gdf.buffer(5000) gdf gdf.to_crs(EPSG:4326) # 转回栅格坐标系 stats zonal_stats(gdf, china_pop_1km.tif, stats[sum], nodata-9999) print(5km 覆盖人口:, stats[0][sum])逻辑说明缓冲区必须在米制坐标系下生成否则 5km 会变成 5 度。Web Mercator 在高纬度有面积变形但做小范围缓冲区近似可用。更严谨的做法是用 UTM 或 Albers。zonal_stats求和得到覆盖人口。参数上buffer(5000)是 5km改半径就改这个数。如果要做多个设施循环处理即可。5.2 人口暴露度叠加洪水、高温等灾害图层暴露度分析是人口格网的高频应用。以洪水为例先拿到洪水淹没范围栅格0/1 或水深和人口栅格做逐像元乘法再汇总。关键是空间对齐两个栅格必须同一网格、同一坐标系、同一分辨率。用rasterio的reproject和resample对齐。下面是对齐后相乘的代码。import rasterio from rasterio.warp import reproject, Resampling import numpy as np # 读取人口栅格作为参考 with rasterio.open(china_pop_1km.tif) as src: pop src.read(1).astype(float) pop[pop src.nodata] np.nan profile src.profile.copy() # 读取洪水栅格重采样到人口栅格网格 with rasterio.open(flood.tif) as fsrc: flood np.zeros_like(pop) reproject( sourcerasterio.band(fsrc, 1), destinationflood, src_transformfsrc.transform, src_crsfsrc.crs, dst_transformprofile[transform], dst_crsprofile[crs], resamplingResampling.nearest ) # 暴露人口 人口 × 洪水淹没1 为淹没 exposed pop * (flood 0) total_exposed np.nansum(exposed) print(暴露人口总数:, total_exposed)逻辑说明reproject把洪水栅格对齐到人口栅格Resampling.nearest保持淹没边界。flood 0生成布尔掩膜乘以人口得到暴露人口。np.nansum忽略 nan 求和。参数上如果洪水栅格是水深可以改成pop * flood得到人口×水深的暴露度。注意两个栅格的 nodata 都要处理否则 0 值会参与计算。5.3 一个我常犯的错误忽略年份和口径最后说一个血泪经验。我曾经拿 2015 年的 WorldPop 和 2020 年的行政区人口做对比结论是「某市人口被低估 15%」后来发现 WorldPop 的 2015 版和 2020 版用了不同的普查基准和建模方法直接比年份就是错的。人口格网数据不是实测是反演结果不同版本、不同机构之间的差异可能比年份变化还大。做任何跨源对比前先在同一组行政区上汇总看总量差异再决定是否融合。如果要做时间序列尽量用同一机构同一方法论的多年产品。另一个习惯是每次分析前把元数据、坐标系、无数据值、年份、单位这五项写在一个 README 里三个月后回头看还能复现。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑