Skip to content

37_创建动态变形地图 QGIS3

原文链接: https://www.qgistutorials.com/en/docs/3/cartogram_animation.html

创建动态变形地图 (QGIS3)

变形地图是一种地图可视化类型,其中每个要素的形状会根据一个变量按比例扭曲。变形地图可以轻松地看到数据中的巨大差异。创建变形地图的最简单方法是根据变量缩放每个区域的大小。此方法保留多边形的原始形状,仅更改其大小。这种被称为非连续同构变形地图。在本教程中,我们将学习如何使用 QGIS 表达式创建变形地图,并使用时间控制器(Temporal Controller)创建将要素逐步变换为目标大小的动画。

本教程基于Hans van der Kwast 的优秀变形地图教程。此处使用的缩放因子公式源自 Judy M Olson 的原始论文非连续面积变形地图

您也可以观看我的 YouTube 视频QGIS 表达式:隐藏的宝藏和意想不到的可能性,其中解释了本教程背后的概念。

任务概述

我们将使用美国各州图层,并通过按人口缩放每个州来创建动态变形地图。生成的地图中,每个州的面积将与其人口成比例。

../../_images/output.gif

获取数据

美国人口普查局提供制图边界文件以及人口统计数据。我们将下载数据并对其进行处理,以创建适合我们任务的数据图层。

  1. 访问美国人口普查局网站上的制图边界文件 - Shapefile数据页面。向下滚动并下载州级 Shapefile cb_2018_us_state_20m.zip。这包含我们可视化所需的州多边形。

../../_images/data19.png

  1. 州多边形没有任何人口统计数据。这些数据需要单独下载并与 shapefile 连接以供 GIS 使用。访问州总人口和构成变化:2020-2023页面,下载年度人口估计数、常住人口变化的估计组成部分以及美国、各州、哥伦比亚特区和波多黎各的常住人口变化组成部分比率:2020年4月1日至2023年7月1日数据集,该数据集将作为 NST-EST2023-ALLDATA.csv 下载。

../../_images/data23.png

  1. 打开 QGIS。在 QGIS 浏览器(Browser)中找到 cb_2018_us_state_20m.zip 文件并展开它。将 cb_2018_us_state_20m.shp 图层拖放到画布上。

../../_images/data33.png

  1. 新图层 cb_2018_us_state_20m 将被添加到图层(Layers)面板。接下来,我们将加载 CSV 文件。点击打开数据源管理器(Open Data Source Manager)按钮。

../../_images/data43.png

  1. 切换到分隔文本(Delimited Text)选项卡。点击文件名旁边的 …,浏览到下载的 NST-EST2023-ALLDATA.csv 文件。展开几何图形定义(Geometry Definition)部分,选择无几何图形(仅属性表)(No geometry (attribute only table))。点击添加(Add)。

../../_images/data53.png

  1. 新图层 NST-EST2023-ALLDATA 将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。此表在 POPESTIMATE2023 列中包含人口计数。每个州在 STATE 列中都有一个唯一的 id,我们将使用该 id 将此表与多边形图层连接。

../../_images/data63.png

  1. 右键单击 cb_2018_us_state_20m 图层并选择打开属性表(Open Attribute Table)。州 ID 包含在 GEOID 列中。虽然数字相同,但它们的格式是 2 位数并用 0 填充。为了与我们的人口表中的 STATE 列匹配,我们需要类似格式的数字。选择 NST-EST2023-ALLDATA。转到处理(Processing)‣工具箱(Toolbox)。搜索并找到矢量表(Vector table)‣字段计算器(Field calculator)算法。双击打开它。

../../_images/data73.png

  1. 在字段计算器(Field calculator)对话框中,选择 NST-EST2023-ALLDATA 作为输入图层(Input layer)。输入 GEOID 作为字段名称(Field name),并将结果字段类型(Result field type)设置为文本(字符串)(Text (string))。现在我们将从 STATE 字段中获取数字,并使用 lpad() 函数创建一个 2 位数的 0 填充字符串。输入以下表达式并点击运行(Run)。

bash lpad("STATE", 2, '0')

