ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

QGIS创建正方形网格:从坐标系选择到自动化脚本全解析

QGIS创建正方形网格:从坐标系选择到自动化脚本全解析

1. 项目缘起:为什么我们需要在QGIS中创建正方形网格?

在地理信息处理的实际工作中,我们常常会遇到一个看似简单却至关重要的需求:如何将一个不规则的研究区域,高效、精确地分割成一个个规整的单元?无论是进行城市地块的抽样调查、生态学中的样方分析、还是区域性的资源评估,将连续的空间离散化为规则的网格,都是后续统计分析、模型构建和可视化表达的基础。手动绘制这些网格不仅效率低下,而且难以保证几何精度和拓扑一致性。

QGIS作为一款功能强大的开源GIS桌面软件,其“创建网格”工具正是为此而生。它允许用户基于指定的范围、形状(如矩形、菱形、六边形)和尺寸,批量生成覆盖目标区域的规则几何图形。其中,正方形网格因其计算简便、易于理解和空间索引高效,成为最常用的网格类型。然而,从“知道有这个工具”到“能稳定产出符合预期、坐标正确的网格”,中间往往隔着对坐标系、参数设置和输出结果处理的深刻理解。很多新手,甚至是有一定经验的用户,都可能在“创建网格”这一步就踩坑,比如生成的网格大小不对、覆盖范围有偏差,或者后续叠加数据时出现错位。

本文将从一个资深GIS从业者的视角,手把手带你深入QGIS的“创建网格”功能。我们不仅会完成“点击按钮生成网格”这个动作,更会拆解其背后的每一个参数逻辑,特别是坐标系(如EPSG:3857)的选择与影响,并分享在实际项目中如何避免常见陷阱,确保你得到的每一个正方形网格都精准、可用。

2. 核心工具解析:“创建网格”的功能界面与参数全解

“创建网格”工具位于QGIS的“矢量”菜单下的“研究工具”子菜单中。打开后,你会看到一个参数对话框。这个对话框里的每一个选项,都直接决定了最终输出网格的形态和空间属性。我们来逐一拆解,并解释其背后的“为什么”。

2.1 网格类型与范围定义

首先,在“网格类型”下拉菜单中,选择“矩形(多边形)”。这里有一个关键点:虽然我们最终想要的是正方形,但QGIS是通过指定“宽度”和“高度”来定义矩形的。当我们将宽度和高度设置为相同的数值时,得到的就是正方形。选择“多边形”而非“线”,是因为多边形网格才能作为一个独立的面要素进行后续的面积计算、空间连接等操作。

接下来是“网格范围”。这是第一个容易出错的环节。你有四种方式定义范围:

  1. 画布范围:直接使用当前地图视图显示的范围。这非常方便,但风险在于,如果你缩放或平移了地图,范围就变了,可重复性差。
  2. 图层范围:选择某个已有矢量图层(如一个表示研究区的面图层),工具会计算该图层的整体外包矩形(即能完全包含该图层所有要素的最小矩形)。这是最常用且推荐的方式,因为它确保了网格能完全覆盖你的目标区域。
  3. 书签范围:如果你之前保存了地图书签,可以调用。
  4. 手动输入坐标:直接输入左下角和右上角的X、Y坐标。这种方式最精确,但需要你事先知道精确的范围坐标。

注意:无论选择哪种方式,这里定义的范围都是一个矩形。如果你的研究区形状极不规则(比如一个弯曲的河流流域),使用其外包矩形创建网格,会在研究区外围产生大量“无效”的网格单元。后续通常需要用一个“按位置选择”或“裁剪”工具,将落在实际研究区之外的网格剔除。

2.2 网格间距:理解“宽度”与“高度”的单位

这是核心参数。在“水平间距”和“垂直间距”中,分别输入你想要的网格单元的宽度和高度。例如,输入“100”,期望得到边长为100单位的正方形。

