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 表达式:隐藏的宝藏和意想不到的可能性,其中解释了本教程背后的概念。
任务概述¶
我们将使用美国各州图层,并通过按人口缩放每个州来创建动态变形地图。生成的地图中,每个州的面积将与其人口成比例。
获取数据¶
美国人口普查局提供制图边界文件以及人口统计数据。我们将下载数据并对其进行处理,以创建适合我们任务的数据图层。
- 访问美国人口普查局网站上的制图边界文件 - Shapefile数据页面。向下滚动并下载州级 Shapefile
cb_2018_us_state_20m.zip。这包含我们可视化所需的州多边形。
- 州多边形没有任何人口统计数据。这些数据需要单独下载并与 shapefile 连接以供 GIS 使用。访问州总人口和构成变化:2020-2023页面,下载年度人口估计数、常住人口变化的估计组成部分以及美国、各州、哥伦比亚特区和波多黎各的常住人口变化组成部分比率:2020年4月1日至2023年7月1日数据集,该数据集将作为
NST-EST2023-ALLDATA.csv下载。
- 打开 QGIS。在 QGIS 浏览器(Browser)中找到
cb_2018_us_state_20m.zip文件并展开它。将cb_2018_us_state_20m.shp图层拖放到画布上。
- 新图层
cb_2018_us_state_20m将被添加到图层(Layers)面板。接下来,我们将加载 CSV 文件。点击打开数据源管理器(Open Data Source Manager)按钮。
- 切换到分隔文本(Delimited Text)选项卡。点击文件名旁边的 …,浏览到下载的
NST-EST2023-ALLDATA.csv文件。展开几何图形定义(Geometry Definition)部分,选择无几何图形(仅属性表)(No geometry (attribute only table))。点击添加(Add)。
- 新图层
NST-EST2023-ALLDATA将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。此表在POPESTIMATE2023列中包含人口计数。每个州在STATE列中都有一个唯一的 id,我们将使用该 id 将此表与多边形图层连接。
- 右键单击
cb_2018_us_state_20m图层并选择打开属性表(Open Attribute Table)。州 ID 包含在GEOID列中。虽然数字相同,但它们的格式是 2 位数并用 0 填充。为了与我们的人口表中的STATE列匹配,我们需要类似格式的数字。选择NST-EST2023-ALLDATA。转到处理(Processing)‣工具箱(Toolbox)。搜索并找到矢量表(Vector table)‣字段计算器(Field calculator)算法。双击打开它。
- 在字段计算器(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')
- 新图层
Calculated将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。注意,新创建的GEOID列具有正确格式的标识符。我们现在可以使用此字段将此表与州图层连接。搜索并找到矢量通用(Vector general)‣按字段值连接属性(Join attributes by field value)算法。双击打开它。
- 在按字段值连接属性(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)。
- 新图层
Joined layer将被添加到图层(Layers)面板。在将此图层用于我们的变形地图之前,让我们将其重新投影到投影坐标系(projected CRS)。搜索并找到矢量通用(Vector general)‣重新投影图层(Reproject layer)算法。双击打开它。
- 在重新投影图层(Reproject layer)对话框中,选择
Joined layer作为输入图层(Input layer)。对于目标坐标系(Target CRS),点击选择坐标系(Select CRS)按钮。搜索North_America_Albers_Equal_Area_Conic坐标系并选择它。这是我们的最终图层,因此我们将将其保存到磁盘。点击已重投影(Reprojected)旁边的 … 按钮,选择保存到文件…(Save to File…)。
- 输入图层名称为
us_states_with_population.gpkg并选择保存(Save)。点击运行(Run)以创建包含重新投影数据的 GeoPackage 文件。
我们将在下一节中使用此图层。为了方便起见,您可以直接从下方下载上述图层的副本:
us_states_with_population.gpkg
数据来源 [USCENSUS]
操作步骤¶
- 启动一个新的 QGIS 项目。在浏览器(Browser)中找到
us_states_with_population.gpkg文件并展开它。将us_states_with_population图层拖放到空画布上。
- 新图层
us_states_with_population将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。对于我们的变形地图,需要使用人口密度作为变量。我们将使用POPESTIMATE2023字段中的人口值。关闭属性表。
- 从处理工具箱(Processing Toolbox)中搜索并找到矢量表(Vector table)‣字段计算器(Field calculator)算法。双击打开它。
- 在字段计算器(Field calculator)对话框中,选择
us_states_with_population作为输入图层(Input layer)。输入density作为字段名称(Field name)。输入以下表达式以计算密度。由于函数area(@geometry)计算的面积是坐标系(CRS)的单位(米),我们应用转换因子将其转换为平方公里。点击运行(Run)。
bash 1000*1000* "POPESTIMATE2023" / area(@geometry)
- 点击已计算(Calculated)旁边的 … 按钮,选择保存到文件…(Save to File…)。输入图层名称为
us_states_population_density.gpkg并选择保存(Save)。点击运行(Run)。
- 新图层
us_states_population_density将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。我们需要选择一个锚点(Anchor)要素,所有其他要素都将相对于它进行缩放。理想情况下,您应选择要用于变形地图的变量值最高的要素。这将确保没有重叠区域。双击密度(density)列标题以按密度对表进行排序。您会注意到,我们数据集中最高的人口密度值与其他值相比非常大,并且它属于一个相当小的州。这将导致所有要素都被缩放到非常小的尺寸。我们可以选择人口密度第二高的要素,该要素面积相对较大,且其密度与其他要素具有可比性。
- 在面积变形地图中,缩放因子决定了要素面积减少的程度。我们必须减小每个要素的面积,使其人口密度与锚点要素的人口密度相同。缩放因子的公式是要素值的平方根与锚点要素值的平方根之比。从处理工具箱(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)
- 新图层
us_states_scale_factor将被添加到图层(Layers)面板。右键单击并选择打开属性表(Open Attribute Table)。scale_factor字段现在包含每个要素必须缩放的比例,以使其具有与锚点要素相同的人口密度。
- 我们只需要
us_states_scale_factor图层进行最终可视化。选择剩余的图层,右键单击并选择移除图层(Remove Layer)。
- 选择
us_states_scale_factor图层,然后点击图层(Layers)面板中的打开图层样式化面板(Open the layer styling panel)按钮。选择简单填充(Simple Fill)并打开符号图层类型(Symbol layer type)的下拉选择器。将符号图层类型(Symbol layer type)设置为轮廓:简单线(Outline: Simple Line)并选择您喜欢的颜色。当我们调整多边形大小时,此符号图层将作为我们地图的参考。
- 点击添加符号图层(+) (Add Symbol layer (+))按钮。将添加一个新的简单填充(Simple Fill)符号图层。将填充颜色(Fill color)设置为与线条相同的颜色,并将描边颜色(Stroke color)设置为稍深的颜色。
- 接下来,打开符号图层类型(Symbol layer type)的下拉选择器,选择
几何图形生成器(Geometry Generator)作为符号图层类型(Symbol layer type)。几何图形生成器允许我们使用表达式来修改用于渲染的几何图形。点击表达式构建器(Expression Builder)按钮。
- 我们将使用 scale() 函数,该函数通过 X 和 Y 缩放因子调整给定几何图形的大小。对于我们的变形地图,我们希望按每个多边形的人口与最高人口的比例调整其大小。输入以下表达式以应用此缩放,然后点击确定(OK)。
bash scale( @geometry, "scale_factor", "scale_factor", centroid(@geometry) )
- 您将看到州多边形现在按其人口与最高人口的比例调整了大小。许多人口密度低的大州现在比原来的尺寸小得多。您会注意到,形状不规则的多边形在缩放后偏离了中心。这是因为缩放的锚点是几何图形的质心(centroid),这通常是多边形的代表点。让我们更新表达式来修复此问题。点击表达式构建器(Expression Builder)按钮。
- scale() 函数接受一个可选参数来指定缩放中心点。我们将使用 pole_of_inaccessibility() 函数为每个多边形找到一个具有代表性的锚点。这类似于质心,但它保证在多边形内部,而对于某些形状,质心可能落在外部。更新表达式如下所示,该表达式计算几何图形的“最难到达点(pole of inaccessibility)”,并设置一个较小的容差值,然后点击确定(OK)。
bash scale( @geometry, "scale_factor", "scale_factor", pole_of_inaccessibility(@geometry, 100) )
- 现在缩放后的多边形位置会好得多。我们看到了另一个问题。图层中的许多要素是多多边形(Multipolygons),即它们具有多个部分。此类要素有 2 个或更多多边形属于同一几何图形。使用我们当前的表达式,两个部分都使用从组合几何图形计算的相同锚点进行缩放。这并不理想。例如,一个包含多个岛屿的大要素应该进行缩放,使得每个岛屿都围绕自己的中心点缩放。为了解决这个问题,我们更新表达式以遍历几何图形的每个部分,并使用其自己的中心进行缩放。点击表达式构建器(Expression Builder)按钮。
- 这里我们使用 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) ) ) )
- 对于多部分要素,缩放效果要好得多。
- 我们的变形地图已准备就绪。这张地图显示了美国东部人口集中,而密西西比河以西各州人口稀少。
- 我们可以通过创建一个动画来改进可视化,该动画将原始渲染缓慢转换为最终大小。点击地图导航工具栏(Map Navigation Toolbar)中带有时钟图标的时间控制面板(Temporal Control Panel)按钮。选择动画时间导航(Animated temporal navigation)按钮。
- 默认的动画范围(Animation range)将以 24 小时为窗口,增量为 1 小时。这对于我们的用例来说是可以的,因为我们将获得 24 帧动画。如果您想要更慢/更快的动画,可以调整此设置。右键单击
us_states_with_population图层并选择属性(Properties)。
- 选择时间(Temporal)选项卡并启用动态时间控制(Dynamic Temporal Control)。此图层将使用表达式更新,因此我们不需要在此处配置它。只需选择
仅重绘图层(Redraw Layer Only),以便在每个时间步长后刷新图层,并使用表达式中的更新值进行渲染。
- 让我们更新几何图形生成器(Geometry Generator)表达式,以使用动画时间步长并逐步缩放几何图形。点击图层(Layers)面板中的打开图层样式化面板(Open the layer styling panel)按钮。选择几何图形生成器(Geometry Generator),然后点击表达式构建器(Expression Builder)按钮。
- 这里我们希望缩放因子从 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) ) ) )
- 回到时间控制器(Temporal Controller)面板,点击播放(Play)按钮查看动画。您应该会看到每个多边形的形状在每一帧之后逐渐缩放。
- 对配置满意后,我们可以导出动画。点击导出动画(Export Animation)按钮。
- 在导出地图动画(Export Map Animation)对话框中,点击输出目录(Output directory)旁边的 …,浏览到您计算机上的任意文件夹。将所有其他选项保持为默认值,然后点击保存(Save)。
- 动画的各个帧将作为图像导出。我们可以从这些帧创建视频或动画 GIF。我推荐使用网站 ezgif.com,它允许您轻松地从单个图像创建 GIF。访问Ezgif 动画 GIF 制作工具。浏览到导出的动画帧,然后点击上传文件!(Upload files!)。
- 配置 GIF 选项,将延迟时间(Delay time)设置为
5。选中交叉淡入淡出帧效果(crossfade frames effect)复选框,并将淡入淡出延迟(Fader delay)和淡入淡出计数(Fader count)设置为2。点击制作 GIF!(Make a GIF!)。
- 点击保存按钮将动画作为 GIF 文件下载。
如果您想提供反馈或分享您对本教程的经验,请在下方评论。(需要 GitHub 帐户)











