../../_images/data82.png

  1. 新图层 Calculated 将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。注意,新创建的 GEOID 列具有正确格式的标识符。我们现在可以使用此字段将此表与州图层连接。搜索并找到矢量通用(Vector general)‣按字段值连接属性(Join attributes by field value)算法。双击打开它。

../../_images/data92.png

  1. 在按字段值连接属性(Join attributes by field value)对话框中,选择 cb_2018_us_state_20m 作为输入图层(Input layer)。选择 GEOID 作为表格字段(Table field)。对于输入图层 2(Input layer 2),选择我们的表 Calculated,表格字段 2(Table field 2)选择 GEOID。该表有很多列,但我们只需要最新年份的人口数据。点击图层 2 要复制的字段(Layer 2 fields to copy)旁边的 … 按钮,仅选择 POPESTIMATE2023 字段。保持其他选项为默认值,然后点击运行(Run)。

../../_images/data102.png

  1. 新图层 Joined layer 将被添加到图层(Layers)面板。在将此图层用于我们的变形地图之前,让我们将其重新投影到投影坐标系(projected CRS)。搜索并找到矢量通用(Vector general)‣重新投影图层(Reproject layer)算法。双击打开它。

../../_images/data114.png

  1. 在重新投影图层(Reproject layer)对话框中,选择 Joined layer 作为输入图层(Input layer)。对于目标坐标系(Target CRS),点击选择坐标系(Select CRS)按钮。搜索 North_America_Albers_Equal_Area_Conic 坐标系并选择它。这是我们的最终图层,因此我们将将其保存到磁盘。点击已重投影(Reprojected)旁边的 … 按钮,选择保存到文件…(Save to File…)。

../../_images/data123.png

  1. 输入图层名称为 us_states_with_population.gpkg 并选择保存(Save)。点击运行(Run)以创建包含重新投影数据的 GeoPackage 文件。

../../_images/data132.png

我们将在下一节中使用此图层。为了方便起见,您可以直接从下方下载上述图层的副本:

us_states_with_population.gpkg

数据来源 [USCENSUS]

操作步骤

  1. 启动一个新的 QGIS 项目。在浏览器(Browser)中找到 us_states_with_population.gpkg 文件并展开它。将 us_states_with_population 图层拖放到空画布上。

../../_images/140.png

  1. 新图层 us_states_with_population 将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。对于我们的变形地图,需要使用人口密度作为变量。我们将使用 POPESTIMATE2023 字段中的人口值。关闭属性表。

../../_images/227.png

  1. 从处理工具箱(Processing Toolbox)中搜索并找到矢量表(Vector table)‣字段计算器(Field calculator)算法。双击打开它。

../../_images/319.png

  1. 在字段计算器(Field calculator)对话框中,选择 us_states_with_population 作为输入图层(Input layer)。输入 density 作为字段名称(Field name)。输入以下表达式以计算密度。由于函数 area(@geometry) 计算的面积是坐标系(CRS)的单位(米),我们应用转换因子将其转换为平方公里。点击运行(Run)。

bash 1000*1000* "POPESTIMATE2023" / area(@geometry)

../../_images/48.png

  1. 点击已计算(Calculated)旁边的 … 按钮,选择保存到文件…(Save to File…)。输入图层名称为 us_states_population_density.gpkg 并选择保存(Save)。点击运行(Run)。

../../_images/58.png

  1. 新图层 us_states_population_density 将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。我们需要选择一个锚点(Anchor)要素,所有其他要素都将相对于它进行缩放。理想情况下,您应选择要用于变形地图的变量值最高的要素。这将确保没有重叠区域。双击密度(density)列标题以按密度对表进行排序。您会注意到,我们数据集中最高的人口密度值与其他值相比非常大,并且它属于一个相当小的州。这将导致所有要素都被缩放到非常小的尺寸。我们可以选择人口密度第二高的要素,该要素面积相对较大,且其密度与其他要素具有可比性。