这里的坑在于“单位”。这个“100”单位是什么?米?公里?度?这完全取决于你为输出网格所选择的坐标系(CRS)

  • 如果输出CRS是地理坐标系(如EPSG:4326, WGS84),单位是十进制度。在地球表面,1度经度的实际长度随纬度变化(在赤道最长,向两极递减至0),1度纬度的长度相对恒定(约111公里)。此时设置“100”度,将得到一个在现实世界中巨大且扭曲的“正方形”,这通常不是我们想要的。
  • 如果输出CRS是投影坐标系(如EPSG:3857 Web Mercator, 或EPSG:32649 UTM Zone 49N),单位通常是米。此时设置“100”,就意味着边长100米的正方形。这才是进行面积计算和空间分析的理想选择。

因此,在输入间距值之前,你必须明确你希望网格在现实世界中的实际尺寸(例如100米 x 100米),并确保为输出图层选择了合适的、以米为单位的投影坐标系。工具界面中的“输出CRS”选项,就是用来定义这一点的。

2.3 输出CRS:坐标系选择的深远影响

“输出CRS”是决定网格空间正确性的灵魂参数。它有两个主要选项:

  • 项目CRS:使用当前QGIS项目设置的坐标系。
  • 指定CRS:手动选择一个坐标系。

强烈建议选择“指定CRS”,并选择一个适合你研究区域的投影坐标系。原因如下:

  1. 保证尺寸准确:如上所述,投影坐标系使用线性单位(米、英尺),能保证网格边长在地图上的测量值与实际地面距离对应。
  2. 避免几何变形:正方形网格在投影坐标系下才能保持较好的形状。在地理坐标系下,由于将球面展开为平面带来的变形,网格会变成梯形或其他形状。
  3. 与底图/业务数据对齐:如果你的在线底图(如OSM、谷歌地图)或业务数据使用的是Web Mercator(EPSG:3857),那么将网格也创建为相同的坐标系,可以确保完美的叠加,没有偏移。

关于EPSG:3857:这是一个全球性的投影坐标系,被几乎所有网络地图服务使用。它的单位是“米”,但在高纬度地区面积和形状变形非常严重。如果你的研究区域范围不大(比如一个城市),且主要为了与在线地图叠加显示,使用3857是方便的选择。但如果需要进行精确的面积计算或大范围的空间分析,应优先使用更优的局部投影坐标系,如UTM(通用横轴墨卡托)分区。你可以通过QGIS的“按图层范围自动选择”功能,或查询当地的测绘标准,来确定最适合的UTM带号(如EPSG:32650 for UTM Zone 50N)。

2.4 其他参数与输出

  • 水平/垂直格网数:这是一个与“间距”互斥的参数。如果你知道范围的总宽度是1000米,并想分成10列,那么可以设置“水平格网数”为10,工具会自动计算间距为100米。我个人更倾向于直接使用“间距”,因为我对实地尺寸更敏感。
  • 网格对齐点:可以设置网格的起始偏移点。通常不需要调整,除非你有特殊的对齐要求(如让网格的角点与某个已知坐标点对齐)。
  • 输出:指定生成网格的保存路径和格式(如GeoPackage、Shapefile)。

点击“运行”,一个全新的、覆盖指定范围的规则网格图层就会加载到你的地图中。

3. 实战演练:从零生成一个用于抽样分析的正方形网格

让我们通过一个完整的模拟案例,将上述理论付诸实践。假设我们有一个“某市森林公园边界.shp”的面数据,我们需要在其范围内创建边长为500米的正方形网格,用于规划生态监测样地。

3.1 数据准备与坐标系检查

首先,将“森林公园边界.shp”加载到QGIS中。右键点击图层,选择“属性”,切换到“信息”选项卡。查看“坐标系”信息。假设它显示为“EPSG:4547”(这是一个适用于该地区的投影坐标系,单位是米)。这很好,我们的数据本身就在一个合适的投影坐标系中。

如果数据是地理坐标系(如EPSG:4326),你需要先考虑是否要将其重新投影到一个投影坐标系。你可以在导入时使用“图层CRS”进行设置,或者后续使用“导出”或“重投影图层”工具来处理。基本原则是:用于创建网格的“范围图层”和“输出网格”最好在同一个、以米为单位的投影坐标系下,以避免不必要的转换误差。

