夜间灯光数据省市裁剪全流程:1986-2024面板数据制作指南 夜间灯光数据这几年在空间经济学、城乡规划、人口估算、城市扩张研究里几乎成了标准原料。但很多人拿到原始数据的第一反应是直接按全国范围出图、算均值真正到省市层面就卡住了——要么坐标对不齐要么多年数据量纲不统一要么裁剪完一片花白。这篇文章就以“裁剪到省市的夜间灯光数据1986-2024”为主线把从数据源选择、边界处理、批量裁剪、时间序列校正到常见坑位排查的完整流程拆开讲清楚。适合刚入门的GIS学生也适合已经会操作但在多年度面板数据上反复出问题的研究者。1. 数据底盘1986—2024年夜间灯光产品到底怎么选1.1 不是所有夜间灯光数据都叫DMSP和VIIRS做夜间灯光分析绕不开三个字母DMSP-OLS、NPP-VIIRS还有珞珈一号。传统研究中DMSP-OLS覆盖1992到2013年分辨率约1公里但它的传感器增益设置在不同年份不完全一致直接跨年比较会有系统性偏差。NPP-VIIRS从2012年运行至今分辨率约500米灵敏度更高但和DMSP之间存在明显的量纲差异。珞珈一号属于国产高分辨率夜光卫星分辨率和成像质量都不错但覆盖年份较短多用于局部精度验证。那标题里为什么会出现1986年严格说公开的DMSP-OLS产品最早到1992年1986-1991这段时间要靠一些重建数据集来补。比如部分研究团队基于DMSP原始数字档案做过反演或利用统计模型把早期灯光值外推出来。这类重建数据在学术圈有流传但使用时一定要仔细看数据说明搞清楚它是“模拟值”还是“传感器实测值”。如果你做的只是省市级别的长周期趋势分析重建数据可以接受如果要做精确的绝对灯光强度对比我更推荐从1992年往后做或者至少把1986-1991单独标注成低置信区间。1.2 各省市的夜间灯光面板为什么要单独裁剪而不是直接求和很多初学者会直接用全国栅格做分区统计得到各省均值后用Excel画趋势线。这个做法本身没问题但一旦进入实际操作会踩到几个隐形坑一是全国栅格范围大loading和处理速度慢尤其是在老电脑上打开一张1公里分辨率的全国tif都费劲更别说VIIRS的500米数据二是行政边界和栅格像元之间必然存在“边界跨像元”问题如果边界数据本身精度不高统计结果会混入邻近省份的灯光三是后期做空间计量模型时需要按省或市输出独立的tif、独立的灯光总值、独立的夜间灯光增长率指标这些都必须在一开始就以省、市为单元把数据切好。把全国影像裁剪到省市级别不是简单的“少看一点地图”而是把后续研究的数据口径固定下来。你拿到的每一省、每一市的影像都能独立归档、独立出图、独立建模这才是面板数据该有的样子。1.3 时间段跨度近四十年先给数据做“体检”再谈裁剪1986到2024横跨了近四十年期间传感器、坐标系、文件格式全变过。早期数据可能是TIFF、Grid或二进制浮点格式投影五花八门后期VIIRS数据基本都是WGS84或正弦投影但不同的发布渠道还会给你不同处理版本。面对这批数据第一件事不是急着裁剪而是做一次完整的“体检”逐年份确认影像格式、栅格大小、像元类型、无效值设定、地理范围。像DMSP数据常见的无效值设定为0而NPP-VIIRS会把背景噪声设为负值或无数据这两种情况如果不提前统一裁剪后就会在统计值里混入大量异常。我之前处理一批数据时发现某个省在个别年份的灯光总值突然跳高查了半天才知道是当年影像的无效值标成了255而不是0统计时把边界外的无效像元全算了进去。这种问题在单年分析里很容易被忽略但在40年的长面板里会被放大成明显的异常峰值。2. 裁剪前必须定死的三件事边界、坐标、工具2.1 行政边界数据你用什么底图决定了裁剪结果多干净省市裁剪的第一步是拿到一套可靠的行政边界矢量。这里的关键词是“可靠”。从网上下载的某些省界数据可能做过简化线边界很粗糙裁剪出的栅格边缘会出现明显的尖角或锯齿更麻烦的是不同来源的省界范围存在几十米的偏差相邻省份之间甚至会有重叠或空隙。我个人的建议是优先使用测绘部门公开出版的基础地理数据或者知名GIS平台自带的行政边界图层并固定一个版本不要在分析中途更换边界文件。拿到矢量后首先要做的是“拓扑检查”。用GIS软件跑一遍看看有没有重叠面、缝隙、自相交。很多程序处理时只对图层做简单裁剪边界重叠区会被重复计算缝隙区则变成NoData这会让省市级统计结果全线失真。拓扑检查看起来多此一举但能救回你后面好几天时间。另外要统一省市两级边界之间的关系。比如你既想按省裁剪又想按市裁剪那就需要确认市级边界是否完全由省级边界细分而来市界拼接后是否能恰好还原省界。如果市界和省界来源不同并行使用就会出现“市的边界对不上省的边界”到最后做省级汇总时数据对不齐。2.2 坐标和投影把所有影像先扔进同一个“坐标系房间”裁剪的本质是“把栅格的像元与矢量面的位置对齐”因此两者必须在同一个坐标系下工作。很多夜间灯光原始数据是WGS84经纬度坐标系而省级边界可能是高斯-克吕格投影或Albers等积投影。直接在两个坐标系下做裁切工具会强行执行转换但转换参数和插值算法不同结果差异非常明显。我通常的做法是先把所有矢量数据统一到与栅格相同的经纬度坐标系下进行初裁再按研究需要投影到Albers等积投影或兰伯特投影做进一步分析。如果所有省市级数据要用于面积统计、密度计算等积投影比等角投影更合适。还有一个细节统一分辨率。DMSP原始分辨率约30弧秒VIIRS约15弧秒两者在省市尺度上差异不大但如果你把VIIRS重采样到1公里与DMSP对齐必须选好重采样方法。双线性插值适合连续数据最邻近法适合分类数据而灯光亮度值介于两者之间我倾向于使用双线性但会注意不要过度模糊城市核心区的峰值。2.3 工具选型ArcGIS、QGIS还是Python做单年度、单省份裁剪ArcGIS的“按掩膜提取”或“裁剪”工具完全够用。QGIS则是免费且跨平台处理上不会有太大代差。但如果你的目标是“1986-2024年、每年全国影像、按省和市分别输出”这种批量任务就别再用鼠标一个个点了直接上Python组合geopandas和rasterio更高效。另外批处理脚本能把操作记录固化下来哪天换了数据源或改了边界重跑一遍就行这在科研项目里特别重要。不过工具选型不是“越高级越好”。数据量小、机器配置一般、只是临时做一次省级裁剪用Python反而要装一堆依赖库升级环境时间比手工时间长。我的建议是一次性探索用小工具批量生产用脚本两者配合不要互相排斥。3. 实操全流程从全国栅格到省市逐年面板3.1 第一步原始影像预处理单位、无效值和范围先理清拿到一景夜间灯光原始数据后先打开属性表看三项像元类型、缩放因子、无效值。DMSP数据常见的单位是“数字值”有时会带有缩放因子需要乘以增益系数得到无单位的相对亮度VIIRS数据则常见单位为“nanoWatts/cm2/sr”也有的产品版已经做过噪声校正。很多人在裁剪前不做单位变换导致跨传感器比较时数字差了几十倍这个在后面做面板模型时会非常头疼。以DMSP和VIIRS衔接问题为例某省2000年的灯光均值可能是10而2014年VIIRS的均值可能是15数字看着接近但两者物理意义完全不同绝对不能直接说“14年比13年上升了50%”。业界常用的方法是选择一个重叠年份如2012或2013用线性回归或分位数映射把VIIRS数据回推到DMSP的尺度。这个校正过程最好在省级裁剪之前完成因为在全国尺度上拟合的参数更稳定分省校正会产生大量局部噪声。预处理还要检查影像范围。有些重建数据的全球范围是[-180,180]有些是东半球局部范围裁剪前哪怕只是做一次简单的“查看范围”都能避免后面产出全空栅格。3.2 第二步按省市面矢量裁剪别忽略缓冲区和NoData在Python里用rasterio做裁剪时核心思路是先读取面矢量的边界范围再通过掩膜把栅格像元按面边界切出来。这个步骤里最容易出的问题有两个一是边界上的像元归属二是裁剪后边缘的NoData。栅格像元本身是一个矩形网格而省界是一条任意曲线两者不可能完全重合。软件处理时有的按“像元中心是否落在面内”归属有的按“像元面积与面的相交比例”归属。如果不做设置默认结果就会在一些边界像元上“偏一半”。解决思路有两种第一种是权衡后不做亚像元处理主要适合市级、省级这种较大尺度边界像元带来的误差可忽略第二种是给自己的研究定义一个缓冲区规则比如裁剪时对外扩1公里把边界判定的不确定留给外部区域而不是让中心城区边缘的灯光被一分为二。裁剪后一定要检查输出栅格的最大最小值如果整片是NoData或只有一个常数基本可以断定是坐标或范围出了问题。批量操作时我用的是这样的循环思路import geopandas as gpd import rasterio from rasterio.mask import mask boundary gpd.read_file(city_boundary.shp) with rasterio.open(national_light.tif) as src: for _, row in boundary.iterrows(): geom row.geometry out_image, out_transform mask(src, [geom], cropTrue, nodata0) # 将 out_image 写为 per_city_light.tif这段逻辑每个人都能写但真正影响产出质量的是你有没有在循环之前统一所有矢量的字段名、坐标系、编码格式。shp文件字段名如果是中文不同环境读出来可能会乱码我是建议在进入批处理前先把字段名改成英文、把坐标系统一成EPSG:4326能省掉大量后续返工。3.3 第三步逐年文件的时间对齐与年度面板输出裁剪完成只是第一步真正的面板数据需要把每一年、每个省市的灯光栅格放到一个统一的时间序列里。这里有一个容易忽略的细节不同年份的影像即使覆盖同一区域栅格的行列号、起始坐标也可能不完全一致。如果直接按年份文件夹存放后面做逐像元回归时会因为像元错位而出现大量空值。这时需要做一次“对齐”也就是让所有年份的栅格拥有完全相同的范围、行列数和投影。在rasterio里可以用reproject或直接给每一年设置统一的transform参数。不用担心这样会损失精度因为省级尺度的分析本来就不需要保留亚像元级别的错位差异。面板数据输出时我习惯同时输出两套文件一套是原始裁剪栅格按省/市单独tif另一套是年度汇总表含灯光总和、均值、最大值、标准差。汇总表可以通过一个非常简单的方法得到用rasterstats库的zonal_stats对每个省市面做分区统计。只要矢量边界和裁剪栅格完全对齐分区统计速度非常快还能一次性生成合并后的CSV。这个CSV后续做趋势图、回归、聚类分析都很方便。3.4 命名规范和成果归档四十年的数据靠目录结构说话四十年的数据如果命名混乱后期等于灾难。我从实践中总结出的命名规则是时间范围_传感器_地理单元_行政区划代码_统计指标.tif。比如省级结果1992_DMSP_Province_Henan_LightSum.tif市级结果2015_VIIRS_City_110100_LightMean.tif。这里的行政区划代码一定用标准代码不要用拼音或编号因为统计分析时需要与统计年鉴字段精准匹配。归档时建议按两级目录组织第一级为数据类型原始影像、省市裁剪、年度面板、校正后数据第二级为行政区划或年份范围。这样不管过多久回头看都能快速定位。四十年的数据看着多但整理好之后存储占用其实没有想象中那么大一个省的全部年度tif加起来不过几个GB。4. 常见问题与排查技巧实录4.1 裁剪后大范围空白或NoData先别急着怀疑数据坏了有次我裁剪某市数据输出tif打开后99%是黑色的无数据区。一开始怀疑投影问题花了半小时查坐标系才发现该市边界的坐标范围跟影像完全对不上——一个用的是经纬度另一个是平面坐标。这个问题的排查顺序应该是先看矢量边界的总范围extent再看影像范围确认两者有交集接着看两个数据集的坐标系是否一致最后再看裁剪后的mask是否照常生成。还有一个不起眼的原因边界矢量里的几何对象为空。从某些平台导出的shp会存在空几何或无效几何裁剪时它们会被跳过或产生空输出。在循环前先跑一次boundary.is_valid和boundary.is_empty检查能直接避免这个问题。4.2 边界锯齿和相邻省份灯光“串味”边界锯齿通常来自边界矢量精度太低。省级尺度下1公里的照明像元落在边界附近时到底算哪个省误差可能影响整个省的灯光总量1%-3%。硬要解决就要用更高精度的边界数据而不是靠重采样“磨平”边界。相邻省份串味则更隐蔽有的软件在按掩膜提取时会把边缘的NoData默认补成0如果这个省份被另一省份的灯光像元覆盖了一部分统计值就会偏高。解决方法是先对全国影像做一次去除背景噪声处理把非灯光区的0值像元真正定义成“无数据”而不是“灯光值0”这样统计时才能区分“没有灯光”与“属于其他省被裁进来了”。4.3 DMSP与VIIRS衔接断层问题这是长面板数据的经典难题。某省2012年DMSP均值为102013年VIIRS均值为10.5乍看变化不大但如果直接用这两年的数字计算增长率结果完全不可信。衔接处理有几种常用方案一是取重叠年份对DMSP和VIIRS做分位数回归然后把VIIRS全部转换到DMSP刻度二是以某一基准年为锚点做比值校正三是在省级尺度直接对两个序列做线性桥接用2012-2013两年的均值比例做调整系数。这些方法各有局限我建议至少做一步输出校正前和校正后两条曲线观察有没有出现明显的“台阶式跳变”。如果校正后2011到2013依然突增就得重新评估桥接模型。4.4 省市面板与统计年鉴数据对不上拿着灯光数据和统计年鉴做回归发现相关系数很低这不一定是你数据处理错也可能是统计口径差异。统计年鉴里的市辖区人口可能不包含郊县而灯光数据覆盖了整个市域年鉴里的GDP按行政区划统计但夜间灯光会把靠近相邻城市的卫星城灯光一并照亮。解决思路是把统计口径和灯光口径对齐要么在边界上排除郊县要么把灯光数据按某种距离衰减加权后再汇总。这个环节没有统一标准需要你的研究问题来决定但一定要在论文里写清楚口径。4.5 批量处理性能和存储优化一次跑40年、300多个市级文件如果代码里每个文件都重新读取一遍全部栅格数据哪怕数据量不大也会耗费大量时间。优化手段有裁剪前先把全国影像按范围分块只读取感兴趣的窗口或者在循环外先把边界按州府城市分组减少边界文件的反复IO直接用numpy数组保存中间结果避免频繁写tif。内存不够的机器建议把中间结果存成.npy最后再统一输出成tif速度会提升很多。5. 裁剪之后还能做什么灯光数据的几条实用延伸路线5.1 灯光指数与经济活动的空间刻画一旦取得了省市尺度的灯光面板数据第一件事不是画趋势折线而是先把灯光总量与已有统计指标做相关性检验。对很多地区来说省级灯光总和与地区生产总值之间的相关性经常可以超过0.9但如果出现明显异常年份往往是该年份的影像质量或统计数据口径出了变化这本身就是值得深挖的信号。市级层面上可以计算灯光重心、灯光基尼系数来反映经济活动的空间集中度这些指标都只需要裁剪后的栅格加一个简单的质心计算但解释力很强。5.2 多源数据融合与未来展望1986到2024这组数据最大的价值在于“长跨度一致性”。但受限于传感器更替、噪声处理和分辨率变化它不可能像高分辨率商业影像那样精细。未来的方向是用珞珈一号、全夜间灯光卫星、以及一些开放的新一代微光传感器产品来补充局部区域的高精度验证。这样做的时候建议仍以本文提到的裁剪流程为母版只是把数据源替换掉即可。裁剪是手段不是目的真正有价值的产出是你从灯光时间序列里识别出的城市扩张节点、经济集聚变化和区域差异变迁。5.3 我踩过的几个坑和最后的提醒最后分享几个我实际踩过后才长记性的细节。第一千万别在省级栅格上直接改无效值你会无意中把邻省灯光变成零第二把行政边界更新版本和夜间灯光数据匹配时一定要重新跑一遍全部裁剪不要只对新增省手动补切否则后面汇总时口径全乱第三中间文件一定要保留原始影像备份校正、对齐、重采样过程会反复修改数据没有备份就只能重新下载。做四十年尺度的时间序列最大的忌讳就是口头记住所有的处理过程而不记录哪怕只是简单的markdown日志也能在下一次跑数据时救你命。