../../_images/68.png

  1. 在面积变形地图中,缩放因子决定了要素面积减少的程度。我们必须减小每个要素的面积,使其人口密度与锚点要素的人口密度相同。缩放因子的公式是要素值的平方根与锚点要素值的平方根之比。从处理工具箱(Processing Toolbox)中打开矢量表(Vector table)‣字段计算器(Field calculator)算法。在字段计算器(Field calculator)对话框中,选择 us_states_population_density 作为输入图层(Input layer)。输入 scale_factor 作为字段名称(Field name)。输入以下表达式以计算缩放因子。该表达式计算要素密度平方根与第二高密度值平方根之比。点击已计算(Calculated)旁边的 … 按钮,选择保存到文件…(Save to File…)。输入图层名称为 us_states_scale_factor.gpkg 并选择保存(Save)。点击运行(Run)。

bash sqrt("density")/array_get(array_agg( expression:=sqrt("density"), order_by:=sqrt("density")), -2)

../../_images/78.png

  1. 新图层 us_states_scale_factor 将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。scale_factor 字段现在包含每个要素必须缩放的比例,以使其具有与锚点要素相同的人口密度。

../../_images/88.png

  1. 我们只需要 us_states_scale_factor 图层进行最终可视化。选择剩余的图层,右键单击并选择移除图层(Remove Layer)。

../../_images/98.png

  1. 选择 us_states_scale_factor 图层,然后点击图层(Layers)面板中的打开图层样式化面板(Open the layer styling panel)按钮。选择简单填充(Simple Fill)并打开符号图层类型(Symbol layer type)的下拉选择器。将符号图层类型(Symbol layer type)设置为轮廓:简单线(Outline: Simple Line)并选择您喜欢的颜色。当我们调整多边形大小时,此符号图层将作为我们地图的参考。

../../_images/1012.png

  1. 点击添加符号图层(+) (Add Symbol layer (+))按钮。将添加一个新的简单填充(Simple Fill)符号图层。将填充颜色(Fill color)设置为与线条相同的颜色,并将描边颜色(Stroke color)设置为稍深的颜色。

../../_images/1115.png

  1. 接下来,打开符号图层类型(Symbol layer type)的下拉选择器,选择几何图形生成器(Geometry Generator)作为符号图层类型(Symbol layer type)。几何图形生成器允许我们使用表达式来修改用于渲染的几何图形。点击表达式构建器(Expression Builder)按钮。

../../_images/1213.png

  1. 我们将使用 scale() 函数,该函数通过 X 和 Y 缩放因子调整给定几何图形的大小。对于我们的变形地图,我们希望按每个多边形的人口与最高人口的比例调整其大小。输入以下表达式以应用此缩放,然后点击确定(OK)。

bash scale( @geometry, "scale_factor", "scale_factor", centroid(@geometry) )

../../_images/1311.png

  1. 您将看到州多边形现在按其人口与最高人口的比例调整了大小。许多人口密度低的大州现在比原来的尺寸小得多。您会注意到,形状不规则的多边形在缩放后偏离了中心。这是因为缩放的锚点是几何图形的质心(centroid),这通常是多边形的代表点。让我们更新表达式来修复此问题。点击表达式构建器(Expression Builder)按钮。

../../_images/1410.png

  1. scale() 函数接受一个可选参数来指定缩放中心点。我们将使用 pole_of_inaccessibility() 函数为每个多边形找到一个具有代表性的锚点。这类似于质心,但它保证在多边形内部,而对于某些形状,质心可能落在外部。更新表达式如下所示,该表达式计算几何图形的“最难到达点(pole of inaccessibility)”,并设置一个较小的容差值,然后点击确定(OK)。

bash scale( @geometry, "scale_factor", "scale_factor", pole_of_inaccessibility(@geometry, 100) )

../../_images/1510.png

  1. 现在缩放后的多边形位置会好得多。我们看到了另一个问题。图层中的许多要素是多多边形(Multipolygons),即它们具有多个部分。此类要素有 2 个或更多多边形属于同一几何图形。使用我们当前的表达式,两个部分都使用从组合几何图形计算的相同锚点进行缩放。这并不理想。例如,一个包含多个岛屿的大要素应该进行缩放,使得每个岛屿都围绕自己的中心点缩放。为了解决这个问题,我们更新表达式以遍历几何图形的每个部分,并使用其自己的中心进行缩放。点击表达式构建器(Expression Builder)按钮。

../../_images/169.png

  1. 这里我们使用 array_foreach() 函数遍历几何图形的每个部分,并生成它们的缩放版本。最后,collect_geometries() 函数将每个缩放部分组合成单个多多边形几何图形。更新表达式如下所示,然后点击确定(OK)。