3.2 执行“创建网格”工具

  1. 打开“矢量” -> “研究工具” -> “创建网格”。
  2. 网格类型:选择“矩形(多边形)”。
  3. 网格范围:选择“图层范围”,并在下拉菜单中选择“森林公园边界”。
  4. 水平间距:输入500
  5. 垂直间距:输入500
  6. 输出CRS:选择“指定CRS”。点击右侧的“选择CRS”按钮(地球图标)。在过滤器中输入“4547”,选择它。关键点:这里我们选择与源数据相同的CRS,确保网格与原始边界完美套合。
  7. 输出:点击“...”按钮,将结果保存为GeoPackage格式,命名为“森林公园_500米网格.gpkg”。

点击“运行”。瞬间,一个整齐的网格图层就覆盖在了森林公园之上。

3.3 结果验证与后处理

生成网格后,不能直接使用,必须进行验证和清理。

验证1:尺寸检查。使用“测量工具”,设置为“米”为单位,随机测量几个网格的边长。确认它们确实是500米左右(由于范围除间距可能不是整数,边缘网格可能略小)。

验证2:范围检查。观察网格是否完全覆盖了森林公园。由于我们用的是图层的“外包矩形”,网格范围肯定会比实际公园边界大,四周会多出很多覆盖了公园外部区域的网格。

后处理:裁剪网格。我们需要只保留落在森林公园内部的网格。

  1. 使用“矢量” -> “地理处理工具” -> “裁剪”工具。
  2. “输入图层”选择我们刚生成的“森林公园_500米网格”。
  3. “叠加图层”选择“森林公园边界”。
  4. 运行。得到的新图层“裁剪后的网格”就只包含完全位于公园内部的完整或部分网格了。

后处理:添加唯一标识。为了方便管理,为每个网格赋予一个ID。

  1. 打开“裁剪后的网格”图层的属性表。
  2. 点击“打开字段计算器”,创建一个新字段,例如叫grid_id
  3. 在表达式框中输入@row_number,这将为每一行(即每个网格)生成一个从1开始的序列号。
  4. 点击“确定”。现在每个网格都有了自己的编号。

至此,一套可用于布设监测样地、进行分区统计的标准化正方形网格就制作完成了。你可以基于grid_id来随机抽样,或者将其他数据(如植被类型、动物观测点)与这些网格进行空间连接,计算每个网格内的统计值。

4. 深度避坑:坐标系(3857 vs. 局部投影)带来的隐秘陷阱

“创建网格”操作中最顽固、最不易察觉的问题,几乎都与坐标系有关。我们重点剖析EPSG:3857这个“熟悉的陌生人”。

4.1 EPSG:3857的便利性与局限性

便利性:EPSG:3857(Web Mercator)是网络地图的“世界语”。几乎所有的在线瓦片地图(Google Maps, OpenStreetMap, 天地图等)都使用它。在QGIS中加载XYZ Tiles连接这些在线地图时,项目CRS会自动切换到3857。如果你在此状态下,直接用画布范围创建网格,并选择“项目CRS”作为输出,那么网格会与底图完美贴合,视觉上非常整齐。

局限性(即大坑)

  1. 严重的面积变形:Web Mercator投影在高纬度地区会极度夸大面积。例如,在赤道附近,一个3857坐标系下边长为100米的网格,其实际地面面积接近1公顷。但在北纬60度地区,同一个“100米”网格所代表的实际地面面积会小得多。这意味着,如果你在3857下创建了“500米网格”并用于面积相关的分析(如计算网格内的森林面积占比),结果将是完全错误的。
  2. 非等角投影:虽然它被宣传为“保角”,但实际上在非标准纬线上并不完全保角,网格的形状也会发生微小变形,并非严格的正方形。

4.2 一个真实的对比实验

假设我们在北京地区(约北纬40度)创建一个边长为1000米的网格。

  • 方案A(错误示范):项目CRS为EPSG:3857,加载OSM底图,以当前视图范围创建网格,输出CRS使用“项目CRS”(即3857)。
  • 方案B(正确做法):将项目CRS设置为更适合该区域的投影,如EPSG:32650(UTM Zone 50N)。重新加载数据(或对数据重投影),然后以图层范围创建网格,输出CRS指定为EPSG:32650。

