前段时间找QGIS相关资料,无意中发现QGIS开发团队成员、Temporal Controller插件的作者Nyall Dawson发布了一期直播视频,以地震数据为例,演示了QGIS 3.14 时间控制和地图动画,看完后深受启发,于是产生了整理出来的想法。
先看一下最终的动画效果:
达到上图效果需要比较复杂的操作过程,我将其拆解为三个章节:数据准备篇、动画设置篇、进度条设置篇,通过详细的步骤,再现Nyall Dawson直播中的地震数据动画效果。本文为系列文章第一篇,即数据准备篇。示范数据采用美国国家海洋和大气局(NOAA)提供的地震数据,下载地址为:
http://www.ngdc.noaa.gov/nndc/struts/results?type_0=Exact&query_0=$ID&t=101650&s=13&d=189&dfn=signif.txt
从【浏览】面板中,展开【XYZ Tiles】节点,双击【OpenStreetMap】,将OpenStreetMap基础底图添加到地图窗口中。
导入地震数据1、点击【图层管理】工具栏的【添加文本数据图层】按钮,打开【数据源管理器】对话框。
2、点击【文件名称】右侧【…】按钮,浏览到地震数据TXT文本文件,将其填入文本框。
3、【图层名称】可以设置为任意字符,此处填写“signif”作为图层名,该名称将会显示在【图层】面板中。
4、【文件格式】选择“自定义分隔符”,勾选右侧的“制表符”。
5、在【几何图形定义】中,【横坐标字段】选择"LONGITUDE" ,【纵坐标字段】选择 “LATITUDE” 。
6、最后,点击下方的【添加】按钮,将数据添加到地图窗口中。
QGIS将为新添加的图层随机选择渲染颜色,如果对默认颜色不满意,可以点击【图层】面板上方的【打开图层样式面板】按钮,在【图层样式】面板中设置自己喜欢的颜色。
在【图层】面板,右键点击“signif”图层,从弹出菜单中选择【打开属性表】。
可以看到,图层共包含6207个地震事件点,“YEAR”、“MONTH”、“DAY”、“HOUR”,“MINUTE”,“SECOND”六个字段组成地震的时间属性。由于时间跨度较大、要素较多,可以选择感兴趣的时间段作为研究区间。本文将使用2018年1月1日至今的地震点作为演示数据。
提取数据子集点击属性表窗口上方工具栏的【使用表达式选择要素】按钮,调出【表达式字符串构建器】对话框。
在表达式代码编辑区中输入下面表达式:
if ( to_int("year") >= 2018,1,0 )
该表达式用if条件语句判断“year”字段是否大于2018,如果是则返回1,否则返回0。"to_int"为转换函数,用于将字符串类型的“year”字段转换为数值类型,便于比较大小。
点击右下角的【选择要素】按钮,地图窗口上方弹出气泡显示选中了151个要素,并用黄色点高亮在地图画布中。
导出选择集接下来导出选中的要素为单独的图层。关闭表达式构造器对话框,在【图层】面板中右键点击“signif”图层,弹出菜单依次选择【导出】->【另存选中的要素为…】,打开【矢量图层另存为…】对话框。
保存格式可以选择Shapefile、SpatiaLite、Geojson、GML、MIF、GeoPackage等,本文选择GeoPackage格式保存。点击【文件名称】右侧的【…】按钮,打开【保存图层为】对话框,设置文件名为“quake2020”,点击【保存】按钮,关闭对话框。
返回【矢量图层另存为…】对话框,其他保持默认选项,确保勾选下方的【将已保存的文件添加到地图中】,点击【OK】,完成选择集导出。
可以看到导出图层“quake2020”添加到了地图窗口中。取消勾选“signif”图层,地图窗口中保留OpenStreetMap底图和“quake2020”图层。
QGIS 3.14使用内置的Temporal Controller插件替代了TimeManager实现时间序列动画,由于动画实现的方式不同,目前Temporal Controller的时间字段仅支持日期/时间日期型(Date/DateTime),而地震数据中的时间属性是由“YEAR”、“MONTH”、“DAY”、“HOUR”,“MINUTE”,“SECOND”六个字符串型字段组成,因此需要将这六个字段合成并转换为Date或者DateTime类型。
(关于Temporal Controller对时间属性的支持,请参考:https://qgis.org/en/site/forusers/visualchangelog314/#feature-cumulative-temporal-range-setting-in-temporal-controller 。)
处理异常数据在【图层】面板右键点击“quake2020”图层,从弹出菜单选择【打开属性表】,打开该图层的属性表窗口。
为了检查是否存在异常数据,依次单击“YEAR”、“MONTH”、“DAY”、“HOUR”,“MINUTE”,“SECOND”列名,对其进行升序排列。很明显看到,“HOUR”、“MINUTE”和“SECOND”字段存在空值(NULL),“SECOND”字段存在空格,这些都是异常数据。由表达式相关知识得知,异常数据参与表达式运算有可能导致表达式出错。
在本文中,如果地震发生的小时、分钟和秒未知(NULL值),时间精确到天是可以接受的,因此可以将“HOUR”、“MINUTE”和“SECOND”的异常数据提前处理为0,即对于未知“HOUR”、“MINUTE”和“SECOND”的地震事件,默认发生在当天的00:00:00。
点击属性表窗口工具栏上的【打开字段计算器】按钮,弹出【Field Calculator】对话框。
勾选【更新现有的字段】,在下拉框中选择字段“HOUR”,表达式代码编辑区输入如下表达式:
if (NOT "HOUR" IS NULL , "HOUR" ,0)
该表达式用if条件语句判断“HOUR”字段是否为NULL,如果“HOUR”为NULL,则“HOUR”赋值0,否则,仍然保留原值。
注意,在对图层做任何更新操作前,应将图层设为可编辑状态,否则表达式构造器下方将出现提示信息:您正在编辑该图层中的信息,但是该图层目前处于非编辑模式。
单击【OK】按钮,关闭【Field Calculator】对话框。可以看到,属性表窗口工具栏的【切换编辑模式】处于选择状态,表示自动打开了当前图层可编辑。再次单击“HOUR”字段名,确保NULL已经更新为0。
重复上一步骤,将更新的字段改为“MINUTE”,处理“MINUTE”字段中NULL情况。
if (NOT "MINUTE" IS NULL , "MINUTE" ,0)“SECOND”字段存在空格的异常数据,因此表达式中除了需要判断是否为NULL之外,增加了判断字符串是否由空格组成。具体的实现逻辑是:先用trim函数将“SECOND”字段取值的前后空格去掉,再用length函数判断去掉空格后字符串的长度,如果长度为0,说明当前要素的“SECOND”字段全部由空格组成,将该取值更新‘0’,否则保留原“SECOND”值。
if( "SECOND" is NULL OR length( trim("SECOND" ))=0,0,"SECOND") 生成时间字段再次检查属性数据表,确保相关字段无异常数据后,点击工具栏的【打开字段计算器】按钮,构建表达式生成时间字段。
在【Field Calculator】对话框中,勾选【新建字段】,【输出字段名称】填写“time”,【输出字段类型】选择“日期和时间”,在表达式代码编辑区输入:
make_datetime( "YEAR" , "MONTH" , "DAY" , "HOUR" , "MINUTE" , "SECOND" )上述表达式用make_datetime函数,将“YEAR”、“MONTH”、“DAY”、“HOUR”,“MINUTE”,“SECOND”六个字段合并为时间字段。点击【OK】按钮,返回属性表窗口。
可以看到,属性表中增加了名为“time”的日期和时间类型字段,且时间与“YEAR”、“MONTH”、“DAY”、“HOUR”,“MINUTE”,“SECOND”对应。
至此,地震数据的准备工作完毕,下一篇将讲解地震数据的动画设置。
吴建玲
2020年8月25日
版权声明
本文欢迎转载,转载时请注明出处。
本文参考了Nyall Dawson的视频整理而成。Nyall Dawson是QGIS开发团队成员,有10年的QGIS开发经验,他的直播条理清楚、思路新颖,感兴趣的朋友可以去观看相关视频,地址为: https://www.youtube.com/watch?v=vgDg5cRwPRw。