bash collect_geometries( array_foreach(generate_series(1, @geometry_part_count), scale(geometry_n(@geometry,@element), "scale_factor", "scale_factor", pole_of_inaccessibility(geometry_n(@geometry,@element), 100) ) ) )

../../_images/1710.png

  1. 对于多部分要素,缩放效果要好得多。

../../_images/188.png

  1. 我们的变形地图已准备就绪。这张地图显示了美国东部人口集中,而密西西比河以西各州人口稀少。

../../_images/197.png

  1. 我们可以通过创建一个动画来改进可视化,该动画将原始渲染缓慢转换为最终大小。点击地图导航工具栏(Map Navigation Toolbar)中带有时钟图标的时间控制面板(Temporal Control Panel)按钮。选择动画时间导航(Animated temporal navigation)按钮。

../../_images/207.png

  1. 默认的动画范围(Animation range)将以 24 小时为窗口,增量为 1 小时。这对于我们的用例来说是可以的,因为我们将获得 24 帧动画。如果您想要更慢/更快的动画,可以调整此设置。右键单击 us_states_with_population 图层并选择属性(Properties)。

../../_images/2112.png

  1. 选择时间(Temporal)选项卡并启用动态时间控制(Dynamic Temporal Control)。此图层将使用表达式更新,因此我们不需要在此处配置它。只需选择仅重绘图层(Redraw Layer Only),以便在每个时间步长后刷新图层,并使用表达式中的更新值进行渲染。

../../_images/228.png

  1. 让我们更新几何图形生成器(Geometry Generator)表达式,以使用动画时间步长并逐步缩放几何图形。点击图层(Layers)面板中的打开图层样式化面板(Open the layer styling panel)按钮。选择几何图形生成器(Geometry Generator),然后点击表达式构建器(Expression Builder)按钮。

../../_images/236.png

  1. 这里我们希望缩放因子从 1 开始,最终达到字段 scale_factor 中要素的最终缩放因子值。我们使用 scale_linear() 函数,该函数获取当前时间步长的时间,并使用开始和结束时间计算缩放因子。更新表达式如下所示,然后点击确定(OK)。

bash collect_geometries( array_foreach(generate_series(1, @geometry_part_count), scale(geometry_n(@geometry,@element), scale_linear( epoch(@map_start_time), epoch(@animation_start_time), epoch(@animation_end_time), 1, "scale_factor"), scale_linear( epoch(@map_start_time), epoch(@animation_start_time), epoch(@animation_end_time), 1, "scale_factor"), pole_of_inaccessibility(geometry_n(@geometry,@element), 100) ) ) )

../../_images/247.png

  1. 回到时间控制器(Temporal Controller)面板,点击播放(Play)按钮查看动画。您应该会看到每个多边形的形状在每一帧之后逐渐缩放。

../../_images/257.png

  1. 对配置满意后,我们可以导出动画。点击导出动画(Export Animation)按钮。

../../_images/266.png

  1. 在导出地图动画(Export Map Animation)对话框中,点击输出目录(Output directory)旁边的 …,浏览到您计算机上的任意文件夹。将所有其他选项保持为默认值,然后点击保存(Save)。

../../_images/277.png

  1. 动画的各个帧将作为图像导出。我们可以从这些帧创建视频或动画 GIF。我推荐使用网站 ezgif.com,它允许您轻松地从单个图像创建 GIF。访问Ezgif 动画 GIF 制作工具。浏览到导出的动画帧,然后点击上传文件!(Upload files!)。

../../_images/287.png

  1. 配置 GIF 选项,将延迟时间(Delay time)设置为 5。选中交叉淡入淡出帧效果(crossfade frames effect)复选框,并将淡入淡出延迟(Fader delay)和淡入淡出计数(Fader count)设置为 2。点击制作 GIF!(Make a GIF!)。

../../_images/296.png

  1. 点击保存按钮将动画作为 GIF 文件下载。

../../_images/output.gif ../../_images/hawaii.gif ../../_images/alaska.gif


如果您想提供反馈或分享您对本教程的经验,请在下方评论。(需要 GitHub 帐户)