对比结果

  • 视觉对齐:方案A的网格与OSM底图严丝合缝。方案B的网格在OSM底图(3857)上会有可见的旋转和偏移,因为底图被动态投影到了3857显示,而网格是32650的。这常常让新手困惑,认为B方案“错了”。实际上,视觉上的错位是投影显示造成的,B方案的数据在它自己的坐标系下才是正确的。
  • 面积真相:使用QGIS的“字段计算器”和$area函数计算两个方案下网格的面积。
    • 方案A(3857网格):$area返回的值接近 1,000,000 平方米(即1平方公里),但这只是地图上的“投影面积”。
    • 方案B(32650网格):$area返回的值也接近 1,000,000 平方米。
    • 关键来了:使用“测量工具”,设置为“椭球体”模式(如WGS84),分别测量两个网格的实际地面距离。你会发现,方案B的网格边长更接近真实的1000米,而方案A的网格实际地面边长要小于1000米。其“投影面积”1平方公里是虚高的。

实操心得永远不要为了与在线底图视觉对齐的便利,而牺牲空间数据的几何精度。正确的 workflow 是:1) 为你的分析确定一个合适的局部投影坐标系(CRS)。2) 将所有业务数据(包括用于定义范围的图层)都转换或创建到这个CRS下。3) 在此CRS下创建网格、进行分析计算。4) 如果需要在线底图作为参考,可以添加XYZ Tiles,QGIS会动态将其投影到你的项目CRS中显示,此时底图可能会有变形和偏移,但你的业务数据之间的空间关系是精确的。记住,底图是“参考背景”,你的分析数据才是“主角”。

4.3 处理“创建网格”时常见的坐标系报错

有时在运行工具时,会遇到“图层范围在输出CRS中无效”或类似错误。这通常是因为“范围图层”的坐标系与“输出CRS”不兼容,或者范围坐标值超出了目标CRS的有效域(例如,试图在UTM投影中创建一个经度跨度过大的网格)。解决方法:

  1. 统一坐标系:确保范围图层和输出CRS一致,或至少是能够相互转换的。
  2. 检查范围值:在范围图层的属性中查看其实际坐标范围,判断是否合理。
  3. 使用项目CRS:一个稳妥的方法是,先将项目和所有相关图层的CRS都设置为你的目标分析CRS,然后使用“画布范围”或“图层范围”来创建网格。

5. 进阶应用与脚本化:让网格创建融入自动化工作流

对于需要频繁创建不同尺寸网格,或作为复杂模型一部分的用户,手动点击GUI效率太低。QGIS的强大之处在于其处理框架和PyQGIS,允许我们将这个过程脚本化。

5.1 使用处理模型设计器构建可复用流程

我们可以将“创建网格”和后续的“裁剪”、“添加ID”等步骤打包成一个自定义模型。

  1. 打开“处理” -> “图形模型设计器”。
  2. 从右侧算法列表中将“创建网格”、“裁剪”、“字段计算器”拖入画布。
  3. 进行连接:将“范围图层”作为“创建网格”和“裁剪”的输入;将“创建网格”的输出连接到“裁剪”的输入图层;将“裁剪”的输出连接到“字段计算器”的输入。
  4. 为每个算法设置参数。对于“创建网格”,你可以将“间距”和“输出CRS”设置为模型参数,这样每次运行模型时都可以动态输入。
  5. 保存模型。以后,你只需要运行这个模型,输入范围图层、间距和CRS,就能一键得到裁剪好并带ID的最终网格。

5.2 使用PyQGIS脚本批量生成

以下是一个示例PyQGIS脚本,演示了如何在Python控制台或外部脚本中自动化完成网格创建。这个脚本假设你已经有一个名为study_area的面图层(矢量层),并希望以指定边长和CRS创建网格。

# 导入必要的模块 from qgis.core import (QgsProject, QgsVectorLayer, QgsGeometry, QgsRectangle, QgsCoordinateReferenceSystem, QgsFields, QgsField, QgsFeature, QgsProcessingFeedback, QgsProcessingContext) from qgis.analysis import QgsNativeAlgorithms import processing from processing.core.Processing import Processing # 初始化Processing框架 Processing.initialize() # 1. 定义参数 input_layer_name = "study_area" # 你的范围图层名 grid_size = 500 # 网格边长,单位取决于输出CRS output_crs_code = "EPSG:32650" # 输出坐标系,例如UTM 50N output_path = "/path/to/your/output/grid_500m.gpkg" # 输出文件路径 # 2. 获取输入图层 project = QgsProject.instance() input_layer = project.mapLayersByName(input_layer_name)[0] if not input_layer: print(f"错误:未找到名为 '{input_layer_name}' 的图层。") exit() # 3. 获取输入图层的范围(在其自身坐标系下) extent = input_layer.extent() xmin, ymin, xmax, ymax = extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() # 4. 设置算法参数 params = { 'TYPE': 2, # 2 代表矩形(多边形) 'EXTENT': f'{xmin},{xmax},{ymin},{ymax}', 'HSPACING': grid_size, 'VSPACING': grid_size, 'HOVERLAP': 0, 'VOVERLAP': 0, 'CRS': output_crs_code, 'OUTPUT': 'memory:' # 先输出到临时内存层 } # 5. 执行“创建网格”算法 feedback = QgsProcessingFeedback() context = QgsProcessingContext() result = processing.run("native:creategrid", params, feedback=feedback, context=context) grid_layer = result['OUTPUT'] # 6. (可选)裁剪网格到输入图层范围 if True: # 如果你想裁剪,将此设为True clip_params = { 'INPUT': grid_layer, 'OVERLAY': input_layer, 'OUTPUT': 'memory:' } result_clip = processing.run("native:clip", clip_params, feedback=feedback, context=context) final_layer = result_clip['OUTPUT'] else: final_layer = grid_layer # 7. 为最终图层添加ID字段并赋值 final_layer.startEditing() provider = final_layer.dataProvider() if final_layer.fields().indexFromName('grid_id') == -1: provider.addAttributes([QgsField('grid_id', QVariant.Int)]) final_layer.updateFields() idx = final_layer.fields().indexFromName('grid_id') for i, feat in enumerate(final_layer.getFeatures()): final_layer.changeAttributeValue(feat.id(), idx, i+1) final_layer.commitChanges() # 8. 将结果保存到文件并加载到项目 options = QgsVectorFileWriter.SaveVectorOptions() options.driverName = "GPKG" options.fileEncoding = 'UTF-8' error = QgsVectorFileWriter.writeAsVectorFormatV2(final_layer, output_path, QgsCoordinateTransformContext(), options) if error[0] == QgsVectorFileWriter.NoError: # 加载保存的图层 vlayer = QgsVectorLayer(output_path, "Final_Grid_500m", "ogr") if vlayer.isValid(): project.addMapLayer(vlayer) print(f"成功!网格已保存并加载:{output_path}") else: print("加载生成的图层失败。") else: print(f"保存文件时出错:{error[1]}")

这个脚本展示了完整的流程:获取范围、创建网格、裁剪、添加属性。你可以将其保存为.py文件,在QGIS的Python控制台中运行,或通过“处理”->“脚本”->“创建新脚本”将其集成到处理工具箱中,实现一键运行。

5.3 处理大型区域与性能优化

当研究区域非常大(如一个省或国家),创建小尺寸网格(如100米)会产生海量要素(数百万甚至上千万个多边形),可能导致QGIS卡顿甚至崩溃。

优化策略

  1. 分块处理:将大区域划分为多个子区域(如按市、县),分别创建网格后再合并。可以使用“分割矢量图层”工具按属性分割范围图层。
  2. 使用数据库:将输出直接保存到PostGIS等空间数据库中,而不是Shapefile或GeoPackage文件。数据库在处理海量要素时性能更优,并支持空间索引。
  3. 调整网格尺寸:在满足分析精度的前提下,适当增大网格边长,能指数级减少要素数量。
  4. 在创建后简化属性:创建网格时,默认会带有idlefttoprightbottom等字段。如果不需要,可以在创建后删除这些字段,减少文件大小。

通过理解工具原理、规避常见陷阱、并掌握自动化方法,你就能将QGIS的“创建网格”功能从一個简单的图形生成器,转变为支撑你空间分析工作的强大、可靠且高效的核心组件。记住,精准的网格是空间量化和模型化的基石,多花几分钟在坐标系和参数设置上,能为后续所有分析省去数小时的纠错时间。

返回列表