From 612c1a4ea7e0c2f66f0a7166b6032f11278fb1c2 Mon Sep 17 00:00:00 2001 From: <> Date: Mon, 6 Nov 2023 07:26:57 +0000 Subject: [PATCH] Deployed 73deb46 with MkDocs version: 1.5.3 --- .nojekyll | 0 "1-\345\274\225\350\250\200/index.html" | 1247 +++ .../index.html" | 1322 ++++ .../index.html" | 1260 ++++ .../index.html" | 927 +++ .../index.html" | 1189 +++ .../index.html" | 1470 ++++ .../index.html" | 1500 ++++ .../index.html" | 2482 ++++++ .../index.html" | 1023 +++ .../index.html" | 1012 +++ .../index.html" | 1519 ++++ .../index.html" | 1024 +++ .../index.html" | 2098 ++++++ .../index.html" | 1884 +++++ .../index.html" | 1327 ++++ .../index.html" | 1127 +++ 404.html | 744 ++ .../index.html" | 1139 +++ .../index.html" | 2946 ++++++++ .../index.html" | 1230 +++ .../index.html" | 1029 +++ .../index.html" | 1487 ++++ assets/1559181577361.png | Bin 0 -> 18959 bytes assets/QgsAuthAuthoritiesEditor.png | Bin 0 -> 183694 bytes assets/QgsAuthConfigSelect.png | Bin 0 -> 22128 bytes assets/QgsAuthEditorWidgets.png | Bin 0 -> 46615 bytes assets/bar-button.png | Bin 0 -> 11459 bytes assets/dialog-with-bar.png | Bin 0 -> 8704 bytes assets/errorbar-timed.png | Bin 0 -> 11468 bytes assets/errorbar.png | Bin 0 -> 5356 bytes assets/images/favicon.png | Bin 0 -> 1870 bytes assets/infobar.png | Bin 0 -> 7917 bytes assets/javascripts/bundle.81fa17fe.min.js | 29 + assets/javascripts/bundle.81fa17fe.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.f886a092.min.js | 42 + .../workers/search.f886a092.min.js.map | 7 + assets/stylesheets/main.4b4a2bd9.min.css | 1 + assets/stylesheets/main.4b4a2bd9.min.css.map | 1 + assets/stylesheets/palette.356b1318.min.css | 1 + .../stylesheets/palette.356b1318.min.css.map | 1 + css/img.css | 11 + images/logo-blue.svg | 1 + images/logo.svg | 1 + index.html | 813 ++ search/search_index.json | 1 + sitemap.xml | 113 + sitemap.xml.gz | Bin 0 -> 747 bytes 82 files changed, 39216 insertions(+) create mode 100644 .nojekyll create mode 100644 "1-\345\274\225\350\250\200/index.html" create mode 100644 "10-\345\234\260\345\233\276\346\270\262\346\237\223\345\222\214\346\211\223\345\215\260/index.html" create mode 100644 "11-\350\241\250\350\276\276\345\274\217\357\274\214\350\277\207\346\273\244\345\222\214\350\256\241\347\256\227\345\200\274/index.html" create mode 100644 "12-\350\257\273\345\217\226\345\222\214\345\255\230\345\202\250\350\256\276\347\275\256/index.html" create mode 100644 "13-\344\270\216\347\224\250\346\210\267\351\200\232\344\277\241/index.html" create mode 100644 "14-\350\256\244\350\257\201\345\237\272\347\241\200/index.html" create mode 100644 "15-\344\273\273\345\212\241\342\200\224\342\200\224\345\234\250\345\220\216\345\217\260\345\201\232\347\271\201\351\207\215\347\232\204\345\267\245\344\275\234/index.html" create mode 100644 "16-\345\274\200\345\217\221Python\346\217\222\344\273\266/index.html" create mode 100644 "17-\347\274\226\345\206\231\345\244\204\347\220\206\346\217\222\344\273\266/index.html" create mode 100644 "18-\344\275\277\347\224\250\346\217\222\344\273\266\345\233\276\345\261\202/index.html" create mode 100644 "19-\347\275\221\347\273\234\345\210\206\346\236\220\345\272\223/index.html" create mode 100644 "2-\345\212\240\350\275\275\351\241\271\347\233\256/index.html" create mode 100644 "20-QGIS\346\234\215\345\212\241\345\231\250\345\222\214Python/index.html" create mode 100644 "21-PyQGIS\351\200\237\346\237\245\350\241\250/index.html" create mode 100644 "3-\345\212\240\350\275\275\345\233\276\345\261\202/index.html" create mode 100644 "4-\350\256\277\351\227\256\345\233\276\345\261\202\347\233\256\345\275\225\346\240\221/index.html" create mode 100644 404.html create mode 100644 "5-\344\275\277\347\224\250\346\240\205\346\240\274\345\233\276\345\261\202/index.html" create mode 100644 "6-\344\275\277\347\224\250\347\237\242\351\207\217\345\233\276\345\261\202/index.html" create mode 100644 "7-\345\207\240\344\275\225\345\244\204\347\220\206/index.html" create mode 100644 "8-\346\212\225\345\275\261\346\224\257\346\214\201/index.html" create mode 100644 "9-\344\275\277\347\224\250\345\234\260\345\233\276\347\224\273\345\270\203/index.html" create mode 100644 assets/1559181577361.png create mode 100644 assets/QgsAuthAuthoritiesEditor.png create mode 100644 assets/QgsAuthConfigSelect.png create mode 100644 assets/QgsAuthEditorWidgets.png create mode 100644 assets/bar-button.png create mode 100644 assets/dialog-with-bar.png create mode 100644 assets/errorbar-timed.png create mode 100644 assets/errorbar.png create mode 100644 assets/images/favicon.png create mode 100644 assets/infobar.png create mode 100644 assets/javascripts/bundle.81fa17fe.min.js create mode 100644 assets/javascripts/bundle.81fa17fe.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.f886a092.min.js create mode 100644 assets/javascripts/workers/search.f886a092.min.js.map create mode 100644 assets/stylesheets/main.4b4a2bd9.min.css create mode 100644 assets/stylesheets/main.4b4a2bd9.min.css.map create mode 100644 assets/stylesheets/palette.356b1318.min.css create mode 100644 assets/stylesheets/palette.356b1318.min.css.map create mode 100644 css/img.css create mode 100644 images/logo-blue.svg create mode 100644 images/logo.svg create mode 100644 index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git "a/1-\345\274\225\350\250\200/index.html" "b/1-\345\274\225\350\250\200/index.html" new file mode 100644 index 0000000..3512fe8 --- /dev/null +++ "b/1-\345\274\225\350\250\200/index.html" @@ -0,0 +1,1247 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1-引言 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +

1 引言⚓︎

+

本文档既可作为教程,也可作为参考指南。虽然没有列举所有可能的案例,但是对主要功能有一个很好的概述。

+

根据GNU免费文档许可证、版本1.3或自由软件基金会发布的任何后续版本的条款,允许复制、分发和/或修改该文档;没有固定章节,没有封面文本,也没有封底文本。

+

GNU免费文档许可证一节中包含了该许可证的副本。

+

本许可证也适用于本文档中的所有代码段。

+

对于Python的支持最初是在QGIS 0.9中引入的。 目前,在QGIS桌面版中有几种方法可以使用Python,如下:

+
    +
  • 在QGIS的Python控制台中
  • +
  • 创建并使用插件
  • +
  • QGIS启动时自动运行Python代码
  • +
  • 创建处理算法
  • +
  • 在QGIS中为表达式创建函数
  • +
  • 基于QGIS API创建自定义应用程序
  • +
+

Python支持在QGIS 0.9中首次引入。这里有几种方法可以在QGIS桌面软件中使用Python(涵盖一下部分):

+
    +
  • 在QGIS中的Python控制台中使用命令
  • +
  • 创建并使用插件
  • +
  • 当QGIS启动时自动运行Python代码
  • +
  • 创建处理算法
  • +
  • 在QGIS中创建函数表达式
  • +
  • 基于QGIS API创建自定义应用
  • +
+

Python绑定也可用于QGIS服务,包括Python插件(请参阅20-QGIS服务器和Python)和Python绑定,可用于将QGIS服务嵌入到Python应用程序中。 +这里有一个完整的QGIS C++ API参考——用于记录QGIS库中的类。 Pythonic QGIS API(pyqgis)几乎与C ++ API相同。 +学习执行常见任务的另一个好办法是从插件仓库下载现有插件并学习它们的代码。

+

1.1 在Python控制台中编写脚本⚓︎

+

QGIS为脚本编写提供了一个集成的python控制台。可以从插件→python控制台菜单中打开:

+

1559181577361

+

上面的截图说明了如何获取图层列表中当前选定的图层,并显示其ID,如果是矢量图层,还可以显示要素个数。对于与QGIS交互环境,有一个iface变量,它是QgisInterface的一个实例。此接口允许访问地图画布、菜单、工具栏和QGIS应用程序的其他部分。

+

为了方便用户,在启动控制台时将会执行以下语句(将来可以设置更多的初始命令)

+
1
+2
from qgis.core import *
+import qgis.utils
+
+

对于经常使用控制台的用户,设置打开控制台的快捷键可能很有用(在设置→键盘快捷键中...)

+

1.2 Python插件⚓︎

+

可以使用插件来扩展QGIS的功能,可以使用Python编写插件。与C ++插件相比,主要优点是分发简单(不对每个平台进行编译)、更容易的开发。

+

自从引入对Python的支持以来,已经编写了许多涵盖各种功能的插件。插件安装程序允许用户很容易获取、升级和删除Python插件。有关插件和插件开发的更多信息,请参阅Python插件页面。

+

使用Python创建插件很简单,详细说明请参阅16-开发Python插件

+
+

提示

+

Python插件也可用于QGIS服务,更多信息,请参阅20-QGIS服务器和Python

+
+

1.2.1 处理插件⚓︎

+

处理插件可被用于处理数据。它们比Python插件更容易开发、更具体、更轻量。17-编写处理插件阐述了什么时候该使用处理算法,并且怎么开发它们。

+

1.3 QGIS启动时运行Python代码⚓︎

+

每次QGIS启动时,都有不同的方法来运行Python代码。

+
    +
  1. 创建startup.py脚本
  2. +
  3. PYQGIS_STARTUP环境变量设置为现有Python文件
  4. +
  5. 使用 --code init_qgis.py 参数指定启动脚本
  6. +
+

1.3.1 startup.py文件⚓︎

+

每次QGIS启动时,都会在用户的Python主目录和系统路径列表中搜索名为startup.py的文件。 如果该文件存在,则由嵌入式Python解释器执行。

+

用户主目录中的路径通常位于:

+
    +
  • Linux: .local/share/QGIS/QGIS3
  • +
  • Windows: AppData\Roaming\QGIS\QGIS3
  • +
  • macOS: Library/Application Support/QGIS/QGIS3
  • +
+

默认系统路径取决于操作系统。要查找适合您的路径,请打开 Python 控制台并运行 QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)以查看默认目录列表。

+

startup.py脚本在QGIS中初始化python时立即执行,在应用程序启动的早期。

+

1.3.2 PYQGIS_STARTUP环境变量⚓︎

+

你可以在QGIS初始化完成之前将PYQGIS_STARTUP环境变量设置为现有Python文件的路径来运行Python代码。

+

此代码将在QGIS初始化完成之前运行。此方法对于清理sys.path非常有用——可能存在不需要的路径,或用于隔离/加载初始环境——无需虚拟环境,例如在Mac上使用homebrew或MacPorts。

+

1.3.3 --code参数⚓︎

+

你可以提供自定义代码作为 QGIS 的启动参数执行。为此,请创建一个 python 文件,例如 qgis_init.py,使用 qgis --code qgis_init.py从命令行执行和启动 QGIS。

+

通过 --code 提供的代码在 QGIS 初始化阶段的后期,在加载应用程序组件之后执行。

+

1.3.4 Python 的其他参数⚓︎

+

要为 --code 脚本或执行的其他 python 代码提供其他参数,可以使用 --py-args 参数。在 --py-args 之后和 -- arg(如果存在)之前的任何参数都将传递给 Python,但被 QGIS 应用程序本身忽略。

+

在下面的例子中,myfile.tif 将通过 Python 中的 sys.argv 提供,但不会由 QGIS 加载。而otherfile.tif将由QGIS加载,但不存在于sys.argv中。

+
1
qgis --code qgis_init.py --py-args myfile.tif -- otherfile.tif
+
+

如果你想从Python中访问每个命令行参数,你可以使用QCoreApplication.arguments()

+
1
QgsApplication.instance().arguments()
+
+

1.4 Python应用程序⚓︎

+

为自动化流程创建脚本通常很方便。使用PyQGIS,这是完全可能的——导入qgis.core模块,初始化它,你就可以进行处理了。

+

或者你可能想要创建一个使用GIS功能的交互式应用程序——执行测量、将地图导出为PDF或任何其他功能。qgis.gui模块提供了各种GUI部件,最值得注意的是可以合并到应用程序中的地图画布控件——支持缩放,平移和任何其他自定义地图工具。

+

必须配置PyQGIS自定义应用程序或独立脚本以定位QGIS资源,例如投影信息,用于读取矢量和栅格图层的数据提供者等。QGIS资源通过在应用程序或脚本的开头添加几行(代码)来初始化。自定义应用程序和独立脚本初始化QGIS的代码类似。以下提供各自的实例。

+
+

提示

+

千万不能使用qgis.py作为你的测试脚本的名称,否则Python将无法导入绑定。

+
+

1.4.1 在独立脚本中使用PyQGIS⚓︎

+

启动独立脚本,在脚本开头初始化QGIS资源,类似于以下代码:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
from qgis.core import *
+# 提供qgis安装位置的路径(windows默认:C:\Program Files\QGIS 3.x\apps\qgis-ltr)
+QgsApplication.setPrefixPath("/path/to/qgis/installation", True)
+# 创建对QgsApplication的引用,第二个参数设置为False将禁用GUI
+qgs = QgsApplication([], False)
+# 加载提供者
+qgs.initQgis()
+# 在这里编写代码,加载一些图层,使用处理算法等
+# 脚本完成后,调用exitQgis()从内存中删除提供者和图层注册
+qgs.exitQgis()
+
+

我们首先导入qgis.core模块,然后配置前缀路径。前缀路径是安装QGIS的路径。它通过调用setPrefixPath方法在脚本中配置。第二个参数设置为True,它控制是否使用默认路径。

+

QGIS安装路径因平台而异,在系统中找到它的最简单方法是在QGIS中使用1.1 在Python控制台中编写脚本运行 QgsApplication.prefixPath()并查看输出。

+

配置前缀路径后,我们在变量qgs中保存了一个对QgsApplication的引用。第二个参数设置为False,表示我们不打算使用GUI,因为我们正在编写一个独立的脚本。配置QgsApplication后 ,我们通过调用qgs.initQgis()方法加载QGIS数据提供者和图层注册。在QGIS初始化后,我们准备编写脚本的其余部分。最后,我们通过调用qgs.exitQgis()从内存中删除数据提供者和图层注册。

+

1.4.2 在自定义应用程序中使用PyQGIS⚓︎

+

1.4.1 在独立脚本中使用PyQGIS和自定义PyQGIS应用程序之间的唯一的区别是在实例化QgsApplication时的第二个参数。传递True而不是False,表示我们计划使用GUI。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
from qgis.core import *
+# 提供qgis安装位置的路径(windows默认:C:\Program Files\QGIS 3.x\apps\qgis-ltr)
+QgsApplication.setPrefixPath("/path/to/qgis/installation", True)
+# 创建对QgsApplication设置的引用第二个参数为True启用GUI,我们需要这样做,因为这是一个自定义应用程序
+qgs = QgsApplication([], True)
+# 加载提供者
+qgs.initQgis()
+# 在这里编写代码,加载一些图层,使用处理算法等
+# 脚本完成后,调用exitQgis()从内存中删除提供者和图层注册
+qgs.exitQgis()
+
+

现在,你可以使用QGIS API——加载图层并执行一些处理或使用地图画布启动GUI。可能性是无止境的:-)

+

1.4.3 运行自定义应用程序⚓︎

+

如果它们不在一个众所周知的位置,你需要告诉系统在哪里搜索QGIS库和合适的Python模块——否则Python会抛出异常:

+
1
+2
>>> import qgis.core
+ImportError: No module named qgis.core
+
+

可以通过设置PYTHONPATH环境变量来修复。在以下命令中,<qgispath>应替换为你的实际QGIS安装路径:

+
    +
  • 在Linux上:export PYTHONPATH=/<qgispath>/share/qgis/python
  • +
  • 在Windows上:set PYTHONPATH=c:\<qgispath>\python
  • +
  • 在macOS上:export PYTHONPATH=/<qgispath>/Contents/Resources/python
  • +
+

现在,PyQGIS模块的路径设置完成,但它们依赖于qgis_coreqgis_gui库(仅仅作为封装的Python模块)。这些库的路径通常是操作系统未知的,因此再次出现导入错误(错误消息可能因系统而异):

+
1
+2
>>> import qgis.core
+ImportError: libqgis_core.so.3.2.0: cannot open shared object file:No such file or directory
+
+

通过将QGIS库所在的目录添加到动态链接器的搜索路径来解决此问题:

+
    +
  • 在Linux上:export LD_LIBRARY_PATH=/qgispath/lib
  • +
  • 在Windows上:set PATH=C:\qgispath\BIN; C:\qgispath\APPS\qgisrelease\BIN;PATH% ,其中qgisrelease应替换成你的发布类型(例如,qgis-ltrqgisqgis-dev
  • +
+

这些命令可以放入一个引导脚本,负责启动。使用PyQGIS部署自定义应用程序时,通常有两种可能:

+
    +
  • 要求用户在安装应用程序之前在其平台上安装QGIS。应用程序安装程序应查找QGIS库的默认位置,并允许用户设置路径(如果未找到)。该方法更简单,但是它需要用户执行更多步骤。
  • +
  • 将QGIS与你的应用程序一起打包。发布应用程序可能更具挑战性,并且程序包将更大,但用户将免于下载和安装其他软件的负担。
  • +
+

这两种部署方式可以混合使用——在Windows和macOS上部署独立应用程序,但是对于Linux,将QGIS的安装留给用户和他的包管理器。

+

1.5 关于PyQt和SIP的技术说明⚓︎

+

我们决定使用Python,因为它是最受欢迎的脚本语言之一。QGIS3中的PyQGIS绑定依赖于SIP和PyQt5。使用SIP而不是使用更广泛使用的SWIG的原因是QGIS代码依赖于Qt库。Qt(PyQt)的Python绑定使用SIP完成,这允许PyQGIS与PyQt无缝集成。

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/10-\345\234\260\345\233\276\346\270\262\346\237\223\345\222\214\346\211\223\345\215\260/index.html" "b/10-\345\234\260\345\233\276\346\270\262\346\237\223\345\222\214\346\211\223\345\215\260/index.html" new file mode 100644 index 0000000..22f3d6e --- /dev/null +++ "b/10-\345\234\260\345\233\276\346\270\262\346\237\223\345\222\214\346\211\223\345\215\260/index.html" @@ -0,0 +1,1322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10-地图渲染和打印 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节代码片段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
import os
+
+from qgis.core import (
+    QgsGeometry,
+    QgsMapSettings,
+    QgsPrintLayout,
+    QgsMapSettings,
+    QgsMapRendererParallelJob,
+    QgsLayoutItemLabel,
+    QgsLayoutItemLegend,
+    QgsLayoutItemMap,
+    QgsLayoutItemPolygon,
+    QgsLayoutItemScaleBar,
+    QgsLayoutExporter,
+    QgsLayoutItem,
+    QgsLayoutPoint,
+    QgsLayoutSize,
+    QgsUnitTypes,
+    QgsProject,
+    QgsFillSymbol,
+    QgsAbstractValidityCheck,
+    check,
+)
+
+from qgis.PyQt.QtGui import (
+    QPolygonF,
+    QColor,
+)
+
+from qgis.PyQt.QtCore import (
+    QPointF,
+    QRectF,
+    QSize,
+)
+
+

10 地图渲染和打印⚓︎

+

当输入数据作为地图呈现时,通常有两种方法:使用QgsMapRendererJob快速进行,或者使用QgsLayout类组合地图来生成更精细的输出。

+

10.1 简单的渲染⚓︎

+

渲染完成后,创建一个QgsMapSettings对象来定义渲染选项,然后使用这些选项构建一个QgsMapRendererJob类。然后使用后者来创建图像。

+

这是一个例子:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
image_location = os.path.join(QgsProject.instance().homePath(), "render.png")
+
+vlayer = iface.activeLayer()
+settings = QgsMapSettings()
+settings.setLayers([vlayer])
+settings.setBackgroundColor(QColor(255, 255, 255))
+settings.setOutputSize(QSize(800, 600))
+settings.setExtent(vlayer.extent())
+
+render = QgsMapRendererParallelJob(options)
+
+def finished():
+    img = render.renderedImage()
+    # 保存图像; e.g. img.save("/Users/myuser/render.png","png")
+    img.save(image_location, "png")
+
+render.finished.connect(finished)
+
+# 开始渲染
+render.start()
+
+# 通常不需要循环,因为这里是单独使用
+from qgis.PyQt.QtCore import QEventLoop
+loop = QEventLoop()
+render.finished.connect(loop.quit)
+loop.exec_()
+
+

10.2 使用不同的CRS渲染图层⚓︎

+

如果你有多个图层并且它们具有不同的CRS,上面的简单示例可能不起作用:从范围计算中获取正确的值,你必须显式设置目标CRS

+
1
+2
+3
+4
layers = [iface.activeLayer()]
+settings = QgsMapSettings()
+settings.setLayers(layers)
+render.setDestinationCrs(layers[0].crs())
+
+

10.3 使用打印布局输出⚓︎

+

如果你想要比上面显示的简单渲染更复杂的输出,打印布局是一个非常方便的工具。可以创建复杂的地图布局,包括地图视图,标签,图例,表格以及通常出现在纸质地图上的其他元素。然后可以将布局导出为PDF,SVG,栅格图像或直接打印在打印机上。

+

布局由一堆类组成。它们都属于核心库。QGIS应用程序有一个方便的GUI布局元素,虽然它在GUI库中不可用。如果你不熟悉 Qt Graphics View框架,那么建议你查看文档,因为布局是基于它的。

+

布局的中心类是QgsLayout 类,它是从Qt QGraphicsScene 类派生的。让我们创建一个它的实例:

+
1
+2
+3
project = QgsProject.instance()
+layout = QgsPrintLayout(project)
+layout.initializeDefaults()
+
+

这将使用一些默认设置初始化布局,将空的A4页添加到布局中。你可以在不调用initializeDefaults()方法的情况下创建布局,但你需要自己向布局中添加页面。

+

之前的代码创建了在GUI中不可见的“临时”布局。它可以方便快速地添加某些项并导出,而不修改项本身,也不会向户公开这些更改。如果你希望将布局与项目一起保存或恢复,并使其在布局管理器中可用,添加:

+
1
+2
layout.setName("MyLayout")
+project.layoutManager().addLayout(layout)
+
+

现在我们可以在布局中添加各种元素(map,label,...)。所有这些对象都继承自基类QgsLayoutItem

+

以下是可以添加到布局的一些主要布局项的说明。

+
    +
  • 地图—— 在这里,我们创建一个地图并将其拉伸到整个纸张大小
  • +
+
1
+2
+3
+4
+5
+6
+7
map = QgsLayoutItemMap(layout)
+# 设置地图项的位置和大小(默认是宽高都是0,位置在0,0)
+map.attemptMove(QgsLayoutPoint(5,5, QgsUnitTypes.LayoutMillimeters))
+map.attemptResize(QgsLayoutSize(200,200, QgsUnitTypes.LayoutMillimeters))
+# 提供渲染范围
+map.zoomToExtent(iface.mapCanvas().extent())
+layout.addItem(map)
+
+
    +
  • 标签——允许显示标签。可以修改其字体,颜色,对齐和边距
  • +
+
1
+2
+3
+4
label = QgsLayoutItemLabel(layout)
+label.setText("Hello world")
+label.adjustSizeToText()
+layout.addItem(label)
+
+
    +
  • 图例
  • +
+
1
+2
+3
legend = QgsLayoutItemLegend(layout)
+legend.setLinkedMap(map) # map是一个QgsLayoutItemMap实例
+layout.addItem(legend)
+
+
    +
  • 比例尺
  • +
+
1
+2
+3
+4
+5
item = QgsLayoutItemScaleBar(layout)
+item.setStyle('Numeric') # 可选择修改样式
+item.setLinkedMap(map)  # map是一个QgsLayoutItemMap实例
+item.applyDefaultSize()
+layout.addItem(item)
+
+
    +
  • +

    箭头

    +
  • +
  • +

    图片

    +
  • +
  • +

    基本形状

    +
  • +
  • +

    基于节点的形状

    +
  • +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
polygon = QPolygonF()
+polygon.append(QPointF(0.0, 0.0))
+polygon.append(QPointF(100.0, 0.0))
+polygon.append(QPointF(200.0, 100.0))
+polygon.append(QPointF(100.0, 200.0))
+
+polygonItem = QgsLayoutItemPolygon(polygon, layout)
+layout.addItem(polygonItem)
+
+props = {}
+props["color"] = "green"
+props["style"] = "solid"
+props["style_border"] = "solid"
+props["color_border"] = "black"
+props["width_border"] = "10.0"
+props["joinstyle"] = "miter"
+
+symbol = QgsFillSymbol.createSimple(props)
+polygonItem.setSymbol(symbol)
+
+
    +
  • 表格
  • +
+

将项添加到布局后,可以移动并调整其大小:

+
1
+2
item.attemptMove(QgsLayoutPoint(1.4, 1.8, QgsUnitTypes.LayoutCentimeters))
+item.attemptResize(QgsLayoutSize(2.8, 2.2, QgsUnitTypes.LayoutCentimeters))
+
+

默认情况下,每个项目周围都会绘制一个框架,你可以按如下方式删除它:

+
1
label.setFrameEnabled(False)
+
+

除了手动创建布局项外,QGIS还支持布局模板,这些布局模板本质上是将所有项保存到.qpt文件中(使用XML语法)。

+

一旦组合准备就绪(布局项已经创建并添加到组合中),我们就可以继续生成栅格或者矢量输出。

+

10.3.1 检查布局有效性⚓︎

+

布局是由一组互连的项组成的,在修改过程中可能会发生这些连接断开的情况(图例连接到已删除的地图、缺少源文件的图像项等),或者你可能希望对布局项应用自定义约束。QgsAbstractValidatyCheck可帮助你实现这一点。

+

基本检查如下:

+
1
+2
+3
+4
@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
+def my_layout_check(context, feedback):
+  results = ...
+  return results
+
+

这里有一个检查,每当布局地图项目设置为WEB墨卡托投影时,就会发出警告:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
+def layout_map_crs_choice_check(context, feedback):
+  layout = context.layout
+  results = []
+  for i in layout.items():
+    if isinstance(i, QgsLayoutItemMap) and i.crs().authid() == 'EPSG:3857':
+      res = QgsValidityCheckResult()
+      res.type = QgsValidityCheckResult.Warning
+      res.title = 'Map projection is misleading'
+      res.detailedDescription = 'The projection for the map item {} is set to <i>Web Mercator (EPSG:3857)</i> which misrepresents areas and shapes. Consider using an appropriate local projection instead.'.format(i.displayName())
+      results.append(res)
+
+  return results
+
+

这里有一个更复杂的例子,如果任何布局地图项被设置为CRS,则会发出警告,该CRS仅在该地图项中显示的范围之外有效:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
@check.register(type=QgsAbstractValidityCheck.TypeLayoutCheck)
+def layout_map_crs_area_check(context, feedback):
+    layout = context.layout
+    results = []
+    for i in layout.items():
+        if isinstance(i, QgsLayoutItemMap):
+            bounds = i.crs().bounds()
+            ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem('EPSG:4326'), i.crs(), QgsProject.instance())
+            bounds_crs = ct.transformBoundingBox(bounds)
+
+            if not bounds_crs.contains(i.extent()):
+                res = QgsValidityCheckResult()
+                res.type = QgsValidityCheckResult.Warning
+                res.title = 'Map projection is incorrect'
+                res.detailedDescription = 'The projection for the map item {} is set to \'{}\', which is not valid for the area displayed within the map.'.format(i.displayName(), i.crs().authid())
+                results.append(res)
+
+    return results
+
+

10.3.2 导出布局⚓︎

+

导出布局,必须使用QgsLayoutExporter类。

+
1
+2
+3
+4
pdf_path = os.path.join(QgsProject.instance().homePath(), "output.pdf")
+
+exporter = QgsLayoutExporter(layout)
+exporter.exportToPdf(pdf_path, QgsLayoutExporter.PdfExportSettings())
+
+

如果要分别导出到SVG或图像文件,请使用exportToSvg()或者exportToImage()导出图像。

+

10.3.3 导出布局图集⚓︎

+

如果要从布局中导出所有页面(在配置中启用了图集选项),则需要在导出器(QgsLayoutExporter)中使用altas()方法,并进行少量调整。在以下示例中,页面导出为PNG图像:

+
1
exporter.exportToImage(layout.atlas(), base_path, 'png', QgsLayoutExporter.ImageExportSettings())
+
+

请注意,输出保存在基本路径文件夹中,使用图集上配置的输出文件名表达式。

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/11-\350\241\250\350\276\276\345\274\217\357\274\214\350\277\207\346\273\244\345\222\214\350\256\241\347\256\227\345\200\274/index.html" "b/11-\350\241\250\350\276\276\345\274\217\357\274\214\350\277\207\346\273\244\345\222\214\350\256\241\347\256\227\345\200\274/index.html" new file mode 100644 index 0000000..85e89cc --- /dev/null +++ "b/11-\350\241\250\350\276\276\345\274\217\357\274\214\350\277\207\346\273\244\345\222\214\350\256\241\347\256\227\345\200\274/index.html" @@ -0,0 +1,1260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 11-表达式,过滤和计算值 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

11 表达式,过滤和计算值⚓︎

+

此页面上的代码段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
from qgis.core import (
+    edit,
+    QgsExpression,
+    QgsExpressionContext,
+    QgsFeature,
+    QgsFeatureRequest,
+    QgsField,
+    QgsFields,
+    QgsVectorLayer,
+    QgsPointXY,
+    QgsGeometry,
+    QgsProject,
+    QgsExpressionContextUtils
+)
+
+

QGIS支持解析类似SQL的表达式。仅支持一小部分SQL语法。表达式可以作为布尔值(返回True或False)或作为函数(返回标量值)来计算。有关可用功能的完整列表,请参阅“用户手册”中的“ 表达式”

+

支持三种基本类型:

+
    +
  • 数字——整数和小数,例如1233.14
  • +
  • 字符串——它们必须用单引号括起来: 'hello world'
  • +
  • 列引用——在评估时,引用将替换为字段的实际值。名称不会被转义。
  • +
+

可以使用以下操作:

+
    +
  • 算术运算符:+-*/^
  • +
  • 括号:用于强制运算符优先级: (1 + 1) * 3
  • +
  • 一元加减:-12+5
  • +
  • 数学函数:sqrtsincostanasinacosatan
  • +
  • 转换函数:to_intto_realto_stringto_date
  • +
  • 几何函数:$area$length
  • +
  • 几何处理函数:$x$y$geometrynum_geometriescentroid
  • +
+

支持以下运算:

+
    +
  • 比较:=!=>>=<<=
  • +
  • 模式匹配:LIKE(使用%和_),~(正则表达式)
  • +
  • 逻辑谓词:ANDORNOT
  • +
  • NULL值检查:,IS NULLIS NOT NULL
  • +
+

示例:

+
    +
  • 1 + 2 = 3
  • +
  • sin(angle) > 0
  • +
  • 'Hello' LIKE 'He%'
  • +
  • (x > 10 AND y > 10) OR z = 0
  • +
+

标量表达式的示例:

+
    +
  • 2 ^ 10
  • +
  • sqrt(val)
  • +
  • $length + 1
  • +
+

11.1 解析表达式⚓︎

+

下面的例子展示了如何检查一个表达式是否能被正确解析:

+
1
+2
+3
+4
+5
+6
+7
exp = QgsExpression('1 + 1 = 2')
+assert(not exp.hasParserError())
+
+exp = QgsExpression('1 + 1 = ')
+assert(exp.hasParserError())
+
+assert(exp.parserErrorString() == '\nsyntax error, unexpected end of file')
+
+

11.2 评估表达式⚓︎

+

表达式可以在不同的情况下使用,例如过滤要素或计算新的字段值。在任何情况下,表达式都必须被评估。这意味着它的值是通过执行指定的计算步骤计算出来的,计算步骤可以从简单的算术到集合表达式。

+

11.2.1 基本表达式⚓︎

+

此基本表达式代表一个简单的算术运算:

+
1
+2
+3
+4
+5
+6
exp = QgsExpression('2 * 3')
+print(exp)
+print(exp.evaluate())
+
+# <QgsExpression: '2 * 3'>
+# 6
+
+

表达式也可用于比较,1为真,0为假

+
1
+2
+3
+4
exp = QgsExpression('1 + 1 = 2')
+exp.evaluate()
+
+# 1
+
+

11.2.2 要素表达式⚓︎

+

要对一个要素进行表达式评估,必须创建一个QgsExpressionContext对象,并将其传递给evaluation函数,以允许表达式访问该要素的字段值。

+

下面的示例展示了如何创建一个名为 "Column "字段的要素,以及如何将该要素添加到表达式上下文中。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
fields = QgsFields()
+field = QgsField('Column')
+fields.append(field)
+feature = QgsFeature()
+feature.setFields(fields)
+feature.setAttribute(0, 99)
+
+exp = QgsExpression('"Column"')
+context = QgsExpressionContext()
+context.setFeature(feature)
+exp.evaluate(context)
+# 99
+
+

下面是一个比较完整的例子,说明如何在矢量图层的上下文中使用表达式,以计算新的字段值:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
from qgis.PyQt.QtCore import QVariant
+
+# 创建矢量图层
+vl = QgsVectorLayer("Point", "Companies", "memory")
+pr = vl.dataProvider()
+pr.addAttributes([QgsField("Name", QVariant.String),
+                  QgsField("Employees",  QVariant.Int),
+                  QgsField("Revenue", QVariant.Double),
+                  QgsField("Rev. per employee", QVariant.Double),
+                  QgsField("Sum", QVariant.Double),
+                  QgsField("Fun", QVariant.Double)])
+vl.updateFields()
+
+# 将数据添加到前三个字段
+my_data = [
+    {'x': 0, 'y': 0, 'name': 'ABC', 'emp': 10, 'rev': 100.1},
+    {'x': 1, 'y': 1, 'name': 'DEF', 'emp': 2, 'rev': 50.5},
+    {'x': 5, 'y': 5, 'name': 'GHI', 'emp': 100, 'rev': 725.9}]
+
+for rec in my_data:
+    f = QgsFeature()
+    pt = QgsPointXY(rec['x'], rec['y'])
+    f.setGeometry(QgsGeometry.fromPointXY(pt))
+    f.setAttributes([rec['name'], rec['emp'], rec['rev']])
+    pr.addFeature(f)
+
+vl.updateExtents()
+QgsProject.instance().addMapLayer(vl)
+
+# 第一个表达式计算每个员工的收入
+# 第二个计算图层中所有收入值的总和。
+# 第三个表达式并没有什么意义,但是说明了我们可以在表达式中使用各种表达式函数(例如area和buffer):
+expression1 = QgsExpression('"Revenue"/"Employees"')
+expression2 = QgsExpression('sum("Revenue")')
+expression3 = QgsExpression('area(buffer($geometry,"Employees"))')
+
+# QgsExpressionContextUtils.globalProjectLayerScopes()是一个方便函数,可一次添加全局,项目和图层范围。另外,这些范围也可以手动添加。无论如何,重要的是始终从“最通用”到“最具体”的范围,即从全局到项目再到图层
+context = QgsExpressionContext()
+context.appendScopes(QgsExpressionContextUtils.globalProjectLayerScopes(vl))
+
+with edit(vl):
+    for f in vl.getFeatures():
+        context.setFeature(f)
+        f['Rev. per employee'] = expression1.evaluate(context)
+        f['Sum'] = expression2.evaluate(context)
+        f['Fun'] = expression3.evaluate(context)
+        vl.updateFeature(f)
+
+print( f['Sum'])
+
+# 876.5
+
+

11.2.3 表达式过滤图层⚓︎

+

以下示例可用于过滤图层并返回与谓词匹配的所有要素:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
layer = QgsVectorLayer("Point?field=Test:integer",
+                           "addfeat", "memory")
+
+layer.startEditing()
+
+for i in range(10):
+    feature = QgsFeature()
+    feature.setAttributes([i])
+    assert(layer.addFeature(feature))
+layer.commitChanges()
+
+expression = 'Test >= 3'
+request = QgsFeatureRequest().setFilterExpression(expression)
+
+matches = 0
+for f in layer.getFeatures(request):
+   matches += 1
+
+print(matches)
+
+# 7
+
+

11.3 处理异常错误⚓︎

+

在表达式解析或评估期间,可能发生表达相关的错误:

+
1
+2
+3
+4
+5
+6
+7
exp = QgsExpression("1 + 1 = 2 ")
+if exp.hasParserError():
+   raise Exception(exp.parserErrorString())
+
+value = exp.evaluate()
+if exp.hasEvalError():
+   raise ValueError(exp.evalErrorString())
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/12-\350\257\273\345\217\226\345\222\214\345\255\230\345\202\250\350\256\276\347\275\256/index.html" "b/12-\350\257\273\345\217\226\345\222\214\345\255\230\345\202\250\350\256\276\347\275\256/index.html" new file mode 100644 index 0000000..0d415aa --- /dev/null +++ "b/12-\350\257\273\345\217\226\345\222\214\345\255\230\345\202\250\350\256\276\347\275\256/index.html" @@ -0,0 +1,927 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12-读取和存储设置 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

12 读取和存储设置⚓︎

+

此页上的代码段需要导入以下模块:

+
1
+2
+3
+4
+5
from qgis.core import (
+  QgsProject,
+  QgsSettings,
+  QgsVectorLayer
+)
+
+

很多时候,插件保存一些变量非常有用,这样用户下次运行插件时就不必再输入或选择它们。

+

借助Qt和QGIS API可以保存和检索这些变量。对于每个变量,你应该选择一个用于访问变量的键——对于用户喜欢的颜色,你可以使用键“favourite_color”或任何其他有意义的字符串。建议为键的命名提供一些结构。

+

我们可以区分几种类型的设置:

+
    +
  • 全局设置: 它们绑定到特定计算机上的用户。QGIS本身存储了许多全局设置,例如,主窗口大小或默认捕捉容差。使用QgsSettings类处理设置,该类提供setValue()value()方法
  • +
+

在这里,你可以看到如何使用这些方法的示例:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
def store():
+    s = QgsSettings()
+    s.setValue("myplugin/mytext", "hello world")
+    s.setValue("myplugin/myint",  10)
+    s.setValue("myplugin/myreal", 3.14)
+
+def read():
+    s = QgsSettings()
+    mytext = s.value("myplugin/mytext", "default text")
+    myint  = s.value("myplugin/myint", 123)
+    myreal = s.value("myplugin/myreal", 2.71)
+    nonexistent = s.value("myplugin/nonexistent", None)
+    print(mytext)
+    print(myint)
+    print(myreal)
+    print(nonexistent)
+
+

value()方法的第二个参数是可选的,如果没有,则返回默认值。

+

通过global_settings.ini文件在全局设置中预配置默认值,更多详情查看Deploying QGIS within an organization

+
    +
  • 项目设置: 在不同项目之间有所不同,因此它们与项目文件相关联。以地图画布背景颜色或目标坐标参考系统(CRS)作为例子——白色背景和WGS84可能适用于一个项目,而黄色背景和UTM投影适用于另一个项目。
  • +
+

以下是一个用法示例:

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
proj = QgsProject.instance()
+
+# 存储值
+proj.writeEntry("myplugin", "mytext", "hello world")
+proj.writeEntry("myplugin", "myint", 10)
+proj.writeEntry("myplugin", "mydouble", 0.01)
+proj.writeEntry("myplugin", "mybool", True)
+
+# 读取值(返回具有值的元组,和一个布尔状态,检索到的值是否可以转换到它的类型, string, an integer, a double and a boolean)
+mytext, type_conversion_ok = proj.readEntry("myplugin", "mytext", "default text")
+myint, type_conversion_ok = proj.readNumEntry("myplugin", "myint", 123)
+mydouble, type_conversion_ok = proj.readDoubleEntry("myplugin", "mydouble", 123)
+mybool, type_conversion_ok = proj.readBoolEntry("myplugin", "mybool", 123)
+
+ 如你所见,writeEntry()方法用于许多数据类型(integer, string, list),但有几种方法可以读取设置值,而且必须为每种数据类型选择相应的一个。

+
    +
  • 地图图层设置:这些设置与项目的图层实例相关。它们不与图层的基础数据源连接,因此如果你创建两个shapefile图层实例,则它们不会共享设置。设置存储在项目文件中,因此如果用户再次打开项目,则与图层相关的设置将再次存在。使用customProperty()方法检索给定设置的值,并可使用setCustomProperty()进行设置。
  • +
+
1
+2
+3
+4
+5
+6
vlayer = QgsVectorLayer()
+# 保存值
+vlayer.setCustomProperty("mytext", "hello world")
+
+# 再次读取(如果没有找到,则返回“default text”)
+mytext = vlayer.customProperty("mytext", "default text")
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/13-\344\270\216\347\224\250\346\210\267\351\200\232\344\277\241/index.html" "b/13-\344\270\216\347\224\250\346\210\267\351\200\232\344\277\241/index.html" new file mode 100644 index 0000000..a55b192 --- /dev/null +++ "b/13-\344\270\216\347\224\250\346\210\267\351\200\232\344\277\241/index.html" @@ -0,0 +1,1189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 13-与用户通信 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

13 与用户通信⚓︎

+

本节代码片段需导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
from qgis.core import (
+    QgsMessageLog,
+    QgsGeometry,
+)
+
+from qgis.gui import (
+    QgsMessageBar,
+)
+
+from qgis.PyQt.QtWidgets import (
+    QSizePolicy,
+    QPushButton,
+    QDialog,
+    QGridLayout,
+    QDialogButtonBox,
+)
+
+

本节介绍用于与用户通信的一些方法和元素,以保持用户接口的一致性。

+

13.1 显示消息——QgsMessageBar⚓︎

+

从用户体验的角度来看,使用消息框可能是个坏主意。为了显示一小行信息或警告/错误消息,QGIS消息栏通常是更好的选择。

+

使用QGIS接口对象的引用,你可以使用以下代码在消息栏中显示消息

+
1
+2
from qgis.core import Qgis
+iface.messageBar().pushMessage("Error", "I'm sorry Dave, I'm afraid I can't do that", level=Qgis.Critical)
+
+

errorbar.png

+

QGIS消息栏

+

你可以设置持续时间,在有限时间内显示它

+
1
iface.messageBar().pushMessage("Ooops", "The plugin is not working as it should", level=Qgis.Critical, duration=3)
+
+

errorbar-timed.png

+

带定时器的QGIS消息栏

+

上面的示例显示了错误栏,但level参数可用于创建警告消息或正常消息——使用Qgis.MessageLevel枚举,你最多可以使用4个不同级别:

+
    +
  1. +

    Info

    +
  2. +
  3. +

    Warning

    +
  4. +
  5. Critical
  6. +
  7. Success
  8. +
+

infobar.png

+

QGIS消息栏(info)

+

控件可以添加到消息栏中,例如用于显示更多信息的按钮

+
1
+2
+3
+4
+5
+6
+7
+8
+9
def showError():
+    pass
+
+widget = iface.messageBar().createMessage("Missing Layers", "Show Me")
+button = QPushButton(widget)
+button.setText("Show Me")
+button.pressed.connect(showError)
+widget.layout().addWidget(button)
+iface.messageBar().pushWidget(widget, Qgis.Warning)
+
+

bar-button.png

+

带有按钮的QGIS消息栏

+

你甚至可以在自己的对话框中使用消息栏,这样就不必显示消息框,或者在主QGIS窗口中显示消息时没有意义

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
class MyDialog(QDialog):
+    def __init__(self):
+        QDialog.__init__(self)
+        self.bar = QgsMessageBar()
+        self.bar.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
+        self.setLayout(QGridLayout())
+        self.layout().setContentsMargins(0, 0, 0, 0)
+        self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok)
+        self.buttonbox.accepted.connect(self.run)
+        self.layout().addWidget(self.buttonbox, 0, 0, 2, 1)
+        self.layout().addWidget(self.bar, 0, 0, 1, 1)
+    def run(self):
+        self.bar.pushMessage("Hello", "World", level=Qgis.Info)
+
+myDlg = MyDialog()
+myDlg.show()
+
+

dialog-with-bar.png

+

自定义对话框中的QGIS消息栏

+

13.2 显示进度条⚓︎

+

进度条也可以放在QGIS消息栏中,正如我们所见,它接受控件。以下是你可以在控制台中尝试的示例:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
import time
+from qgis.PyQt.QtWidgets import QProgressBar
+from qgis.PyQt.QtCore import *
+progressMessageBar = iface.messageBar().createMessage("Doing something boring...")
+progress = QProgressBar()
+progress.setMaximum(10)
+progress.setAlignment(Qt.AlignLeft|Qt.AlignVCenter)
+progressMessageBar.layout().addWidget(progress)
+iface.messageBar().pushWidget(progressMessageBar, Qgis.Info)
+
+for i in range(10):
+    time.sleep(1)
+    progress.setValue(i + 1)
+
+iface.messageBar().clearWidgets()
+
+

此外,你可以使用内置状态栏报告进度,如下示例所示:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
vlayer = iface.activeLayer()
+
+count = vlayer.featureCount()
+features = vlayer.getFeatures()
+
+for i, feature in enumerate(features):
+    # 做一些耗时任务
+    print('') # 给予足够的时间来打印进度
+
+    percent = i / float(count) * 100
+    # iface.mainWindow().statusBar().showMessage("Processed {} %".format(int(percent)))
+    iface.statusBarIface().showMessage("Processed {} %".format(int(percent)))
+
+iface.statusBarIface().clearMessage()
+
+

13.3 日志⚓︎

+

QGIS有三种不同类型的日志记录,记录和保存有关代码执行的所有信息。每种类型都有特定的输出位置。请考虑使用正确的日志记录方式:

+
    +
  • QgsMessageLog 用于向用户传达问题。QgsMessageLog的输出显示在日志消息面板中。
  • +
  • Python的 logging 模块用于调试QGIS Python API(PyQGIS)。建议Python开发人员使用其调试代码,例如,要素的id或者几何。
  • +
  • QgsLogger 用于QGIS内部调试/开发(例如:当你怀疑某些内容引起了崩溃)。只有QGIS的开发者版本可用。
  • +
+

不同日志记录类型的示例如下所示。

+
+

警告

+

在多线程的代码中使用Python print语句是不安全的,并且很大程度降低算法的速度。包括函数表达式,渲染器,符号层和处理算法(等等)。在这些情况下,你应该始终使用Python logging 模块或线程安全类(QgsLoggerQgsMessageLog)。

+
+

13.3.1 QgsMessageLog⚓︎

+
1
+2
+3
+4
# 你可以选择传递'tag'和'level'参数
+QgsMessageLog.logMessage("Your plugin code has been executed correctly", 'MyPlugin', level=Qgis.Info)
+QgsMessageLog.logMessage("Your plugin code might have some problems", level=Qgis.Warning)
+QgsMessageLog.logMessage("Your plugin code has crashed!", level=Qgis.Critical)
+
+
+

提示

+

你可以在日志消息面板中查看QgsMessageLog输出的日志。

+
+

13.3.2 python logging模块⚓︎

+
1
+2
+3
+4
+5
+6
+7
+8
+9
import logging
+formatter = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+logfilename=r'c:\temp\example.log'
+logging.basicConfig(filename=logfilename, level=logging.DEBUG, format=formatter)
+logging.info("This logging info text goes into the file")
+logging.debug("This logging debug text goes into the file as well")
+
+# 2020-10-08 13:14:42,998 - root - INFO - This logging text goes into the file
+# 2020-10-08 13:14:42,998 - root - DEBUG - This logging debug text goes into the file as well
+
+

basicConfig方法配置日志记录的基本设置。在上面的代码中,定义了文件名,日志记录级别和格式。文件名指的是将日志文件写入的位置,日志记录级别定义输出的级别,格式定义输出每个消息的格式。

+

如果你想要每次在执行脚本时删除日志文件,你可以执行以下操作:

+
1
+2
+3
if os.path.isfile(logfilename):
+    with open(logfilename, 'w') as file:
+        pass
+
+

如何使用如何使用Python日志记录工具的更多资源:

+ +
+

警告

+

请注意,如果不设置日志文件,日志可能是多线程的,这会严重减慢输出速度

+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/14-\350\256\244\350\257\201\345\237\272\347\241\200/index.html" "b/14-\350\256\244\350\257\201\345\237\272\347\241\200/index.html" new file mode 100644 index 0000000..47f567a --- /dev/null +++ "b/14-\350\256\244\350\257\201\345\237\272\347\241\200/index.html" @@ -0,0 +1,1470 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 14-认证基础 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +

14 认证基础⚓︎

+

本节的代码片段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
from qgis.core import (
+  QgsApplication,
+  QgsRasterLayer,
+  QgsAuthMethodConfig,
+  QgsDataSourceUri,
+  QgsPkiBundle,
+  QgsMessageLog,
+)
+
+from qgis.gui import (
+    QgsAuthAuthoritiesEditor,
+    QgsAuthConfigEditor,
+    QgsAuthConfigSelect,
+    QgsAuthSettingsWidget,
+)
+
+from qgis.PyQt.QtWidgets import (
+    QWidget,
+    QTabWidget,
+)
+
+from qgis.PyQt.QtNetwork import QSslCertificate
+
+

14.1 介绍⚓︎

+

认证基础的用户参考可以在用户手册的“认证系统概述”中阅读。

+

本章是描述从开发人员角度使用认证系统的最佳实践。

+

在QGIS桌面中,当需要凭证来访问特定资源时,例如当一个图层建立在Postgres数据库连接时,数据提供者就广泛地使用了认证系统。

+

QGIS gui库中还有一些部件,插件开发人员可以使用它们轻松地将认证基础集成到代码中:

+ +

可以从认证基础测试代码中学习良好的代码引用。

+
+

警告

+

由于认证基础设计过程中考虑到安全约束,只有选定的内部方法集可以暴露给Python。

+
+

14.2 词汇表⚓︎

+

以下是本章中最常见对象的一些定义。

+
    +
  • 主密码
  • +
+

允许访问和解密存储在QGIS认证中凭据的密码

+
    +
  • 认证数据库
  • +
+

一个主密码加密后的SQLite数据库qgis-auth.db ,其中认证配置存储在这里。例如用户名/密码,个人证书和密钥,证书颁发机构

+
    +
  • 认证数据库
  • +
+

认证数据库

+
    +
  • 认证配置
  • +
+

一组认证数据,取决于认证方法。例如,基本认证方法存储一对用户名/密码。

+
    +
  • 认证方法
  • +
+

用于获取认证的特定方法。每个方法都有自己的协议,用于获取认证级别。每个方法都是在QGIS认证基础初始化期间动态加载的共享库。

+

14.3 QgsAuthManager入口⚓︎

+

单例类QgsAuthManager是使用存储在QGIS认证数据库加密证书的入口,即,在当前用户资料文件夹下的qgis-auth.db文件。

+

此类负责用户交互:通过设置主密码或透明地使用它来访问加密的存储信息。

+

14.3.1 初始化管理器并设置主密码⚓︎

+

以下代码段提供了一个示例,设置主密码来打开对认证设置的访问权限。代码注释对于理解代码非常重要。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
authMgr = QgsApplication.authManager()
+# 检查QgsAuthManager是否已经初始化... QgsAuthManager.init()的额外作用是设置了AuthDbPath。
+# QgsAuthManager.init()在QGIS应用程序初始化期间执行,因此通常不需要直接调用它。
+if authMgr.authenticationDatabasePath():
+    if authMgr.masterPasswordIsSet():
+        msg = 'Authentication master password not recognized'
+        assert authMgr.masterPasswordSame( "your master password" ), msg
+    else:
+        msg = 'Master password could not be set'
+        # 验证verify参数密码的哈希值是否已保存在认证数据库中
+        assert authMgr.setMasterPassword( "your master password",verify=True), msg
+else:
+    # 在qgis环境之外,例如在测试环境中=>在数据库初始化之前设置环境变量
+    os.environ['QGIS_AUTH_DB_DIR_PATH'] = "/path/where/located/qgis-auth.db"
+    msg = 'Master password could not be set'
+    assert authMgr.setMasterPassword("your master password", True), msg
+    authMgr.init( "/path/where/located/qgis-auth.db" )
+
+

14.3.2 使用新的认证配置项填充认证数据库⚓︎

+

任何存储的凭证都是QgsAuthMethodConfig类的认证配置实例——使用唯一字符串访问:

+
1
authcfg = 'fm1s770'
+
+

该字符串是在使用QGIS API或GUI创建条目时自动生成的,但如果配置必须在一个组织内的多个用户之间共享(不同的凭证),将其手动设置为一个已知值可能是有用的。

+

QgsAuthMethodConfig是任何认证方法的基类。任何认证方法集都会配置哈希映射,其中存储认证信息。下面是一个有用的代码片段,用于存储alice用户的PKI路径凭证:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
authMgr = QgsApplication.authManager()
+# 设置 alice PKI 数据
+p_config = QgsAuthMethodConfig()
+p_config.setName("alice")
+p_config.setMethod("PKI-Paths")
+p_config.setUri("https://example.com")
+p_config.setConfig("certpath", "path/to/alice-cert.pem" )
+p_config.setConfig("keypath", "path/to/alice-key.pem" )
+# 检查方法参数是否正确设置
+assert p_config.isValid()
+
+# 在认证数据库中注册alice数据,返回存储的‘authcfg’配置
+authMgr.storeAuthenticationConfig(p_config)
+newAuthCfgId = p_config.id()
+assert (newAuthCfgId)
+
+

14.3.2.1 可用的认证方法⚓︎

+

认证方法在认证管理器初始化时动态加载。可用的认证方法列表如下:

+
    +
  1. Basic 用户和密码验证
  2. +
  3. Esri-Token ESRI token 基础认证
  4. +
  5. Identity-Cert 身份证书认证
  6. +
  7. PKI-Paths PKI路径认证
  8. +
  9. PKI-PKCS#12 PKI PKCS#12认证
  10. +
+

14.3.2.2 填充权限⚓︎

+
1
+2
+3
+4
+5
+6
+7
+8
+9
authMgr = QgsApplication.authManager()
+# 添加权限
+cacerts = QSslCertificate.fromPath( "/path/to/ca_chains.pem" )
+assert cacerts is not None
+# 存储 CA
+authMgr.storeCertAuthorities(cacerts)
+# 重建CA缓存
+authMgr.rebuildCaCertsCache()
+authMgr.rebuildTrustedCaCertsCache()
+
+

14.3.2.3 使用QgsPkiBundle管理PKI⚓︎

+

QgsPkiBundle类是一个方便的类,用于打包由SslCert、SslKey和CA链组成的PKI包。下面是一个获得密码保护的片段:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
# 密钥与密码一起添加alice证书
+caBundlesList = [] 
+bundle = QgsPkiBundle.fromPemPaths( "/path/to/alice-cert.pem",
+                                     "/path/to/alice-key_w-pass.pem",
+                                     "unlock_pwd",
+                                     caBundlesList )
+assert bundle is not None
+# 你可以检查它是否正确
+# bundle.isValid()
+
+

请参阅QgsPkiBundle类文档,从包中提取证书/密钥/ CA.

+

14.3.3 从认证数据库中删除条目⚓︎

+

我们可以使用authcfg标识符从认证数据库中删除条目:

+
1
+2
authMgr = QgsApplication.authManager()
+authMgr.removeAuthenticationConfig( "authCfg_Id_to_remove" )
+
+

14.3.4 使用QgsAuthManager扩展authcfg⚓︎

+

使用存储在认证数据库中的认证配置的最好方法是用唯一的标识符authcfg来引用它。扩展,意味着把它从一个标识符转换成一套完整的凭证。使用存储的认证配置的最佳做法,是让它由认证管理器自动管理。存储配置的常见用途是连接到一个启用了认证的服务,如WMS、WFS或数据库连接。

+
+

提示

+

并非所有的QGIS数据提供者都与认证基础集成。每个认证方法都是从基类QgsAuthMethod派生出来的,并支持一组不同的提供者。例如,certIdentity()方法支持以下的提供者列表: +

1
+2
+3
+4
authM = QgsApplication.authManager()
+print(authM.authMethod("Identity-Cert").supportedDataProviders())
+
+# ['ows', 'wfs', 'wcs', 'wms', 'postgres']
+

+
+

例如,要使用authcfg = 'fm1s770'标识的存储凭证访问WMS服务,我们只需在数据源URL中使用authcfg,如以下代码: +

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
authCfg = 'fm1s770'
+quri = QgsDataSourceUri()
+quri.setParam("layers", 'usa:states')
+quri.setParam("styles", '')
+quri.setParam("format", 'image/png')
+quri.setParam("crs", 'EPSG:4326')
+quri.setParam("dpiMode", '7')
+quri.setParam("featureCount", '10')
+quri.setParam("authcfg", authCfg)   # <---- authCfg url 配置
+quri.setParam("contextualWMSLegend", '0')
+quri.setParam("url", 'https://my_auth_enabled_server_ip/wms')
+rlayer = QgsRasterLayer(str(quri.encodedUri(), "utf-8"), 'states', 'wms')
+
+以上案例,wms提供者在设置HTTP连接之前,会将authcfg URI参数与凭证展开。

+
+

警告

+

开发者必须将authcfg扩展留给QgsAuthManager,这样,它就能确保扩展不会太早完成。

+
+

通常情况下,使用QgsDataSourceURI类构建的URI字符串被用来设置数据源,其方式如下:

+
1
+2
+3
+4
authCfg = 'fm1s770'
+quri = QgsDataSourceUri("my WMS uri here")
+quri.setParam("authcfg", authCfg)
+rlayer = QgsRasterLayer( quri.uri(False), 'states', 'wms')
+
+
+

提示

+

False参数很重要,可以避免URL中已存在的authcfg

+
+

14.3.4.1 使用其它数据提供者的PKI例子⚓︎

+

其它例子可以在QGIS测试文件夹中直接读取,如test_authmanager_pki_owstest_authmanager_pki_postgres.

+

14.4 调整插件使用认证基础⚓︎

+

许多第三方插件使用httplib2或其他Python网络库来管理HTTP连接,而不是与QgsNetworkAccessManager及其相关认证基础集成。

+

为了便于集成,我们创建了一个名为NetworkAccessManager的Python帮助函数。它的代码可以在这里找到。

+

此帮助程序类可以在以下代码段中使用:

+
1
+2
+3
+4
+5
+6
http = NetworkAccessManager(authid="my_authCfg", exception_class=My_FailedRequestError)
+try:
+  response, content = http.request( "my_rest_url" )
+except My_FailedRequestError, e:
+  # 处理异常
+  pass
+
+

14.5 认证GUI⚓︎

+

本节列出了可用于在自定义接口中集成认证基础的可用GUI。

+

14.5.1 GUI选择证书⚓︎

+

如果需要从存储在认证数据库中选择认证配置,则可以在GUI类QgsAuthConfigSelect中使用。

+

QgsAuthConfigSelect.png

+

可在以下代码段中使用:

+
1
+2
+3
+4
+5
+6
# 创建一个QgsAuthConfigSelect GUI实例,该实例与`parent`有父子关系
+parent = QWidget()  # GUI父级控件
+gui = QgsAuthConfigSelect( parent, "postgres" )
+# 在一个新标签中添加上述创建的GUI控件。
+tabGui = QTabWidget()
+tabGui.insertTab( 1, gui, "Configurations" )
+
+

以上示例摘自QGIS源代码。GUI构造函数的第二个参数引用数据提供者类型。参数用于限制与指定提供者兼容的认证方法

+

14.5.2 认证编辑器⚓︎

+

用于管理凭据、权限和访问认证实用程序的完整GUI由QgsAuthEditorWidgets类管理。

+

QgsAuthEditorWidgets.png

+

可在以下代码段中使用:

+
1
+2
+3
+4
# 创建一个QgsAuthConfigSelect GUI实例,该实例与`parent`有父子关系
+parent = QWidget()  # GUI父级控件
+gui = QgsAuthConfigSelect( parent )
+gui.show()
+
+

可以在相关的测试代码中找到一个综合的例子。

+

14.5.3 机构(证书颁发)编辑器⚓︎

+

仅用于管理机构的GUI由QGSAuthoritiesEditor类管理。

+

QgsAuthAuthoritiesEditor.png

+

可在以下代码段中使用:

+
1
+2
+3
+4
# 创建一个QgsAuthConfigSelect GUI实例,该实例与`parent`有父子关系
+parent = QWidget() # GUI父级控件
+gui = QgsAuthAuthoritiesEditor( parent )
+gui.show()
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/15-\344\273\273\345\212\241\342\200\224\342\200\224\345\234\250\345\220\216\345\217\260\345\201\232\347\271\201\351\207\215\347\232\204\345\267\245\344\275\234/index.html" "b/15-\344\273\273\345\212\241\342\200\224\342\200\224\345\234\250\345\220\216\345\217\260\345\201\232\347\271\201\351\207\215\347\232\204\345\267\245\344\275\234/index.html" new file mode 100644 index 0000000..5b8f121 --- /dev/null +++ "b/15-\344\273\273\345\212\241\342\200\224\342\200\224\345\234\250\345\220\216\345\217\260\345\201\232\347\271\201\351\207\215\347\232\204\345\267\245\344\275\234/index.html" @@ -0,0 +1,1500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 15-任务 - 在后台做繁重的工作 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节代码片段需导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
from qgis.core import (
+  QgsProcessingContext,
+  QgsTaskManager,
+  QgsTask,
+  QgsProcessingAlgRunnerTask,
+  Qgis,
+  QgsProcessingFeedback,
+  QgsApplication,
+  QgsMessageLog,
+)
+
+

15 任务 - 在后台做繁重的工作⚓︎

+

15.1 引言⚓︎

+

使用线程的后台处理,是在进行繁重处理时保持用户界面响应的一种方式。任务可用于在QGIS中实现线程。

+

任务(QgsTask)是在后台执行代码的容器,任务管理(QgsTaskManager)用于控制任务的运行。这些类通过提供信号传递机制、进度报告和后台进程状态访问机制,简化了QGIS中的后台处理。可以使用子任务对任务进行分组。

+

全局任务管理器(QgsApplication.taskManager())通常被使用。这意味着你的任务可能不是由任务管理器控制的唯一任务。

+

有几种方法可以创建QGIS任务:

+
    +
  • 通过扩展QgsTask创建自己的任务
  • +
+
1
+2
class SpecialisedTask(QgsTask):
+    pass
+
+
    +
  • 从函数创建任务
  • +
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
def heavyFunction():
+    # 一些CPU密集型处理 ...
+    pass
+
+def workdone():
+    # ... 使用结果做一些有用的事情
+    pass
+
+task = QgsTask.fromFunction('heavy function', heavyFunction,
+                     onfinished=workdone)
+
+
    +
  • 从处理算法创建任务
  • +
+
1
+2
+3
+4
+5
+6
params = dict()
+context = QgsProcessingContext()
+feedback = QgsProcessingFeedback()
+
+buffer_alg = QgsApplication.instance().processingRegistry().algorithmById('native:buffer')
+task = QgsProcessingAlgRunnerTask(buffer_alg, params, context,feedback)
+
+
+

Warning

+

任何后台任务(无论如何创建)决不能使用任何主线程上的QObject,比如访问QgsVectorLayer, QgsProject或者执行任何GUI操作——比如创建新的部件或者与现有部件交互。只能从主线程访问或修改Qt控件。在任务启动之前,必须复制任务中使用的数据。试图从后台线程使用它们将导致崩溃。

+
+

可以使用QgsTask中的addSubTask() 函数来描述任务之间的依赖关系。当声明依赖关系时,任务管理器将自动确定如何执行这些依赖关系。只要有可能,依赖项将并行执行,以便尽快满足它们。如果取消了一个任务所依赖的任务,则相关任务也将被取消。循环依赖可能造成死锁,所以要小心。

+

如果任务依赖于可用的图层,则可以使用QgsTask中的setDependentLayers 函数来声明。如果任务所依赖的图层不可用,则该任务将被取消。

+

创建任务后,可以使用任务管理器的addTask()函数调度任务运行。向管理器添加任务会自动将该任务的所有权转移给管理员,管理员将在执行完后清理和删除任务。任务的调度受任务优先级的影响,任务优先级在addTask()中设置。

+

任务的状态可以使用QgsTaskQgsTaskManager的信号和函数进行监控。

+

15.2 示例⚓︎

+

15.2.1 扩展QgsTask⚓︎

+

在此示例中,RandomIntegerSumTask扩展了QgsTask,它将在指定的时间段内生成0到500之间的100个随机整数。如果随机数为42,则中止任务并引发异常。RandomIntegerSumTask(带子任务)生成了几个实例并将其添加到任务管理器,展示两种类型的依赖项:

+
  1
+  2
+  3
+  4
+  5
+  6
+  7
+  8
+  9
+ 10
+ 11
+ 12
+ 13
+ 14
+ 15
+ 16
+ 17
+ 18
+ 19
+ 20
+ 21
+ 22
+ 23
+ 24
+ 25
+ 26
+ 27
+ 28
+ 29
+ 30
+ 31
+ 32
+ 33
+ 34
+ 35
+ 36
+ 37
+ 38
+ 39
+ 40
+ 41
+ 42
+ 43
+ 44
+ 45
+ 46
+ 47
+ 48
+ 49
+ 50
+ 51
+ 52
+ 53
+ 54
+ 55
+ 56
+ 57
+ 58
+ 59
+ 60
+ 61
+ 62
+ 63
+ 64
+ 65
+ 66
+ 67
+ 68
+ 69
+ 70
+ 71
+ 72
+ 73
+ 74
+ 75
+ 76
+ 77
+ 78
+ 79
+ 80
+ 81
+ 82
+ 83
+ 84
+ 85
+ 86
+ 87
+ 88
+ 89
+ 90
+ 91
+ 92
+ 93
+ 94
+ 95
+ 96
+ 97
+ 98
+ 99
+100
+101
+102
+103
+104
+105
+106
+107
+108
+109
+110
+111
+112
+113
+114
+115
+116
+117
+118
+119
import random
+from time import sleep
+
+from qgis.core import (
+    QgsApplication, QgsTask, QgsMessageLog, Qgis
+    )
+
+MESSAGE_CATEGORY = 'RandomIntegerSumTask'
+
+class RandomIntegerSumTask(QgsTask):
+    """展示如何子类化QgsTask"""
+    def __init__(self, description, duration):
+        super().__init__(description, QgsTask.CanCancel)
+        self.duration = duration
+        self.total = 0
+        self.iterations = 0
+        self.exception = None
+
+    def run(self):
+        """在这里你要实现你的任务。
+        应该定期测试isCanceled(),以便优雅地终止。
+        此方法必须返回True或False。
+        引发异常将使QGIS崩溃,因此我们在内部处理这些异常,并在self.finished中抛出。
+        """
+        QgsMessageLog.logMessage('Started task "{}"'.format(
+                                     self.description()),
+                                 MESSAGE_CATEGORY, Qgis.Info)
+        wait_time = self.duration / 100
+        for i in range(100):
+            sleep(wait_time)
+            # 使用setProgress报告进度
+            self.setProgress(i)
+            arandominteger = random.randint(0, 500)
+            self.total += arandominteger
+            self.iterations += 1
+            # 检查isCanceled()处理取消
+            if self.isCanceled():
+                return False
+            # 模拟异常情况
+            if arandominteger == 42:
+                # 不要raise Exception('bad value!'),否则将使QGIS崩溃
+                self.exception = Exception('bad value!')
+                return False
+        return True
+
+    def finished(self, result):
+        """
+        当任务完成(无论成功与否)时,这个函数会被自动调用。
+        你可以通过实现 finished() 来执行任务完成后的后续事情。
+        finished总是从主线程中调用的,因此在这里进行GUI操作和引发 Python 异常是安全的。
+        result是self.run的返回值。
+        """
+        if result:
+            QgsMessageLog.logMessage(
+                'Task "{name}" completed\n' \
+                'Total: {total} (with {iterations} '\
+              'iterations)'.format(
+                  name=self.description(),
+                  total=self.total,
+                  iterations=self.iterations),
+              MESSAGE_CATEGORY, Qgis.Success)
+        else:
+            if self.exception is None:
+                QgsMessageLog.logMessage(
+                    'Task "{name}" not successful but without '\
+                    'exception (probably the task was manually '\
+                    'canceled by the user)'.format(
+                        name=self.description()),
+                    MESSAGE_CATEGORY, Qgis.Warning)
+            else:
+                QgsMessageLog.logMessage(
+                    'Task "{name}" Exception: {exception}'.format(
+                        name=self.description(),
+                        exception=self.exception),
+                    MESSAGE_CATEGORY, Qgis.Critical)
+                raise self.exception
+
+    def cancel(self):
+        QgsMessageLog.logMessage(
+            'Task "{name}" was canceled'.format(
+                name=self.description()),
+            MESSAGE_CATEGORY, Qgis.Info)
+        super().cancel()
+
+
+longtask = RandomIntegerSumTask('waste cpu long', 20)
+shorttask = RandomIntegerSumTask('waste cpu short', 10)
+minitask = RandomIntegerSumTask('waste cpu mini', 5)
+shortsubtask = RandomIntegerSumTask('waste cpu subtask short', 5)
+longsubtask = RandomIntegerSumTask('waste cpu subtask long', 10)
+shortestsubtask = RandomIntegerSumTask('waste cpu subtask shortest', 4)
+
+# 添加子任务(shortsubtask)到shorttask——必须在minitask和longtask完成后执行
+shorttask.addSubTask(shortsubtask, [minitask, longtask])
+# 添加子任务(longsubtask)到longtask——必须父级任务之前运行
+longtask.addSubTask(longsubtask, [], QgsTask.ParentDependsOnSubTask)
+# 添加子任务(shortestsubtask)到longtask
+longtask.addSubTask(shortestsubtask)
+
+QgsApplication.taskManager().addTask(longtask)
+QgsApplication.taskManager().addTask(shorttask)
+QgsApplication.taskManager().addTask(minitask)
+
+
+
+
+# RandomIntegerSumTask(0): Started task "waste cpu subtask shortest"
+# RandomIntegerSumTask(0): Started task "waste cpu short"
+# RandomIntegerSumTask(0): Started task "waste cpu mini"
+# RandomIntegerSumTask(0): Started task "waste cpu subtask long"
+# RandomIntegerSumTask(3): Task "waste cpu subtask shortest" completed
+# RandomTotal: 25452 (with 100 iterations)
+# RandomIntegerSumTask(3): Task "waste cpu mini" completed
+# RandomTotal: 23810 (with 100 iterations)
+# RandomIntegerSumTask(3): Task "waste cpu subtask long" completed
+# RandomTotal: 26308 (with 100 iterations)
+# RandomIntegerSumTask(0): Started task "waste cpu long"
+# RandomIntegerSumTask(3): Task "waste cpu long" completed
+# RandomTotal: 22534 (with 100 iterations)
+
+

15.2.2 从函数创建任务⚓︎

+

从函数创建任务(本示例中的doSomething)。该函数的第一个参数为QgsTask 。一个重要的参数是on_finished,它是在任务完成时被调用的函数。示例中的doSomething函数有另一个参数wait_time

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
import random
+from time import sleep
+
+MESSAGE_CATEGORY = 'TaskFromFunction'
+
+def doSomething(task, wait_time):
+    """
+    抛出一个异常终止任务
+    成功则返回结果
+    结果将和异常一起传递给 (成功则为空)on_finished函数.
+    如果存在异常,结果为空
+    """
+    QgsMessageLog.logMessage('Started task {}'.format(task.description()),
+                             MESSAGE_CATEGORY, Qgis.Info)
+    wait_time = wait_time / 100
+    total = 0
+    iterations = 0
+    for i in range(100):
+        sleep(wait_time)
+        # 使用task.setProgress报告进度
+        task.setProgress(i)
+        arandominteger = random.randint(0, 500)
+        total += arandominteger
+        iterations += 1
+        # 检查task.isCanceled()处理取消
+        if task.isCanceled():
+            stopped(task)
+            return None
+        # 抛出异常终止任务
+        if arandominteger == 42:
+            raise Exception('bad value!')
+    return {'total': total, 'iterations': iterations,
+            'task': task.description()}
+
+def stopped(task):
+    QgsMessageLog.logMessage(
+        'Task "{name}" was canceled'.format(
+            name=task.description()),
+        MESSAGE_CATEGORY, Qgis.Info)
+
+def completed(exception, result=None):
+    """当doSomething完成时呗调佣
+    如果抛出异常则异常信息不是空
+    结果是doSomething返回的结果"""
+    if exception is None:
+        if result is None:
+            QgsMessageLog.logMessage(
+                'Completed with no exception and no result '\
+                '(probably manually canceled by the user)',
+                MESSAGE_CATEGORY, Qgis.Warning)
+        else:
+            QgsMessageLog.logMessage(
+                'Task {name} completed\n'
+                'Total: {total} ( with {iterations} '
+                'iterations)'.format(
+                    name=result['task'],
+                    total=result['total'],
+                    iterations=result['iterations']),
+                MESSAGE_CATEGORY, Qgis.Info)
+    else:
+        QgsMessageLog.logMessage("Exception: {}".format(exception),
+                                 MESSAGE_CATEGORY, Qgis.Critical)
+        raise exception
+
+# 创建一些任务
+task1 = QgsTask.fromFunction(u'Waste cpu 1', doSomething,
+                             on_finished=completed, wait_time=4)
+task2 = QgsTask.fromFunction(u'Waste cpu 2', dosomething,
+                             on_finished=completed, wait_time=3)
+QgsApplication.taskManager().addTask(task1)
+QgsApplication.taskManager().addTask(task2)
+
+
+# RandomIntegerSumTask(0): Started task "waste cpu subtask short"
+# RandomTaskFromFunction(0): Started task Waste cpu 1
+# RandomTaskFromFunction(0): Started task Waste cpu 2
+# RandomTaskFromFunction(0): Task Waste cpu 2 completed
+# RandomTotal: 23263 ( with 100 iterations)
+# RandomTaskFromFunction(0): Task Waste cpu 1 completed
+# RandomTotal: 25044 ( with 100 iterations)
+
+

14.2.3 处理算法任务⚓︎

+

创建一个使用算法qgis:randompointsinextent的任务,在指定范围内生成50000个随机点。结果以安全的方式添加到项目中。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
from functools import partial
+from qgis.core import (QgsTaskManager, QgsMessageLog,
+                       QgsProcessingAlgRunnerTask, QgsApplication,
+                       QgsProcessingContext, QgsProcessingFeedback,
+                       QgsProject)
+
+MESSAGE_CATEGORY = 'AlgRunnerTask'
+
+def task_finished(context, successful, results):
+    if not successful:
+        QgsMessageLog.logMessage('Task finished unsucessfully',
+                                 MESSAGE_CATEGORY, Qgis.Warning)
+    output_layer = context.getMapLayer(results['OUTPUT'])
+    # 因为getMapLayer没有移交所有权, 当上下文超出范围时图层将被删除,你会遇到崩溃
+    # takeMapLayer移交所有权,因此它将安全地添加到QgsProject中,并赋予QgsProject所有权
+    if output_layer and output_layer.isValid():
+        QgsProject.instance().addMapLayer(
+             context.takeResultLayer(output_layer.id()))
+
+alg = QgsApplication.processingRegistry().algorithmById(
+                                      u'qgis:randompointsinextent')
+context = QgsProcessingContext()
+feedback = QgsProcessingFeedback()
+params = {
+    'EXTENT': '0.0,10.0,40,50 [EPSG:4326]',
+    'MIN_DISTANCE': 0.0,
+    'POINTS_NUMBER': 50000,
+    'TARGET_CRS': 'EPSG:4326',
+    'OUTPUT': 'memory:My random points'
+}
+task = QgsProcessingAlgRunnerTask(alg, params, context, feedback)
+task.executed.connect(partial(task_finished, context))
+QgsApplication.taskManager().addTask(task)
+
+

也可以查看博客:https://www.opengis.ch/2018/06/22/threads-in-pyqgis3/

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/16-\345\274\200\345\217\221Python\346\217\222\344\273\266/index.html" "b/16-\345\274\200\345\217\221Python\346\217\222\344\273\266/index.html" new file mode 100644 index 0000000..a44d7dc --- /dev/null +++ "b/16-\345\274\200\345\217\221Python\346\217\222\344\273\266/index.html" @@ -0,0 +1,2482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16-开发Python插件 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

16 开发Python插件⚓︎

+

可以用Python编程语言创建插件。与用C ++编写插件相比,由于Python语言的动态特性,这些插件应该更容易编写,理解,维护和分发。

+

Python插件与QGIS插件管理器中的C ++插件一起列出。他们在~/(UserProfile)/python/plugins和以下路径中搜索:

+
    +
  • UNIX / Mac上: (qgis_prefix)/share/qgis/python/plugins
  • +
  • Windows: (qgis_prefix)/python/plugins
  • +
+

有关~(UserProfile)的定义,请查看Core和External插件

+
+

提示

+

将QGIS_PLUGINPATH设置为一个存在的目录路径,可以将此路径添加到插件的搜索路径列表中。

+
+

16.1 构建Python插件⚓︎

+

创建插件,需要以下步骤:

+
    +
  1. 想法:你想要使用新的QGIS插件做什么。你为什么要这么做?你想解决什么问题?这个问题已经有另一个插件吗?
  2. +
  3. 创建文件:一些必要文件(查看插件文件
  4. +
  5. 编写代码:在恰当的文件中写代码
  6. +
  7. 文档:编写插件文档
  8. +
  9. 可选:翻译:将插件翻译成不同的语言
  10. +
  11. 测试:如果准备就绪,重新加载插件
  12. +
  13. 发布:在QGIS仓库中发布你的插件或将你自己的仓库作为个人“GIS武器”的“武器库”。
  14. +
+

16.1.1 准备开始⚓︎

+

在开始编写新的插件之前,先看看Python官方插件库。现有插件的源代码可以帮助你了解更多编程知识。你也可能会发现类似的插件已经存在,你可以扩展它,或者至少在它的基础上开发自己的插件。

+

16.1.1.1 插件文件结构⚓︎

+

开始使用一个新的插件,我们需要设置必要的插件文件。

+

有两种插件模板资源可以帮助你入门:

+
    +
  • 为了教育目的或者在需要采用极简主义方法时,最小化插件模板提供了创建有效的QGIS Python插件所需的基本文件(框架)。
  • +
  • 更加完整的插件模板,插件构建器可以创建多种不同类型的插件模板,包括本地化(翻译)和测试等功能。
  • +
+

典型的插件目录包括以下文件:

+
    +
  • metadata.txt - 必须 - 包含插件网站和插件基础结构使用的常规信息,版本、名称和一些其他元数据。
  • +
  • __init__.py - 必须 - 插件的入口。它必须具有 classFactory()方法,还可以具有任何其他初始化代码。
  • +
  • mainPlugin.py - 核心代码 - 插件的主要工作代码。包含有关插件操作和主要代码的所有信息。
  • +
  • form.ui - 插件自定义UI - Qt设计师创建的GUI。
  • +
  • form.py - 编译的GUI - 将上面描述的form.ui转换为Python代码。
  • +
  • resources.qrc - 可选 - Qt设计师创建的xml文档。包含表单资源的相对路径。
  • +
  • resources.py - 编译的资源文件,可选 - 将上述.qrc文件转换为Python代码。
  • +
+
+

Warning

+

如果您打算将插件上传到Python官方插件库,则必须检查插件是否遵循插件验证所必需的一些附加规则

+
+

16.1.2 编写插件代码⚓︎

+

以下部分显示了应该在上面介绍的每个文件中添加哪些内容。

+

16.1.2.1 metadata.txt⚓︎

+

首先,插件管理器需要检索有关插件的一些基本信息,例如其名称、描述等。文件metadata.txt存储此信息。

+
+

提示

+

所有元数据必须采用UTF-8编码。

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
元数据名称是否必需描述
name插件名称,短字符串
qgisMinimumVersion最小QGIS版本
qgisMaximumVersion最大QGIS版本
description描述插件的简短文本,不支持HTML
about较长的文本,详细描述插件,不支持HTML
version版本
author作者姓名
email作者的电子邮件,在网站上仅显示给登录的用户,但在插件安装后可在插件管理器中看到
changelog字符串,可以是多行,不支持HTML
experimental实验性,布尔值,True或False
deprecated弃用,boolean值,True或False,适用于整个插件,而不仅仅适用于上传的版本
tags以逗号分隔的列表,允许在单个标记内使用空格
homepage指向插件主页的有效网址
repository源代码存储库的有效URL
tracker故障和错误报告的有效URL
icon对于web友好的图像(PNG,JPEG)文件名或相对路径(相对于插件压缩包的文件夹)
categoryRaster, Vector, Database, Mesh and Web(栅格、矢量、数据库和网络)
plugin_dependencies类似于PIP的逗号分隔的其他插件列表,使用来自元数据名称字段的插件名称
server布尔值,True或False,确定插件是否具有服务器接口
hasProcessingProvider布尔值,True或False,确定插件是否提供处理算法
+

默认情况下,插件放在 Plugins 菜单中(我们将在下一节中看到如何为插件添加菜单项),但也可以将它们放入 RasterVectorDatabaseMeshWeb 菜单中。

+

输入指定的“category”元数据,可以相应地对插件进行分类。此元数据用于提示用户,并告诉他们可以在哪里(在哪个菜单中)找到该插件。“category”的允许值为:Vector, Raster, Database或者Web。例如,如果你的插件可以从Raster菜单中找到,请将其添加到metadata.txt

+
1
category=Raster
+
+
+

提示

+

如果qgisMaximumVersion为空,则在上传到官方Python插件库时,它将自动设置为主要版本加上.99(例如:3.99)。

+
+

metadata.txt示例:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
; 以下是强制性的
+
+[general]
+name=HelloWorld
+email=me@example.com
+author=Just Me
+qgisMinimumVersion=3.0
+description=This is an example plugin for greeting the world.\
+    Multiline is allowed:\
+    lines starting with spaces belong to the same\
+    field, in this case to the "description" field.\
+    HTML formatting is not allowed.
+about=This paragraph can contain a detailed description\
+    of the plugin. Multiline is allowed, HTML is not.
+version=version 1.2
+tracker=http://bugs.itopen.it
+repository=http://www.itopen.it/repo
+; 结束强制
+
+; 以下是可选的
+category=Raster
+changelog=The changelog lists the plugin versions\
+    and their changes as in the example below:\
+    1.0 - First stable release\
+    0.9 - All features implemented\
+    0.8 - First testing release
+
+; 标签采用逗号分隔,标签名称允许使用空格
+; 标签应该是英文的,在创建之前请检查现有标签和同义词
+tags=wkt,raster,hello world
+
+; 这些元数据可以为空,最终将成为强制性的。
+homepage=https://www.itopen.it
+icon=icon.png
+
+; 实验标志(适用于单一版本)
+experimental=True
+
+; 弃用标志 (适用于整个插件,不仅适用于上传的版本)
+deprecated=False
+
+; 如果为空,它将自动设置为主要版本+.99
+qgisMaximumVersion=3.99
+
+; 从 QGIS 3.8开始,可以指定以逗号分隔指定要安装(或更新)的插件列表
+; 下面示例安装或更新版本1.12的“MyOtherPlugin”和任何版本的“YetAnotherPlugin”
+plugin_dependencies=MyOtherPlugin==1.12,YetAnotherPlugin
+
+

16.1.2.2 __init__.py⚓︎

+

Python包需要此文件。此外,QGIS要求此文件包含一个classFactory()函数,该函数在插件被加载到QGIS时调用。它接收QgisInterface实例, 并且必须返回mainplugin.py中插件类的对象——在我们的例子中它被命名为TestPlugin(见下文)。__init__.py应该是这样的:

+
1
+2
+3
+4
+5
def classFactory(iface):
+    from .mainPlugin import TestPlugin
+    return TestPlugin(iface)
+
+# 任何其他初始化
+
+

16.1.2.3 mainPlugin.py⚓︎

+

这就是魔法发生的地方,下面就是魔法的例子:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
from qgis.PyQt.QtGui import *
+from qgis.PyQt.QtWidgets import *
+
+# 从文件resources.py初始化Qt的资源
+from . import resources
+
+
+class TestPlugin:
+
+    def __init__(self, iface):
+        # 保存QGIS interface引用
+        self.iface = iface
+
+    def initGui(self):
+        # 创建操作,它将启动插件配置
+        self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow())
+        self.action.setObjectName("testAction")
+        self.action.setWhatsThis("Configuration for test plugin")
+        self.action.setStatusTip("This is status tip")
+        self.action.triggered.connect(self.run)
+
+        # 添加工具栏按钮和菜单项
+        self.iface.addToolBarIcon(self.action)
+        self.iface.addPluginToMenu("&Test plugins", self.action)
+
+        # 连接信号renderComplete——画布渲染完成后发送的信号
+        self.iface.mapCanvas().renderComplete.connect(self.renderTest)
+
+    def unload(self):
+        # 删除插件菜单项和图标
+        self.iface.removePluginMenu("&Test plugins", self.action)
+        self.iface.removeToolBarIcon(self.action)
+
+        # 断开信号
+        self.iface.mapCanvas().renderComplete.disconnect(self.renderTest)
+
+    def run(self):
+        # 创建并显示一个配置对话框或类似的事情
+        print("TestPlugin: run called!")
+
+    def renderTest(self, painter):
+
+        # 使用painter绘制地图画布
+        print("TestPlugin: renderTest called!")
+
+

主插件源文件中(例如 mainPlugin.py)必须存在的插件函数是:

+
    +
  • __init__ - >可以访问QGIS界面
  • +
  • initGui() - >加载插件时调用
  • +
  • unload() - >卸载插件时调用
  • +
+

在上面的例子中,addPluginToMenu()被使用。这会将相应的菜单操作添加到 Plugins 菜单中。存在额外的方法将操作(action)添加到不同菜单。以下是这些方法的列表:

+ +

它们都具有与addPluginToMenu()方法相同的语法 。

+

建议将插件菜单添加到其中一个预定义方法,以保持插件条目组织方式的一致性。但是,你可以将自定义菜单组直接添加到菜单栏,如下面示例所示:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
def initGui(self):
+    self.menu = QMenu(self.iface.mainWindow())
+    self.menu.setObjectName("testMenu")
+    self.menu.setTitle("MyMenu")
+
+    self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow())
+    self.action.setObjectName("testAction")
+    self.action.setWhatsThis("Configuration for test plugin")
+    self.action.setStatusTip("This is status tip")
+    self.action.triggered.connect(self.run)
+    self.menu.addAction(self.action)
+
+    menuBar = self.iface.mainWindow().menuBar()
+    menuBar.insertMenu(self.iface.firstRightStandardMenu().menuAction(), self.menu)
+
+def unload(self):
+    self.menu.deleteLater()
+
+

不要忘记设置QActionQMenu objectName插件的特定名称,以便可以自定义。

+

虽然帮助和关于操作也可以添加到你的自定义菜单中,但QGIS主 帮助插件 菜单中有一个简便的地方。这是使用pluginHelpMenu()方法完成的。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
def initGui(self):
+
+    self.help_action = QAction(
+        QIcon(":/plugins/testplug/icon.png"),
+        self.tr("Test Plugin..."),
+        self.iface.mainWindow()
+    )
+    # 添加操作到帮助菜单
+    self.iface.pluginHelpMenu().addAction(self.help_action)
+
+    self.help_action.triggered.connect(self.show_help)
+
+@staticmethod
+def show_help():
+    """打开在线帮助"""
+    QDesktopServices.openUrl(QUrl('https://docs.qgis.org'))
+
+def unload(self):
+
+    self.iface.pluginHelpMenu().removeAction(self.help_action)
+    del self.help_action
+
+

16.1.3 文档⚓︎

+

该插件的文档可以编写为HTML帮助文档。qgis.utils模块提供了一个函数,showPluginHelp()将打开帮助文档浏览器,与其他QGIS帮助文档的方式相同。

+

showPluginHelp()函数在与调用模块相同的目录中查找帮助文档。它会寻找index-ll_cc.htmlindex-ll.htmlindex-en.htmlindex-en_us.htmlindex.html,显示它找到的第一个文档。这ll_cc是QGIS语言环境,这允许文档有多个翻译。

+

showPluginHelp()函数还可以使用参数packageName——它显示指定插件的帮助文档,filename——可以替换被搜索的文件名中的“index”,section——它是html锚标记的名称,浏览器将定位到该位置。

+

16.1.4 翻译⚓︎

+

通过几个步骤,你可以设置插件本地化的环境,以便根据计算机的区域,插件将以不同语言加载。

+

16.1.4.1 软件要求⚓︎

+

创建和管理所有翻译文件的最简单方法是安装 Qt Linguist。在基于Debian的GNU / Linux环境中,你可以这样安装它:

+
1
sudo apt-get install qttools5-dev-tools
+
+

16.1.4.2 文件和目录⚓︎

+

创建插件时,你将在主插件目录中找到该文件夹i18n

+

所有翻译文件都必须放在此目录中。

+
16.1.4.2.1 .pro文件⚓︎
+

首先,你应该创建一个.pro文件,这是一个可以由 Qt Linguist 管理的 项目 文件。

+

在此.pro文件中,你必须指定要翻译的所有文件和窗体(.ui文件)。此文件用于设置本地化文件和变量。下面是一个项目文件,匹配我们的示例插件的结构 :

+
1
+2
+3
FORMS = ../form.ui
+SOURCES = ../your_plugin.py
+TRANSLATIONS = your_plugin_it.ts
+
+

你的插件可能有更复杂的结构,并且可能分布在多个文件中。如果是这种情况,请记住,使用pylupdate5读取.pro文件并更新可翻译字符串,这不会扩展通配符,因此你需要将每个文件显式放在.pro文件中。你的项目文件可能看起来像这样:

+
1
+2
+3
+4
FORMS = ../ui/about.ui ../ui/feedback.ui \
+        ../ui/main_dialog.ui
+SOURCES = ../your_plugin.py ../computation.py \
+          ../utils.py
+
+

此外,your_plugin.py文件是调用 QGIS工具栏中插件的所有菜单和子菜单的文件,你希望将它们全部翻译。

+

最后,使用TRANSLATIONS变量,你可以指定所需的翻译语言。

+
+

警告

+

确保ts文件名称为your_plugin_ + language + .ts,否则语言将加载失败。使用两个字母的语言缩写(it对应Italian,de对应German,等等)

+
+
16.1.4.2.2 .ts文件⚓︎
+

创建.pro完成后,你就可以为插件的语言生成.ts文件了。

+

打开终端,转到your_plugin/i18n目录并输入:

+
1
pylupdate5 your_plugin.pro
+
+

你应该可以看到your_plugin_language.ts文件。

+

Qt Linguist 打开.ts文件并开始翻译。

+
16.1.4.2.3 .qm文件⚓︎
+

当你完成插件翻译时(如果某些字符串未完成,将使用这些字符串的源语言),你必须创建.qm 文件(将被QGIS使用的.ts编译文件)。

+

只需在your_plugin/i18n目录中打开终端cd 并输入:

+
1
lrelease your_plugin.ts
+
+

现在,在i18n目录中你将看到your_plugin.qm文件。

+

16.1.4.3 使用MakeFile进行翻译⚓︎

+

或者,如果你使用Plugin Builder创建了插件,则可以使用makefile从python代码和Qt对话框中提取消息。在Makefile的开头有一个LOCALES变量:

+
1
LOCALES = en
+
+

将该语言的缩写添加到此变量中,例如匈牙利语:

+
1
LOCALES = en hu
+
+

现在,你可以通过以下方式从源生成或更新hu.ts文件(以及其中的en.ts):

+
1
make transup
+
+

在此之后,你已在LOCALES变量中更新.ts了所有语言的文件。使用 Qt Linguist 翻译程序消息。完成翻译后,.qm可以通过transcompile创建:

+
1
make transcompile
+
+

你必须在你的插件中分发.ts文件。

+

16.1.4.4 加载插件⚓︎

+

要查看插件的翻译,只需打开QGIS,更改语言( 设置选项通用 )并重新启动QGIS。

+

你应该看到你的插件使用正确的语言。

+
+

警告

+

如果你改变了一些东西(新的UI,新的菜单,等等),你必须重新生成.ts.qm文件,因此需要再一次执行以上命令。

+
+

16.1.5 提示和技巧⚓︎

+

16.1.5.1 插件重载⚓︎

+

在开发插件期间,你经常需要在QGIS中重新加载它以进行测试。使用 Plugin Reloader 插件非常容易。你可以在插件管理器中找到它。

+

16.1.5.2 使用 qgis-plugin-ci自动打包、发布和翻译⚓︎

+

qgis-plugin-ci提供了一个命令行界面,用于在你的计算机上执行 QGIS 插件的自动打包和部署,或使用持续集成(如 GitHub 工作流Gitlab-CI 以及 Transifex 进行翻译)。

+

16.1.5.3 访问插件⚓︎

+

你可以使用python从QGIS中访问所有已安装插件类,这可以方便调试:

+
1
my_plugin = qgis.utils.plugins['My Plugin']
+
+

16.1.5.4 日志消息⚓︎

+

插件在日志消息面板中有自己的选项卡。

+

16.1.5.5 分享你的插件⚓︎

+

QGIS在插件仓库中托管了数百个插件。考虑分享你的插件!它将扩展QGIS,人们将能够从你的代码中学习。可以使用插件管理器在QGIS中找到并安装所有托管的插件。

+

信息和要求:plugins.qgis.org

+

16.1.6 提示和技巧⚓︎

+

16.1.6.1 插件重新加载程序⚓︎

+

在开发插件过程中,你经常需要重新加载它以进行测试。使用Plugin Reloader插件非常容易。你可以在插件管理器中找到它。

+

16.1.6.2 使用qgis插件ci自动打包、发布和翻译⚓︎

+

qgis-plugin-ci 提供了一个命令行界面,可以在你的计算机上或使用持续集成工具(如 GitHub 工作流Gitlab-CI)以及 Transifex 进行自动打包和部署 QGIS 插件。它允许通过 CLI 或在 CI 操作中发布、翻译、发布或生成 XML 插件存储库文件。

+

16.1.6.3 访问插件⚓︎

+

你可以使用Python在QGIS中访问所有已安装插件的类,这对于调试很有用。

+
1
my_plugin = qgis.utils.plugins['My Plugin']
+
+

16.1.6.4 日志信息⚓︎

+

插件在日志消息面板中有自己的选项卡。

+

16.1.6.5 资源文件⚓︎

+

你可以看到在initGui()中我们使用了资源文件中的图标(在我们的案例中是resources.qrc

+
1
+2
+3
+4
+5
<RCC>
+  <qresource prefix="/plugins/testplug" >
+     <file>icon.png</file>
+  </qresource>
+</RCC>
+
+

最好使用不会与其他插件或QGIS的任何部分发生冲突的前缀,否则你可能会得到你不想要的资源。现在你只需要生成一个包含资源的Python文件。它是用 pyrcc5 命令完成的:

+
1
pyrcc5 -o resources.py resources.qrc
+
+
+

提示

+

在Windows环境中,尝试从CMD或Powershell运行pyrcc5可能会导致错误“Windows无法访问指定的设备,路径,或文件[...]”。最简单的解决方案可能是使用osgeo4wshell,但如果你愿意修改PATH环境变量或显式指定可执行文件的路径,你应该可以在<Your QGIS Install Directory>\bin\pyrcc5.exe找到它

+
+

就这些……没什么复杂的:)

+

如果你已正确完成所有操作,则应该能够在插件管理器中查找并加载插件,在点击工具栏图标或相应的菜单项时,可以在控制台中查看到消息。

+

在处理真正的插件时,最好将插件写入另一个(工作)目录并创建一个makefile,它将生成UI和资源文件并将插件安装到QGIS安装中。

+

16.2 代码片段⚓︎

+

本节以代码片段为例,讲解插件开发

+

16.2.1 如何通过快捷键调用方法⚓︎

+

initGui()中添加:

+
1
+2
+3
+4
self.key_action = QAction("Test Plugin", self.iface.mainWindow())
+self.iface.registerMainWindowAction(self.key_action, "Ctrl+I")  # 操作被Ctrl+I触发
+self.iface.addPluginToMenu("&Test plugins", self.key_action)
+self.key_action.triggered.connect(self.key_action_triggered)
+
+

unload()中添加:

+
1
self.iface.unregisterMainWindowAction(self.keyAction)
+
+

按下CTRL+I时调用方法:

+
1
+2
def key_action_triggered(self):
+    QMessageBox.information(self.iface.mainWindow(),"Ok", "You pressed Ctrl+I")
+
+

还可以允许用户为提供的操作自定义快捷键。这是通过添加以下内容来完成的:

+
1
+2
+3
+4
+5
# 在 initGui() 方法中
+QgsGui.shortcutsManager().registerAction(self.key_action)
+
+# 在 unload() 方法中
+QgsGui.shortcutsManager().unregisterAction(self.key_action)
+
+

16.2.2 如何重用QGIS图标⚓︎

+

因为它们是众所周知的,并且向用户传达了明确的信息,所以有时你可能希望在插件中重用QGIS图标,而不是绘制和设置新图标。使用getThemeIcon()方法。

+

例如,重用 QGIS 代码存储库中可用的 mActionFileOpen.svg 图标:

+
1
+2
+3
+4
+5
+6
+7
# 例如:在初始化GUI时
+self.file_open_action = QAction(
+    QgsApplication.getThemeIcon("/mActionFileOpen.svg"),
+    self.tr("Select a File..."),
+    self.iface.mainWindow()
+)
+self.iface.addPluginToMenu("MyPlugin", self.file_open_action)
+
+

iconPath()是调用 QGIS 图标的另一种方法。有关调用主题图标的示例,请访问QGIS嵌入式图像—速查表

+

16.2.3 选项对话框中的插件接口⚓︎

+

你可以在 设置选项 中添加一个自定义插件选项标签。这比为你的插件选项添加一个特定的主菜单条目更可取,因为它将所有的QGIS应用程序设置和插件设置保存在一个单一的地方,便于用户发现和导航。

+

下面的代码片段将为插件的设置添加一个新的空白选项卡,为你填充所有选项和你的插件特定设置做好准备。你可以将下面的类拆分成不同的文件。在这个例子中,我们在mainPlugin.py文件中添加了两个类。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
class MyPluginOptionsFactory(QgsOptionsWidgetFactory):
+
+    def __init__(self):
+        super().__init__()
+
+    def icon(self):
+        return QIcon('icons/my_plugin_icon.svg')
+
+    def createWidget(self, parent):
+        return ConfigOptionsPage(parent)
+
+
+class ConfigOptionsPage(QgsOptionsPageWidget):
+
+    def __init__(self, parent):
+        super().__init__(parent)
+        layout = QHBoxLayout()
+        layout.setContentsMargins(0, 0, 0, 0)
+        self.setLayout(layout)
+
+

最后我们添加导入和修改__init__函数:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
from qgis.gui import QgsOptionsWidgetFactory, QgsOptionsPageWidget
+
+
+class MyPlugin:
+    def __init__(self, iface):
+        """构造函数.
+
+        :param iface: 将传递给该类的接口实例它提供了一个钩子,你可以通过它来操作QGIS运行时应用程序。
+        :type iface: QgsInterface
+        """
+        # 保存引用
+        self.iface = iface
+
+
+    def initGui(self):
+        self.options_factory = MyPluginOptionsFactory()
+        self.options_factory.setTitle(self.tr('My Plugin'))
+        iface.registerOptionsWidgetFactory(self.options_factory)
+
+    def unload(self):
+        iface.unregisterOptionsWidgetFactory(self.options_factory)
+
+
+

提示

+

将自定义选项卡添加到图层属性对话框

+

你可以应用类似的逻辑,使用QgsMapLayerConfigWidgetFactoryQgsMapLayerConfigWidget类将插件自定义选项添加到图层属性对话框中。

+
+

16.2.4 在图层树中嵌入图层的自定义小组件⚓︎

+

除了在图层面板中常见的图层符号元素之外,你还可以添加自己的小部件,以便快速访问一些经常与图层一起使用的操作(设置过滤、选择、样式、使用按钮小部件刷新图层、创建基于时间轴的图层或仅在标签中显示额外的图层信息等)。这些所谓的图层树嵌入式小部件通过图层的属性图例选项卡为各个图层提供。

+

以下代码片段在图例中创建一个下拉菜单,显示图层可用的图层样式,允许快速在不同的图层样式之间切换。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
class LayerStyleComboBox(QComboBox):
+    def __init__(self, layer):
+        QComboBox.__init__(self)
+        self.layer = layer
+        for style_name in layer.styleManager().styles():
+            self.addItem(style_name)
+
+        idx = self.findText(layer.styleManager().currentStyle())
+        if idx != -1:
+          self.setCurrentIndex(idx)
+
+        self.currentIndexChanged.connect(self.on_current_changed)
+
+    def on_current_changed(self, index):
+        self.layer.styleManager().setCurrentStyle(self.itemText(index))
+
+class LayerStyleWidgetProvider(QgsLayerTreeEmbeddedWidgetProvider):
+    def __init__(self):
+        QgsLayerTreeEmbeddedWidgetProvider.__init__(self)
+
+    def id(self):
+        return "style"
+
+    def name(self):
+        return "Layer style chooser"
+
+    def createWidget(self, layer, widgetIndex):
+        return LayerStyleComboBox(layer)
+
+    def supportsLayer(self, layer):
+        return True   # any layer is fine
+
+provider = LayerStyleWidgetProvider()
+QgsGui.layerTreeEmbeddedWidgetRegistry().addProvider(provider)
+
+

从给定图层的图例属性选项卡中,将“图层样式选择器”从“可用小部件”拖动到“已用小部件”,以在图层树中启用小部件。嵌入式小部件始终显示在其相关图层节点子项的顶部。

+

如果你想从插件中使用小部件,可以按以下方法添加它们:

+
1
+2
+3
+4
+5
+6
+7
layer = iface.activeLayer()
+counter = int(layer.customProperty("embeddedWidgets/count", 0))
+layer.setCustomProperty("embeddedWidgets/count", counter+1)
+layer.setCustomProperty("embeddedWidgets/{}/id".format(counter), "style")
+view = self.iface.layerTreeView()
+view.layerTreeModel().refreshLayerLegend(view.currentLegendNode())
+view.currentNode().setExpanded(True)
+
+

16.3 编写和调试插件的IDE设置⚓︎

+

TODO

+

16.4 发布你的插件⚓︎

+

一旦你的插件准备好了,并且你认为这个插件可能对某些人有帮助,不要犹豫,把它上传到官方Python插件仓库。在该页面上,你还可以找到关于如何准备插件以与插件安装程序良好配合的打包指南。或者,如果你想建立自己的插件库,可以创建一个简单的XML文件,列出插件及其元数据。

+

请特别注意一下建议:

+

16.4.1 元数据和名称⚓︎

+
    +
  • 避免使用与现有插件过于相似的名称
  • +
  • 如果你的插件与现有的插件有类似的功能,请在 "关于 "一栏中解释其区别,这样用户就会知道使用哪一个,而不需要安装和测试。
  • +
  • 避免在插件本身的名称中重复使用"plugin"。
  • +
  • 使用元数据中的描述字段进行单行描述,使用关于字段进行更详细的说明
  • +
  • 包括一个代码库、一个错误跟踪器和一个主页;这将极大地提高合作的可能性,并且可以通过现有的网络基础设施(GitHub、GitLab、Bitbucket等)非常容易地完成。
  • +
  • 谨慎选择标签:避免不具参考价值的标签(如vector),最好选择已经被他人使用的标签(见插件网站)。
  • +
  • 添加一个合适的图标,不要使用默认的图标;参见QGIS界面,了解要使用的风格建议
  • +
+

16.4.2 代码和帮助⚓︎

+
    +
  • +

    不要把生成的文件(ui_*.py, resources_rc.py, 生成的帮助文件...)和无用的东西(如.gitignore)包括在版本库中

    +
  • +
  • +

    将插件添加到适当的菜单中(Vector, Raster, Web, Database)。

    +
  • +
  • +

    在适当的时候(执行分析的插件),考虑将插件添加为Processing框架的子插件:这将允许用户批量运行它,将它集成到更复杂的工作流中,并将你从设计界面的负担中解放出来

    +
  • +
  • +

    至少包括最基本的文档,如果对测试和理解有用的话,还包括样本数据。

    +
  • +
+

16.4.3 官方python插件仓库⚓︎

+

你可以找到官方python插件仓库:https://plugins.qgis.org/

+

为了使用官方python插件仓库,你必须从OSGEO web portal获得OSGEO ID。

+

一旦你上传了你的插件,它将得到一个工作人员的批准,你会得到通知。

+

16.4.3.1 权限⚓︎

+

这些规则已经在官方插件库中实现:

+
    +
  • +

    每个注册用户都可以添加一个新的插件

    +
  • +
  • +

    员工用户可以批准或不批准所有的插件版本

    +
  • +
  • +

    拥有特殊权限plugins.can_approve的用户可以自动批准他们上传的版本

    +
  • +
  • +

    拥有特殊权限plugins.can_approve的用户可以批准其他人上传的版本,只要他们在插件所有者的列表中。

    +
  • +
  • +

    一个特定的插件只能由员工用户和插件所有者删除和编辑。

    +
  • +
  • +

    如果一个没有plugins.can_approve权限的用户上传了一个新的版本,该插件的版本会自动取消审批。

    +
  • +
+

16.4.3.2 信任管理⚓︎

+

工作人员可以通过前端应用程序设置plugins.can_approve权限,向选定的插件创建者授予信任。

+

插件详情视图提供了直接链接,以授予对插件创建者或插件所有者的信任。

+

16.4.3.3 验证⚓︎

+

在上传插件时,插件的元数据会自动从压缩包中导入并进行验证。

+

这里有一些验证规则,当你想在官方仓库上传一个插件时,你应该注意:

+
    +
  1. 包含插件的主文件夹的名称必须只包含ASCII字符(A-Z和a-z)、数字和下划线(_)和减号(-),而且不能以数字开头。
  2. +
  3. metadata.txt是必需的
  4. +
  5. 元数据表中列出的所有必需的元数据都必须存在
  6. +
  7. 元数据version字段必须是唯一的
  8. +
+

16.4.3.4 插件结构⚓︎

+

按照验证规则,你的插件的压缩包(.zip)必须有一个特定的结构,才能作为一个功能性插件进行验证。由于该插件将被解压在用户的plugins文件夹内,它必须在.zip文件内有自己的目录,以不干扰其他插件。必需的文件有:metadata.txt__init__.py。但如果能有一个README,当然还有一个代表该插件的图标(resources.qrc),那就更好了。以下是一个plugin.zip的例子,它应该是这样的。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
plugin.zip
+  pluginfolder/
+  |-- i18n
+  |   |-- translation_file_de.ts
+  |-- img
+  |   |-- icon.png
+  |   `-- iconsource.svg
+  |-- __init__.py
+  |-- Makefile
+  |-- metadata.txt
+  |-- more_code.py
+  |-- main_code.py
+  |-- README
+  |-- resources.qrc
+  |-- resources_rc.py
+  `-- ui_Qt_user_interface_file.ui
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/17-\347\274\226\345\206\231\345\244\204\347\220\206\346\217\222\344\273\266/index.html" "b/17-\347\274\226\345\206\231\345\244\204\347\220\206\346\217\222\344\273\266/index.html" new file mode 100644 index 0000000..7c8cfd6 --- /dev/null +++ "b/17-\347\274\226\345\206\231\345\244\204\347\220\206\346\217\222\344\273\266/index.html" @@ -0,0 +1,1023 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 17-编写处理插件 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

17 编写处理插件⚓︎

+

取决于你将开发的插件类型,它可能是更好的选择—作为处理算法(或者算法集合)去增加功能。这将在QGIS中提供给更好的集成,额外的功能(它可以运行在处理组件中,如建模或批处理界面运行),和更快的开发时间(处理将花费的很大一部分工作)。

+

为了分发这些算法,你应该创建一个新的插件,并将它们添加到处理工具箱。该插件应该包含一个算法提供者,它在插件实例化时进行注册。

+

17.1 从头开始创建⚓︎

+

从头开始创建一个插件,它包含一个算法提供者,你可以使用插件构造器,按照下列步骤操作:

+
    +
  1. 安装Plugin Builder插件
  2. +
  3. 使用Plugin Builder创建一个新的插件。当Plugin Builder要求你使用模板时,选择“Processing provider”。
  4. +
  5. 创建的插件包含一个算法提供者。无论是提供者文件和算法文件都被完全注释,并包含有关如何修改提供者,并添加额外的算法的信息。参考它们以获取更多信息。
  6. +
+

17.2 更新插件⚓︎

+

如果你想添加你现有的插件到“Processing”,你需要添加一些代码。

+
    +
  1. +

    在你的metadata.txt,你需要添加一个变量:

    +
    1
    hasProcessingProvider=yes
    +
    +
  2. +
  3. +

    在Python文件中,插件使用initGui方法安装,你需要改写这样的例子:

    +
     1
    + 2
    + 3
    + 4
    + 5
    + 6
    + 7
    + 8
    + 9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    from qgis.core import QgsApplication
    +from .processing_provider import Provider
    +
    +class YourPluginName():
    +
    +    def __init__(self):
    +        self.provider = None
    +
    +    def initProcessing(self):
    +        self.provider = Provider()
    +        QgsApplication.processingRegistry().addProvider(self.provider)
    +
    +    def initGui(self):
    +        self.initProcessing()
    +
    +    def unload(self):
    +        QgsApplication.processingRegistry().removeProvider(self.provider)
    +
    +
  4. +
  5. +

    你可以创建一个processing_provider文件夹,其中包含三个文件

    +
      +
    • +

      __init__.py没有任何东西。这是Python包所必需的。

      +
    • +
    • +

      provider.py 这将创建处理提供者并公开你的算法。

      +
    • +
    +
     1
    + 2
    + 3
    + 4
    + 5
    + 6
    + 7
    + 8
    + 9
    +10
    +11
    +12
    +13
    +14
    +15
    +16
    +17
    +18
    +19
    +20
    +21
    +22
    +23
    +24
    +25
    +26
    +27
    +28
    from qgis.core import QgsProcessingProvider
    +from .example_processing_algorithm import ExampleProcessingAlgorithm
    +
    +class Provider(QgsProcessingProvider):
    +
    +    def loadAlgorithms(self, *args, **kwargs):
    +        self.addAlgorithm(ExampleProcessingAlgorithm())
    +        # 在这里添加其他算法
    +        # self.addAlgorithm(MyOtherAlgorithm())
    +
    +    def id(self, *args, **kwargs):
    +    """插件的ID,用于标识提供者。
    +
    +    该字符串应该是唯一的,短的,仅字符串,例如“qgis”或“gdal”。此字符串不应被翻译(localised)。
    +    """
    +    return 'yourplugin'
    +
    +    def name(self, *args, **kwargs):
    +    """插件的人性化名称。
    +
    +    该字符串应尽可能短(例如“Lastools”,而不是"Lastools version 1.0.1 64-bit"),并且需要翻译(localised)。
    +    """
    +    return self.tr('Your plugin')
    +
    +    def icon(self):
    +    """应该返回用于处理工具箱中提供者的QIcon。
    +    """
    +    return QgsProcessingProvider.icon(self) 
    +
    +
      +
    • example_processing_algorithm.py,其中包含示例算法文件。复制/粘贴模板文件中的内容,并根据需要更新它。
    • +
    +
  6. +
  7. +

    现在,你可以在QGIS中重新加载插件,你应该可以在处理工具箱和模型中看到你的示例脚本。

    +
  8. +
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/18-\344\275\277\347\224\250\346\217\222\344\273\266\345\233\276\345\261\202/index.html" "b/18-\344\275\277\347\224\250\346\217\222\344\273\266\345\233\276\345\261\202/index.html" new file mode 100644 index 0000000..cb11454 --- /dev/null +++ "b/18-\344\275\277\347\224\250\346\217\222\344\273\266\345\233\276\345\261\202/index.html" @@ -0,0 +1,1012 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 18-使用插件图层 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节代码片段需导入以下模块:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
from qgis.core import (
+    QgsPluginLayer,
+    QgsPluginLayerType,
+    QgsMapLayerRenderer,
+    QgsApplication,
+    QgsProject,
+)
+
+from qgis.PyQt.QtGui import QImage
+
+

18 使用插件图层⚓︎

+

如果你的插件使用自己的方法渲染图层,基于QgsPluginLayer编写你自己的图层类型是最有效的方式。

+

18.1 子类化QgsPluginLayer⚓︎

+

下面例子是QgsPluginLayer的最小实现。它是基于Watermark example plugin的原版实现。

+

自定义渲染器是实现画布中实际图形工具的一部分。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
class WatermarkLayerRenderer(QgsMapLayerRenderer):
+
+    def __init__(self, layerId, rendererContext):
+        super().__init__(layerId, rendererContext)
+
+    def render(self):
+        image = QImage("/usr/share/icons/hicolor/128x128/apps/qgis.png")
+        painter = self.renderContext().painter()
+        painter.save()
+        painter.drawImage(10, 10, image)
+        painter.restore()
+        return True
+
+
+class WatermarkPluginLayer(QgsPluginLayer):
+    LAYER_TYPE = "watermark"
+
+    def __init__(self):
+        super().__init__(WatermarkPluginLayer.LAYER_TYPE, "Watermark plugin layer")
+        self.setValid(True)
+
+    def createMapRenderer(self, rendererContext):
+        return WatermarkLayerRenderer(self.id(), rendererContext)
+
+    def setTransformContext(self, ct):
+        pass
+
+    # 可以添加读写项目特定信息的方法
+
+    def readXml(self, node, context):
+        pass
+
+    def writeXml(self, node, doc, context):
+        pass
+
+

可以将插件图层添加到项目中并作为任何其它图层添加到画布中:

+
1
+2
plugin_layer = WatermarkPluginLayer()
+QgsProject.instance().addMapLayer(plugin_layer)
+
+

加载包含此类图层的项目时,需要工厂类:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
class WatermarkPluginLayerType(QgsPluginLayerType):
+
+    def __init__(self):
+        super().__init__(WatermarkPluginLayer.LAYER_TYPE)
+
+    def createLayer(self):
+        return WatermarkPluginLayer()
+
+    # 你可以添加GUI代码来自定义显示图层的属性信息
+    def showLayerProperties(self, layer):
+        pass
+
+
+# 保持引用该实例,防止被垃圾回收
+plt = WatermarkPluginLayerType()
+
+assert QgsApplication.pluginLayerRegistry().addPluginLayerType(plt)
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/19-\347\275\221\347\273\234\345\210\206\346\236\220\345\272\223/index.html" "b/19-\347\275\221\347\273\234\345\210\206\346\236\220\345\272\223/index.html" new file mode 100644 index 0000000..d88527f --- /dev/null +++ "b/19-\347\275\221\347\273\234\345\210\206\346\236\220\345\272\223/index.html" @@ -0,0 +1,1519 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 19-网络分析库 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节代码片段需导入以下模块:

+
1
+2
+3
+4
from qgis.core import (
+  QgsVectorLayer,
+  QgsPointXY,
+)
+
+

19 网络分析库⚓︎

+

网络分析库可用于:

+
    +
  • 从地理数据(矢量线折线图层)创建图
  • +
  • 实现图论中的基本算法(目前只有Dijkstra的算法)
  • +
+

网络分析库是通过从RoadGraph核心插件导出基本函数创建的,现在你可以在插件中使用它的方法,也可以直接从Python控制台使用它。

+

19.1 一般信息⚓︎

+

简而言之,一个典型用例可以描述为:

+
    +
  • 从地理数据创建图(矢量折线图层)
  • +
  • 运行图算法
  • +
  • 使用分析结果
  • +
+

19.2 构建一个图⚓︎

+

你需要做的第一件事是准备输入数据,也就是将矢量图层转换为图。所有进一步的操作都将使用这个图,而不是图层。

+

作为数据源,我们可以使用任何折线矢量图层。折线的节点成为图的顶点,折线的线段成为图的边。如果几个节点具有相同的坐标,那么它们就是相同的图顶点。因此,具有公共节点的两条线就会相互连接。

+

此外,在图创建过程中,可以将任意数量的附加点“固定”(“绑”)到输入矢量图层。对于每个附加点,将找到一个匹配——最近的顶点或最近的边。在后一种情况下,边将被分割并添加一个新顶点。

+

矢量图层属性和边的长度可以用作边的属性。

+

使用Builder编程模式完成从矢量图层到图的转换。图是使用所谓的控制器构造的。目前只有一个控制器:QgsVectorLayerDirector。控制器设置了基本的设置——这些设置将用于从线矢量图层构造图,构建器用来创建图。目前,控制器一样,只有一个构建器存在:QgsGraphBuilder,它可以创建QgsGraph对象。你可能希望实现自己的构建器,以建立一个与BGLNetworkX等库兼容的图。

+

为了计算边属性,使用编程模式策略。目前只有QGSNetworkDistanceTreatgy策略(考虑到路线的长度)和QgsNetworkSpeedStrategy(也考虑到速度)可用。你可以实现自己的策略,使用所有必要的参数。例如,RoadGraph插件使用的策略是使用边长度和属性中的速度值来计算行程时间。

+

是时候深入研究这个过程了。

+

首先,要使用这个库,我们应该导入分析模块

+
1
from qgis.analysis import *
+
+

然后是一些创建控制器的示例:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
# 不要使用图层属性中有关道路方向的信息,所有道路都是双向的
+director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth)
+
+# 使用第5个字段作为道路的方向信息.
+# 正向单方向道路使用 "yes",
+# 反向单方向道路使用 "1"
+# 因此,双向道路使用 “no”。默认情况道路是双向路。
+# 此方案可用于OpenStreetMap数据
+director = QgsVectorLayerDirector(vectorLayer, 5, 'yes', '1', 'no', QgsVectorLayerDirector.DirectionBoth)
+
+

为了构造一个控制器,我们应该传递一个矢量图层,该矢量图层作为图结构的源,以及关于每个路段上允许移动的信息(单向或双向移动、直接或反向),像这样:

+
1
+2
+3
+4
+5
+6
director = QgsVectorLayerDirector(vectorLayer,
+                                  directionFieldId,
+                                  directDirectionValue,
+                                  reverseDirectionValue,
+                                  bothDirectionValue,
+                                  defaultDirection)
+
+

以下是这些参数的全部含义:

+
    +
  • vectorLayer——用于构建图的矢量图层
  • +
  • directionFieldId——字段的索引值,用于存储道路方向的信息。如果是-1,表示不使用这些信息。整型。
  • +
  • directDirectionValue——正向的字段值(从第一个直线点移动到最后一个直线点)。字符串。
  • +
  • reverseDirectionValue——反向道路的字段值(从最后一个直线点移动到第一个直线点)。字符串。
  • +
  • bothDirectionValue——双向道路的字段值(对于这样的道路,我们可以从第一点移动到最后一点,也可以从最后一点移动到第一点)。字符串。
  • +
  • defaultDirectio——默认道路方向。该值将用于这些道路当字段directionFieldId未设置时或具有与上面指定的三个值中的任何一个不同的值。可用的值是:
  • +
  • QgsVectorLayerDirector.DirectionForward——正向单向道路
  • +
  • QgsVectorLayerDirector.DirectionBackward——反向单向道路
  • +
  • QgsVectorLayerDirector.DirectionBoth双向道路
  • +
+

然后有必要创建用于计算边属性的策略:

+
1
+2
+3
+4
+5
+6
+7
# 包含速度信息的字段索引值
+attributeId = 1
+# 默认速度
+defaultValue = 50
+# 转化速度到米制单位 ('1' 表示不转化)
+toMetricFactor = 1
+strategy = QgsNetworkSpeedStrategy(attributeId, defaultValue, toMetricFactor)
+
+

告诉控制器这个策略

+
1
+2
director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', 3)
+director.addStrategy(strategy)
+
+

现在我们可以使用构造器来创建图。QgsGraphBuilder类构造函数接受几个参数:

+
    +
  • crs——坐标参考系统。必需。
  • +
  • otfEnabled——使用“on the fly” 重投影. 默认为True (use OTF).
  • +
  • topologyTolerance——拓扑容差。默认为0。
  • +
  • ellipsoidID——参考椭球。默认为“WGS84”。
  • +
+
1
+2
# 只设置CRS,,其它值默认
+builder = QgsGraphBuilder(vectorLayer.crs())
+
+

我们还可以定义几个点,这些点将用于分析。例如:

+
1
+2
startPoint = QgsPointXY(1179720.1871, 5419067.3507)
+endPoint = QgsPointXY(1180616.0205, 5419745.7839)
+
+

现在一切就绪,我们可以构建图表并将这些点“连接”到它

+
1
tiedPoints = director.makeGraph(builder, [startPoint, endPoint])
+
+

构建图可能需要一些时间(这取决于图层中的要素数量和图层大小)。tiedPoints是一个包含“绑定”点坐标的列表。当构建操作完成时,我们可以得到图并将其用于分析

+
1
graph = builder.graph()
+
+

通过下面的代码,我们可以得到点的顶点索引

+
1
+2
startId = graph.findVertex(tiedPoints[0])
+endId = graph.findVertex(tiedPoints[1])
+
+

19.3 图分析⚓︎

+

网络分析用于找到两个问题的答案:哪些顶点是相连的,以及如何找到最短路径。为了解决这些问题,网络分析库提供了Dijkstra算法。

+

Dijkstra算法找到从图的一个顶点到所有其他顶点的最短路径以及优化参数的值。结果可以表示为最短路径树。

+

最短路径树是一个有向加权图(或更准确地说是一棵树),具有以下特性:

+
    +
  • 只有一个顶点没有入射边——树的根
  • +
  • 所有其他顶点只有一条入射边
  • +
  • 如果顶点B可以从顶点A到达,那么从A到B的路径是唯一可用的路径,并且它在该图上是最优的(最短的)路径
  • +
+

要获得最短路径树,可以使用QgsGraphAnalyzer类的shortestTree()dijkstra()方法。建议使用dijkstra()方法,因为它更快,而且更有效地使用内存。

+

shortestTree()方法在你想在最短路径树上行走时很有用。它总是创建一个新的图对象(QgsGraph)并接受三个变量:

+
    +
  • source——输入的图
  • +
  • startVertexIdx——树上的点的索引(树的根)
  • +
  • criterionNum——使用的边属性的数量(从0开始)
  • +
+
1
tree = QgsGraphAnalyzer.shortestTree(graph, startId, 0)
+
+

dijkstra()方法有相同的参数,但返回两个数组。在第一个数组中,n元素包含传入边的索引,如果没有传入边则为-1。在第二个数组中,n元素包含从树的根到顶点n的距离,如果顶点n从根部无法到达,则为DOUBLE_MAX。

+
1
(tree, cost) = QgsGraphAnalyzer.dijkstra(graph, startId, 0)
+
+

下面是一些非常简单的代码,使用shortestTree()方法创建的图显示最短路径树(在Layers面板中选择linestring图层,用你自己的坐标替换)。

+
+

警告

+

这段代码仅作为一个例子,它创建了大量的QgsRubberBand对象,在大数据集上可能会很慢。

+
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
from qgis.core import *
+from qgis.gui import *
+from qgis.analysis import *
+from qgis.PyQt.QtCore import *
+from qgis.PyQt.QtGui import *
+
+vectorLayer = QgsVectorLayer('testdata/network.gpkg|layername=network_lines', 'lines')
+director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth)
+strategy = QgsNetworkDistanceStrategy()
+director.addStrategy(strategy)
+builder = QgsGraphBuilder(vectorLayer.crs())
+
+pStart = QgsPointXY(1179661.925139,5419188.074362)
+tiedPoint = director.makeGraph(builder, [pStart])
+pStart = tiedPoint[0]
+
+graph = builder.graph()
+
+idStart = graph.findVertex(pStart)
+
+tree = QgsGraphAnalyzer.shortestTree(graph, idStart, 0)
+
+i = 0
+while (i < tree.edgeCount()):
+  rb = QgsRubberBand(iface.mapCanvas())
+  rb.setColor (Qt.red)
+  rb.addPoint (tree.vertex(tree.edge(i).fromVertex()).point())
+  rb.addPoint (tree.vertex(tree.edge(i).toVertex()).point())
+  i = i + 1
+
+

使用dijkstra()方法

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
from qgis.core import *
+from qgis.gui import *
+from qgis.analysis import *
+from qgis.PyQt.QtCore import *
+from qgis.PyQt.QtGui import *
+
+vectorLayer = QgsVectorLayer('testdata/network.gpkg|layername=network_lines', 'lines')
+
+director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth)
+strategy = QgsNetworkDistanceStrategy()
+director.addStrategy(strategy)
+builder = QgsGraphBuilder(vectorLayer.crs())
+
+pStart = QgsPointXY(1179661.925139,5419188.074362)
+tiedPoint = director.makeGraph(builder, [pStart])
+pStart = tiedPoint[0]
+
+graph = builder.graph()
+
+idStart = graph.findVertex(pStart)
+
+(tree, costs) = QgsGraphAnalyzer.dijkstra(graph, idStart, 0)
+
+for edgeId in tree:
+  if edgeId == -1:
+    continue
+  rb = QgsRubberBand(iface.mapCanvas())
+  rb.setColor (Qt.red)
+  rb.addPoint (graph.vertex(graph.edge(edgeId).fromVertex()).point())
+  rb.addPoint (graph.vertex(graph.edge(edgeId).toVertex()).point())
+
+

19.3.1 查找最短路径⚓︎

+

为了找到两点之间的最佳路径,采用了以下方法。两点(起点A和终点B)在图建立时都被 "捆绑 "在一起。然后使用shortestTree()dijkstra()方法,我们建立以起点A为根的最短路径树。在同一棵树上,我们也找到了终点B,并开始从B点走到A点,整个算法可以写成这样:

+
1
+2
+3
+4
+5
+6
+7
assign T = B
+while T != B
+    add point T to path
+    get incoming edge for point T
+    look for point TT, that is start point of this edge
+    assign T = TT
+add point A to path
+
+

在这一点上,我们有一个路径,其形式是顶点的倒置列表(顶点是按照从终点到起点的相反顺序排列的),这些顶点将在这条路径的行程中被访问。

+

以下是使用shortestTree()方法的QGIS Python控制台示例代码(你可能需要在图层目录树中中加载并选择一个线图层,并将代码中的坐标替换为你的坐标)。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
from qgis.core import *
+from qgis.gui import *
+from qgis.analysis import *
+
+from qgis.PyQt.QtCore import *
+from qgis.PyQt.QtGui import *
+
+vectorLayer = QgsVectorLayer('testdata/network.gpkg|layername=network_lines', 'lines')
+builder = QgsGraphBuilder(vectorLayer.sourceCrs())
+director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth)
+
+startPoint = QgsPointXY(1179661.925139,5419188.074362)
+endPoint = QgsPointXY(1180942.970617,5420040.097560)
+
+tiedPoints = director.makeGraph(builder, [startPoint, endPoint])
+tStart, tStop = tiedPoints
+
+graph = builder.graph()
+idxStart = graph.findVertex(tStart)
+
+tree = QgsGraphAnalyzer.shortestTree(graph, idxStart, 0)
+
+idxStart = tree.findVertex(tStart)
+idxEnd = tree.findVertex(tStop)
+
+if idxEnd == -1:
+    raise Exception('No route!')
+
+# 添加最后一个点
+route = [tree.vertex(idxEnd).point()]
+
+# 遍历图
+while idxEnd != idxStart:
+    edgeIds = tree.vertex(idxEnd).incomingEdges()
+    if len(edgeIds) == 0:
+        break
+    edge = tree.edge(edgeIds[0])
+    route.insert(0, tree.vertex(edge.fromVertex()).point())
+    idxEnd = edge.fromVertex()
+
+# 显示
+rb = QgsRubberBand(iface.mapCanvas())
+rb.setColor(Qt.green)
+
+# 如果项目的坐标系和图层的坐标系不一样,则需要坐标转换
+for p in route:
+    rb.addPoint(p)
+
+

使用dijkstra()方法

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
from qgis.core import *
+from qgis.gui import *
+from qgis.analysis import *
+
+from qgis.PyQt.QtCore import *
+from qgis.PyQt.QtGui import *
+
+vectorLayer = QgsVectorLayer('testdata/network.gpkg|layername=network_lines', 'lines')
+director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth)
+strategy = QgsNetworkDistanceStrategy()
+director.addStrategy(strategy)
+
+builder = QgsGraphBuilder(vectorLayer.sourceCrs())
+
+startPoint = QgsPointXY(1179661.925139,5419188.074362)
+endPoint = QgsPointXY(1180942.970617,5420040.097560)
+
+tiedPoints = director.makeGraph(builder, [startPoint, endPoint])
+tStart, tStop = tiedPoints
+
+graph = builder.graph()
+idxStart = graph.findVertex(tStart)
+idxEnd = graph.findVertex(tStop)
+
+(tree, costs) = QgsGraphAnalyzer.dijkstra(graph, idxStart, 0)
+
+if tree[idxEnd] == -1:
+    raise Exception('No route!')
+
+# Total cost
+cost = costs[idxEnd]
+
+# 添加最后一个点
+route = [graph.vertex(idxEnd).point()]
+
+# 遍历图
+while idxEnd != idxStart:
+    idxEnd = graph.edge(tree[idxEnd]).fromVertex()
+    route.insert(0, graph.vertex(idxEnd).point())
+
+# 显示
+rb = QgsRubberBand(iface.mapCanvas())
+rb.setColor(Qt.red)
+
+# 如果项目的坐标系和图层的坐标系不一样,则需要坐标转换
+for p in route:
+    rb.addPoint(p)
+
+

19.3.2 可达区域⚓︎

+

顶点A的可达区域是指可以从顶点A进入的图形顶点的子集,并且从A到这些顶点的路径成本不超过某个值。

+

这一点可以通过以下例子更清楚地表明。"有一个消防站,消防车可以在5分钟内到达城市的哪些地方?10分钟?15分钟?"。这些问题的答案就是消防站的可达区域。

+

为了找到可达的区域,我们可以使用QgsGraphAnalyzer类的dijkstra()方法。只需将cost数组的元素与预定义的值进行比较。如果cost[i]小于或等于一个预定义的值,那么顶点i就在可达区域内,否则它就在外面。

+

一个更困难的问题是要得到可达区域的边界。底部边界是仍可访问的顶点集合,而顶部边界是不可访问的顶点集合。事实上这很简单:它是基于最短路径树的边的可达性边界,对于这些边的源顶点是可访问的,而边的目标顶点则不是。

+

下面是一个例子:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
director = QgsVectorLayerDirector(vectorLayer, -1, '', '', '', QgsVectorLayerDirector.DirectionBoth)
+strategy = QgsNetworkDistanceStrategy()
+director.addStrategy(strategy)
+builder = QgsGraphBuilder(vectorLayer.crs())
+
+
+pStart = QgsPointXY(1179661.925139, 5419188.074362)
+delta = iface.mapCanvas().getCoordinateTransform().mapUnitsPerPixel() * 1
+
+rb = QgsRubberBand(iface.mapCanvas())
+rb.setColor(Qt.green)
+rb.addPoint(QgsPointXY(pStart.x() - delta, pStart.y() - delta))
+rb.addPoint(QgsPointXY(pStart.x() + delta, pStart.y() - delta))
+rb.addPoint(QgsPointXY(pStart.x() + delta, pStart.y() + delta))
+rb.addPoint(QgsPointXY(pStart.x() - delta, pStart.y() + delta))
+
+tiedPoints = director.makeGraph(builder, [pStart])
+graph = builder.graph()
+tStart = tiedPoints[0]
+
+idStart = graph.findVertex(tStart)
+
+(tree, cost) = QgsGraphAnalyzer.dijkstra(graph, idStart, 0)
+
+upperBound = []
+r = 1500.0
+i = 0
+tree.reverse()
+
+while i < len(cost):
+    if cost[i] > r and tree[i] != -1:
+        outVertexId = graph.edge(tree [i]).toVertex()
+        if cost[outVertexId] < r:
+            upperBound.append(i)
+    i = i + 1
+
+for i in upperBound:
+    centerPoint = graph.vertex(i).point()
+    rb = QgsRubberBand(iface.mapCanvas())
+    rb.setColor(Qt.red)
+    rb.addPoint(QgsPointXY(centerPoint.x() - delta, centerPoint.y() - delta))
+    rb.addPoint(QgsPointXY(centerPoint.x() + delta, centerPoint.y() - delta))
+    rb.addPoint(QgsPointXY(centerPoint.x() + delta, centerPoint.y() + delta))
+    rb.addPoint(QgsPointXY(centerPoint.x() - delta, centerPoint.y() + delta))
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/2-\345\212\240\350\275\275\351\241\271\347\233\256/index.html" "b/2-\345\212\240\350\275\275\351\241\271\347\233\256/index.html" new file mode 100644 index 0000000..2353e9e --- /dev/null +++ "b/2-\345\212\240\350\275\275\351\241\271\347\233\256/index.html" @@ -0,0 +1,1024 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2-加载项目 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节的代码片段需要导入以下模块:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
from qgis.core import (
+    Qgis,
+    QgsProject,
+    QgsPathResolver
+)
+
+from qgis.gui import (
+    QgsLayerTreeMapCanvasBridge,
+)
+
+

2 加载项目⚓︎

+

有时你需要从插件加载现有项目,或者(更常见)在开发独立的QGIS Python应用程序时加载(请参阅:Python应用程序)。

+

将项目加载到当前QGIS应用程序中,需要创建QgsProject类的实例。这是一个单例类,因此你必须使用其instance()方法来执行此操作。你可以调用read()方法,传递加载项目的路径:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
# 如果你不在QGIS控制台内运行,首先需要导入qgis和PyQt类,如下所示:
+from qgis.core import QgsProject
+# 获取项目实例
+project = QgsProject.instance()
+# 打印当前项目的文件名(可能为空,因为没有项目加载)
+# print(project.fileName())
+
+# 加载另一个项目
+project.read('testdata/01_project.qgs')
+print(project.fileName())
+# testdata/01_project.qgs
+
+

如果你需要对项目进行修改(例如添加或删除某些图层)并保存更改,调用write()方法。该方法还支持将项目保存到新的位置:

+
1
+2
+3
+4
# 将项目保存到同一个文件
+project.write()
+# ...或新文件
+project.write('testdata/my_new_qgis_project.qgs')
+
+

read()write()函数都返回一个布尔值,你可以使用它来检查操作是否成功。

+
+

提示

+

如果你正在编写QGIS独立应用程序,为了将加载的项目与画布同步,你需要实例化QgsLayerTreeMapCanvasBridge,如下所示:

+
1
+2
+3
bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), canvas)
+# 现在你可以安全地加载项目,并在画布上看到它
+project.read('testdata/my_new_qgis_project.qgs')
+
+
+

2.1 解决错误路径⚓︎

+

在项目中加载的图层可能被移动到另一个位置。当项目再次加载时,所有的图层路径都被破坏。QgsPathResolver类帮助你在项目中重写图层路径。

+

setPathPreprocessor()方法允许设置一个自定义的路径预处理函数,它允许在将路径和数据源解析为文件引用或图层源之前对它们进行操作。

+

该处理函数必须接受一个单一的字符串参数(代表原始文件路径或数据源),并返回该路径的处理版本。

+

路径预处理器函数在任何错误图层处理程序之前被调用。如果设置了多个预处理器,将根据最初设置的顺序依次调用它们。

+

一些应用案例:

+
    +
  1. 替换过时的路径:
  2. +
+
1
+2
+3
+4
def my_processor(path):
+    return path.replace('c:/Users/ClintBarton/Documents/Projects', 'x:/Projects/')
+
+QgsPathResolver.setPathPreprocessor(my_processor)
+
+
    +
  1. 用一个新的数据库主机地址来替换:
  2. +
+
1
+2
+3
+4
def my_processor(path):
+    return path.replace('host=10.1.1.115', 'host=10.1.1.116')
+
+QgsPathResolver.setPathPreprocessor(my_processor)
+
+
    +
  1. 替换新的数据库证书:
  2. +
+
1
+2
+3
+4
+5
+6
def my_processor(path):
+    path= path.replace("user='gis_team'", "user='team_awesome'")
+    path = path.replace("password='cats'", "password='g7as!m*'")
+    return path
+
+QgsPathResolver.setPathPreprocessor(my_processor)
+
+

同样,路径编写器函数也可以使用setPathWriter()方法。

+

使用变量替换路径的示例:

+
1
+2
+3
+4
def my_processor(path):
+  return path.replace('c:/Users/ClintBarton/Documents/Projects', '$projectdir$')
+
+QgsPathResolver.setPathWriter(my_processor)
+
+

这两种方法都返回一个id,可用于删除它们添加的预处理器或写入程序。查看removePathPreprocessor()removePathWriter()

+

2.2 使用标识符⚓︎

+

在某些情况下,你可能不需要使用功能齐全的项目,而只是出于特定原因想要访问它,标识可能会有所帮助。完整的标识列表可在ProjectReadFlag下找到。可以将多个标志添加在一起。

+

例如,如果我们不关心实际的图层和数据,而只是想访问项目(例如布局或3D视图设置),则可以使用DontResolveLayers标识绕过数据验证步骤并防止出现无效图层对话框。可以执行以下操作:

+
1
+2
+3
+4
readflags = Qgis.ProjectReadFlags()
+readflags |= Qgis.ProjectReadFlag.DontResolveLayers
+project = QgsProject()
+project.read('C:/Users/ClintBarton/Documents/Projects/mysweetproject.qgs', readflags)
+
+

添加更多标志,必须使用 python 按位或运算符 (|)。

+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/20-QGIS\346\234\215\345\212\241\345\231\250\345\222\214Python/index.html" "b/20-QGIS\346\234\215\345\212\241\345\231\250\345\222\214Python/index.html" new file mode 100644 index 0000000..42a633a --- /dev/null +++ "b/20-QGIS\346\234\215\345\212\241\345\231\250\345\222\214Python/index.html" @@ -0,0 +1,2098 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 20-QGIS服务器和Python - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +

20-QGIS服务器和Python⚓︎

+

20.1 介绍⚓︎

+

要了解有关QGIS服务器的更多信息,请阅读QGIS服务器指南/手册

+

QGIS服务器是三个不同的东西:

+
    +
  1. QGIS服务器库:一个为创建OGC网络服务提供API的库
  2. +
  3. QGIS服务器FCGI:一个FCGI二进制应用程序qgis_mapserv.fcgi,与网络服务器一起实现一套OGC服务(WMS、WFS、WCS等)和OGC APIs(WFS3/OAPIF)。
  4. +
  5. QGIS开发服务器:一个开发服务器二进制应用程序qgis_mapserver,实现了一套OGC服务(WMS、WFS、WCS等)和OGC APIs(WFS3/OAPIF)。
  6. +
+

本章的重点是第一个话题,通过解释QGIS服务器API的用法来说明如何使用Python来扩展、增强或定制服务器行为,或如何使用QGIS服务器API将QGIS服务器嵌入到另一个应用程序。

+

你可以通过一些不同的方式来改变QGIS服务器的行为或扩展其功能,以提供新的定制服务或API,一下是你可能面临的主要情况:

+
    +
  • 融合 → 从另一个Python应用程序中使用QGIS服务器API
  • +
  • 独立 → 以独立的WSGI/HTTP服务方式运行QGIS服务器
  • +
  • 过滤 → 使用过滤插件增强/定制QGIS服务器
  • +
  • 服务 → 添加一个新的服务
  • +
  • OGC APIs → 添加一个新的OGC API
  • +
+

嵌入和独立的应用程序需要直接从另一个Python脚本或应用程序中使用QGIS服务器的Python API。其余的选项更适合于当你想在标准的QGIS服务器二进制应用程序(FCGI或开发服务器)中添加自定义功能时:在这种情况下,你需要为服务器应用程序编写一个Python插件,并注册你的自定义过滤器、服务或API。

+

20.2 服务器API基础⚓︎

+

一个典型的QGIS服务器应用程序所涉及的基本类是:

+ +

QGIS服务器FCGI或开发服务器的工作流程可以概括为以下几点:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
initialize the QgsApplication
+create the QgsServer
+the main server loop waits forever for client requests:
+    for each incoming request:
+        create a QgsServerRequest request
+        create a QgsServerResponse response
+        call QgsServer.handleRequest(request, response)
+            filter plugins may be executed
+        send the output to the client
+
+

QgsServer.handleRequest(request, response)方法中,过滤器插件的回调函数被调用,QgsServerRequestQgsServerResponse通过QgsServerInterface类被提供给插件。

+
+

警告

+

QGIS服务器类不是线程安全的,在构建基于QGIS服务器API的可扩展应用程序时,你应该始终使用多进程模型或容器。

+
+

20.3 独立或嵌入⚓︎

+

对于独立的服务器应用或嵌入,你需要直接使用上述的服务器类,将它们包装成一个Web服务器实现,管理所有与客户端的HTTP协议交互。

+

这里有一个关于QGIS服务器API应用的最小例子(没有HTTP部分):

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
from qgis.core import QgsApplication
+from qgis.server import *
+app = QgsApplication([], False)
+
+# 创建服务器实例,它可能是一个单一的实例,在多个请求中重复使用
+server = QgsServer()
+
+# 通过指定完整的URL和一个可选的主体来创建请求(例如POST请求)
+request = QgsBufferServerRequest(
+    'http://localhost:8081/?MAP=/qgis-server/projects/helloworld.qgs' +
+    '&SERVICE=WMS&REQUEST=GetCapabilities')
+
+# 创建响应对象
+response = QgsBufferServerResponse()
+
+# 处理请求
+server.handleRequest(request, response)
+
+print(response.headers())
+print(response.body().data().decode('utf8'))
+
+app.exitQgis()
+
+

这里有一个完整的独立应用实例,它是为QGIS源代码库的持续集成测试而开发的,它展示了一系列不同的插件过滤器和认证方案(不意味着可用于生产环境,因为它们只是为测试目的而开发的,但对于学习来说仍然很有趣):https://github.com/qgis/QGIS/blob/master/tests/src/python/qgis_wrapped_server.py

+

20.4 服务器插件⚓︎

+

服务器python插件在QGIS服务器应用程序启动时被加载一次,可用于注册过滤器、服务或API。

+

服务器插件的结构与桌面版的插件非常相似,一个QgsServerInterface对象被提供给插件,插件可以通过使用服务器接口暴露的方法将一个或多个自定义过滤器、服务或API注册到相应的注册表。

+

20.4.1 服务器过滤插件⚓︎

+

过滤器有三种不同的类型,它们可以通过子类化下面的一个类并调用QgsServerInterface的相应方法来实例化。

+ + + + + + + + + + + + + + + + + + + + + + + + + +
过滤器类型基类QgsServerInterface 注册
I/OQgsServerFilterregisterFilter()
Access ControlQgsAccessControlFilterregisterAccessControl()
CacheQgsServerCacheFilterregisterServerCache()
+

20.4.1.1 I/O过滤器⚓︎

+

I/O过滤器可以修改核心服务(WMS、WFS等)的服务器输入和输出(请求和响应),允许对服务工作流进行任何形式的操作。例如,可以限制对选定图层的访问,向XML响应注入XSL样式表,向生成的WMS图像添加水印等等。

+

从这一点来看,你可能会发现快速浏览一下服务器插件的API文档很有用。

+

每一个插件应该至少实现以下三个回调函数:

+ +

所有的过滤器都可以访问请求/响应对象(QgsRequestHandler),并且可以操作它的所有属性(输入/输出)和引发异常(同时以一种相当特别的方式,我们将在下面看到)。

+

所有这些方法都返回一个布尔值,指示调用是否应传播到后续过滤器。如果其中一个方法返回False,则传播链停止,否则调用将传播到下一个过滤器。

+

下面是显示服务器如何处理一个典型请求以及何时调用过滤器回调函数的伪代码。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
for each incoming request:
+    create GET/POST request handler
+    pass request to an instance of QgsServerInterface
+    call onRequestReady filters
+
+    if there is not a response:
+        if SERVICE is WMS/WFS/WCS:
+            create WMS/WFS/WCS service
+            call service’s executeRequest
+                possibly call onSendResponse for each chunk of bytes
+                sent to the client by a streaming services (WFS)
+        call onResponseComplete
+    request handler sends the response to the client
+
+

下面几个段落详细描述了可用的回调函数。

+
20.4.1.1.1 请求就绪⚓︎
+

当请求准备就绪时,将被调用:传入的URL和数据已经被解析,在进入核心服务(WMS,WFS等)开关之前,这是你可以操作输入和执行动作的地方:

+
    +
  • 认证授权
  • +
  • 重定向
  • +
  • 添加删除某些参数 (例如类型名称)
  • +
  • 抛出异常
  • +
+

你甚至可以通过改变 SERVICE 参数来完全替代一个核心服务,从而完全绕过核心服务(不过这并没有什么意义)。

+
20.4.1.1.2 发送响应⚓︎
+

无论何时从响应缓冲区刷新任何部分输出,都会调用该函数(例如 FCGI 标准输出被使用),并从缓冲区刷新到客户端。当大量内容被流式传输(比如WFS GetFeature)时,就会发生这种情况。在这种情况下,onSendResponse()可能会被多次调用。

+

请注意,如果响应没有流式传输,则根本不会调用onSendResponse()

+

在所有情况下,调用onResponseComplete()后,最后一个(或唯一的)块将被发送到客户端。

+

返回False将防止向客户端刷新数据。当插件希望从响应中收集所有块,并在onResponseComplete()中检查或更改响应时,这是可取的。

+
20.4.1.1.3 响应完成⚓︎
+

当核心服务(如果被击中的话)完成它们的过程,并且请求准备好被发送到客户端时,将被调用一次。如上所述,通常在onSendResponse()之前调用,除了流媒体服务(或其他插件过滤器)可能在之前调用sendResponse()

+

onResponseComplete()是提供新服务实现(WPS或自定义服务)和对来自核心服务的输出进行直接操作的理想场所(例如,在WMS图像上添加水印)。

+

请注意,返回False将阻止下一个插件执行onResponseComplete(),但在任何情况下,都会阻止将响应发送到客户端。

+

20.4.1.2 从插件引发异常⚓︎

+

在这个问题上还有一些工作要做:目前的实现可以通过将QgsRequestHandler属性设置为QgsMapServiceException的一个实例来区分已处理和未处理的异常,这样,主要的C++代码可以捕获已处理的Python异常而忽略未处理的异常(或者更好的是:记录日志)。

+

这种方法基本上是可行的,但它不是很 "pythonic":一个更好的方法是在python代码中引发异常,并看到它们进入到C++循环中被处理。

+

20.4.1.3 编写一个服务器插件⚓︎

+

服务器插件是一个标准的 QGIS Python 插件,如16-开发Python插件中所述,它只是提供了一个额外的(或替代的)接口:典型的 QGIS 桌面插件通过 QgisInterface实例访问 QGIS 应用程序,而服务器插件只有在 QGIS Server 应用程序上下文中执行时才能访问。

+

为了让QGIS Server知道一个插件有一个服务器接口,需要一个特殊的元数据条目(在metadata.txt中)。

+
1
server=True
+
+
+

重要的

+

只有设置了server=True元数据的插件才能被QGIS Server加载和执行。

+
+

这里讨论的 qgis3-server-vagrant 示例插件(以及更多插件)可在 Github 上找到,一些服务器插件也发布在官方的 QGIS 插件仓库中。

+

20.4.1.4 插件文件⚓︎

+

下面是我们的示例服务器插件的目录结构。

+
1
+2
+3
+4
+5
PYTHON_PLUGINS_PATH/
+  HelloServer/
+    __init__.py    --> *required*
+    HelloServer.py  --> *required*
+    metadata.txt   --> *required*
+
+
20.4.1.4.1 __init__.py⚓︎
+

这个文件是Python的导入系统所要求的。此外,QGIS Server要求该文件包含一个serverClassFactory()函数,当服务器启动时,插件被加载到QGIS Server中时,该函数将被调用。它接收对QgsServerInterface实例的引用,并必须返回你的插件类的实例。以下是插件示例 __init__.py的样子:

+
1
+2
+3
def serverClassFactory(serverIface):
+    from .HelloServer import HelloServerServer
+    return HelloServerServer(serverIface)
+
+
20.4.1.4.2 HelloServer.py⚓︎
+

这就是魔法发生的地方,这就是魔法的模样。(例如:HelloServer.py)

+

一个服务器插件通常由一个或多个回调函数组成,被打包到QgsServerFilter的实例中。

+

每个QgsServerFilter都实现了一个或多个以下的回调函数:

+ +

下面的例子实现了一个最小的过滤器,当 SERVICE 参数等于 " HELLO "时,打印出HelloServer!

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
class HelloFilter(QgsServerFilter):
+
+    def __init__(self, serverIface):
+        super().__init__(serverIface)
+
+    def onRequestReady(self) -> bool:
+        QgsMessageLog.logMessage("HelloFilter.onRequestReady")
+        return True
+
+    def onSendResponse(self) -> bool:
+        QgsMessageLog.logMessage("HelloFilter.onSendResponse")
+        return True
+
+    def onResponseComplete(self) -> bool:
+        QgsMessageLog.logMessage("HelloFilter.onResponseComplete")
+        request = self.serverInterface().requestHandler()
+        params = request.parameterMap()
+        if params.get('SERVICE', '').upper() == 'HELLO':
+            request.clear()
+            request.setResponseHeader('Content-type', 'text/plain')
+            # 注意内容类型是"bytes"
+            request.appendBody(b'HelloServer!')
+        return True
+
+

过滤器必须被注册到 serverIface 中,如下例所示:

+
1
+2
+3
class HelloServerServer:
+    def __init__(self, serverIface):
+        serverIface.registerFilter(HelloFilter(serverIface), 100)
+
+

registerFilter()的第二个参数设置了一个优先级,定义了同名回调函数的顺序(优先级低的回调先被调用)。

+

通过使用这三个回调函数,插件可以以许多不同的方式操纵服务器的输入输出。在每个时刻,插件实例都可以通过QgsServerInterface访问QgsRequestHandlerQgsRequestHandler类有很多方法,可以用来在进入服务器的核心处理之前(通过使用requestReady())或在请求被核心服务处理之后(通过使用sendResponse())改变输入参数。

+

下面的例子涵盖了一些常见的使用案例。

+
20.4.1.4.3 修改输入⚓︎
+

示例插件包含一个改变来自查询字符串的输入参数的测试例子,在这个例子中,一个新的参数被注入到(已经解析过的)parameterMap中,然后这个参数被核心服务(WMS等)看到,在核心服务处理结束时,我们检查这个参数是否仍然存在:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
class ParamsFilter(QgsServerFilter):
+
+    def __init__(self, serverIface):
+        super(ParamsFilter, self).__init__(serverIface)
+
+    def onRequestReady(self) -> bool:
+        request = self.serverInterface().requestHandler()
+        params = request.parameterMap( )
+        request.setParameter('TEST_NEW_PARAM', 'ParamsFilter')
+        return True
+
+    def onResponseComplete(self) -> bool:
+        request = self.serverInterface().requestHandler()
+        params = request.parameterMap( )
+        if params.get('TEST_NEW_PARAM') == 'ParamsFilter':
+            QgsMessageLog.logMessage("SUCCESS - ParamsFilter.onResponseComplete")
+        else:
+            QgsMessageLog.logMessage("FAIL    - ParamsFilter.onResponseComplete")
+        return True
+
+

这是日志文件中内容的摘录:

+
1
+2
+3
+4
+5
+6
src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] HelloServerServer - loading filter ParamsFilter
+ src/core/qgsmessagelog.cpp: 45: (logMessage) [1ms] 2014-12-12T12:39:29 Server[0] Server plugin HelloServer loaded!
+ src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 Server[0] Server python plugins loaded
+ src/mapserver/qgshttprequesthandler.cpp: 547: (requestStringToParameterMap) [1ms] inserting pair SERVICE // HELLO into the parameter map
+ src/mapserver/qgsserverfilter.cpp: 42: (onRequestReady) [0ms] QgsServerFilter plugin default onRequestReady called
+ src/core/qgsmessagelog.cpp: 45: (logMessage) [0ms] 2014-12-12T12:39:29 plugin[0] SUCCESS - ParamsFilter.onResponseComplete
+
+

在突出显示的一行,"SUCCESS "字符串表示该插件通过了测试。

+

同样的技术可以被利用来代替核心服务:例如,你可以跳过 WFS SERVICE 请求或任何其他核心请求,只需将 SERVICE 参数改为不同的参数,核心服务就会被跳过。然后,你可以将你的自定义结果注入到输出中,并将其发送给客户端(这将在下面解释)。

+
+

提示

+

如果你真的想实现一个自定义的服务,建议将QgsService子类化,并通过调用其registerService(service)方法在registerFilter()方法上注册你的服务。

+
+
20.4.1.4.4 修改或替换输出⚓︎
+

水印过滤器的例子显示了如何用一个新的图像替换WMS的输出,该图像是通过在WMS核心服务生成的WMS图像上添加一个水印图像而获得:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
from qgis.server import *
+from qgis.PyQt.QtCore import *
+from qgis.PyQt.QtGui import *
+
+class WatermarkFilter(QgsServerFilter):
+
+    def __init__(self, serverIface):
+        super().__init__(serverIface)
+
+    def responseComplete(self):
+        request = self.serverInterface().requestHandler()
+        params = request.parameterMap( )
+        # 一些检查
+        if (params.get('SERVICE').upper() == 'WMS' \
+                and params.get('REQUEST').upper() == 'GETMAP' \
+                and not request.exceptionRaised() ):
+            QgsMessageLog.logMessage("WatermarkFilter.responseComplete: image ready %s" % request.parameter("FORMAT"))
+            # 获取图像
+            img = QImage()
+            img.loadFromData(request.body())
+            # 添加水印
+            watermark = QImage(os.path.join(os.path.dirname(__file__), 'media/watermark.png'))
+            p = QPainter(img)
+            p.drawImage(QRect( 20, 20, 40, 40), watermark)
+            p.end()
+            ba = QByteArray()
+            buffer = QBuffer(ba)
+            buffer.open(QIODevice.WriteOnly)
+            img.save(buffer, "PNG" if "png" in request.parameter("FORMAT") else "JPG")
+            # 设置数据体
+            request.clearBody()
+            request.appendBody(ba)
+
+

在这个例子中,SERVICE 参数值被检查,如果传入的请求是一个 WMS GETMAP ,并且没有被先前执行的插件或核心服务(在这个例子中是WMS)设置过异常,那么WMS生成的图像就会从输出缓冲区中被检索出来,并且添加水印图像。最后一步是清除输出缓冲区,用新生成的图像替换它。请注意,在现实世界中,我们还应该检查所要求的图像类型,而不是只支持PNG或JPG。

+

20.4.1.5 访问控制过滤器⚓︎

+

访问控制过滤器为开发者提供了对哪些层、要素和属性可以被访问的细粒度控制,以下回调函数可以在访问控制过滤器中实现:

+ +
20.4.1.5.1. 插件文件⚓︎
+

下面是我们的示例服务器插件的目录结构:

+
1
+2
+3
+4
+5
PYTHON_PLUGINS_PATH/
+  MyAccessControl/
+    __init__.py    --> *required*
+    AccessControl.py  --> *required*
+    metadata.txt   --> *required*
+
+
20.4.1.5.2 __init__.py⚓︎
+

这个文件是Python的导入系统所要求的。对于所有的QGIS服务器插件来说,这个文件包含一个serverClassFactory()函数,当插件在启动时被加载到QGIS服务器中时,它将被调用。它接收一个对QgsServerInterface实例的引用,并必须返回一个你的插件类的实例。以下是插件实例 __init__.py的样子:

+
1
+2
+3
def serverClassFactory(serverIface):
+    from MyAccessControl.AccessControl import AccessControlServer
+    return AccessControlServer(serverIface)
+
+
20.4.1.5.3. AccessControl.py⚓︎
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
class AccessControlFilter(QgsAccessControlFilter):
+
+    def __init__(self, server_iface):
+        super().__init__(server_iface)
+
+    def layerFilterExpression(self, layer):
+        """ 返回一个额外的表达式过滤器 """
+        return super().layerFilterExpression(layer)
+
+    def layerFilterSubsetString(self, layer):
+        """ 返回一个额外的子集字符串(通常是SQL)过滤器 """
+        return super().layerFilterSubsetString(layer)
+
+    def layerPermissions(self, layer):
+        """ 返回该层的权限 """
+        return super().layerPermissions(layer)
+
+    def authorizedLayerAttributes(self, layer, attributes):
+        """ 返回授权的图层属性 """
+        return super().authorizedLayerAttributes(layer, attributes)
+
+    def allowToEdit(self, layer, feature):
+        """ 我们是否被授权修改以下几何图形 """
+        return super().allowToEdit(layer, feature)
+
+    def cacheKey(self):
+        return super().cacheKey()
+
+class AccessControlServer:
+
+   def __init__(self, serverIface):
+      """ 注册访问控制过滤器 """
+      serverIface.registerAccessControl(AccessControlFilter(serverIface), 100)
+
+

这个例子为每个人提供了一个完整的访问权限。

+

插件的作用是知道谁在登录。

+

在所有这些方法中,我们都有一个图层的参数,以便能够定制每个图层的限制。

+
20.4.1.5.4. layerFilterExpression⚓︎
+

用于添加一个表达式来限制结果。

+

例如:限制要素的属性role等于user

+
1
+2
def layerFilterExpression(self, layer):
+    return "$role = 'user'"
+
+
20.4.1.5.5. layerFilterSubsetString⚓︎
+

与前者相同,但使用SubsetString(在数据库中执行)。

+
1
+2
def layerFilterSubsetString(self, layer):
+    return "role = 'user'"
+
+

限制要素的属性role等于“user”

+
20.4.1.5.6. layerPermissions⚓︎
+

限制访问图层。

+

返回一个LayerPermissions()对象,它有以下属性:

+
    +
  • canRead可以在GetCapabilities中看到它并有读取权限。
  • +
  • canInsert能够插入一个新的要素。
  • +
  • canUpdate能够更新一个要素。
  • +
  • canDelete能够删除一个要素。
  • +
+

例如:

+
1
+2
+3
+4
+5
def layerPermissions(self, layer):
+    rights = QgsAccessControlFilter.LayerPermissions()
+    rights.canRead = True
+    rights.canInsert = rights.canUpdate = rights.canDelete = False
+    return rights
+
+

限制每一个人只读访问。

+
20.4.1.5.7. authorizedLayerAttributes⚓︎
+

用于限制一个特定的属性子集的可见性。

+

参数 attribute 返回当前的可见属性集。

+

例如:

+
1
+2
def authorizedLayerAttributes(self, layer, attributes):
+    return [a for a in attributes if a != "role"]
+
+

隐藏‘role’属性。

+
20.4.1.5.8. allowToEdit⚓︎
+

这被用来限制对一个属性子集的编辑。

+

它在WFS-Transaction协议中使用。

+

例如:

+
1
+2
def allowToEdit(self, layer, feature):
+    return feature.attribute('role') == 'user'
+
+

只能够编辑具有‘role’属性的要素。

+
20.4.1.5.9. cacheKey⚓︎
+

QGIS服务器会对能力进行缓存,如果要对每个角色进行缓存,你可以在此方法中返回角色。或者返回None以完全禁用缓存。

+

20.4.2 自定义服务⚓︎

+

在QGIS服务器中,WMS、WFS和WCS等核心服务是作为QgsService的子类实现的。

+

为了实现一个新的服务,当查询字符串参数SERVICE与服务名称相匹配时将被执行,你可以实现你自己的QgsService,并通过调用registerService(service)serviceRegistry()上注册你的服务。

+

下面是一个名为CUSTOM的自定义服务的例子:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
from qgis.server import QgsService
+from qgis.core import QgsMessageLog
+
+class CustomServiceService(QgsService):
+
+    def __init__(self):
+        QgsService.__init__(self)
+
+    def name(self):
+        return "CUSTOM"
+
+    def version(self):
+        return "1.0.0"
+
+    def executeRequest(self, request, response, project):
+        response.setStatusCode(200)
+        QgsMessageLog.logMessage('Custom service executeRequest')
+        response.write("Custom service executeRequest")
+
+
+class CustomService():
+
+    def __init__(self, serverIface):
+        serverIface.serviceRegistry().registerService(CustomServiceService())
+
+

20.4.3. 自定义APIs⚓︎

+

在QGIS Server中,诸如OAPIF(又称WFS3)等核心OGC APIs被实现为QgsServerOgcApiHandler子类的集合,这些子类被注册到QgsServerOgcApi(或其父类QgsServerApi)的实例中。

+

要实现一个新的API,当url路径与某个URL相匹配时就会被执行,你可以实现你自己的QgsServerOgcApiHandler实例,将它们添加到QgsServerOgcApi中,并通过调用其registerApi(api)serviceRegistry()上注册该API。

+

下面是一个自定义API的例子,当URL包含/customapi时将被执行:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
import json
+import os
+
+from qgis.PyQt.QtCore import QBuffer, QIODevice, QTextStream, QRegularExpression
+from qgis.server import (
+    QgsServiceRegistry,
+    QgsService,
+    QgsServerFilter,
+    QgsServerOgcApi,
+    QgsServerQueryStringParameter,
+    QgsServerOgcApiHandler,
+)
+
+from qgis.core import (
+    QgsMessageLog,
+    QgsJsonExporter,
+    QgsCircle,
+    QgsFeature,
+    QgsPoint,
+    QgsGeometry,
+)
+
+
+class CustomApiHandler(QgsServerOgcApiHandler):
+
+    def __init__(self):
+        super(CustomApiHandler, self).__init__()
+        self.setContentTypes([QgsServerOgcApi.HTML, QgsServerOgcApi.JSON])
+
+    def path(self):
+        return QRegularExpression("/customapi")
+
+    def operationId(self):
+        return "CustomApiXYCircle"
+
+    def summary(self):
+        return "Creates a circle around a point"
+
+    def description(self):
+        return "Creates a circle around a point"
+
+    def linkTitle(self):
+        return "Custom Api XY Circle"
+
+    def linkType(self):
+        return QgsServerOgcApi.data
+
+    def handleRequest(self, context):
+        """Simple Circle"""
+
+        values = self.values(context)
+        x = values['x']
+        y = values['y']
+        r = values['r']
+        f = QgsFeature()
+        f.setAttributes([x, y, r])
+        f.setGeometry(QgsCircle(QgsPoint(x, y), r).toCircularString())
+        exporter = QgsJsonExporter()
+        self.write(json.loads(exporter.exportFeature(f)), context)
+
+    def templatePath(self, context):
+        # 模板路径用于提供HTML内容
+        return os.path.join(os.path.dirname(__file__), 'circle.html')
+
+    def parameters(self, context):
+        return [QgsServerQueryStringParameter('x', True, QgsServerQueryStringParameter.Type.Double, 'X coordinate'),
+                QgsServerQueryStringParameter(
+                    'y', True, QgsServerQueryStringParameter.Type.Double, 'Y coordinate'),
+                QgsServerQueryStringParameter('r', True, QgsServerQueryStringParameter.Type.Double, 'radius')]
+
+
+class CustomApi():
+
+    def __init__(self, serverIface):
+        api = QgsServerOgcApi(serverIface, '/customapi',
+                            'custom api', 'a custom api', '1.1')
+        handler = CustomApiHandler()
+        api.registerHandler(handler)
+        serverIface.serviceRegistry().registerApi(api)
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/21-PyQGIS\351\200\237\346\237\245\350\241\250/index.html" "b/21-PyQGIS\351\200\237\346\237\245\350\241\250/index.html" new file mode 100644 index 0000000..8f97f32 --- /dev/null +++ "b/21-PyQGIS\351\200\237\346\237\245\350\241\250/index.html" @@ -0,0 +1,1884 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 21-PyQGIS速查表 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

21 PyQGIS速查表⚓︎

+

21.1 用户接口⚓︎

+

改变外观

+

1
+2
+3
+4
+5
+6
+7
from qgis.PyQt.QtWidgets import QApplication
+
+app = QApplication.instance()
+app.setStyleSheet(".QWidget {color: blue; background-color: yellow;}")
+# 你可以从文件读取样式
+with  open("testdata/file.qss") as qss_file_content:
+    app.setStyleSheet(qss_file_content.read())
+
+改变图标和标题

+
1
+2
+3
+4
+5
from qgis.PyQt.QtGui import QIcon
+
+icon = QIcon(r"/path/to/logo/file.png")
+iface.mainWindow().setWindowIcon(icon)
+iface.mainWindow().setWindowTitle("My QGIS")
+
+

21.2 设置⚓︎

+

获得设置列表

+
1
+2
+3
+4
+5
+6
from qgis.PyQt.QtCore import QSettings
+
+qs = QSettings()
+
+for k in sorted(qs.allKeys()):
+    print (k)
+
+

21.3 工具栏⚓︎

+

移除工具栏

+

1
+2
+3
+4
+5
+6
+7
+8
from qgis.utils import iface
+
+toolbar = iface.helpToolBar()
+parent = toolbar.parentWidget()
+parent.removeToolBar(toolbar)
+
+# 添加
+parent.addToolBar(toolbar)
+
+移除操作

+
1
+2
+3
+4
actions = iface.attributesToolBar().actions()
+iface.attributesToolBar().clear()
+iface.attributesToolBar().addAction(actions[4])
+iface.attributesToolBar().addAction(actions[3])
+
+

21.4 菜单⚓︎

+

移除菜单

+
1
+2
+3
+4
+5
+6
+7
+8
+9
from qgis.utils import iface
+
+# 帮助菜单
+menu = iface.helpMenu()
+menubar = menu.parentWidget()
+menubar.removeAction(menu.menuAction())
+
+# 添加
+menubar.addAction(menu.menuAction())
+
+

21.5 画布⚓︎

+

访问画布

+

1
+2
+3
from qgis.utils import iface
+
+canvas = iface.mapCanvas()
+
+改变画布颜色

+

1
+2
+3
+4
from qgis.PyQt.QtCore import Qt
+
+iface.mapCanvas().setCanvasColor(Qt.black)
+iface.mapCanvas().refresh()
+
+画布刷新间隔

+
1
+2
+3
from qgis.PyQt.QtCore import QSettings
+# 150毫秒
+QSettings().setValue("/qgis/map_update_interval", 150)
+
+

21.6 图层⚓︎

+

添加图层

+

1
+2
+3
+4
+5
from qgis.utils import iface
+
+layer = iface.addVectorLayer("/path/to/shapefile/file.shp", "layer name you like", "ogr")
+if not layer:
+    print("Layer failed to load!")
+
+获取当前图层

+

1
layer = iface.activeLayer()
+
+图层列表

+
1
+2
+3
from qgis.core import QgsProject
+
+QgsProject.instance().mapLayers().values()
+
+

获得图层名称

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
from qgis.core import QgsVectorLayer
+layer = QgsVectorLayer("Point?crs=EPSG:4326", "layer name you like", "memory")
+QgsProject.instance().addMapLayer(layer)
+
+layers_names = []
+for layer in QgsProject.instance().mapLayers().values():
+    layers_names.append(layer.name())
+
+print("layers TOC = {}".format(layers_names))
+
+layers_names = [layer.name() for layer in QgsProject.instance().mapLayers().values()]
+print("layers TOC = {}".format(layers_names))
+
+通过名称查找图层

+

1
+2
+3
+4
from qgis.core import QgsProject
+
+layer = QgsProject.instance().mapLayersByName("layer name you like")[0]
+print(layer.name())
+
+设置当前图层

+

1
+2
+3
+4
from qgis.core import QgsProject
+
+layer = QgsProject.instance().mapLayersByName("layer name you like")[0]
+iface.setActiveLayer(layer)
+
+刷新图层

+

1
+2
+3
+4
+5
+6
+7
from qgis.core import QgsProject
+
+layer = QgsProject.instance().mapLayersByName("layer name you like")[0]
+# 5秒
+layer.setAutoRefreshInterval(5000)
+# 自动刷新
+layer.setAutoRefreshEnabled(True)
+
+添加表单要素

+

1
+2
+3
+4
+5
+6
+7
+8
from qgis.core import QgsFeature, QgsGeometry
+
+feat = QgsFeature()
+geom = QgsGeometry()
+feat.setGeometry(geom)
+feat.setFields(layer.fields())
+
+iface.openFeatureForm(layer, feat, False)
+
+添加无表单要素

+

1
+2
+3
+4
+5
+6
from qgis.core import QgsPointXY
+
+pr = layer.dataProvider()
+feat = QgsFeature()
+feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
+pr.addFeatures([feat])
+
+获得要素

+

1
+2
for f in layer.getFeatures():
+    print (f)
+
+获得已选要素

+

1
+2
for f in layer.selectedFeatures():
+    print (f)
+
+获得要素id

+

1
+2
selected_ids = layer.selectedFeatureIds()
+print(selected_ids)
+
+从已选要素id创建内存图层

+

1
+2
+3
+4
from qgis.core import QgsFeatureRequest
+
+memory_layer = layer.materialize(QgsFeatureRequest().setFilterFids(layer.selectedFeatureIds()))
+QgsProject.instance().addMapLayer(memory_layer)
+
+获得几何

+

1
+2
+3
+4
# 点图层
+for f in layer.getFeatures():
+    geom = f.geometry()
+    print ('%f, %f' % (geom.asPoint().y(), geom.asPoint().x()))
+
+移动几何

+

1
+2
+3
+4
+5
+6
from qgis.core import QgsFeature, QgsGeometry
+poly = QgsFeature()
+geom = QgsGeometry.fromWkt("POINT(7 45)")
+geom.translate(1, 1)
+poly.setGeometry(geom)
+print(poly.geometry())
+
+设置坐标参考系统

+

1
+2
+3
+4
from qgis.core import QgsProject, QgsCoordinateReferenceSystem
+
+for layer in QgsProject.instance().mapLayers().values():
+    layer.setCrs(QgsCoordinateReferenceSystem(4326, QgsCoordinateReferenceSystem.EpsgCrsId))
+
+查看坐标参考系统

+

1
+2
+3
+4
+5
from qgis.core import QgsProject
+
+for layer in QgsProject.instance().mapLayers().values():
+    crs = layer.crs().authid()
+    layer.setName('{} ({})'.format(layer.name(), crs))
+
+隐藏字段

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
from qgis.core import QgsEditorWidgetSetup
+
+def fieldVisibility (layer,fname):
+    setup = QgsEditorWidgetSetup('Hidden', {})
+    for i, column in enumerate(layer.fields()):
+        if column.name()==fname:
+            layer.setEditorWidgetSetup(idx, setup)
+            break
+        else:
+            continue
+
+图层添加WKT要素

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsProject
+
+layer = QgsVectorLayer('Polygon?crs=epsg:4326', 'Mississippi', 'memory')
+pr = layer.dataProvider()
+poly = QgsFeature()
+geom = QgsGeometry.fromWkt("POLYGON ((-88.82 34.99,-88.0934.89,-88.39 30.34,-89.57 30.18,-89.73 31,-91.63 30.99,-90.8732.37,-91.23 33.44,-90.93 34.23,-90.30 34.99,-88.82 34.99))")
+poly.setGeometry(geom)
+pr.addFeatures([poly])
+layer.updateExtents()
+QgsProject.instance().addMapLayers([layer])
+
+从GeoPackage加载所有图层

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
from qgis.core import QgsVectorLayer, QgsProject
+
+fileName = "/path/to/gpkg/file.gpkg"
+layer = QgsVectorLayer(fileName,"test","ogr")
+subLayers =layer.dataProvider().subLayers()
+
+for subLayer in subLayers:
+    name = subLayer.split('!!::!!')[1]
+    uri = "%s|layername=%s" % (fileName, name,)
+    # 创建图层
+    sub_vlayer = QgsVectorLayer(uri, name, 'ogr')
+    # Add layer to map
+    QgsProject.instance().addMapLayer(sub_vlayer)
+
+加载瓦片图层(xyz)

+

1
+2
+3
+4
+5
+6
+7
+8
from qgis.core import QgsRasterLayer, QgsProject
+
+def loadXYZ(url, name):
+    rasterLyr = QgsRasterLayer("type=xyz&url=" + url, name, "wms")
+    QgsProject.instance().addMapLayer(rasterLyr)
+
+urlWithParams = 'type=xyz&url=https://a.tile.openstreetmap.org/%7Bz%7D/%7Bx%7D/%7By%7D.png&zmax=19&zmin=0&crs=EPSG3857'
+loadXYZ(urlWithParams, 'OpenStreetMap')
+
+移除所有图层

+

1
QgsProject.instance().removeAllMapLayers()
+
+移除全部

+
1
QgsProject.instance().clear()
+
+

21.7 目录⚓︎

+

访问选中的图层

+

1
+2
+3
from qgis.utils import iface
+
+iface.mapCanvas().layers()
+
+移除上下文菜单

+
1
+2
+3
+4
+5
ltv = iface.layerTreeView()
+mp = ltv.menuProvider()
+ltv.setMenuProvider(None)
+# 恢复
+ltv.setMenuProvider(mp)
+
+

21.8 高级目录⚓︎

+

根节点

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
from qgis.core import QgsVectorLayer, QgsProject, QgsLayerTreeLayer
+
+root = QgsProject.instance().layerTreeRoot()
+node_group = root.addGroup("My Group")
+
+layer = QgsVectorLayer("Point?crs=EPSG:4326", "layer name you like", "memory")
+QgsProject.instance().addMapLayer(layer, False)
+
+node_group.addLayer(layer)
+
+print(root)
+print(root.children())
+
+访问第一个子节点

+

1
+2
+3
+4
+5
+6
+7
from qgis.core import QgsLayerTreeGroup, QgsLayerTreeLayer, QgsLayerTree
+
+child0 = root.children()[0]
+print (child0.name())
+print (type(child0))
+print (isinstance(child0, QgsLayerTreeLayer))
+print (isinstance(child0.parent(), QgsLayerTree))
+
+查找图层组和所有节点

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
from qgis.core import QgsLayerTreeGroup, QgsLayerTreeLayer
+
+def get_group_layers(group):
+   print('- group: ' + group.name())
+   for child in group.children():
+      if isinstance(child, QgsLayerTreeGroup):
+         # 遍历嵌套图层组
+         get_group_layers(child)
+      else:
+         print('  - layer: ' + child.name())
+
+
+root = QgsProject.instance().layerTreeRoot()
+for child in root.children():
+   if isinstance(child, QgsLayerTreeGroup):
+      get_group_layers(child)
+   elif isinstance(child, QgsLayerTreeLayer):
+      print ('- layer: ' + child.name())
+
+通过名称查找图层组

+

1
print (root.findGroup("My Group"))
+
+通过id查找图层组

+

1
print (root.findLayer(layer.layerId()))
+
+添加图层

+

1
+2
+3
+4
+5
from qgis.core import QgsVectorLayer, QgsProject
+
+layer1 = QgsVectorLayer("Point?crs=EPSG:4326", "layer name you like", "memory")
+QgsProject.instance().addMapLayer(layer1, False)
+node_layer1 = root.addLayer(layer1)
+
+添加图层组

+

1
+2
+3
+4
from qgis.core import QgsLayerTreeGroup
+
+node_group2 = QgsLayerTreeGroup("Group 2")
+root.addChildNode(node_group2)
+
+移除加载的图层

+

 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
layer = QgsProject.instance().mapLayersByName("layer name you like")[0]
+root = QgsProject.instance().layerTreeRoot()
+
+myLayer = root.findLayer(layer.id())
+myClone = myLayer.clone()
+parent = myLayer.parent()
+
+myGroup = root.findGroup("My Group")
+# 插入到第一个位置
+myGroup.insertChildNode(0, myClone)
+
+parent.removeChildNode(myLayer)
+
+移除指定图层组

+

1
+2
+3
+4
+5
+6
+7
+8
QgsProject.instance().addMapLayer(layer, False)
+
+root = QgsProject.instance().layerTreeRoot()
+myGroup = root.findGroup("My Group")
+myOriginalLayer = root.findLayer(layer.id())
+myLayer = myOriginalLayer.clone()
+myGroup.insertChildNode(0, myLayer)
+parent.removeChildNode(myOriginalLayer)
+
+改变可见性

+
1
+2
+3
+4
root = QgsProject.instance().layerTreeRoot()
+node = root.findLayer(layer.id())
+new_state = Qt.Checked if node.isVisible() == Qt.Unchecked else Qt.Unchecked
+node.setItemVisibilityChecked(new_state)
+
+

图层组是否被选择

+
1
+2
+3
+4
+5
def isMyGroupSelected( groupName ):
+    myGroup = QgsProject.instance().layerTreeRoot().findGroup( groupName )
+    return myGroup in iface.layerTreeView().selectedNodes()
+
+print(isMyGroupSelected( 'my group name' ))
+
+

移动节点

+

1
+2
+3
cloned_group1 = node_group.clone()
+root.insertChildNode(0, cloned_group1)
+root.removeChildNode(node_group)
+
+展开节点

+
1
+2
print(myGroup.isExpanded())
+myGroup.setExpanded(False)
+
+

隐藏节点

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
from qgis.core import QgsProject
+
+model = iface.layerTreeView().layerTreeModel()
+ltv = iface.layerTreeView()
+root = QgsProject.instance().layerTreeRoot()
+
+layer = QgsProject.instance().mapLayersByName('layer name you like')[0]
+node = root.findLayer(layer.id())
+
+index = model.node2index( node )
+ltv.setRowHidden( index.row(), index.parent(), True )
+node.setCustomProperty( 'nodeHidden', 'true')
+ltv.setCurrentIndex(model.node2index(root))
+
+

节点信号

+
1
+2
+3
+4
+5
+6
+7
+8
def onWillAddChildren(node, indexFrom, indexTo):
+    print ("WILL ADD", node, indexFrom, indexTo)
+
+def onAddedChildren(node, indexFrom, indexTo):
+    print ("ADDED", node, indexFrom, indexTo)
+
+root.willAddChildren.connect(onWillAddChildren)
+root.addedChildren.connect(onAddedChildren)
+
+

移除图层

+
1
root.removeLayer(layer)
+
+

移除图层组

+
1
root.removeChildNode(node_group2)
+
+

创建新的目录树

+
1
+2
+3
+4
+5
root = QgsProject.instance().layerTreeRoot()
+model = QgsLayerTreeModel(root)
+view = QgsLayerTreeView()
+view.setModel(model)
+view.show()
+
+

移动节点

+
1
+2
+3
cloned_group1 = node_group.clone()
+root.insertChildNode(0, cloned_group1)
+root.removeChildNode(node_group)
+
+

重命名节点

+
1
+2
cloned_group1.setName("Group X")
+node_layer1.setName("Layer X")
+
+

21.9 处理算法⚓︎

+

获得算法列表

+

1
+2
+3
+4
+5
from qgis.core import QgsApplication
+
+for alg in QgsApplication.processingRegistry().algorithms():
+    if 'buffer' == alg.name():
+        print("{}:{} --> {}".format(alg.provider().name(), alg.name(), alg.displayName()))
+
+获得算法帮助

+

1
+2
+3
from qgis import processing
+
+processing.algorithmHelp("qgis:randomselection")
+
+运行算法

+

本示例,结果存储在添加到项目的临时内存层中。

+

1
+2
+3
from qgis import processing
+result = processing.run("native:buffer", {'INPUT': layer, 'OUTPUT': 'memory:'})
+QgsProject.instance().addMapLayer(result['OUTPUT'])
+
+算法统计

+

1
+2
+3
from qgis.core import QgsApplication
+
+len(QgsApplication.processingRegistry().algorithms())
+
+数据提供者统计

+

1
+2
+3
from qgis.core import QgsApplication
+
+len(QgsApplication.processingRegistry().providers())
+
+表达式统计

+
1
+2
+3
from qgis.core import QgsExpression
+
+len(QgsExpression.Functions())
+
+

21.10 装饰器⚓︎

+

版权

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
from qgis.PyQt.Qt import QTextDocument
+from qgis.PyQt.QtGui import QFont
+
+mQFont = "Sans Serif"
+mQFontsize = 9
+mLabelQString = "© QGIS 2019"
+mMarginHorizontal = 0
+mMarginVertical = 0
+mLabelQColor = "#FF0000"
+
+INCHES_TO_MM = 0.0393700787402 # 1 毫米 = 0.0393700787402 英寸
+case = 2
+
+def add_copyright(p, text, xOffset, yOffset):
+    p.translate( xOffset , yOffset  )
+    text.drawContents(p)
+    p.setWorldTransform( p.worldTransform() )
+
+def _on_render_complete(p):
+    deviceHeight = p.device().height() # 获取绘制设备高度
+    deviceWidth  = p.device().width() # 获取绘制设备宽度
+    # 创建一个新的富文本容器
+    text = QTextDocument()
+    font = QFont()
+    font.setFamily(mQFont)
+    font.setPointSize(int(mQFontsize))
+    text.setDefaultFont(font)
+    style = "<style type=\"text/css\"> p {color: " + mLabelQColor + "}</style>"
+    text.setHtml( style + "<p>" + mLabelQString + "</p>" )
+    # 文本大小
+    size = text.size()
+
+    # 渲染
+    pixelsInchX  = p.device().logicalDpiX()
+    pixelsInchY  = p.device().logicalDpiY()
+    xOffset  = pixelsInchX  * INCHES_TO_MM * int(mMarginHorizontal)
+    yOffset  = pixelsInchY  * INCHES_TO_MM * int(mMarginVertical)
+
+    # 计算点位
+    if case == 0:
+        # 左上
+        add_copyright(p, text, xOffset, yOffset)
+
+    elif case == 1:
+        # 左下
+        yOffset = deviceHeight - yOffset - size.height()
+        add_copyright(p, text, xOffset, yOffset)
+
+    elif case == 2:
+        # 右上
+        xOffset  = deviceWidth  - xOffset - size.width()
+        add_copyright(p, text, xOffset, yOffset)
+
+    elif case == 3:
+        # 右下
+        yOffset  = deviceHeight - yOffset - size.height()
+        xOffset  = deviceWidth  - xOffset - size.width()
+        add_copyright(p, text, xOffset, yOffset)
+
+    elif case == 4:
+        # 上中心点
+        xOffset = deviceWidth / 2
+        add_copyright(p, text, xOffset, yOffset)
+
+    else:
+        # 下中心点
+        yOffset = deviceHeight - yOffset - size.height()
+        xOffset = deviceWidth / 2
+        add_copyright(p, text, xOffset, yOffset)
+
+# 当画布渲染完成后发送信号
+iface.mapCanvas().renderComplete.connect(_on_render_complete)
+# 重绘画布
+iface.mapCanvas().refresh()
+
+

21.11 创作者⚓︎

+

按名称获取打印布局

+
1
+2
+3
+4
+5
composerTitle = 'MyComposer' # 创作者名称
+
+project = QgsProject.instance()
+projectLayoutManager = project.layoutManager()
+layout = projectLayoutManager.layoutByName(composerTitle)
+
+

21.12 来源⚓︎

+ + + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/3-\345\212\240\350\275\275\345\233\276\345\261\202/index.html" "b/3-\345\212\240\350\275\275\345\233\276\345\261\202/index.html" new file mode 100644 index 0000000..d12448d --- /dev/null +++ "b/3-\345\212\240\350\275\275\345\233\276\345\261\202/index.html" @@ -0,0 +1,1327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3-加载图层 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

3 加载图层⚓︎

+

此页面上的代码片段需要导入以下模块:

+
1
+2
+3
+4
import os # pyqgis控制台同样需要
+from qgis.core import (
+    QgsVectorLayer
+)
+
+

让我们使用数据打开一些图层。QGIS可识别矢量和栅格图层。此外,自定义图层类型也可以使用,但我们不打算在此讨论。

+

3.1 矢量图层⚓︎

+

创建一个矢量图层实例,指定图层的数据源标识、图层名称和提供者名称:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
# 获取shapefile的路径,例如:/home/project/data/ports.shp 
+path_to_airports_layer = "testdata/airports.shp"
+
+# 格式为:
+# vlayer = QgsVectorLayer(data_source, layer_name, provider_name)
+
+vlayer = QgsVectorLayer(path_to_ports_layer, "Airports layer", "ogr")
+
+if not vlayer.isValid():
+    print("图层加载失败!")
+else:
+    QgsProject.instance().addMapLayer(vlayer)
+
+

数据源标识是一个字符串,它特定于每个矢量数据提供者。图层名称用于图层列表部件。检查图层是否已成功加载非常重要。如果不是,则返回无效的图层实例。

+

geopackage矢量图层:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
# 获取geopackage的路径,例如:/home/project/data/data.gpkg 
+path_to_gpkg = os.path.join(QgsProject.instance().homePath(), "data", "data.gpkg")
+# 追加图层名称
+gpkg_places_layer = path_to_gpkg + "|layername=places"
+# 例如:gpkg_places_layer = "/home/project/data/data.gpkg|layername=places"
+vlayer = QgsVectorLayer(gpkg_places_layer, "Places layer", "ogr")
+if not vlayer.isValid():
+    print("图层加载失败!")
+else:
+    QgsProject.instance().addMapLayer(vlayer)
+
+

在QGIS中打开并显示矢量图层的最快方式是使用QgisInterface类的 addVectorLayer() 方法:

+
1
+2
+3
vlayer = iface.addVectorLayer(path_to_ports_layer, "Ports layer", "ogr")
+if not vlayer:
+    print("图层加载失败!")
+
+

这将创建一个新图层,并将其添加到当前QGIS项目中(使其显示在图层列表中)。该函数返回图层实例,如果无法加载图层则返回None

+

以下列表展示了如何使用矢量数据提供者访问各种数据源:

+
    +
  • +

    GDAL 库(Shapefile和许多其他文件格式)——数据源是文件的路径:

    +
  • +
  • +

    Shapefile:

    +
    1
    +2
    vlayer = QgsVectorLayer("/path/to/shapefile/file.shp", "layer_name_you_like", "ogr")
    +QgsProject.instance().addMapLayer(vlayer)
    +
    +
  • +
  • +

    dxf(注意数据源uri中的内部选项):

    +
    1
    +2
    +3
    uri = "/path/to/dxffile/file.dxf|layername=entities|geometrytype=Point"
    +vlayer = QgsVectorLayer(uri, "layer_name_you_like", "ogr")
    +QgsProject.instance().addMapLayer(vlayer)
    +
    +
  • +
  • +

    PostGIS 数据库——数据源是一个字符串,其中包含与PostgreSQL数据库创建连接所需的所有信息。

    +

    QgsDataSourceUri类可以为你生成这个字符串。请注意,必须在编译QGIS时支持Postgres,否则此提供者不可用:

    +
    1
    +2
    +3
    +4
    +5
    +6
    uri = QgsDataSourceUri()
    +# 设置主机,端口,数据库名称,用户名和密码
    +uri.setConnection("localhost", "5432", "dbname", "johny", "xxx")
    +# 设置数据库架构,表名,几何列和可选项(WHERE 语句)
    +uri.setDataSource("public", "roads", "the_geom", "cityid = 2643", "primary_key_field")
    +vlayer = QgsVectorLayer(uri.uri(False), "layer name you like", "postgres")
    +
    +
  • +
+

: !!! 提示

+
1
    `uri.uri(False)`中的`False`参数可以防止扩展认证配置参数,如果你没有使用任何身份验证配置,则此参数不会产生任何差异。
+
+
    +
  • +

    CSV或其他分隔的文本文件——打开一个用分号作为分隔符的文件,X坐标使用字段“x”,Y坐标使用字段“y”:

    +
    1
    +2
    +3
    uri = "file://{}/testdata/delimited_xy.csv?delimiter={}&xField={}&yField={}".format(os.getcwd(), ";", "x", "y")
    +vlayer = QgsVectorLayer(uri, "layer name you like", "delimitedtext")
    +QgsProject.instance().addMapLayer(vlayer)
    +
    +
  • +
+

: !!! 提示

+
1
+2
+3
+4
+5
    提供者的字符串作为一个URL,因此路径必须以file://为前缀。它还允许WKT((well known text)格式的几何作为`x`和`y`的替代字段,并允许指定坐标参考系统。例如:
+
+    ```python
+    uri = "file:///some/path/file.csv?delimiter={}&crs=epsg:4723&wktField={}".format(";", "shape")
+    ```
+
+
    +
  • GPX文件——“gpx”数据提供者从gpx文件中读取轨迹,路线和路点。要打开文件,需要在url中指定类型(track / route / waypoint):
  • +
+
1
+2
+3
uri = "path/to/gpx/file.gpx?type=track"
+vlayer = QgsVectorLayer(uri, "layer name you like", "gpx")
+QgsProject.instance().addMapLayer(vlayer)
+
+
    +
  • SpatiaLite数据库——与PostGIS数据库类似, QgsDataSourceUri可用于生成数据源标识:
  • +
+
1
+2
+3
+4
+5
+6
+7
+8
+9
uri = QgsDataSourceUri()
+uri.setDatabase('/home/martin/test-2.3.sqlite')
+schema = ''
+table = 'Towns'
+geom_column = 'Geometry'
+uri.setDataSource(schema, table, geom_column)
+display_name = 'Towns'
+vlayer = QgsVectorLayer(uri.uri(), display_name, 'spatialite')
+QgsProject.instance().addMapLayer(vlayer)
+
+
    +
  • 基于MySQL WKB的几何,通过GDAL——数据源是连接表的字符串:
  • +
+
1
+2
+3
uri = "MySQL:dbname,host=localhost,port=3306,user=root,password=xxx|layername=my_table"
+vlayer = QgsVectorLayer( uri, "my table", "ogr" )
+QgsProject.instance().addMapLayer(vlayer)
+
+
    +
  • WFS连接:连接使用URI定义并使用WFS提供者:
  • +
+

1
+2
uri = "https://demo.geo-solutions.it/geoserver/ows?service=WFS&version=1.1.0&request=GetFeature&typename=geosolutions:regioni"
+vlayer = QgsVectorLayer(uri, "my wfs layer", "WFS")
+
+ 可以使用标准库urllib创建uri: +
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
import urllib
+
+params = {
+    'service': 'WFS',
+    'version': '1.1.0',
+    'request': 'GetFeature',
+    'typename': 'geosolutions:regioni',
+    'srsname': "EPSG:4326"
+}
+uri2 = 'https://demo.geo-solutions.it/geoserver/ows?' + urllib.parse.unquote(urllib.parse.urlencode(params))
+

+

: !!! 提示

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
     可以通过调用[`QgsVectorLayer`](https://qgis.org/pyqgis/master/core/QgsVectorLayer.html#qgis.core.QgsVectorLayer)的 [`setDataSource()`](https://qgis.org/pyqgis/master/core/QgsMapLayer.html#qgis.core.QgsMapLayer.setDataSource) 方法更改现有图层的数据源,如下面例子:
+
+    ```python
+    uri = "https://demo.geo-solutions.it/geoserver/ows?service=WFS&version=1.1.0&request=GetFeature&typename=geosolutions:regioni"
+    provider_options = QgsDataProvider.ProviderOptions()
+    # 使用项目的转换上下文
+    provider_options.transformContext = QgsProject.instance().transformContext()
+    vlayer.setDataSource(uri, "layer name you like", "WFS", provider_options)
+
+    del(vlayer)
+    ```
+
+

3.2 栅格图层⚓︎

+

访问栅格文件,用到了GDAL库。它支持多种文件格式。如果你在打开某些文件时遇到问题,请检查你的GDAL是否支持(默认情况下并非所有格式都可用)。从文件加载栅格,需要指定其文件名和显示名称:

+
1
+2
+3
+4
+5
# 获取tif文件的路径,例如:/home/project/data/srtm.tif 
+path_to_tif = "qgis-projects/python_cookbook/data/srtm.tif"
+rlayer = QgsRasterLayer(path_to_tif, "SRTM layer name")
+if not rlayer.isValid():
+    print("图层加载失败!")
+
+

geopackage中加载栅格:

+
1
+2
+3
+4
+5
+6
+7
# 获取geopackage的路径,例如:/home/project/data/data.gpkg 
+path_to_gpkg = os.path.join(os.getcwd(), "testdata", "sublayers.gpkg")
+# gpkg_raster_layer = "GPKG:/home/project/data/data.gpkg:srtm"
+gpkg_raster_layer = "GPKG:" + path_to_gpkg + ":srtm"
+rlayer = QgsRasterLayer(gpkg_raster_layer, "layer name you like", "gdal")
+if not rlayer.isValid():
+    print("图层加载失败!")
+
+

与矢量图层类似,可以使用QgisInterface对象的addRasterLayer函数加载栅格图层:

+
1
iface.addRasterLayer("/path/to/raster/file.tif", "layer name you like")
+
+

这将创建一个新图层,并将其添加到当前项目中(使其显示在图层列表中)。

+

3.2.1 加载PostGIS栅格图层⚓︎

+

PostGIS栅格图层,类似于PostGIS矢量图层,可以使用URI字符串添加到项目中。为数据库连接参数保留可复用字典是有效的。这样可以轻松编辑适用连接的字典。使用postgresraster提供者的元数据对象将字典编码为URI。之后,可以将栅格图层添加到项目中。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
uri_config = {
+    # 数据库参数
+    'dbname':'gis_db',      # 数据库名称
+    'host':'localhost',     # IP地址或者localhost
+    'port':'5432',          # 端口
+    'sslmode':QgsDataSourceUri.SslDisable, # SslAllow, SslPrefer, SslRequire, SslVerifyCa, SslVerifyFull
+    # 如果存储在authcfg或服务中,则不需要用户名和密码
+    'authcfg':'QconfigId',  # QGIS认证数据库ID包含连接信息
+    'service': None,         # 连接数据库的服务
+    'username':None,        # 用户名
+    'password':None,        # 密码
+    # 数据库表和栅格字段信息
+    'schema':'public',      # 数据库架构
+    'table':'my_rasters',   # 数据库表名称
+    'geometrycolumn':'rast',# 栅格字段名称
+    'sql':None,             # WHERE查询语句,应该放在字符串末尾。
+    'key':None,             # 数据库表主键
+    'srid':None,            # 字符串,指定坐标参考系
+    'estimatedmetadata':'False', # A boolean value telling if the metadata is estimated.
+    'type':None,            # WKT字符串,指定WKB类型
+    'selectatid':None,      # 设置为True可禁用按要素ID进行的选择。
+    'options':None,         # 不在此列表中的其他PostgreSQL连接选项。
+    'enableTime': None,
+    'temporalDefaultTime': None,
+    'temporalFieldIndex': None,
+    'mode':'2',             # GDAL参数, 2 unions raster tiles, 1 adds tiles separately (may require user input)
+}
+# 删除空参数
+uri_config = {key:val for key, val in uri_config.items() if val is not None}
+# 获取数据提供者和配置的元数据
+md = QgsProviderRegistry.instance().providerMetadata('postgresraster')
+uri = QgsDataSourceUri(md.encodeUri(uri_config))
+
+# 加载栅格图层到项目
+rlayer = iface.addRasterLayer(uri.uri(False), "raster layer name", "postgresraster")
+
+

3.2.2 加载WCS服务栅格图层:⚓︎

+
1
+2
+3
layer_name = 'nurc:mosaic'
+uri = "https://demo.geo-solutions.it/geoserver/ows?identifier={}".format(layer_name)
+rlayer = QgsRasterLayer(uri, 'my wcs layer', 'wcs')
+
+

以下是WCS URI可以包含的参数说明:

+

WCS URI由 键=值 对组成,分隔符:&。它与URL中的查询字符串格式相同,编码方式相同。QgsDataSourceUri 可以用于构造URI以确保正确编码特殊字符。

+
    +
  • url(必填):WCS服务器URL。不要在URL中使用VERSION,因为每个版本的WCS对 GetCapabilities 版本使用不同的参数名称,请参阅param版本。
  • +
  • identifier(必填):覆盖范围名称
  • +
  • time(可选):时间位置或时间段(beginPosition / endPosition / timeResolution)
  • +
  • format(可选):支持的格式名称。默认是第一个支持的格式,其名称为tif或第一个支持的格式。
  • +
  • crs(可选):CRS格式为AUTHORITY:ID,例如,EPSG:4326。默认为EPSG:4326(如果支持)或第一个支持的CRS。
  • +
  • username(可选):基本身份验证的用户名。
  • +
  • password(可选):基本身份验证的密码。
  • +
  • IgnoreGetMapUrl(可选,hack):如果指定(设置为1),则忽略GetCapabilities公布的GetCoverage URL。如果未正确配置服务器,则可能需要。
  • +
  • InvertAxisOrientation(可选,hack):如果指定(设置为1),则在GetCoverage请求中切换轴。如果服务器使用错误的轴顺序,则可能需要地理CRS。
  • +
  • IgnoreAxisOrientation(可选,hack):如果指定(设置为1),则不要根据地理CRS的WCS标准反转轴方向。
  • +
  • cache(可选):缓存加载控制,如QNetworkRequest :: CacheLoadControl中所述,但如果使用AlwaysCache失败,请求将重新发送为PreferCache。允许的值:AlwaysCache,PreferCache,PreferNetwork,AlwaysNetwork。默认为AlwaysCache。
  • +
+

另外,你可以从WMS服务器加载栅格图层。但是目前无法从API访问GetCapabilities响应——你必须知道所需的图层:

+
1
+2
+3
+4
urlWithParams = 'url=http://irs.gis-lab.info/?layers=landsat&styles=&format=image/jpeg&crs=EPSG:4326'
+rlayer = QgsRasterLayer(urlWithParams, 'some layer name', 'wms')
+if not rlayer.isValid():
+    print("图层加载失败!")
+
+

3.3 QgsProject 实例⚓︎

+

如果你想使用打开的图层进行渲染,请不要忘记将它们添加到QgsProject实例中。QgsProject实例拥有图层的所有权,可以通过其唯一ID从应用程序的任何部分访问它们。从项目中删除图层时,它也会被删除。用户可以在QGIS接口中删除图层,也可以通过Python使用removeMapLayer()方法删除图层。

+

使用addMapLayer()方法将图层添加到当前项目:

+
1
QgsProject.instance().addMapLayer(rlayer)
+
+

在绝对位置添加图层:

+
1
+2
+3
+4
+5
+6
# 首先添加图层但不显示
+QgsProject.instance().addMapLayer(rlayer, False)
+# 在项目中获取图层树的根图层组
+layerTree = iface.layerTreeCanvasBridge().rootGroup()
+# 第一个参数是一个从0开始的数字,-1表示结束
+layerTree.insertChildNode(-1, QgsLayerTreeLayer(rlayer))
+
+

如果要删除图层,使用removeMapLayer()方法:

+
1
QgsProject.instance().removeMapLayer(rlayer.id())
+
+

在上面的代码中,传递了图层ID(你可以调用图层的id()方法),但是你也可以传递图层对象本身。

+

获取已加载图层和图层ID的列表,使用mapLayers()方法:

+
1
QgsProject.instance().mapLayers()
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/4-\350\256\277\351\227\256\345\233\276\345\261\202\347\233\256\345\275\225\346\240\221/index.html" "b/4-\350\256\277\351\227\256\345\233\276\345\261\202\347\233\256\345\275\225\346\240\221/index.html" new file mode 100644 index 0000000..d88e546 --- /dev/null +++ "b/4-\350\256\277\351\227\256\345\233\276\345\261\202\347\233\256\345\275\225\346\240\221/index.html" @@ -0,0 +1,1127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4-访问图层目录树 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

4 访问图层目录树⚓︎

+

此页面上的代码片段需要导入以下模块:

+
1
+2
+3
+4
from qgis.core import (
+    QgsProject,
+    QgsVectorLayer,
+)
+
+

4.1 QgsProject类⚓︎

+

可以使用QgsProject检索有关目录和所有已加载图层的信息。

+

必须创建QgsProject的实例instance,并使用其方法(函数)来获取已加载的图层。

+

mapLayers方法返回已加载图层的字典:

+
1
+2
+3
+4
layers = QgsProject.instance().mapLayers()
+print(layers)
+
+# {'countries_89ae1b0f_f41b_4f42_bca4_caf55ddbe4b6': <QgsMapLayer: 'countries' (ogr)>}
+
+

字典的keys是图层的唯一id,而values是图层的对象。

+

现在直接去获得有关图层的任何其他信息:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
# 使用列表表达式获得图层名称
+l = [layer.name() for layer in QgsProject.instance().mapLayers().values()]
+# 键值对存储图层名称和图层对象
+layers_list = {}
+for l in QgsProject.instance().mapLayers().values():
+  layers_list[l.name()] = l
+
+print(layers_list)
+
+# {'countries': <QgsMapLayer: 'countries' (ogr)>}
+
+

还可以使用图层名称查询目录:

+
1
country_layer = QgsProject.instance().mapLayersByName("countries")[0]
+
+
+

提示

+

返回所有匹配图层的列表,因此我们使用索引[0]获取第一个图层。

+
+

4.2 QgsLayerTreeGroup类⚓︎

+

图层树是由节点构建的经典树结构。当前有两种类型的节点:图层组节点(QgsLayerTreeGroup)和图层节点(QgsLayerTreeLayer)。

+
+

提示

+

更多信息请访问Martin Dobias的博客

+
+

可以使用QgsProject类的layerLayerRoot()方法轻松访问项目图层树:

+
1
root = QgsProject.instance().layerTreeRoot()
+
+

root是一个图层组节点,具有子节点:

+
1
root.children()
+
+

返回直接子节点列表。子组节点可以从他们自己的直接父级访问。

+

我们可以检索其中一个子节点:

+
1
+2
+3
+4
child0 = root.children()[0]
+print(child0)
+
+# <qgis._core.QgsLayerTreeLayer object at 0x7f1e1ea54168>
+
+

可以使用它们的唯一id来检索:

+
1
+2
+3
ids = root.findLayerIds()
+#  访问第一个图层
+root.findLayer(ids[0])
+
+

可以使用图层组名称检索:

+
1
root.findGroup('Group Name')
+
+

QgsLayerTreeGroup还有许多其他有用的方法,可用于获取有关目录树的更多信息:

+
1
+2
+3
+4
+5
# 获得所有已选图层
+checked_layers = root.checkedLayers()
+print(checked_layers)
+
+# [<QgsMapLayer: 'countries' (ogr)>]
+
+

现在,我们把一些图层添加到项目的图层树中,有两种方法可以做到:

+
    +
  1. 显式添加: 使用addLayer()或者insertLayer()方法:
  2. +
+
1
+2
+3
+4
+5
+6
# 创建临时图层
+layer1 = QgsVectorLayer("path_to_layer", "Layer 1", "memory")
+# 添加图层到图层树末尾
+root.addLayer(layer1)
+# 插入图层到指定位置
+root.insertLayer(5, layer1)
+
+
    +
  1. 隐式添加: 由于项目的图层树已连接到图层注册表,因此可以在图层注册表中添加图层:
  2. +
+
1
QgsProject.instance().addMapLayer(layer1)
+
+

你可以轻松地在QgsVectorLayerQgsLayerTreeLayer之间切换:

+
1
+2
+3
+4
+5
+6
node_layer = root.findLayer(country_layer.id())
+print("Layer node:", node_layer)
+print("Map layer:", node_layer.layer())
+
+# Layer node: <qgis._core.QgsLayerTreeLayer object at 0x7fecceb46ca8>
+# Map layer: <QgsMapLayer: 'countries' (ogr)>
+
+

可以使用addGroup()方法添加组。在下面的示例中,前者将在目录树的末尾添加一个组,而后者则可以在现有的组中添加另一个组:

+
1
+2
+3
node_group1 = root.addGroup('Simple Group')
+# 在图层组中添加子组
+node_subgroup1 = node_group1.addGroup("I'm a sub group")
+
+

移动节点和组有许多有用的方法。

+

移动现有节点分三个步骤:

+
    +
  1. 克隆已存在的节点
  2. +
  3. 移动克隆的节点到想要的位置
  4. +
  5. 删除原始节点
  6. +
+
1
+2
+3
+4
+5
+6
# 克隆图层组
+cloned_group1 = node_group1.clone()
+# 移动图层组(包括子组和图层)到顶层
+root.insertChildNode(0, cloned_group1)
+# 删除原始图层组节点
+root.removeChildNode(node_group1)
+
+

移动一个图层要稍微复杂一点:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
# 获得一个图层
+vl = QgsProject.instance().mapLayersByName("countries")[0]
+# 从图层树中获取
+myvl = root.findLayer(vl.id())
+# 克隆
+myvlclone = myvl.clone()
+# 获取父级,如果图层不在图层组中返回空字符串
+parent = myvl.parent()
+# 移动图层节点到顶层
+parent.insertChildNode(0, myvlclone)
+# 删除原有节点
+root.removeChildNode(myvl)
+
+

或者移动到已存在的图层组中:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
# 获得一个图层
+vl = QgsProject.instance().mapLayersByName("countries")[0]
+# 从图层树中获取
+myvl = root.findLayer(vl.id())
+# 克隆
+myvlclone = myvl.clone()
+# 创建一个新组
+group1 = root.addGroup("Group1")
+# 获取父级,如果图层不在图层组中返回空字符串
+parent = myvl.parent()
+# 移动图层节点到顶层
+group1.insertChildNode(0, myvlclone)
+# 从原有组中删除
+parent.removeChildNode(myvl)
+
+

可用于修改组和图层的其他一些方法:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
node_group1 = root.findGroup("Group1")
+# 修改图层组名称
+node_group1.setName("Group X")
+node_layer2 = root.findLayer(country_layer.id())
+# 修改图层名称
+node_layer2.setName("Layer X")
+# 改变图层可见状态
+node_group1.setItemVisibilityChecked(True)
+node_layer2.setItemVisibilityChecked(False)
+# 折叠/展开图层组
+node_group1.setExpanded(True)
+node_group1.setExpanded(False)
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 0000000..8c99a8c --- /dev/null +++ b/404.html @@ -0,0 +1,744 @@ + + + + + + + + + + + + + + + + + + + + + + + PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/5-\344\275\277\347\224\250\346\240\205\346\240\274\345\233\276\345\261\202/index.html" "b/5-\344\275\277\347\224\250\346\240\205\346\240\274\345\233\276\345\261\202/index.html" new file mode 100644 index 0000000..d79a85f --- /dev/null +++ "b/5-\344\275\277\347\224\250\346\240\205\346\240\274\345\233\276\345\261\202/index.html" @@ -0,0 +1,1139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5-使用栅格图层 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

5 使用栅格图层⚓︎

+

此页面上的代码段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
from qgis.core import (
+    Qgis,
+    QgsRasterLayer,
+    QgsProject,
+    QgsPointXY,
+    QgsRaster,
+    QgsRasterBlock,
+    QgsRasterShader,
+    QgsColorRampShader,
+    QgsSingleBandPseudoColorRenderer,
+    QgsSingleBandColorDataRenderer,
+    QgsSingleBandGrayRenderer,
+)
+
+from qgis.PyQt.QtGui import (
+    QColor,
+)
+
+

5.1 图层细节⚓︎

+

栅格图层由一个或多个栅格波段组成——称为单波段和多波段栅格。一个波段代表一个矩阵。彩色图像(例如航拍照片)是由红色,蓝色和绿色波段组成。单波段栅格通常表示连续变量(例如高程)或离散变量(例如土地使用)。在某些情况下,栅格图层带有调色板,栅格值指的是调色板中存储的颜色。

+

以下代码假定rlayer是一个 QgsRasterLayer对象。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
rlayer = QgsProject.instance().mapLayersByName('srtm')[0]
+# 获取图层分辨率
+rlayer.width(), rlayer.height()
+(919, 619)
+# 获取图层范围,返回QgsRectangle对象
+rlayer.extent()
+# <QgsRectangle: 20.06856808199999875 -34.27001076999999896, 20.83945284300000012,-33.75077500700000144>
+# 获取图层范围并转换为字符串
+rlayer.extent().toString()
+# '20.0685680819999988,-34.2700107699999990 : 20.8394528430000001,-33.7507750070000014'
+# 获取栅格类型: 0 = 灰度值(单波段), 1 = 调色板(单波段), 2 = 多波段
+rlayer.rasterType()
+# 0
+# 获取波段个数
+rlayer.bandCount()
+# 1
+# 获取第一个波段名称
+print(rlayer.bandName(1))
+# Band 1: Height
+# 获取所有可用的元数据,返回QgsLayerMetadata对象
+rlayer.metadata()
+# '<qgis._core.QgsLayerMetadata object at 0x13711d558>'
+
+

5.2 渲染⚓︎

+

加载栅格图层时,它会根据其类型获取默认渲染器。它可以在图层属性中更改,也可以通过编程方式更改。

+

查询当前渲染器:

+
1
+2
+3
+4
rlayer.renderer()
+# <qgis._core.QgsSingleBandGrayRenderer object at 0x7f471c1da8a0>
+rlayer.renderer().type()
+# 'singlebandgray'
+
+

设置渲染器,使用QgsRasterLayersetRenderer 方法。下面有许多渲染器类(派生自QgsRasterRenderer):

+ +

单波段栅格图层可以以灰色(低值=黑色,高值=白色)或使用伪彩色算法绘制,该算法为值指定颜色。也可以使用调色板绘制带调色板的单波段栅格。多波段图层通常通过将波段映射到RGB来绘制颜色。另一种可能是仅使用一个波段来绘图。

+

5.2.1 单波段栅格⚓︎

+

假设我们想要渲染一个单波段栅格图层,颜色范围从绿色到黄色(对应于0到255之间的像素值)。在第一阶段,我们将准备一个:QgsRasterShader对象并配置其着色器函数:

+
1
+2
+3
+4
+5
+6
fcn = QgsColorRampShader()
+fcn.setColorRampType(QgsColorRampShader.Interpolated)
+lst = [ QgsColorRampShader.ColorRampItem(0, QColor(0,255,0)),QgsColorRampShader.ColorRampItem(255, QColor(255,255,0)) ]
+fcn.setColorRampItemList(lst)
+shader = QgsRasterShader()
+shader.setRasterShaderFunction(fcn)
+
+

着色器按其颜色映射指定的颜色。彩色地图被提供为具有相关颜色的像素值列表。插值有三种模式:

+
    +
  • 线性(Interpolated):从像素值上方和下方的颜色表条带中线性插值颜色
  • +
  • 离散(Discrete):颜色取自具有相同或更高值的最接近的颜色表条带
  • +
  • 精确(Exact):不插值颜色,只绘制值等于颜色表条带的像素
  • +
+

在第二步中,我们将此着色器与栅格图层相关联:

+
1
+2
renderer = QgsSingleBandPseudoColorRenderer(rlayer.dataProvider(), 1, shader)
+rlayer.setRenderer(renderer)
+
+

上面代码中数值1是波段号(栅格波段的一个索引)。

+

最后我们必须使用 triggerRepaint方法来查看结果:

+
1
rlayer.triggerRepaint()
+
+

5.2.2 多波段栅格⚓︎

+

默认情况下,QGIS将前三个波段映射为红色,绿色和蓝色来创建彩色图像(这是MultiBandColor绘图样式。在某些情况下,你可能希望覆盖这些设置。以下代码互换红色波段(1)和绿色波段(2):

+
1
+2
+3
rlayer_multi = QgsProject.instance().mapLayersByName('multiband')[0]
+rlayer_multi.renderer().setGreenBand(1)
+rlayer_multi.renderer().setRedBand(2)
+
+

如果只需要一个波段来实现光栅的可视化,则可以选择单波段绘制,灰度级或伪彩色。

+

我们必须使用triggerRepaint 更新地图并查看结果:

+
1
rlayer.triggerRepaint()
+
+

5.3 查询值⚓︎

+

查询栅格值的第一种方法是使用 QgsRasterDataProvidersample()方法查询。你必须指定栅格图层的QgsPointXY的和波段号。该方法返回一个value和result(true或false):

+
1
val, res = rlayer.dataProvider().sample(QgsPointXY(20.50, -34), 1)
+
+

查询栅格值的另一种方法是使用identify()方法,返回QgsRasterIdentifyResult对象 。

+
1
+2
+3
+4
+5
ident = rlayer.dataProvider().identify(QgsPointXY(20.5, -34), QgsRaster.IdentifyFormatValue)
+if ident.isValid():
+    print(ident.results())
+
+# {1: 323.0}
+
+

在这种情况下,results() 方法返回一个字典,波段索引作为字典键,波段值作为字典值。例如{1: 323.0}

+

5.4 编辑栅格数据⚓︎

+

可以使用QgsRasterBlock类创建栅格图层。例如,创建每像素一字节的2x2栅格块:

+
1
+2
block = QgsRasterBlock(Qgis.Byte, 2, 2)
+block.setData(b'\xaa\xbb\xcc\xdd')
+
+

使用writeBlock()方法可以覆盖栅格像素。用2x2块覆盖位置0,0处的现有栅格数据:

+
1
+2
+3
+4
provider = rlayer.dataProvider()
+provider.setEditable(True)
+provider.writeBlock(block, 1, 0, 0)
+provider.setEditable(False)
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/6-\344\275\277\347\224\250\347\237\242\351\207\217\345\233\276\345\261\202/index.html" "b/6-\344\275\277\347\224\250\347\237\242\351\207\217\345\233\276\345\261\202/index.html" new file mode 100644 index 0000000..9a8ba04 --- /dev/null +++ "b/6-\344\275\277\347\224\250\347\237\242\351\207\217\345\233\276\345\261\202/index.html" @@ -0,0 +1,2946 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6-使用矢量图层 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节代码片段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
from qgis.core import (
+  QgsApplication,
+  QgsDataSourceUri,
+  QgsCategorizedSymbolRenderer,
+  QgsClassificationRange,
+  QgsPointXY,
+  QgsProject,
+  QgsExpression,
+  QgsField,
+  QgsFields,
+  QgsFeature,
+  QgsFeatureRequest,
+  QgsFeatureRenderer,
+  QgsGeometry,
+  QgsGraduatedSymbolRenderer,
+  QgsMarkerSymbol,
+  QgsMessageLog,
+  QgsRectangle,
+  QgsRendererCategory,
+  QgsRendererRange,
+  QgsSymbol,
+  QgsVectorDataProvider,
+  QgsVectorLayer,
+  QgsVectorFileWriter,
+  QgsWkbTypes,
+  QgsSpatialIndex,
+  QgsVectorLayerUtils
+)
+
+from qgis.core.additions.edit import edit
+
+from qgis.PyQt.QtGui import (
+    QColor,
+)
+
+

6 使用矢量图层⚓︎

+

本节总结了使用矢量图层执行各种操作。

+

这里的大部分工作都是基于QgsVectorLayer类的方法。

+

6.1 检索相关属性信息⚓︎

+

你可以通过调用QgsVectorLayer对象的fields()方法检索一个矢量图层相关字段的信息:

+
1
+2
+3
+4
+5
+6
+7
+8
+9
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
+for field in vlayer.fields():
+    print(field.name(), field.typeName())
+
+# ID Integer64
+# fk_region Integer64
+# ELEV Real
+# NAME String
+# USE String
+
+

displayField()mapTipTemplate() 方法提供有关显示属性选项卡中使用的字段和模板的信息。

+

加载矢量层时,QGIS始终选择一个字段作为显示名称,而HTML地图提示默认情况下为空。使用这些方法,可以轻松地同时获得:

+
1
+2
+3
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
+print(vlayer.displayField())
+# NAME
+
+
+

提示

+

如果将显示名称从字段更改为表达式,则必须使用 displayExpression() 而不是 displayField()

+
+

6.2 遍历矢量图层⚓︎

+

遍历矢量图层要素是最常见的任务之一。下面是执行此任务的简单基本代码示例,并显示有关每个要素的一些信息。layer变量被假定为一个QgsVectorLayer对象。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
layer = iface.activeLayer()
+features = layer.getFeatures()
+
+for feature in features:
+    # 检索每一个要素的几何和属性
+    print("Feature ID: ", feature.id())
+    # 获取几何
+    geom = feature.geometry()
+    geomSingleType = QgsWkbTypes.isSingleType(geom.wkbType())
+    if geom.type() == QgsWkbTypes.PointGeometry:
+        # 几何类型可以是单个或多个类型,
+        if geomSingleType:
+            x = geom.asPoint()
+            print("Point: ", x)
+        else:
+            x = geom.asMultiPoint()
+            print("MultiPoint: ", x)
+    elif geom.type() == QgsWkbTypes.LineGeometry:
+        if geomSingleType:
+            x = geom.asPolyline()
+            print("Line: ", x, "length: ", geom.length())
+        else:
+            x = geom.asMultiPolyline()
+            print("MultiLine: ", x, "length: ", geom.length())
+    elif geom.type() == QgsWkbTypes.PolygonGeometry:
+        if geomSingleType:
+            x = geom.asPolygon()
+            print("Polygon: ", x, "Area: ", geom.area())
+        else:
+            x = geom.asMultiPolygon()
+            print("MultiPolygon: ", x, "Area: ", geom.area())
+    else:
+        print("Unknown or invalid geometry")
+    # 获取属性
+    attrs = feature.attributes()
+    # attrs是一个列表。它包含要素的所有属性值
+    print(attrs)
+    # 本测试只打印第一个要素
+    break
+
+
+    # Feature ID:  1
+    # Point:  <QgsPointXY: POINT(7 45)>
+    # [1, 'First feature']
+
+

6.3 选择要素⚓︎

+

在QGIS桌面中,可以通过不同方式选择要素:用户可以单击要素、在地图画布上绘制矩形或使用表达式过滤器。所选要素通常以不同颜色突出显示(默认为黄色),以引起用户对已选要素的注意。

+

有时,以编程方式选择要素或更改默认颜色会很有用。

+

选择所有要素,可以使用selectAll()方法:

+
1
+2
+3
# 获取当前图层(必须是矢量图层)
+layer = iface.activeLayer()
+layer.selectAll()
+
+

使用表达式进行选择,使用selectByExpression()方法:

+
1
+2
+3
+4
# 假设当前图层是来自QGIS测试套件的points.shp文件
+#(Class(字符串)和Heading(数字)是points.shp中的属性)
+layer = iface.activeLayer()
+layer.selectByExpression('"Class"=\'B52\' and "Heading" > 10 and "Heading" <70', QgsVectorLayer.SetSelection)
+
+

更改选择颜色,可以使用QgsMapCanvassetSelectionColor()方法 :

+
1
iface.mapCanvas().setSelectionColor(QColor("red"))
+
+

给图层的所选要素列表添加要素,你可以调用select()添加到要素ID列表:

+
1
+2
+3
+4
+5
+6
+7
+8
selected_fid  =  []
+
+# 获取图层的第一个要素ID 
+feature = next(layer.getFeatures())
+if feature:
+    selected_fid.append(feature.id())
+# 将这些要素添加到选定的列表
+layer.select(selected_fid)
+
+

清除选择:

+
1
layer.removeSelection()
+
+

6.3.1 访问属性⚓︎

+

属性可以通过名称来获得:

+
1
+2
+3
print(feature['name'])
+
+# First feature
+
+

或者,可以通过索引引用属性。这比使用名称快一点。例如,获取第二个属性:

+
1
+2
+3
print(feature[1])
+
+# Second feature
+
+

6.3.2 遍历选中的要素⚓︎

+

如果你只需要选中的要素,则可以使用矢量图层的selectedFeatures()方法:

+
1
+2
+3
+4
+5
selection = layer.selectedFeatures()
+print(len(selection))
+for feature in selection:
+    # 使用要素执行任何操作
+    pass
+
+

6.3.3 遍历一部分要素⚓︎

+

如果要遍历图层中特定的要素子集(例如给定区域内的要素),则必须添加QgsFeatureRequest对象到getFeatures()方法。下面是一个例子:

+
1
+2
+3
+4
+5
+6
+7
areaOfInterest = QgsRectangle(450290,400520, 450750,400780)
+
+request = QgsFeatureRequest().setFilterRect(areaOfInterest)
+
+for feature in layer.getFeatures(request):
+    # 使用要素执行任何操作
+    pass
+
+

为了速度,相交通常仅使用要素的范围(bbox)来完成。但是有一个标志ExactIntersect可以确保只返回相交的要素:

+
1
request = QgsFeatureRequest().setFilterRect(areaOfInterest).setFlags(QgsFeatureRequest.ExactIntersect)
+
+

使用setLimit()你可以限制要素的数量。下面是一个例子:

+
1
+2
+3
+4
+5
request = QgsFeatureRequest()
+request.setLimit(2)
+for feature in layer.getFeatures(request):
+    print(feature)
+    # <qgis._core.QgsFeature object at 0x7f9b78590948>
+
+

如果你需要一个基于属性的过滤器来代替(或增加)一个空间过滤器,如上面的例子所示,你可以构建一个QgsExpression对象并将其传递给QgsFeatureRequest函数。下面是一个例子:

+
1
+2
+3
+4
# 表达式将过滤字段“location_name” 
+# 包含单词“Lake”(不区分大小写)
+exp = QgsExpression("location_name ILIKE \'%Lake%\'")
+request = QgsFeatureRequest(exp)
+
+

有关支持语法(QgsExpression)的详细信息,请参阅11-表达式,过滤和计算值

+

该请求可用于检索每个要素,因此迭代器返回所有要素,但返回每个要素的部分数据。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
# 仅返回选定的字段以增加请求速度
+request.setSubsetOfAttributes([0,2])
+
+# 更加友好的方式
+request.setSubsetOfAttributes(['name','id'],layer.fields())
+
+# 不返回几何对象以增加请求速度
+request.setFlags(QgsFeatureRequest.NoGeometry)
+
+# 仅获取ID为45的要素
+request.setFilterFid(45)
+
+# 这些选项可以是链式的
+request.setFilterRect(areaOfInterest).setFlags(QgsFeatureRequest.NoGeometry).setFilterFid(45).setSubsetOfAttributes([0,2])
+
+

6.4 修改矢量图层⚓︎

+

大多数矢量数据提供者都支持编辑图层数据。有时它们仅支持编辑子集。使用capabilities()功能可以找出支持的功能集。

+
1
+2
+3
+4
caps = layer.dataProvider().capabilities()
+# 检查是否支持特定功能:
+if caps & QgsVectorDataProvider.DeleteFeatures:
+    print('图层支持删除要素')
+
+

有关所有可用功能的列表,请参阅 :QgsVectorDataProvider接口文档

+

打印图层功能的文本描述,结果是以逗号分隔的列表,你可以使用capabilitiesString() 方法,如下例所示:

+
1
+2
+3
+4
+5
+6
caps_string = layer.dataProvider().capabilitiesString()
+# Print:
+# 'Add Features, Delete Features, Change Attribute Values, Add Attributes,
+# Delete Attributes, Rename Attributes, Fast Access to Features at ID,
+# Presimplify Geometries, Presimplify Geometries with Validity Check,
+# Transactions, Curved Geometries'
+
+

通过使用以下任何方法进行矢量图层编辑,更改将直接提交到基础数据存储(文件,数据库等)。如果你只想进行临时修改,请跳到下一节6.4.4 使用编辑缓冲区修改矢量图层

+
+

提示

+

如果你在QGIS内部(从控制台或从插件中),可能需要强制重绘地图画布,以便查看你对几何、样式或属性所做的更改:

+
1
+2
+3
+4
+5
# 如果启用了缓存,简单的画布刷新可能不足以触发重绘,并且必须清除图层的缓存图像。
+if iface.mapCanvas().isCachingEnabled():
+    layer.triggerRepaint()
+else:
+    iface.mapCanvas().refresh()
+
+
+

6.4.1 添加要素⚓︎

+

创建一些QgsFeature实例并将它们的列表传递给提供者的 addFeatures()方法。它将返回两个值:result(true / false)和添加的要素列表(它们的ID由数据存储设置)。

+

设置要素的属性,可以通过传递QgsFields对象(可以从fields()矢量图层的方法获取 )或调用initAttributes()传递要添加的字段数来初始化要素。

+
1
+2
+3
+4
+5
+6
+7
+8
if caps & QgsVectorDataProvider.AddFeatures:
+    feat = QgsFeature(layer.fields())
+    feat.setAttributes([0, 'hello'])
+    # 或按key或index设置单个属性:
+    feat.setAttribute('name', 'hello')
+    feat.setAttribute(0, 'hello')
+    feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(123, 456)))
+    (res, outFeats) = layer.dataProvider().addFeatures([feat])
+
+

6.4.2 删除要素⚓︎

+

删除某些要素,只需提供其要素ID列表即可。

+
1
+2
if caps & QgsVectorDataProvider.DeleteFeatures:
+    res = layer.dataProvider().deleteFeatures([5, 10])
+
+

6.4.3 修改要素⚓︎

+

可以更改要素的几何图形或更改某些属性。以下示例首先更改索引为0和1的属性值,然后更改要素的几何。

+
1
+2
+3
+4
+5
+6
+7
+8
+9
fid = 100  # 我们将修改的要素ID
+
+if caps & QgsVectorDataProvider.ChangeAttributeValues:
+    attrs = { 0 : "hello", 1 : 123 }
+    layer.dataProvider().changeAttributeValues({ fid : attrs })
+
+if caps & QgsVectorDataProvider.ChangeGeometries:
+    geom = QgsGeometry.fromPointXY(QgsPointXY(111,222))
+    layer.dataProvider().changeGeometryValues({ fid : geom })
+
+
+

提示

+

QgsVectorLayerEditUtils类进行仅几何编辑

+

如果你只需要更改几何图形,可以考虑使用QgsVectorLayerEditUtils,它提供一些有用的方法来编辑几何图形(平移、插入或移动顶点等)。

+
+

6.4.4 使用编辑缓冲区修改矢量图层⚓︎

+

在QGIS应用程序中编辑矢量时,必须首先为特定图层设置开始编辑模式,然后进行一些修改,最后提交(或回滚)更改。你所做的所有更改在你提交之前都不会写入——它们保留在图层的内存编辑缓冲区中。也可以通过编程方式使用此功能——它仅仅是是矢量图层编辑的另一种方法,可以补充直接使用数据提供者。在为矢量图层编辑提供一些GUI工具时使用此选项,因为这将允许用户决定是否提交/回滚,并允许使用撤销/重做。提交更改后,编辑缓冲区中的所有更改都将保存到数据提供者中。

+

这些方法类似于我们在提供者中看到的方法,但它们在QgsVectorLayer 对象上调用。

+

使这些方法起作用,图层必须处于编辑模式。开始编辑模式,使用startEditing()方法。停止编辑,使用commitChanges()rollBack()方法。第一个方法将提交对数据源的所有更改,而第二个方法将丢弃它们,并且不会修改数据源。

+

确定图层是否处于编辑模式,使用isEditable()方法。

+

这里有一些示例演示,如何使用这些编辑方法。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
from qgis.PyQt.QtCore import QVariant
+
+feat1 = feat2 = QgsFeature(layer.fields())
+fid = 99
+feat1.setId(fid)
+
+# 添加两个要素(QgsFeature实例)
+layer.addFeatures([feat1,feat2])
+# 删除指定ID的要素 
+layer.deleteFeature(fid)
+
+# 为要素设置新几何(QgsGeometry实例)。
+layer.changeGeometry(fid, geometry)
+# 将给定字段索引(int)的属性更新为给定值
+fieldIndex =1
+value ='My new name'
+layer.changeAttributeValue(fid, fieldIndex, value)
+
+# 添加新的字段
+layer.addAttribute(QgsField("mytext", QVariant.String))
+# 删除字段
+layer.deleteAttribute(fieldIndex)
+
+

为了使撤消/重做正常工作,上述调用必须包含在撤消命令中。(如果你不关心撤消/重做并希望立即存储更改,那么通过6.4 修改矢量图层,你将可以更轻松地完成工作 。)

+

以下是使用撤消功能的方法:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
layer.beginEditCommand("Feature triangulation")
+
+# ...调用图层的编辑方法......
+
+if problem_occurred:
+  layer.destroyEditCommand()
+  return
+
+# ...更多编辑...
+
+layer.endEditCommand()
+
+

beginEditCommand()方法将创建一个内部“活动”命令,并记录矢量图层中的后续更改。随着对endEditCommand() 命令的调用被推送到撤销栈,用户将能够从GUI撤消/重做它。如果在执行更改时出现问题, destroyEditCommand()方法将删除该命令并回滚此命令处于活动状态时所做的所有更改。

+

你还可以使用with edit(layer)——将提交和回滚包装在更具语义的代码块中,如下例所示:

+
1
+2
+3
+4
with edit(layer):
+  feat = next(layer.getFeatures())
+  feat[0] = 5
+  layer.updateFeature(feat)
+
+

结束后将自动调用commitChanges()。如果发生任何异常,它将进行rollBack()所有更改。如果commitChanges()(当该方法返回False时)遇到问题将引发QgsEditError异常。

+

6.4.5 添加和删除字段⚓︎

+

添加字段(属性),你需要指定字段定义列表。删除字段,只需提供字段索引列表。

+
1
+2
+3
+4
+5
+6
+7
+8
+9
from qgis.PyQt.QtCore import QVariant
+
+if caps & QgsVectorDataProvider.AddAttributes:
+    res = layer.dataProvider().addAttributes(
+        [QgsField("mytext", QVariant.String),
+        QgsField("myint", QVariant.Int)])
+
+if caps & QgsVectorDataProvider.DeleteAttributes:
+    res = layer.dataProvider().deleteAttributes([0])
+
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
# 删除字段的备用方法
+# 首先创建要删除的临时字段 (f1-3)
+layer.dataProvider().addAttributes([QgsField("f1",QVariant.Int),QgsField("f2",QVariant.Int),QgsField("f3",QVariant.Int)])
+layer.updateFields()
+count=layer.fields().count() # 图层字段个数
+ind_list=list((count-3, count-2)) # 创建列表
+
+# 使用索引删除字段
+layer.dataProvider().deleteAttributes([count-1])
+
+# 使用索引列表删除多个字段
+layer.dataProvider().deleteAttributes(ind_list)
+
+

在数据提供程者中添加或删除字段后,需要更新图层的字段,因为更改不会自动传播。

+
1
layer.updateFields()
+
+
+

提示

+

使用with语句直接保存更改

+

使用with edit(layer): 更改将在结束后调用commitChanges()自动提交。如果发生任何异常,它将rollBack()所有更改。请参见6.4.4 使用编辑缓冲区修改矢量图层

+
+

6.5 使用空间索引⚓︎

+

如果需要对矢量图层进行频繁查询,空间索引可以显著提高代码的性能。例如,想象一下,你正在编写插值算法,并且对于给定位置你需要知道点图层中最近的10个点,以便使用这些点来计算插值。如果没有空间索引,QGIS找到这10个点的唯一方法是计算从每个点到指定位置的距离,然后比较这些距离。这可能是一项非常耗时的任务,特别是如果需要在多个位置重复这项任务。如果图层存在空间索引,则操作更有效。

+

可以将没有空间索引的图层视为电话簿,其中不对电话号码进行排序或索引。找到给定人员的电话号码的唯一方法是从头开始阅读,直到找到它为止。

+

默认情况下,QGIS矢量图层不会创建空间索引,但你可以轻松创建它们。这是你要做的:

+ +
1
index = QgsSpatialIndex()
+
+
    +
  • 向索引添加要素——索引获取QgsFeature对象并将其添加到内部数据结构。你可以手动创建对象,也可以使用先前提供者的getFeatures()方法。
  • +
+
1
index.insertFeature(feat)
+
+
    +
  • 或者,你可以批量加载图层的所有要素
  • +
+
1
index = QgsSpatialIndex(layer.getFeatures())
+
+
    +
  • 一旦空间索引填充了一些值,你就可以进行一些查询
  • +
+
1
+2
+3
+4
+5
# 以数组形式返回五个最近要素的ID
+nearest = index.nearestNeighbor(QgsPointXY(25.4, 12.7), 5)
+
+# 以数组形式返回与矩形相交的要素
+intersect = index.intersects(QgsRectangle(22.5, 15.3, 23.1, 17.2))
+
+

你可以使用QgsSpatialIndexKDBush空间索引。这个空间索引与标准空间库QgsSpatialIndex相似,但是有以下特性:

+
    +
  • 支持支持单点要素
  • +
  • 静态 (构造后无法将其他要素添加到索引)
  • +
  • 更快
  • +
  • 允许直接检索原始要素的点,而无需其他要素请求
  • +
  • 支持基于真实距离的搜索,即返回搜索半径范围内的所有点
  • +
+

6.6 QgsVectorLayerUtils类⚓︎

+

QgsVectorLayerUtils 类包含一些非常有用的方法,你可以在矢量图层中使用。

+

例如, createFeature()方法准备将 QgsFeature添加到矢量层,并保留每个字段的所有最终约束和默认值

+
1
+2
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
+feat = QgsVectorLayerUtils.createFeature(vlayer)
+
+

使用getValues()方法可以快速获取字段或表达式的值:

+
1
+2
+3
+4
+5
+6
+7
vlayer = QgsVectorLayer("testdata/airports.shp", "airports", "ogr")
+# 只选择第一个要素
+vlayer.selectByIds([1])
+val = QgsVectorLayerUtils.getValues(vlayer, "NAME", selectedOnly=True)
+print(val)
+
+# (['AMBLER'], True)
+
+

6.7 创建矢量图层⚓︎

+

有几种方法可以生成矢量图层数据集:

+
    +
  • QgsVectorFileWriter类:用于将矢量文件写入硬盘,通过静态调用writeAsVectorFormatV3()保存整个矢量图层,或创建该类的实例并调用addFeature()方法。该类支持 GDAL 支持的所有矢量格式(GeoPackage,Shapefile,GeoJSON,KML等)。
  • +
  • QgsVectorLayer类:实例化一个数据提供者,提供的数据源路径(url)以连接和访问数据。它可以用来创建临时的、基于内存存储的图层(memory),还可以连接到GDAL数据集( ogr),数据库(postgresspatialitemysqlmssql),更多(wfsgpxdelimitedtext...)。
  • +
+

6.7.1 从QgsVectorFileWriter实例创建⚓︎

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
# SaveVectorOptions包含很多设置项
+save_options = QgsVectorFileWriter.SaveVectorOptions()
+transform_context = QgsProject.instance().transformContext()
+# 写入GeoPackage (默认)
+error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
+                                                  "testdata/my_new_file.gpkg",
+                                                  transform_context,
+                                                  save_options)
+if error[0] == QgsVectorFileWriter.NoError:
+    print("success!")
+else:
+  print(error)
+
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
# 使用utf-8编码写入ESRI Shapefile
+save_options = QgsVectorFileWriter.SaveVectorOptions()
+save_options.driverName = "ESRI Shapefile"
+save_options.fileEncoding = "UTF-8"
+transform_context = QgsProject.instance().transformContext()
+error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
+                                                  "testdata/my_new_shapefile",
+                                                  transform_context,
+                                                  save_options)
+if error[0] == QgsVectorFileWriter.NoError:
+    print("success again!")
+else:
+  print(error)
+
+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
# 写入ESRI GDB文件
+save_options = QgsVectorFileWriter.SaveVectorOptions()
+save_options.driverName = "FileGDB" # 译者注:默认情况下没有FileGDB驱动
+# 如果没有几何
+save_options.overrideGeometryType = QgsWkbTypes.Unknown
+save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer
+save_options.layerName = 'my_new_layer_name'
+transform_context = QgsProject.instance().transformContext()
+gdb_path = "testdata/my_example.gdb"
+error = QgsVectorFileWriter.writeAsVectorFormatV3(layer,
+                                                gdb_path,
+                                                transform_context,
+                                                save_options)
+if error[0] == QgsVectorFileWriter.NoError:
+  print("success!")
+else:
+  print(error)
+
+

你还可以使用 FieldValueConverter 转换字段使其与不同格式兼容。例如,要将数组变量类型(例如在Postgres中)转换为文本类型,可以执行以下操作 :

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
LIST_FIELD_NAME = 'xxxx'
+
+
+class ESRIValueConverter(QgsVectorFileWriter.FieldValueConverter):
+
+  def __init__(self, layer, list_field):
+    QgsVectorFileWriter.FieldValueConverter.__init__(self)
+    self.layer = layer
+    self.list_field_idx = self.layer.fields().indexFromName(list_field)
+
+  def convert(self, fieldIdxInLayer, value):
+    if fieldIdxInLayer == self.list_field_idx:
+      return QgsListFieldFormatter().representValue(layer=vlayer,
+                                                    fieldIndex=self.list_field_idx,
+                                                    config={},
+                                                    cache=None,
+                                                    value=value)
+    else:
+      return value
+
+  def fieldDefinition(self, field):
+    idx = self.layer.fields().indexFromName(field.name())
+    if idx == self.list_field_idx:
+      return QgsField(LIST_FIELD_NAME, QVariant.String)
+    else:
+      return self.layer.fields()[idx]
+
+converter = ESRIValueConverter(vlayer, LIST_FIELD_NAME)
+opts = QgsVectorFileWriter.SaveVectorOptions()
+opts.fieldValueConverter = converter
+
+

还可以指定目标CRS——如果将一个有效的QgsCoordinateReferenceSystem实例作为第四个参数,则将该图层转换为这个CRS。

+

有关有效的驱动程序的名称,请调用supportedFiltersAndFormats方法或查阅GDAL支持的格式——你应该将“Code”列中的值作为驱动程序名称传递。

+

(可选)你可以设置是仅导出选中的要素,传递更多驱动程序特定的选项进行创建,或者告诉数据写入类不要创建属性...还有许多其他(可选)参数; 请参阅QgsVectorFileWriter的详细信息

+

6.7.2 直接从要素创建⚓︎

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
from qgis.PyQt.QtCore import QVariant
+
+# 为要素属性定义字段。需要QgsFields对象
+fields = QgsFields()
+fields.append(QgsField("first", QVariant.Int))
+fields.append(QgsField("second", QVariant.String))
+
+"""
+创建一个矢量文件编写器的实例,它将创建矢量文件
+参数:
+1. 新文件的路径(如果已存在则失败)
+2. 字段映射
+3. 几何类型 - WKBTYPE枚举
+4. 图层的空间参考(QgsCoordinateReferenceSystem的实例)
+5. 坐标转换上下文
+6. 输出选项(驱动名称,编码等)
+"""
+
+crs = QgsProject.instance().crs()
+transform_context = QgsProject.instance().transformContext()
+save_options = QgsVectorFileWriter.SaveVectorOptions()
+save_options.driverName = "ESRI Shapefile"
+save_options.fileEncoding = "UTF-8"
+
+writer = QgsVectorFileWriter.create(
+  "testdata/my_new_shapefile.shp",
+  fields,
+  QgsWkbTypes.Point,
+  crs,
+  transform_context,
+  save_options
+)
+
+if writer.hasError() != QgsVectorFileWriter.NoError:
+    print("Error when creating shapefile: ",  writer.errorMessage())
+
+# 添加一个要素
+fet = QgsFeature()
+
+fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
+fet.setAttributes([1, "text"])
+writer.addFeature(fet)
+
+# 删除writer写入到磁盘
+del writer
+
+

6.7.3 从QgsVectorLayer实例创建⚓︎

+

QgsVectorLayer类支持的所有数据提供者中,让我们关注基于内存的图层。内存提供者主要供插件或第三方应用程序开发人员使用。它不会将数据存储在磁盘中,允许开发人员快速在后台使用临时图层。

+

提供者支持string,int和double字段。

+

内存提供者还支持空间索引,通过调用提供者的createSpatialIndex()方法来启用。创建空间索引后,你将能够更快地迭代较小区域内的要素(因为没有必要遍历所有要素,只遍历指定矩形内的要素)。

+

通过将"memory"作为QgsVectorLayer构造函数的参数来创建内存提供者。

+

这个构造函数也需要定义图层几何类型的URI,包括:"Point""LineString""Polygon""MultiPoint""MultiLineString""MultiPolygon" or "None"

+

URI还可以指定坐标参考系统、字段和索引。语法是:

+
    +
  • crs=definition
  • +
+

指定坐标参考系统,其中定义可以是接受的任何形式 QgsCoordinateReferenceSystem.createFromString()

+
    +
  • index=yes
  • +
+

指定提供者将使用空间索引

+
    +
  • field=name:type(length,precision)
  • +
+

指定图层的属性。该属性具有名称,可选的类型(integer, double, or string),长度和精度。可能有多个字段定义。

+

以下URI示例包含所有这些选项

+
1
"Point?crs=epsg:4326&field=id:integer&field=name:string(20)&index=yes"
+
+

以下示例代码说明了如何创建和填充内存提供者

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
from qgis.PyQt.QtCore import QVariant
+
+
+# 创建图层
+vl = QgsVectorLayer("Point", "temporary_points", "memory")
+pr = vl.dataProvider()
+
+
+# 添加字段
+pr.addAttributes([QgsField("name", QVariant.String),
+                    QgsField("age",  QVariant.Int),
+                    QgsField("size", QVariant.Double)])
+vl.updateFields() # 告诉矢量图层从提供者获取更改
+
+# 添加一个要素
+fet = QgsFeature()
+fet.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(10,10)))
+fet.setAttributes(["Johny", 2, 0.3])
+pr.addFeatures([fet])
+
+# 在添加新要素时更新图层的范围,因为提供者中的范围更改不会传播到图层
+vl.updateExtents()
+
+

最后,让我们检查一切是否顺利

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
# 显示一些统计
+print("fields:", len(pr.fields()))
+print("features:", pr.featureCount())
+e = vl.extent()
+print("extent:", e.xMinimum(), e.yMinimum(), e.xMaximum(), e.yMaximum())
+
+# 遍历要素
+features = vl.getFeatures()
+for fet in features:
+    print("F:", fet.id(), fet.attributes(), fet.geometry().asPoint())
+
+# fields: 3
+# features: 1
+# extent: 10.0 10.0 10.0 10.0
+# F: 1 ['Johny', 2, 0.3] <QgsPointXY: POINT(10 10)>
+
+

6.8 矢量图层的外观(符号系统)⚓︎

+

渲染矢量图层时,数据的外观由 渲染器 和与图层相关联的 符号 提供 。符号是一个类——负责绘制要素的可视化,而渲染器确定用于特定要素的符号。

+

获得图层的渲染器,如下所示:

+
1
renderer = layer.renderer()
+
+

有了这个参考,让我们来探讨一下

+
1
+2
print("Type:", renderer.type())
+# Type: singleSymbol
+
+

QGIS核心库中有几种已知的渲染器类型:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
类型描述
singleSymbolQgsSingleSymbolRenderer使用相同的符号呈现所有要素
categorizedSymbolQgsCategorizedSymbolRenderer使用每个类别的不同符号呈现要素
graduatedSymbolQgsGraduatedSymbolRenderer为每个值范围使用不同的符号呈现要素
+

可能还有一些自定义渲染器类型,所以永远不要假设只有这些类型。你可以查询QgsApplicationQgsRendererRegistry方法查找当前可用的渲染器:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
print(QgsApplication.rendererRegistry().renderersList())
+# Print:
+['nullSymbol',
+'singleSymbol',
+'categorizedSymbol',
+'graduatedSymbol',
+'RuleRenderer',
+'pointDisplacement',
+'pointCluster',
+'invertedPolygonRenderer',
+'heatmapRenderer',
+'25dRenderer']
+
+

可以以文本形式获取渲染器的内容——可用于调试

+
1
+2
print(renderer.dump())
+# SINGLE: MARKER SYMBOL (1 layers) color 190,207,80,255
+
+

6.8.1 单一符号渲染器⚓︎

+

你可以通过调用symbol()方法获取用于渲染的符号,使用setSymbol()方法更改它(C ++开发人员注意:渲染器将获取符号的所有权。)

+

你可以通过调用setSymbol()并传递适当的符号实例来更改矢量图层的符号。线多边形图层的符号可以通过调用相应的类QgsMarkerSymbolQgsLineSymbolQgsFillSymbolcreateSimple()方法来创建。

+

给传递createSimple()的字典参数来设置符号的样式属性。

+

例如,你可以通过调用setSymbol()并传递QgsMarkerSymbol实例, 来替换特定 图层的符号,如下面的代码示例所示:

+
1
+2
+3
+4
symbol = QgsMarkerSymbol.createSimple({'name': 'square', 'color': 'red'})
+layer.renderer().setSymbol(symbol)
+# 显示更改
+layer.triggerRepaint()
+
+

name 表示符号的形状,可以是以下任何一种:

+
    +
  • circle
  • +
  • square
  • +
  • cross
  • +
  • rectangle
  • +
  • diamond
  • +
  • pentagon
  • +
  • triangle
  • +
  • equilateral_triangle
  • +
  • star
  • +
  • regular_star
  • +
  • arrow
  • +
  • filled_arrowhead
  • +
  • x
  • +
+

获取符号实例的第一个符号图层的完整属性列表,可以按照以下示例代码进行操作:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
print(layer.renderer().symbol().symbolLayers()[0].properties())
+# Prints
+{'angle': '0',
+'cap_style': 'square',
+'color': '0,128,0,255',
+'horizontal_anchor_point': '1',
+'joinstyle': 'bevel',
+'name': 'circle',
+'offset': '0,0',
+'offset_map_unit_scale': '0,0',
+'offset_unit': 'MM',
+'outline_color': '0,0,0,255',
+'outline_style': 'solid',
+'outline_width': '0',
+'outline_width_map_unit_scale': '0,0',
+'outline_width_unit': 'MM',
+'scale_method': 'area',
+'size': '2',
+'size_map_unit_scale': '0,0',
+'size_unit': 'MM',
+'vertical_anchor_point': '1'}
+
+

如果要更改某些属性,这可能很有用:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
# 你可以更改单个属性... 
+layer.renderer().symbol().symbolLayer(0).setSize(3)
+# ...但并非所有属性都可以从方法访问,
+# 你也可以完全替换符号:
+props = layer.renderer().symbol().symbolLayer(0).properties()
+props['color'] = 'yellow'
+props['name'] = 'square'
+layer.renderer().setSymbol(QgsMarkerSymbol.createSimple(props))
+# 显示更改
+layer.triggerRepaint()
+
+

6.8.2 分类符号渲染器⚓︎

+

使用分类渲染器时,可以查询和设置用于分类的属性:使用 classAttribute()setClassAttribute()方法。

+

获取类别列表:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
categorized_renderer = QgsCategorizedSymbolRenderer()
+# 添加一些类别
+cat1 = QgsRendererCategory('1', QgsMarkerSymbol(), 'category 1')
+cat2 = QgsRendererCategory('2', QgsMarkerSymbol(), 'category 2')
+categorized_renderer.addCategory(cat1)
+categorized_renderer.addCategory(cat2)
+
+for cat in categorized_renderer.categories():
+    print("{}: {} :: {}".format(cat.value(), cat.label(), cat.symbol()))
+
+# 1: category 1 :: <qgis._core.QgsMarkerSymbol object at 0x7f378ffcd9d8>
+# 2: category 2 :: <qgis._core.QgsMarkerSymbol object at 0x7f378ffcd9d8>
+
+

其中value()是类别之间用于区别的值, label()是用于类别描述的文本,symbol()方法返回所分配的符号。

+

渲染器通常还存储用于分类的原始符号和色带:sourceColorRamp()sourceSymbol()方法。

+

6.8.3 渐变符号渲染器⚓︎

+

此渲染器与上面描述的分类符号渲染器非常相似,但它不是每个类的一个属性值,而是使用值范围,因此只能用于数字属性。

+

了解有关渲染器中使用范围的更多信息

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
graduated_renderer = QgsGraduatedSymbolRenderer()
+# 添加一些类别
+graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 0-100', 0, 100), QgsMarkerSymbol()))
+graduated_renderer.addClassRange(QgsRendererRange(QgsClassificationRange('class 101-200', 101, 200), QgsMarkerSymbol()))
+
+for ran in renderer.ranges():
+    print("{} - {}: {} {}".format(
+        ran.lowerValue(),
+        ran.upperValue(),
+        ran.label(),
+        ran.symbol()
+      ))
+# 0.0 - 100.0: class 0-100 <qgis._core.QgsMarkerSymbol object at 0x7f8bad281b88>
+# 101.0 - 200.0: class 101-200 <qgis._core.QgsMarkerSymbol object at 0x7f8bad281b88>
+
+

你可以再次使用 classAttribute() (查找分类属性名称) sourceSymbol()sourceColorRamp()方法。此外,还有一种mode()方法可以确定范围的创建方式:使用等间隔,分位数或其他方法。

+

如果你希望创建自己的渐变符号渲染器,则可以执行此操作,如下面的示例代码段所示(这将创建一个简单的两个类别)

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
from qgis.PyQt import QtGui
+
+myVectorLayer = QgsVectorLayer(myVectorPath, myName, 'ogr')
+myTargetField = 'target_field'
+myRangeList = []
+myOpacity = 1
+# 创建我们的第一符号和范围... 
+myMin = 0.0
+myMax = 50.0
+myLabel = 'Group 1'
+myColour = QtGui.QColor('#ffee00')
+mySymbol1 = QgsSymbol.defaultSymbol(myVectorLayer.geometryType())
+mySymbol1.setColor(myColour)
+mySymbol1.setOpacity(myOpacity)
+myRange1 = QgsRendererRange(myMin, myMax, mySymbol1, myLabel)
+myRangeList.append(myRange1)
+# 现在创建另一个符号和范围... 
+myMin = 50.1
+myMax = 100
+myLabel = 'Group 2'
+myColour = QtGui.QColor('#00eeff')
+mySymbol2 = QgsSymbol.defaultSymbol(
+     myVectorLayer.geometryType())
+mySymbol2.setColor(myColour)
+mySymbol2.setOpacity(myOpacity)
+myRange2 = QgsRendererRange(myMin, myMax, mySymbol2, myLabel)
+myRangeList.append(myRange2)
+myRenderer = QgsGraduatedSymbolRenderer('', myRangeList)
+myRenderer.setMode(QgsGraduatedSymbolRenderer.EqualInterval)
+myRenderer.setClassAttribute(myTargetField)
+
+myVectorLayer.setRenderer(myRenderer)
+QgsProject.instance().addMapLayer(myVectorLayer)
+
+

6.8.4 使用符号⚓︎

+

对于符号的表示,QgsSymbol基类有三个派生类:

+ +

每个符号由一个或多个符号图层 (从QgsSymbolLayer派生的类)。符号图层执行实际渲染,符号类本身仅用作符号图层的容器。

+

拥有一个符号实例(例如来自渲染器),可以探索它:type()方法说明它是标记、线还是填充符号。dump() 方法可以返回符号的简短描述。获取符号图层列表:

+
1
+2
+3
+4
+5
+6
marker_symbol = QgsMarkerSymbol()
+for i in range(marker_symbol.symbolLayerCount()):
+    lyr = marker_symbol.symbolLayer(i)
+    print("{}: {}".format(i, lyr.layerType()))
+
+# 0: SimpleMarker
+
+

找出符号的颜色使用color()方法,setColor()改变其颜色。使用标记符号,你还可以使用size()angle()方法查询符号大小和旋转。对于线符号,width()方法返回线宽。

+

默认情况下,大小和宽度以毫米为单位,角度以度为单位。

+

6.8.4.1 使用符号图层⚓︎

+

如前所述,符号层(QgsSymbolLayer的子类)决定要素的外观。有一些基本的符号图层类用于一般用途。可以实现新的符号图层类型,任意定制要素的呈现方式。layerType() 方法唯一标识符号图层类——基本类和默认类SimpleMarkerSimpleLine以及SimpleFill符号图层类型。

+

你可以使用以下代码获取可以为符号图层创建符号图层类型的完整列表:

+
1
+2
+3
+4
+5
from qgis.core import QgsSymbolLayerRegistry
+myRegistry = QgsApplication.symbolLayerRegistry()
+myMetadata = myRegistry.symbolLayerMetadata("SimpleFill")
+for item in myRegistry.symbolLayersForType(QgsSymbol.Marker):
+    print(item)
+
+

输出:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
AnimatedMarker
+EllipseMarker
+FilledMarker
+FontMarker
+GeometryGenerator
+MaskMarker
+RasterMarker
+SimpleMarker
+SvgMarker
+VectorField
+
+

QgsSymbolLayerRegistry类管理一个所有可用符号层类型的数据库。

+

访问符号图层数据,使用properties()方法返回属性的键值字典,该字典决定外观。每个符号图层类型都有一组特定的属性。此外,还有通用的方法color()size()angle()width()。当然,尺寸和角度仅适用于标记符号图层,宽度适用于线符号图层。

+

6.8.4.2 创建自定义符号图层类型⚓︎

+

想象一下,你想要自定义数据的呈现方式。你可以创建自己的符号图层类,完全按照你的意愿绘制要素。以下是绘制具有指定半径的红色圆圈的标记示例:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
from qgis.core import QgsMarkerSymbolLayer
+from qgis.PyQt.QtGui import QColor
+
+class FooSymbolLayer(QgsMarkerSymbolLayer):
+
+  def __init__(self, radius=4.0):
+      QgsMarkerSymbolLayer.__init__(self)
+      self.radius = radius
+      self.color = QColor(255,0,0)
+
+  def layerType(self):
+     return "FooMarker"
+
+  def properties(self):
+      return { "radius" : str(self.radius) }
+
+  def startRender(self, context):
+    pass
+
+  def stopRender(self, context):
+      pass
+
+  def renderPoint(self, point, context):
+      # 渲染取决于是否选择了符号 (QGIS >= 1.5)
+      color = context.selectionColor() if context.selected() else self.color
+      p = context.renderContext().painter()
+      p.setPen(color)
+      p.drawEllipse(point, self.radius, self.radius)
+
+  def clone(self):
+      return FooSymbolLayer(self.radius)
+
+

layerType()方法确定符号图层的名称,它必须在所有符号层中是唯一的。properties()方法用于属性的持久化。clone() 方法必须返回符号图层的副本,其中所有属性完全相同。最后,渲染方法: startRender()在渲染第一个要素之前被调用,stopRender() 渲染完成时被调用,renderPoint()渲染时被调用。点的坐标已经转换为输出坐标。

+

对于线和多边形,唯一的区别在于渲染方法:你将使用 renderPolyline()接收线列表,renderPolygon()接收外环上的点列表作为第一个参数和内环列表(或None)作为第二个参数。

+

通常可以便利地添加用于设置符号图层类型属性的GUI,以允许用户自定义外观:在上面的示例中,我们可以让用户设置圆半径。以下代码实现了这样的控件

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
from qgis.gui import QgsSymbolLayerWidget
+
+class FooSymbolLayerWidget(QgsSymbolLayerWidget):
+    def __init__(self, parent=None):
+        QgsSymbolLayerWidget.__init__(self, parent)
+
+        self.layer = None
+
+        # 设置简单的UI 
+        self.label = QLabel("Radius:")
+        self.spinRadius = QDoubleSpinBox()
+        self.hbox = QHBoxLayout()
+        self.hbox.addWidget(self.label)
+        self.hbox.addWidget(self.spinRadius)
+        self.setLayout(self.hbox)
+        self.connect(self.spinRadius, SIGNAL("valueChanged(double)"), \
+            self.radiusChanged)
+
+    def setSymbolLayer(self, layer):
+        if layer.layerType() != "FooMarker":
+            return
+        self.layer = layer
+        self.spinRadius.setValue(layer.radius)
+
+    def symbolLayer(self):
+        return self.layer
+
+    def radiusChanged(self, value):
+        self.layer.radius = value
+        self.emit(SIGNAL("changed()"))
+
+

此窗口控件可以嵌入到符号属性对话框中。在符号属性对话框中选择符号图层类型时,它会创建符号图层的实例和符号图窗口控件的实例。然后它调用setSymbolLayer()方法将符号图层分配给窗口控件。在该方法中,控件应该更新UI以反映符号层的属性。symbolLayer()方法用于通过属性对话框再次检索符号图层,将其用于符号。

+

在每次更改属性时,窗口控件都应发出changed()信号,让属性对话框更新符号预览。

+

现在我们只缺少最后的粘合剂:让QGIS了解这些新类。这是通过将符号图层添加到注册表来完成的。也可以在不将其添加到注册表的情况下使用符号图层,但某些功能不起作用:例如,使用自定义符号图层加载项目文件或无法在GUI中编辑图层的属性。

+

我们必须为符号图层创建元数据

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
from qgis.core import QgsSymbol, QgsSymbolLayerAbstractMetadata, QgsSymbolLayerRegistry
+
+
+class FooSymbolLayerMetadata(QgsSymbolLayerAbstractMetadata):
+
+    def __init__(self):
+        QgsSymbolLayerAbstractMetadata.__init__(self, "FooMarker", QgsSymbol.Marker)
+
+    def createSymbolLayer(self, props):
+        radius = float(props["radius"]) if "radius" in props else 4.0
+        return FooSymbolLayer(radius)
+
+    def createSymbolLayer(self, props):
+        radius = float(props["radius"]) if "radius" in props else 4.0
+        return FooSymbolLayer(radius)
+
+
+fslmetadata = FooSymbolLayerMetadata()
+QgsApplication.symbolLayerRegistry().addSymbolLayerType(fslmetadata)
+
+

你应该将图层类型(与图层返回的相同)和符号类型(marker/line/fill)传递给父类的构造函数。createSymbolLayer()方法负责使用props字典中指定的属性创建符号图层的实例。createSymbolLayerWidget()方法可以返回此符号图层类型的设置控件。

+

最后一步是将此符号图层添加到注册表中——我们完成了。

+

6.8.5 创建自定义渲染器⚓︎

+

如果要自定义如何选择符号呈现要素的规则,则创建新的渲染器可能很有用。你可能希望做一些用例:符号由字段组合确定,符号大小根据当前比例而变化等。

+

下面的代码显示了一个简单的自定义渲染器,它可以创建两个标记符号,并为每个要素随机选择其中一个

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
import random
+from qgis.core import QgsWkbTypes, QgsSymbol, QgsFeatureRenderer
+
+
+class RandomRenderer(QgsFeatureRenderer):
+  def __init__(self, syms=None):
+    QgsFeatureRenderer.__init__(self, "RandomRenderer")
+    self.syms = syms if syms else [QgsSymbol.defaultSymbol(QgsWkbTypes.geometryType(QgsWkbTypes.Point))]
+
+  def symbolForFeature(self, feature):
+    return random.choice(self.syms)
+
+  def startRender(self, context, vlayer):
+    for s in self.syms:
+      s.startRender(context)
+
+  def stopRender(self, context):
+    for s in self.syms:
+      s.stopRender(context)
+
+  def usedAttributes(self):
+    return []
+
+  def clone(self):
+    return RandomRenderer(self.syms)
+
+

父类QgsFeatureRenderer 的构造函数需要一个渲染器名称(在渲染器中必须是唯一的)。symbolForFeature()方法决定什么符号用于特定的要素。 startRender()stopRender()负责符号渲染的初始化/完成。usedAttributes() 方法返回渲染器的字段名称列表。最后,clone()方法应返回渲染器的副本。

+

与符号图层一样,可以附加GUI配置渲染器,它必须来源于QgsRendererWidget。以下示例代码创建一个允许用户设置第一个符号的按钮

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
from qgis.gui import QgsRendererWidget, QgsColorButton
+
+
+class RandomRendererWidget(QgsRendererWidget):
+    def __init__(self, layer, style, renderer):
+        QgsRendererWidget.__init__(self, layer, style)
+        if renderer is None or renderer.type() != "RandomRenderer":
+            self.r = RandomRenderer()
+        else:
+            self.r = renderer
+        # setup UI
+        self.btn1 = QgsColorButton()
+        self.btn1.setColor(self.r.syms[0].color())
+        self.vbox = QVBoxLayout()
+        self.vbox.addWidget(self.btn1)
+        self.setLayout(self.vbox)
+        self.connect(self.btn1, SIGNAL("clicked()"), self.setColor1)
+
+    def setColor1(self):
+        color = QColorDialog.getColor(self.r.syms[0].color(), self)
+        if not color.isValid(): return
+        self.r.syms[0].setColor(color)
+        self.btn1.setColor(self.r.syms[0].color())
+
+    def renderer(self):
+        return self.r
+
+

构造函数接收当前图层(QgsVectorLayer),全局样式(QgsStyle)和当前渲染器的实例。如果没有渲染器或渲染器具有不同的类型,它将被我们的新渲染器替换,否则我们将使用当前渲染器(已经是我们需要的类型)。应该更新窗口控件内容以显示渲染器的当前状态。当接受渲染器对话框时,将调用窗口控件的renderer()方法获取当前渲染器——它将被分配给该图层。

+

最后一个缺失的是渲染器元数据和注册表中的注册项,否则使用渲染器加载图层将不起作用,用户将无法从渲染器列表中选择它。RandomRenderer示例:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
from qgis.core import QgsRendererAbstractMetadata, QgsRendererRegistry, QgsApplication
+
+
+class RandomRendererMetadata(QgsRendererAbstractMetadata):
+    def __init__(self):
+        QgsRendererAbstractMetadata.__init__(self, "RandomRenderer", "Random renderer")
+
+    def createRenderer(self, element):
+        return RandomRenderer()
+
+    def createRendererWidget(self, layer, style, renderer):
+        return RandomRendererWidget(layer, style, renderer)
+
+
+rrmetadata = RandomRendererMetadata()
+QgsApplication.rendererRegistry().addRenderer(rrmetadata)
+
+

与符号图层类似,抽象元数据构造函数等待渲染器名称,对用户可见的名称以及渲染器图标的可选名称。createRenderer() 方法传递一个QDomElement实例,该实例可用于从DOM树恢复渲染器的状态。createRendererWidget() 方法创建配置控件。如果渲染器没有GUI,它不必存在或可以返回None

+

要将图标与渲染器关联,可以在QgsRendererAbstractMetadata 构造函数中将其指定为第三个(可选)参数

+
1
+2
+3
+4
QgsRendererAbstractMetadata.__init__(self,
+       "RandomRenderer",
+       "Random renderer",
+       QIcon(QPixmap("RandomRendererIcon.png", "png")))
+
+

也可以使用元数据类的setIcon方法关联该图标。图标可以从文件加载(如上所示),也可以从Qt资源加载 (PyQt5包含Python的.qrc编译文件)。

+

6.9 更多话题⚓︎

+

TODO:

+
    +
  • 创建/修改符号
  • +
  • 使用样式(QgsStyle
  • +
  • 使用色带(QgsColorRamp
  • +
  • 探索符号图层和渲染器注册表
  • +
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/7-\345\207\240\344\275\225\345\244\204\347\220\206/index.html" "b/7-\345\207\240\344\275\225\345\244\204\347\220\206/index.html" new file mode 100644 index 0000000..5d5dca0 --- /dev/null +++ "b/7-\345\207\240\344\275\225\345\244\204\347\220\206/index.html" @@ -0,0 +1,1230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 7-几何处理 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

7 几何处理⚓︎

+

此页面上的代码片段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
from qgis.core import (
+  QgsGeometry,
+  QgsGeometryCollection
+  QgsPoint,
+  QgsPointXY,
+  QgsWkbTypes,
+  QgsProject,
+  QgsFeatureRequest,
+  QgsVectorLayer,
+  QgsDistanceArea,
+  QgsUnitTypes,
+  QgsCoordinateTransform,
+  QgsCoordinateReferenceSystem
+)
+
+

表示空间要素的点、线和多边形通常称为几何。在QGIS中,它们用QgsGeometry类来表示 。

+

有时,一种几何实际上是简单(单部件)几何的集合。另一种几何形状称为多部件几何。如果它只包含一种类型的简单几何,我们称之为多点、多线或多多边形。例如,由多个岛组成的国家可以表示为多多边形。

+

几何的坐标可以在任何坐标参考系统(CRS)中。从图层中提取要素时,关联的几何图形将在图层的CRS中具有坐标。

+

有关所有可访问的几何结构和关系说明,请参阅OGC简单要素访问标准,以获取更详细的信息。

+

7.1 几何构造⚓︎

+

PyQGIS提供了几种创建几何的选项:

+
    +
  • 坐标
  • +
+
1
+2
+3
+4
+5
+6
+7
gPnt = QgsGeometry.fromPointXY(QgsPointXY(1,1))
+print(gPnt)
+gLine = QgsGeometry.fromPolyline([QgsPoint(1, 1), QgsPoint(2, 2)])
+print(gLine)
+gPolygon = QgsGeometry.fromPolygonXY([[QgsPointXY(1, 1),
+    QgsPointXY(2, 2), QgsPointXY(2, 1)]])
+print(gPolygon)
+
+

使用QgsPoint类或QgsPointXY 类创建坐标。这些类之间的区别在于QgsPoint支持M和Z维度。

+

折线(Linestring)由一系列点表示。

+

多边形由线环列表(即闭合的线段)表示。第一个环是外环(边界),第二个可选项线环是多边形中的孔。请注意,与某些程序不同,QGIS会为你闭合环,因此无需将第一个点复制为最后一个。

+

多部件几何图形更进一步:多点是一个点列表,多线是一个线列表,多多边形是一个多边形列表。

+
    +
  • WKT
  • +
+
1
+2
gem = QgsGeometry.fromWkt("POINT(3 4)")
+print(geom)
+
+
    +
  • WKB
  • +
+
1
+2
+3
+4
+5
+6
g = QgsGeometry()
+wkb = bytes.fromhex("010100000000000000000045400000000000001440")
+g.fromWkb(wkb)
+
+#使用WKT打印几何
+print(g.asWkt())
+
+

7.2 访问几何⚓︎

+

首先,你应该找出几何类型。wkbType() 方法是其中一种方法。它从QgsWkbTypes.Type 枚举中返回一个值。

+
1
+2
+3
+4
+5
+6
+7
+8
+9
if gPnt.wkbType() == QgsWkbTypes.Point:
+  print(gPnt.wkbType())
+  # output: 1 for Point
+if gLine.wkbType() == QgsWkbTypes.LineString:
+  print(gLine.wkbType())
+  # output: 2 for LineString
+if gPolygon.wkbType() == QgsWkbTypes.Polygon:
+  print(gPolygon.wkbType())
+  # output: 3 for Polygon
+
+

作为替代方案,可以使用type() ,从QgsWkbTypes.GeometryType 枚举中返回值。

+

你可以使用displayString() 函数来获取人类可读的几何类型。

+
1
+2
+3
+4
+5
+6
print(QgsWkbTypes.displayString(gPnt.wkbType()))
+# output: 'Point'
+print(QgsWkbTypes.displayString(gLine.wkbType()))
+# output: 'LineString'
+print(QgsWkbTypes.displayString(gPolygon.wkbType()))
+# output: 'Polygon'
+
+

还有一个辅助函数 isMultipart()可以确定几何是否是多部件 。

+

从几何中提取信息,每种矢量类型都有访问器函数。以下是如何使用这些访问器的示例:

+
1
+2
+3
+4
+5
+6
gPnt.asPoint()
+# output: <QgsPointXY: POINT(1 1)>
+gLine.asPolyline()
+# output: [<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>]
+gPolygon.asPolygon()
+# output: [[<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>, <QgsPointXY: POINT(2 1)>, <QgsPointXY: POINT(1 1)>]]
+
+
+

提示

+

元组(x,y)不是真正的元组,它们是QgsPoint 对象,可以使用x()y()方法访问这些值。

+
+

对于多部件几何也有类似的访问函数: asMultiPoint()asMultiPolyline()asMultiPolygon()

+

不用管几何的类型,直接遍历所有几何是可以的,例如:

+
1
+2
+3
+4
+5
+6
+7
geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
+for part in geom.parts():
+  print(part.asWkt())
+
+# Point (0 0)
+# Point (1 1)
+# Point (2 2)
+
+
1
+2
+3
+4
+5
geom = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
+for part in geom.parts():
+  print(part.asWkt())
+
+# LineString (0 0, 10 10)
+
+
1
+2
+3
+4
+5
gc = QgsGeometryCollection()
+gc.fromWkt('GeometryCollection( Point(1 2), Point(11 12), LineString(33 34, 44 45))')
+print(gc[1].asWkt())
+
+# Point (11 12)
+
+

使用 QgsGeometry.parts() 方法修改所有几何。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
+for part in geom.parts():
+  part.transform(QgsCoordinateTransform(
+    QgsCoordinateReferenceSystem("EPSG:4326"),
+    QgsCoordinateReferenceSystem("EPSG:3111"),
+    QgsProject.instance())
+  )
+
+print(geom.asWkt())
+
+# MultiPoint ((-10334728.12541878595948219 -5360106.25905461423099041),(-10462135.16126426123082638 -5217485.4735023295506835),(-10589399.84444035589694977 -5072021.45942386891692877))
+
+

7.3 几何谓词与操作⚓︎

+

QGIS使用GEOS库进行高级几何操作,如几何谓词(contains()intersects(),...),操作(combine()difference(),...)。它还可以计算几何的几何属性,例如面积(多边形)或长度(多边形和线)。

+

让我们看一个结合遍历指定图层要素,并基于它们的几何执行一些几何计算的示例。下面的代码计算并打印countries 图层中每个国家的面积和周长。

+

以下代码假定layer是具有多边形要素类型的QgsVectorLayer对象。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
# 访问'countries'图层
+layer = QgsProject.instance().mapLayersByName('countries')[0]
+
+# 过滤以Z开头的国家,然后获取其要素
+query = '"name" LIKE \'Z%\''
+features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
+
+
+# 现在遍历要素,执行几何计算并打印结果
+for f in features:
+  geom = f.geometry()
+  name = f.attribute('NAME')
+  print(name)
+  print('Area: ', geom.area())
+  print('Perimeter: ', geom.length())
+
+
+# Zambia
+# Area:  62.822790653431205
+# Perimeter:  50.65232014052552
+# Zimbabwe
+# Area:  33.41113559136521
+# Perimeter:  26.608288555013935
+
+

现在,你已经计算并打印了几何图形的面积和周长。但是,你可能会很快注意到这些值很奇怪。这是因为当使用 QgsGeometry类中的area()length() 方法计算时,面积和周长不会考虑CRS。可以使用更强大的QgsDistanceArea 类计算面积和周长,它可以执行基于椭球的计算:

+

以下代码假定layer是具有多边形要素类型的QgsVectorLayer对象。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
d = QgsDistanceArea()
+d.setEllipsoid('WGS84')
+
+layer = QgsProject.instance().mapLayersByName('countries')[0]
+
+# 过滤以Z开头的国家,然后获取其要素
+query = '"name" LIKE \'Z%\''
+features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
+
+for f in features:
+  geom = f.geometry()
+  name = f.attribute('NAME')
+  print(name)
+  print("Perimeter (m):", d.measurePerimeter(geom))
+  print("Area (m2):", d.measureArea(geom)) ))
+  # 打印(“面积(m2):” , d 。measureArea (GEOM ))
+
+  # 计算并重新打印面积,单位为平方公里
+  print("Area (km2):", d.convertAreaMeasurement(d.measureArea(geom), QgsUnitTypes.AreaSquareKilometers))
+
+# Zambia
+# Perimeter (m): 5539361.250294601
+# Area (m2): 751989035032.9031
+# Area (km2): 751989.0350329031
+# Zimbabwe
+# Perimeter (m): 2865021.3325076113
+# Area (m2): 389267821381.6008
+# Area (km2): 389267.8213816008
+
+

或者,你可能想知道两点之间的距离和方位。

+
1
+2
+3
+4
+5
+6
+7
+8
+9
d = QgsDistanceArea()
+d.setEllipsoid('WGS84')
+
+# 让我们创造两个点
+# 圣诞老人是一个工作狂,他需要放个暑假,让我们看一下他家离特内里费有多远
+santa = QgsPointXY(25.847899, 66.543456)
+tenerife = QgsPointXY(-16.5735, 28.0443)
+
+print("Distance in meters: ", d.measureLine(santa, tenerife))
+
+

你可以在QGIS中找到许多算法示例,并使用这些方法来分析和转换矢量数据。以下是一些代码的链接。

+ + + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/8-\346\212\225\345\275\261\346\224\257\346\214\201/index.html" "b/8-\346\212\225\345\275\261\346\224\257\346\214\201/index.html" new file mode 100644 index 0000000..1a83d19 --- /dev/null +++ "b/8-\346\212\225\345\275\261\346\224\257\346\214\201/index.html" @@ -0,0 +1,1029 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 8-投影支持 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + +

8 投影支持⚓︎

+

此页面上的代码片段需要导入以下模块:

+
1
+2
+3
+4
+5
+6
from qgis.core import (
+    QgsCoordinateReferenceSystem,
+    QgsCoordinateTransform,
+    QgsProject,
+    QgsPointXY,
+)
+
+

8.1 坐标参考系统⚓︎

+

坐标参考系统(CRS)由QgsCoordinateReferenceSystem 类封装 。可以通过几种不同的方式创建此类的实例:

+
    +
  • ID
  • +
+
1
+2
+3
+4
# WGS84:4326
+crs = QgsCoordinateReferenceSystem("EPSG:4326")
+print(crs.isValid())
+# True
+
+

QGIS支持不同的CRS识别符,支持以下格式:

+ +

如果未指定前缀,则默认使用WKT定义。

+
    +
  • 通过WKT指定CRS
  • +
+
1
+2
+3
+4
+5
+6
wkt = 'GEOGCS["WGS84", DATUM["WGS84", SPHEROID["WGS84", 6378137.0, 298.257223563]],' \
+      'PRIMEM["Greenwich", 0.0], UNIT["degree",0.017453292519943295],' \
+      'AXIS["Longitude",EAST], AXIS["Latitude",NORTH]]'
+crs = QgsCoordinateReferenceSystem(wkt)
+print(crs.isValid())
+# True
+
+
    +
  • 创建一个无效的CRS,然后使用其中一个create*函数进行初始化。在下面的示例中,我们使用Proj字符串初始化投影。
  • +
+
1
+2
+3
+4
crs = QgsCoordinateReferenceSystem()
+crs.createFromProj("+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs")
+print(crs.isValid())
+# True
+
+

检查CRS的创建(即在数据库中查找)是否成功是明智的:isValid()必须返回True

+

请注意,对于空间参考系统的初始化,QGIS需要在其内部数据库srs.db中查找适当的值。因此,如果你创建一个独立的应用程序,你需要使用QgsApplication.setPrefixPath()正确设置路径 ,否则将无法找到数据库。如果你在QGIS Python控制台中运行命令或开发插件,则无需关注:一切都已经为你准备好了。

+

访问空间参考系统信息:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
crs = QgsCoordinateReferenceSystem(4326)
+
+print("QGIS CRS ID:", crs.srsid())
+print("PostGIS SRID:", crs.postgisSrid())
+print("Description:", crs.description())
+print("Projection Acronym:", crs.projectionAcronym())
+print("Ellipsoid Acronym:", crs.ellipsoidAcronym())
+print("Proj4 String:", crs.toProj4())
+# 检查是地理坐标系统还是投影坐标系统
+print("Is geographic:", crs.isGeographic())
+# 检查CRS的地图单位类型(在QGis::units枚举中定义)
+print("Map units:", crs.mapUnits())
+
+

输出:

+
1
+2
+3
+4
+5
+6
+7
+8
QGIS CRS ID: 3452
+PostGIS SRID: 4326
+Description: WGS 84
+Projection Acronym: longlat
+Ellipsoid Acronym: WGS84
+Proj4 String: +proj=longlat +datum=WGS84 +no_defs
+Is geographic: True
+Map units: DistanceUnit.Degrees
+
+

8.2 坐标参考系统转换⚓︎

+

你可以使用QgsCoordinateTransform类在不同的空间参考系之间进行转换。使用它的最简单方法是创建原始和目标CRS,并在当前项目中构造QgsCoordinateTransform实例。然后只需反复调用 transform()函数进行转换。默认情况下,它会正向转换,但也可以逆向转换。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
crsSrc = QgsCoordinateReferenceSystem(4326)    # WGS 84
+crsDest = QgsCoordinateReferenceSystem(32633)  # WGS 84 / UTM zone 33N
+xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance())
+
+# 正向转换: src -> dest
+pt1 = xform.transform(QgsPointXY(18,5))
+print("Transformed point:", pt1)
+
+# 逆向转换: dest -> src
+pt2 = xform.transform(pt1, QgsCoordinateTransform.ReverseTransform)
+print("Transformed back:", pt2)
+
+

输出:

+
1
+2
Transformed point: <QgsPointXY: POINT(832713.79873844375833869 553423.98688333143945783)>
+Transformed back: <QgsPointXY: POINT(18 4.99999999999999911)>
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git "a/9-\344\275\277\347\224\250\345\234\260\345\233\276\347\224\273\345\270\203/index.html" "b/9-\344\275\277\347\224\250\345\234\260\345\233\276\347\224\273\345\270\203/index.html" new file mode 100644 index 0000000..0426ce2 --- /dev/null +++ "b/9-\344\275\277\347\224\250\345\234\260\345\233\276\347\224\273\345\270\203/index.html" @@ -0,0 +1,1487 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 9-使用地图画布 - PyQGIS开发者手册 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + +

本节代码片段需要导入以下模块:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
from qgis.PyQt.QtGui import (
+    QColor,
+)
+
+from qgis.PyQt.QtCore import Qt, QRectF
+
+from qgis.PyQt.QtWidgets import QMenu
+
+from qgis.core import (
+    QgsVectorLayer,
+    QgsPoint,
+    QgsPointXY,
+    QgsProject,
+    QgsGeometry,
+    QgsMapRendererJob,
+    QgsWkbTypes,
+)
+
+from qgis.gui import (
+    QgsMapCanvas,
+    QgsVertexMarker,
+    QgsMapCanvasItem,
+    QgsMapMouseEvent,
+    QgsRubberBand,
+)
+
+

9 使用地图画布⚓︎

+

地图画布控件可能是QGIS中最重要的控件,因为它显示了由重叠地图图层组成的地图,并允许与地图和图层进行交互。画布始终显示由当前画布范围定义地图的一部分。通过使用 地图工具 完成交互:有平移,缩放,识别图层,测量,矢量编辑等工具。与其他图形程序类似,总有一个工具处于活动状态,用户可以在可用工具之间切换。

+

地图画布由qgis.gui模块中的QgsMapCanvas类实现。该类基于Qt Graphics View框架。该框架通常提供场景和视图,其中放置自定义图形项,并且用户可以与它们交互。我们假设你对Qt足够熟悉,了解图形场景,视图和项的概念。如果没有,请阅读框架概述

+

无论何时平移,放大/缩小(或触发刷新的其他操作)地图,地图都会在当前范围内再次渲染。图层将渲染为图像(使用QgsMapRendererJob类),并显示在画布上。QgsMapCanvas类还控制渲染图的刷新。除了作为背景的项,可能还有更多的 地图画布项

+

典型的地图画布项是橡皮条(用于测量,矢量编辑等)或顶点标记。画布项通常用于给地图工具提供视觉反馈,例如,在创建新多边形时,地图工具会创建一个橡皮条画布项,显示多边形的当前形状。所有地图画布项都是QgsMapCanvasItem 的子类,它为基类QGraphicsItem对象添加了更多功能。

+

总而言之,地图画布架构包含三个概念:

+
    +
  • 地图画布——用于浏览地图
  • +
  • 地图画布项——可以在地图画布上显示的其他项
  • +
  • 地图工具——用于与地图画布交互
  • +
+

9.1 嵌入地图画布⚓︎

+

地图画布是一个控件,就像任何其他Qt控件一样,因此使用它就像创建和显示它一样简单

+
1
+2
canvas = QgsMapCanvas()
+canvas.show()
+
+

这将生成一个带有地图画布的独立窗口。它也可以嵌入到现有的控件或窗口中。使用.ui文件和Qt设计师时,在表单上放置一个QWidget并将其提升为新类:设置QgsMapCanvas为类名并设置qgis.gui为头文件。pyuic5工具将搞定它(译者注:编译为py脚本文件)。这是嵌入画布的一种非常方便的方法。另一种可能性是手动编写代码构造地图画布和其他控件(作为主窗口或对话框的子窗口)并创建布局。

+

默认情况下,地图画布具有黑色背景,不使用消除锯齿。设置白色背景并启用抗锯齿来实现平滑渲染

+
1
+2
canvas.setCanvasColor(Qt.white)
+canvas.enableAntiAliasing(True)
+
+

(如果你想知道,Qt来自PyQt.QtCore模块,并且 Qt.white是预定义的QColor实例之一。)

+

现在是时候添加一些地图图层了。我们首先打开一个图层并将其添加到当前项目中。然后我们将设置画布范围并设置画布的图层列表

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
vlayer = QgsVectorLayer("testdata/airports.shp", "Airports layer", "ogr")
+if not vlayer.isValid():
+    print("图层加载失败!")
+
+# 将图层添加到注册表
+QgsProject.instance().addMapLayer(vlayer)
+
+# 缩放到图层
+canvas.setExtent(vlayer.extent())
+
+# 设置地图画布的图层集
+canvas.setLayers([vlayer])
+
+

执行这些命令后,画布将显示已加载的图层。

+

9.2 橡皮条和顶点标记⚓︎

+

在画布上的地图顶部显示一些其他数据,使用地图画布项。可以创建自定义画布项类(如下所述),但为方便起见,有两个有用的画布项类:QgsRubberBand用于绘制折线或多边形,QgsVertexMarker绘制点。它们都使用地图坐标,因此在平移或缩放画布时会自动移动/缩放形状。

+

显示折线

+
1
+2
+3
r = QgsRubberBand(canvas, QgsWkbTypes.LineGeometry)  # 线
+points = [QgsPointXY(-100, 45), QgsPointXY(10, 60), QgsPointXY(120, 45)]
+r.setToGeometry(QgsGeometry.fromPolyline(points), None)
+
+

显示多边形

+
1
+2
+3
r = QgsRubberBand(canvas, QgsWkbTypes.PolygonGeometry)  # 多边形
+points = [[QgsPointXY(-100, 35), QgsPointXY(10, 50), QgsPointXY(120, 35)]]
+r.setToGeometry(QgsGeometry.fromPolygonXY(points), None)
+
+

请注意,多边形的点不是普通列表:实际上,它是包含多边形线环的环列表:第一个环是外边框,第二个(可选)环对应于多边形中的孔。

+

橡皮条允许一些定制,即改变它们的颜色和线宽

+
1
+2
r.setColor(QColor(0, 0, 255))
+r.setWidth(3)
+
+

画布项绑定到画布场景。要暂时隐藏它们(并再次显示它们),请使用hide()show()组合。完全删除该项,你必须将其从画布的场景中删除

+
1
canvas.scene().removeItem(r)
+
+

(在C ++中,可以只删除该项,但是在Pythondel r中 只删除引用,并且该对象仍然存在,因为它由画布拥有)

+

橡皮条也可用于绘制点,但 QgsVertexMarker类更适合于此(QgsRubberBand仅在所需点周围绘制一个矩形)。

+

你可以像这样使用顶点标记:

+
1
+2
m = QgsVertexMarker(canvas)
+m.setCenter(QgsPointXY(10,40))
+
+

这将在位置 [10,45] 上绘制一个红十字。可以自定义图标类型,大小,颜色和宽度

+
1
+2
+3
+4
m.setColor(QColor(0, 255, 0))
+m.setIconSize(5)
+m.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X
+m.setPenWidth(3)
+
+

临时隐藏顶点标记并从画布中删除它们,使用与橡皮条相同的方法。

+

9.3 在画布中使用地图工具⚓︎

+

以下示例构造一个窗口,其中包含用于地图平移和缩放的地图画布和基本地图工具。激活每个工具:平移工具QgsMapToolPan,放大缩小工具QgsMapToolZoom。设置为可被选中,允许自动处理选中/未选中的操作状态——当激活地图工具时,一个工具被选中时,取消选中上一个工具。使用setMapTool()方法激活地图工具。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
from qgis.gui import *
+from qgis.PyQt.QtWidgets import QAction, QMainWindow
+from qgis.PyQt.QtCore import Qt
+
+
+class MyWnd(QMainWindow):
+    def __init__(self, layer):
+        QMainWindow.__init__(self)
+
+        self.canvas = QgsMapCanvas()
+        self.canvas.setCanvasColor(Qt.white)
+
+        self.canvas.setExtent(layer.extent())
+        self.canvas.setLayers([layer])
+
+        self.setCentralWidget(self.canvas)
+
+        self.actionZoomIn = QAction("Zoom in", self)
+        self.actionZoomOut = QAction("Zoom out", self)
+        self.actionPan = QAction("Pan", self)
+
+        self.actionZoomIn.setCheckable(True)
+        self.actionZoomOut.setCheckable(True)
+        self.actionPan.setCheckable(True)
+
+        self.actionZoomIn.triggered.connect(self.zoomIn)
+        self.actionZoomOut.triggered.connect(self.zoomOut)
+        self.actionPan.triggered.connect(self.pan)
+
+        self.toolbar = self.addToolBar("Canvas actions")
+        self.toolbar.addAction(self.actionZoomIn)
+        self.toolbar.addAction(self.actionZoomOut)
+        self.toolbar.addAction(self.actionPan)
+
+        # 创建地图工具
+        self.toolPan = QgsMapToolPan(self.canvas)
+        self.toolPan.setAction(self.actionPan)
+        self.toolZoomIn = QgsMapToolZoom(self.canvas, False)  # false = in
+        self.toolZoomIn.setAction(self.actionZoomIn)
+        self.toolZoomOut = QgsMapToolZoom(self.canvas, True)  # true = out
+        self.toolZoomOut.setAction(self.actionZoomOut)
+
+        self.pan()
+
+    def zoomIn(self):
+        self.canvas.setMapTool(self.toolZoomIn)
+
+    def zoomOut(self):
+        self.canvas.setMapTool(self.toolZoomOut)
+
+    def pan(self):
+        self.canvas.setMapTool(self.toolPan)
+
+

你可以在Python控制台编辑器中尝试上述代码。调用画布窗口,添加以下代码以实例化MyWnd类。它们将在新创建的画布上渲染当前选定的图层

+
1
+2
w = MyWnd(iface.activeLayer())
+w.show()
+
+

9.3.1 使用QgsMapToolIdentifyFeature选择要素⚓︎

+

你可以使用地图工具QgsMapToolIdentifyFeature为用户选择一个要素,这个要素将被传递给回调函数。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
def callback(feature):
+    """当用户选择要素后被调用"""
+    print("You clicked on feature {}".format(feature.id()))
+
+
+canvas = iface.mapCanvas()
+feature_identifier = QgsMapToolIdentifyFeature(canvas)
+
+# 表示被选择的图层
+feature_identifier.setLayer(vlayer)
+
+# 当用户识别要素时触发槽函数,使用回调函数
+feature_identifier.featureIdentified.connect(callback)
+
+# 激活这个地图工具
+canvas.setMapTool(feature_identifier)
+
+

9.3.2 将项目添加到地图画布上下文菜单⚓︎

+

使用地图画布也可以通过使用contextMenuAboutToShow信号添加到其上下文菜单中的条目来完成。

+

当你右键单击地图画布时,以下代码会在默认条目旁边添加 My MenuMy Action 操作。

+
1
+2
+3
+4
+5
+6
+7
+8
+9
# 用于填充上下文菜单的插槽
+def populateContextMenu(menu: QMenu, event: QgsMapMouseEvent):
+    subMenu = menu.addMenu('My Menu')
+    action = subMenu.addAction('My Action')
+    action.triggered.connect(lambda *args:
+                             print(f'Action triggered at {event.x()},{event.y()}'))
+
+canvas.contextMenuAboutToShow.connect(populateContextMenu)
+canvas.show()
+
+

9.4 编写自定义地图工具⚓︎

+

你可以编写自定义工具,来实现用户在画布上执行自定义行为的操作。

+

地图工具应继承自QgsMapTool类或任何派生类,并使用setMapTool() 在画布中选择为激活工具。

+

下面是一个地图工具示例,它允许通过在画布上单击并拖动来定义矩形范围。定义矩形后,它会在控制台中打印其边界坐标。它使用前面描述的橡皮条元素来显示所定义的矩形。

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
class RectangleMapTool(QgsMapToolEmitPoint):
+    def __init__(self, canvas):
+        self.canvas = canvas
+        QgsMapToolEmitPoint.__init__(self, self.canvas)
+        self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.PolygonGeometry)
+        self.rubberBand.setColor(Qt.red)
+        self.rubberBand.setWidth(1)
+        self.reset()
+
+    def reset(self):
+        self.startPoint = self.endPoint = None
+        self.isEmittingPoint = False
+        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
+
+    def canvasPressEvent(self, e):
+        self.startPoint = self.toMapCoordinates(e.pos())
+        self.endPoint = self.startPoint
+        self.isEmittingPoint = True
+        self.showRect(self.startPoint, self.endPoint)
+
+    def canvasReleaseEvent(self, e):
+        self.isEmittingPoint = False
+        r = self.rectangle()
+        if r is not None:
+            print("Rectangle:", r.xMinimum(),
+                  r.yMinimum(), r.xMaximum(), r.yMaximum()
+                  )
+
+    def canvasMoveEvent(self, e):
+        if not self.isEmittingPoint:
+            return
+
+        self.endPoint = self.toMapCoordinates(e.pos())
+        self.showRect(self.startPoint, self.endPoint)
+
+    def showRect(self, startPoint, endPoint):
+        self.rubberBand.reset(QgsWkbTypes.PolygonGeometry)
+        if startPoint.x() == endPoint.x() or startPoint.y() == endPoint.y():
+            return
+
+        point1 = QgsPointXY(startPoint.x(), startPoint.y())
+        point2 = QgsPointXY(startPoint.x(), endPoint.y())
+        point3 = QgsPointXY(endPoint.x(), endPoint.y())
+        point4 = QgsPointXY(endPoint.x(), startPoint.y())
+
+        self.rubberBand.addPoint(point1, False)
+        self.rubberBand.addPoint(point2, False)
+        self.rubberBand.addPoint(point3, False)
+        self.rubberBand.addPoint(point4, True)  # true to update canvas
+        self.rubberBand.show()
+
+    def rectangle(self):
+        if self.startPoint is None or self.endPoint is None:
+            return None
+        elif (self.startPoint.x() == self.endPoint.x() or \
+              self.startPoint.y() == self.endPoint.y()):
+            return None
+
+        return QgsRectangle(self.startPoint, self.endPoint)
+
+    def deactivate(self):
+        QgsMapTool.deactivate(self)
+        self.deactivated.emit()
+
+

9.5 编写自定义地图画布项⚓︎

+

这是一个自定义画布项的示例,该画布项绘制了一个圆:

+
 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
class CircleCanvasItem(QgsMapCanvasItem):
+    def __init__(self, canvas):
+        super().__init__(canvas)
+        self.center = QgsPoint(0, 0)
+        self.size = 100
+
+    def setCenter(self, center):
+        self.center = center
+
+    def center(self):
+        return self.center
+
+    def setSize(self, size):
+        self.size = size
+
+    def size(self):
+        return self.size
+
+    def boundingRect(self):
+        return QRectF(self.center.x() - self.size / 2,
+                      self.center.y() - self.size / 2,
+                      self.center.x() + self.size / 2,
+                      self.center.y() + self.size / 2)
+
+    def paint(self, painter, option, widget):
+        path = QPainterPath()
+        path.moveTo(self.center.x(), self.center.y());
+        path.arcTo(self.boundingRect(), 0.0, 360.0)
+        painter.fillPath(path, QColor("red"))
+
+
+# 使用自定义项:
+item = CircleCanvasItem(iface.mapCanvas())
+item.setCenter(QgsPointXY(200, 200))
+item.setSize(80)
+
+ + + + + + +
+
+ + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + \ No newline at end of file diff --git a/assets/1559181577361.png b/assets/1559181577361.png new file mode 100644 index 0000000000000000000000000000000000000000..c869d1506b3e98e1580f274278721e1980697d70 GIT binary patch literal 18959 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 literal 0 HcmV?d00001 diff --git a/assets/QgsAuthAuthoritiesEditor.png b/assets/QgsAuthAuthoritiesEditor.png new file mode 100644 index 0000000000000000000000000000000000000000..001f989d84781660f733372b75552701499cf3db GIT binary patch literal 183694 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

m+hDBO=Dl>nw*vITiK>}^_yFdc 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%0D_{0vH&Kagd5 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 literal 0 HcmV?d00001 diff --git a/assets/QgsAuthConfigSelect.png b/assets/QgsAuthConfigSelect.png new file mode 100644 index 0000000000000000000000000000000000000000..3df3642b636a1aa1d4a71974f49d241a44e37f45 GIT binary patch literal 22128 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 z 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 literal 0 HcmV?d00001 diff --git a/assets/bar-button.png b/assets/bar-button.png new file mode 100644 index 0000000000000000000000000000000000000000..f5ba619ff1ecc9cbbb5330d85977fed713280cbf GIT binary patch literal 11459 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 E2jn+N=Kufz literal 0 HcmV?d00001 diff --git a/assets/errorbar-timed.png b/assets/errorbar-timed.png new file mode 100644 index 0000000000000000000000000000000000000000..d7def66bd2caaece2256da98d3285ae4ddbd05ff GIT binary patch literal 11468 zcmZ8{1ymf(5^m7ovJfD+`y#>J39tm$g4 z!A0Y>^s};I@*Us?*;49_)U#)mahUg}D8N1X2L*kXXU}jt|9%j99gE%re}Y`)^jtL^ zEnGcJoz0)Ub~ZP6aItc9HPS`{ny||#%1UW@8XaVyYZ2!7<#|r>Lqb;eEN^m&DZB(c$dk;^knz(Bey`{kHzY%J1)*EG#V6*49O} zH8o$#b)Jb|oviiRJvx%kG&%RgQ5hQ<#lBSL9sxB5bw?5Z`5oky`6B?GEos>3fTZp)YgK7!w_Qkx!(6i^F{i~RD!*%~3;O^Yd)W75RSNFuz4qYe0uBq;83R{8 zYEpM)r>X8A9=yPaL=?$U#GEacPmf!Rtv=nW9!C#j)>93$2IF63|BRp-xA>&$zW=l{ zQFygK>$27p<16J&FVII%S!qWaC{OgTn%DS}C32~Abk=;U68y3+GM)!i_+(lduJ}MoP?>=$a z*`{N3`jrg5g}jRErn#Mzj?Pq-`GEVuoHkTv%6&`auGzcZXjFE8rm}N-T0lf3ZL2zx zqqHd}XSFR5$qK97xOHhMw&93RK!EOKFqP*yE@eqc$@&c;-j@$H(Y-o5^d6W(0yC2y ztIC|SFs2OAjpbFLCGWV;E;?3o0qB%XL~m8n1y6hn zGZuCoQrg<~l@i$HTspG)cwKQ2$S5rQ^($GGOVw~sPtPixotb(0h1d&JED|+6JrWS8 z%Ij=%bX2XV@cZ{2f!3QLOwRk$ZsMoC2-t`3%da{;5F}REdgOxGo`(zZ@x735pIdD7 zkl_)n6Z+8h>eVYH2*jy*1ult=j*brO9&}vXxaerv414?Q(;+%r>JQS(^OjK<`gINz zq~?O?nuBLr26!)j@{9V^5>{KPcGf+2KR7k6wM(hT2pnump$ymH6k?CXUEplB*yK!4 zOHO9vQS^mqj4`;l=W>NM-* z|1{Y|gb)=ANw%>*n5((FJV@g*4U+POhR-$!i>|J&esIf9W5_+%^V~5yT0L-4!a>es zq&hi04GJI56w!5g_^9dPVyZs&O(r}($kh;~DLMcIR#bsj-Z6Iw^N3l^f`2Gr^eL7& zr|h^)hg$y(ZPMFPP*Q@{*t~E*J8bsGr4*AU_;72un{nyROI+u$DDAScwH3~pwN;C! zzo9XcciPk>3O8|cbE_DWK`4=yXe=l|4cGP>;FuVX&YpPa`RN#^6mRdf7?Wl^qQ~uQ zTJhPX)(QMg&c6KWGsRGB1OA>)j6^EDv4;DoJ(x&Pu{8hr(1AyJ4c`lXf%jjM^R%_K zO^;w4ZzEqC=}vdT>4o|#VAQy1NN-rlSigMv@-3QIOW5yTus_X#LUwdV2D=D@k6he` zL@79aZEcM^CNff*4pBl%MG)2$;M9eRnC-?jcK;GND+q?UMz8$EC&!sOElt3mDYWrU zwjlpt-ZFBBJwBC~NA`Nf=jjtxOZG-vAPHv{Luu(yViI0{^G7jZud3s2V$L60XoDt| z>kmd{nk{SHSl`qjKeA~j-4I8u|f{loXh<^7cy)2pq%j2)cT%?oT z&$q{pny;2bj+%JPy3h#M+0MBdJ>s8}vKS85Cz@+*8Z*xeweof`OrA~T^_3gzNqoCe zQt#~y*Wl&l{XIRMCE_tWF+tGmXrvsa5AI9v>cHTMp^#9T3ZZ$gVurwj?d*2cl=_N? z=Nq=*wct%^svwhSdog2eXH-!8y$WPJzl2h81<8Asx7(LI+QIIg{4EuVg zAbJ~{l@eFKsSLv>WDQfHp`hH@f|nGESm`U2R_JD(g2WfOKPC!QWt@)*I21~{r`w}m zN=-@XJ^xrB;&IsZnR6a380zfdQ6~+N`*NXT6yYO`AWliJ(U7;-G6aQ_MU! ze|I!Kf`h=5TZVaZT+jH+AL|T4bLguW5O`=2R_Urb98`f>6H40etA3#7kBX3Aq6#2FGD}fI1jN?0+smC&9 zS?_M^I0@??t869&aY|LQ>LHS(z}+Y$CS-27|3i^x}vprIEg-rLke4n`}qRgS-`T;5Q85+8gA_NjF z7fUHdN=_b)n!Le_{gI8t?i-;^aQp|^8w~6jm)~BG_g+58Q_?Eim0vHbXC#Y`P7EIH z&|%$b=%vpJQntR=wuR*~ELSks{*dR`IfZQe`RQk%>7qTZ(RX+BN05XLTxDg`z~|6G zV$_2d2`6+jnDj1Uy-ZNasY>%Kh>NlY56vcKrg(wNe+cE4d^=dm{hgSm_8C4^0{ftj z{qhwu8IlPjR!YH}G{#1`XccsNxcxkTOUX_+4N5k}iXx)L70#)k`##B>luv`W?f|B$ zvNxUoxIgb(mi=Ed3uB)7R<{Fg`TB$Mv{p(!zP_e(Mpdx9IvmHy_e#eBqWyz8P~-?%r5eZ_|L}%RQh3zH)J&*CemLR?Cm*L z9EfJ6O3X~&HBO$WjagmAb28oeJeF<=9*Hq#f-gQ^X`fJAFviVP%|1W;>0gec(%|t_ zz)Vl{bJp^k&1D2o8a?Kh_1L7wK_TyVXF;{-B4I16r1(BRNyWbZacw&h_c$aUc@IKE zt?CY6Y0DCJi!8J$DlhNles{)2Ag@(VJ^LVLIM2RFU4`0l&V%J!p-4$dS+dhkcvb+m z!ms^U+jWcqiBaPJKwvR_n1QgT!}GvvVL{k*Yh&w@OHWS(2EX|eP0@`>BAo!+Jk)tJ z&#t%UJjf3ItkjIuj5ow%$^W1?Lgk~K`JXN;~K@} zY-%&|Gi^HUXH$?>X<;{UKcdFPx`hBC+-wi&9m7XK!?|TwPQIc}O}D3e#zEa~cFV%> z==gZN^(}_Iy%_5qX`@OzuJ_82Uj^$9nVtlkOup=_2}Jm28wYinO>ctb_6|2!uw*TX z-{6uBRcwj%z(W`jjWIdDKMVds);Zqqz!2@4o0~f}v{-G4i~7fQ zy38CZefK>taE{WK5GfaX9@lJu&J0A( z2Kg&%7K~GkujazVX32qpW@PZ98a*dh0?h$pzN2n&RRlF{B?1^(0*OtdHZB%eUv&-#LEA+lr}nvD@$jJy0k_bLe0#K zcF^crj+!%$6{EG|Fa_|6Habltwy@7f$|&Ufh{V;U}QrepAAn<$>2 zpIqA+X(TwUWg+`siOLc(i%s!CZCI+~0fY=Asd;Ih`(7X=#i5r$lY?d*U5=hF-)u~Y zSwgGcE4sN|426X`THJQXvlpOdcG2{F2FF4PZ)4I_-@c`Yt*@&S02WTi%hn)mdUrVi zGbsPo%}na}$}iCB6MHH7TT60Ac%Yn|9P=%f1N<(*wR)Mnyv*fg7O-c3UPqUluWojapu!nKl-lhaB^=$V&;Ov?E9 zH~*s5#~;uNRX(Sh;Q<`>hR50QCi|DBy#vIdmS%qMq`7}Aelese)y~PMkqKMIcrBH1 ze0*yFkx`1D!BMgz&2wA?hnb<)%IuTCN&HZx0QGUr zNR|ZE^kT$1;m$A8X#KR2EM=9RFEt-lq>X0CH8$+)97TryFc+&ut7G!7_*B48KYc<= zmpC{$z@-wGb((!68kWZZFSQc#GIjZ6Fx=y1BSg}B4wH!=NIoaPHN4)ev{aQthc>Xg zfB+?#T(7;tJ5O4Ibu85!6#EIySXfe1wM5`vLF`oskc zhR5+lEmFMO`uh6U*?D>N=dVAok6FGKlIud1_Ko|KXbJg>kaJH{(Js4ZWoz467s3K% zPAhU)?SDrcS$2XFn7@>ns{itW`wk+09Uz2`zyzR5kmg`vej$f(W%L$*h&uS79Q`k&sAjQx1$9XfFKBFqSzqJ8XIIHODBe zf@+1D(xo4=%#^h+MpLP6uHAi}%>$_N8&fVt2P%?L`YG@YUQF5!TZhO^H_nDD^@G11Lu?;K_18a+g$~^La+^o}V=`s1$bL2k zrU(RqV{$}g5;HEIZ=g(>WK5w5W?FAz^1!aUH;r7f-Rwa2L1z4mM55 z&d!dGjy8yixuO!aYW0Y#38sq?H4yfHY?6!f$I@}Yg0Q`AeR=TWYdLUkH;EsDYZ(}L z@BU_SW&EuHf`=^CcA!R@x)0dy?P_XN&x+2Uo-fcE=_Lc_PD%gg>X7c8P61^05N{~y z*&$r}(@`jdrsXtXuX~wHa*Ae7A>FcK4Adfk+9Mt2xN2I>HP!C0M!T*^1R@})cKp*K z6OMCYj=g(F`1O#rOmc8pay7W(3}9u5ISdt4RW0jIqrIftt5LgG5sd*lS8X{0V8`FWo{J4`f@EU|t2ey70s>dNlY3hd zPe0qoggW<^Ql~&=+*Y8%I{*N>YNYs)BnM?+7Pfd=2Pb_b-H#?*m-RC`WwBx5X+@SZ zA=YOJ{W5|XEotU1#WG?V`}yqUk_`({}H_MMQ73Lf{hrL8Lfw2#EThxbam-+cHGLj z&wceG!l{~rjOp8LwT*4%Ym0KeC_H=mVWb^K$_Wrx34=+FwI<@%XS{els0OQCIhB?8 zYpmo)gM-S3$BgnMRjATJGbz|){KqJPJgLMKZ;4kU$Kxt7A1Yv5JbmZps!M5YJ@48= zI8>#i5I$;Wyadq)$cc|6%-n^0dteg5ndGN09^U3G2KSt8Eb74g=`unL#?|_NxEY3O0<#tfcUKY4g56_*ZI7c1! zNiBzgmjIK9z3nplb*Ls4G08_!fH7MucGGYEcyTr&;drYO^_M6a4VRe@QT_eA2LlIZ z%1R-8ZgmjA9*ZTv?8RROuz2Gl3MEkGpVuHa95PZW41mekcZUbN`m2_UQMzgd6`4~K zET!(EmfCXUqDHAE;dSZi%8)~Y>nc=U%$Amx@jX^0F1?>pB}L>_QjTQc7Zz%Gmc+ZJ zJXVU68siI%NZa?{^gC$*_(1^py`f_a${H?Do8<_aB;UII2#TXkKG(2TZt&Zm&Kui- zPB}0fcw_FC+{)Mn$Np131yz#ON&U2K8E;E`P+|*h&+Z z&WEvV0{Ee1<3{~} zK%Lr`&26FH{i6#>T!1n1=s7zSIOSlL=%2Kq4+tPEHkRcuNG6lCk?SRQSA@dBvE;@T$ofcu?#AvS-6ZwHW5zw7Ic?RAb&CabwX|j0QX0zVH!Dr=PUFQ=q#8P+ z2&5av79s)6T!pXR*KK6_-A4+dsiV_gTyFGBLBeVEI+dqzq%hyY!rZ7qtL&alyOpNd zs?|_PLsDV*ku`khN2&Iyw(8!jhKY~5#-ZD6Fhd9k{7v<^*!TfMrGI3kcQsoon9HyG z*W}n(f_Z$K*L8K)1J+xM7CYo>Z%ZgdU0vPS_hNGNn~bK0MtuS=fDi=HClIHN8Q=Jc zF&z-HnJYsAQWf2ul{)EWm(f`;GP+DG619hIEZUQJO~x>oOnC!fSNX|CA8$62xq~^A zYMN$jFPA6`WJNaqGLJFDnqigw=D?bTE#i%1VW+S#Gmi=?;NXy_=iTRI$4zISChJ3ZjT`;Z7((oY@VH-s@d0sg`r*; zE3{A46J>|g*iNr9SA5LG$$?cMv>jWqFfwieTzAT;d9i|m!l&ryt^h@Thl&W;vsm(F z%*{4t1Y-f?(V#sH)Sx6TaA`djQ@+JpQFnf%D70BQE3BFrVM=Pdviu)k4RE5&Zh~NJ zE}L2-83V-duB=90KXXul&n*4slf%xagcjRK(W9vR#g6A>6WRq8S0(=B#K+ptnzNrU zbPq<7^V!Bg*ArQf^bh)o9K%4$B9|X4EtXuU1N2hxuwKdO&8a!vUuFu4#oXN7P!RFt zsK2Wu}Cb7JiV!@DT&~!)=Ky1vHUM< z(bAf=0G%S(2heCx>F_lm5wLv%kd?U}aV(o-m~k;NHLmF);H=!njl;vkD)1i)il@*4 zP@nQH*G-xyNKvtui1RB!rG3O2@q>}q#*64wfHwtbu&GjmaCyL`y_}D(6&4hn0f;!c0bW7woewqT#sAdZ0rB*N~4CjH&&$nwhu5YfG z_hpLMi=o2|Zg7T#zu)e9gfT8v2OL?#_vzt^hK42_mkLNtNy!B7RBl(u;)WAUEd4Uz z%X6OpJdhA&%lRVPE(Y}-bi6l3$S$aq_4QighsVLN#O27m^3hX7__9^NXuac7lh9KFQ3q2u#0&gAV_m%Nr|5bi?dzkT!D=@!@Hd5Cc{5|K1+g_aQ;&UMExS5-V+ZB#%N-5z-UTo_93rTa34rPZ7rGlwR+$H-iptBDrZMKoY z0-p@+Ld>sw1V%DLK}<|Mm0)^SKS&Rz5Gs9qO-UPt^t*S2eQn{vw}SvLWks>JQ-YT6 z{x~VKJqGEylEgI-tY$pn^ie5yKk%cp2&RjCV#P8nAw3ooNQF4LJ0i`%kmJdNmFZ@%t?A-H*;|=!JF) z&B9M*vIOI(UQ$twOuu}-MlZOto7(KAosUPtRm^ai9?_h}6ng4Gu1wbgT(}(x{(`TF zI?&5U`n)aN8Sgh&>4*=#u(b~wRAj96IoYl7TU&SyUO12OF2YND(?nf%l=qpC=jih+ zWM1s58+W_B)v1qGJFXhdg18{rwddKWF28%WC16c!Z~0bsX?gO(OCyh}qO0vlP< zfEwUALVt!^v(#0Dq+0g|cCu5npUuWObu9j@!WCD0`rLVURIS~XBb6A!2UYU9ca)M7 z`*U4dU0wb7<=vQ5WfbgOsyympdDOPNxhE+aI0e6lCJOa+dl72Purd^7X!)r>%*njX z&<7)=rrK@LhYc$0h*&Qz?7ZE|D=Q{fkuGf7OXu~t)4F zv=15#Vc9e-C@Ai)Y#|X2mRw|YUkvx%0oicuQbONbnwf2FCF}9 zZzdab)UVtZ2E`d&-^+E&{sN-fzgVbhSkdJAcqP0}M2zEhVRP=;Td0?b3H8^;vJar` z-E#BSZMtz$sILA@2-a|VQ$0v;996HHqWh6b4i9t}%M9sTORwWfHG^*t$9WP~9u>xu zLtm_(02D}3IH#`ft_sgqT^}~KK0O@mW-*T6F>pZQARXUA;@`BhOUp>BW(t1ivb*!| zv$3@WxEZ@k>!jIcf*Rg|p^FdO#+1b8HA{-5?BgI4i>jYVB>Rh^ApmXzcoi%6T>kTF zuQkjD_-SM$#ta%$#83uugX_RpzepSv#i~ffoe26TXpl!hENq#KA)BQ9XouF8y2P`V zeoc8(?wy5nLx-`~H8G8l&}?VtyS_vQ@2kTNMW~&hVZ#fHD~H?j9VaKJjg1Wwm%{w~ zc6ly>f|S>hZd^j&EFNECFte8PUd~HrFcoLZ-Z*0J1T&I6c%%umL<6qYq9dhmQ^m}6 z2bTNo^~T~K#b;1f78X-!#Fdp5Vd(151~xM!gi&Ep$j~1N&XG$efDpU8I?57qVMLX; z`;_$ctFTKCz4C01TlO0#4-bLM`M1a??Co%5vOs{uy?3#V*k%-+@0IIY0-Tv@8ozzs zfuwy=(zFWR2(K~(FA>=~Vw*jWA&9yJ70L<*o69hu{_meKYwJQu2xNc&yn;cG7DWB2 z5EZVaqf=`;&GD}ha&X$tK5j{9s{Jj@xCQw@_M3+(a5xqQAhl(r{#N%KfHKI=Rs-e0 zNSwji`e@b>K|tx)jfcZu2f$^efMS|O4Oagvz%|zd%F4+Zhv#`&j zMfkV4qbouZNG}f;qpWRh_o}}$Y%}dwKW3_^7)&Ft=jAB`%F&y>xcMu+qop10x--sz zrnox$dweM{7$z(tBkk{K&qtyciuH?;P2q2^?W?P!riJ9>Yqz*FPx=zFc25W_II#-s z69d+63edU2?$0nS((549htG%K8Op_dFYTAu&*Ix9`QO7{6arJaCXUNlp#!1fER5c* zc{#T7oL#jhn=Q>dmLDzN9k)sSqGmID%LUff(vp>vbK(S2!8UOEj|%C@+hNck3lJUc z``hnqU~`XSE;@tI!S{?9K;iCq{OvI&ax{GZV33mpKY`sf9?=Gud~&0)W)T)9wJ=Nh z8PRLkwXBFpWW94Ialf)gDQ0xH!RdXqZTdEw-A3=$h*N(^W1m7$fnO!aO_cn(x{>MzWZjo68)xLVrh*>Z!;6 za4eI2wRHBdO;@{pHugGc$=lKEXsK#dBb!nmBdTKLHtI;QP4U$Pi3O8N?-G&=gklP$xE>{&_fbGEBmhk;4{Tk9*pm}7o0^m(5~Qq>mj zeMM`?kayR9n7_0mopid0TP3m~4xVegUSp3A9lE%;RZF7COAZVQTFqi#y~QIsFk9c+_Ze%w1)2)qIJsv!y757vB}D(YQG$3JYbS?Tm(s)%QH?@ zSYufiN!n)WT;R7YJ~zwl!G=mBc`(`bln;p!8uFbLu%J0A35Sv4o)oESrP^(+7Im?a zQP`KQ@4Y2P)n&yDEWiD1HnlCLZGRws6zU#}zC6sTU^pPlx4Sr)-7Wb_>Pl3ptx+>W zvPhFq#1LW;=b7P?#Ct4?TC6d`WmO?3HHIy14B20zm;hOru;`;2ShMJ0-k-DE_Q8{) zzr1?UAaLSpV7k*DcbH@HY6`on+h0H2!S{nFa~OH#nE!`PvsrNb_bXYeA!a zVe}KJM;Ow`JMR{SqNj9X_7{)$92lSKYR$EDcE7kn*l9MuF zyhPlk3@zAUwOyauxXPE_l;WUdapNm#qmuy?+8Fq+)7$gDf*IjS5RfM}Gf98!MOe!# zS@b?P#@pf;r!AgvRe(kk*ZbaG5`d+6U>z}e?HFK`i=DHA%a{-BbGs-`52GHS9{JS~ zLY9wG3(ny0=Go1Qg$|#*=D}<^4prcv5ru{jw32bVfRAf(E)`xp&~ZA%W2?$DaB#`j9}klx3MWZFg{ROy3V{U@LxBtzRG1#Xd1b`5 zz>MDctFS#hl-Q~37-ED1*I_M5TwI_(IIpGT{8fq_6j3~bZ8abZ^5h5hWgOFq*WXl8 z8AxBhf*Y8rKz0d{=6fa>TzkcK7fs)H%4q0&X?Mt1;c8osI*?#q5a%lq{sIIoT_e$&N$x^ZPornyct_#ImbEOZA$^#+T*CO`s6S zpS?-It#VK8lwOgQU?7>V&>tBQ-D|NSqRuhjYZZsDnarw2PpH;>jV$ym#tvp8#Z^~x z4QHklrv*IeMYA{AeJ_!o`MkfK`rEg@de8X1av1z%Y4iktak;AW3H?cnv zKJT4*M9Pua3*T<=NAkSbouq?iN`YyzUp{ar0nyAE1bFVyX6f;9b%dF9{Rxm6=fi~S z-}VkviLhAoHpY=`7#?vrHttkf5d9H2O2sU$M< zQ24$0Yv@tVJC{66r1<~F{%uCU355_{?;I{Rj?}{dJp-Dy>KkPJy|(rej>=egdFgIo^ACs!lJ?>PXN1MS}? zcWrRjv&|vpKme5`{Ubz#+J*&!`M(DNDD=N4e~*sFCnY77hQej>kfmmx7vvk%Ic(Sj zr2jv1Z35GUg@yl}0!2Lh^Z!47E-)WI|0d>tWKvsymjr;+f20)@6i5dGxtD({MSx0$ z|694axoL(l4wAw{Hl?BmfBEnDsV6k7ty%sZE?FXsnjZZ>&!oVhP-s6grMQp6zZwk4 z%Xj{-0HD&qfD`>cqxe_F4fu&LR^W|CRS1+VCUrztd;f4n~hSk%e$R dPCq>t-N6=SJ^qpaylnhTQBFm+Oxh&q{{VG2wAugw literal 0 HcmV?d00001 diff --git a/assets/errorbar.png b/assets/errorbar.png new file mode 100644 index 0000000000000000000000000000000000000000..5907cb85f9d34448bf3304a295c0c4ba137cfecc GIT binary patch literal 5356 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 IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/assets/infobar.png b/assets/infobar.png new file mode 100644 index 0000000000000000000000000000000000000000..269349fae2a8eb63e1e09e7d11ea8550e6417ee6 GIT binary patch literal 7917 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 wi=Object.create;var ur=Object.defineProperty;var Si=Object.getOwnPropertyDescriptor;var Ti=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Oi=Object.getPrototypeOf,dr=Object.prototype.hasOwnProperty,Zr=Object.prototype.propertyIsEnumerable;var Xr=(e,t,r)=>t in e?ur(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,R=(e,t)=>{for(var r in t||(t={}))dr.call(t,r)&&Xr(e,r,t[r]);if(kt)for(var r of kt(t))Zr.call(t,r)&&Xr(e,r,t[r]);return e};var eo=(e,t)=>{var r={};for(var o in e)dr.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&kt)for(var o of kt(e))t.indexOf(o)<0&&Zr.call(e,o)&&(r[o]=e[o]);return r};var hr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Mi=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of Ti(t))!dr.call(e,n)&&n!==r&&ur(e,n,{get:()=>t[n],enumerable:!(o=Si(t,n))||o.enumerable});return e};var Ht=(e,t,r)=>(r=e!=null?wi(Oi(e)):{},Mi(t||!e||!e.__esModule?ur(r,"default",{value:e,enumerable:!0}):r,e));var ro=hr((br,to)=>{(function(e,t){typeof br=="object"&&typeof to!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(br,function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(C){return!!(C&&C!==document&&C.nodeName!=="HTML"&&C.nodeName!=="BODY"&&"classList"in C&&"contains"in C.classList)}function c(C){var it=C.type,Ne=C.tagName;return!!(Ne==="INPUT"&&s[it]&&!C.readOnly||Ne==="TEXTAREA"&&!C.readOnly||C.isContentEditable)}function p(C){C.classList.contains("focus-visible")||(C.classList.add("focus-visible"),C.setAttribute("data-focus-visible-added",""))}function l(C){C.hasAttribute("data-focus-visible-added")&&(C.classList.remove("focus-visible"),C.removeAttribute("data-focus-visible-added"))}function f(C){C.metaKey||C.altKey||C.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(C){o=!1}function d(C){a(C.target)&&(o||c(C.target))&&p(C.target)}function v(C){a(C.target)&&(C.target.classList.contains("focus-visible")||C.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(C.target))}function b(C){document.visibilityState==="hidden"&&(n&&(o=!0),z())}function z(){document.addEventListener("mousemove",G),document.addEventListener("mousedown",G),document.addEventListener("mouseup",G),document.addEventListener("pointermove",G),document.addEventListener("pointerdown",G),document.addEventListener("pointerup",G),document.addEventListener("touchmove",G),document.addEventListener("touchstart",G),document.addEventListener("touchend",G)}function K(){document.removeEventListener("mousemove",G),document.removeEventListener("mousedown",G),document.removeEventListener("mouseup",G),document.removeEventListener("pointermove",G),document.removeEventListener("pointerdown",G),document.removeEventListener("pointerup",G),document.removeEventListener("touchmove",G),document.removeEventListener("touchstart",G),document.removeEventListener("touchend",G)}function G(C){C.target.nodeName&&C.target.nodeName.toLowerCase()==="html"||(o=!1,K())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",b,!0),z(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var Vr=hr((Ot,Dr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Ot=="object"&&typeof Dr=="object"?Dr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Ot=="object"?Ot.ClipboardJS=r():t.ClipboardJS=r()})(Ot,function(){return function(){var e={686:function(o,n,i){"use strict";i.d(n,{default:function(){return Ei}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(W){try{return document.execCommand(W)}catch(O){return!1}}var d=function(O){var S=f()(O);return u("cut"),S},v=d;function b(W){var O=document.documentElement.getAttribute("dir")==="rtl",S=document.createElement("textarea");S.style.fontSize="12pt",S.style.border="0",S.style.padding="0",S.style.margin="0",S.style.position="absolute",S.style[O?"right":"left"]="-9999px";var $=window.pageYOffset||document.documentElement.scrollTop;return S.style.top="".concat($,"px"),S.setAttribute("readonly",""),S.value=W,S}var z=function(O,S){var $=b(O);S.container.appendChild($);var F=f()($);return u("copy"),$.remove(),F},K=function(O){var S=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},$="";return typeof O=="string"?$=z(O,S):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?$=z(O.value,S):($=f()(O),u("copy")),$},G=K;function C(W){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?C=function(S){return typeof S}:C=function(S){return S&&typeof Symbol=="function"&&S.constructor===Symbol&&S!==Symbol.prototype?"symbol":typeof S},C(W)}var it=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},S=O.action,$=S===void 0?"copy":S,F=O.container,Q=O.target,_e=O.text;if($!=="copy"&&$!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(Q!==void 0)if(Q&&C(Q)==="object"&&Q.nodeType===1){if($==="copy"&&Q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if($==="cut"&&(Q.hasAttribute("readonly")||Q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(_e)return G(_e,{container:F});if(Q)return $==="cut"?v(Q):G(Q,{container:F})},Ne=it;function Pe(W){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Pe=function(S){return typeof S}:Pe=function(S){return S&&typeof Symbol=="function"&&S.constructor===Symbol&&S!==Symbol.prototype?"symbol":typeof S},Pe(W)}function ui(W,O){if(!(W instanceof O))throw new TypeError("Cannot call a class as a function")}function Jr(W,O){for(var S=0;S0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Pe(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var Q=this;this.listener=p()(F,"click",function(_e){return Q.onClick(_e)})}},{key:"onClick",value:function(F){var Q=F.delegateTarget||F.currentTarget,_e=this.action(Q)||"copy",Ct=Ne({action:_e,container:this.container,target:this.target(Q),text:this.text(Q)});this.emit(Ct?"success":"error",{action:_e,text:Ct,trigger:Q,clearSelection:function(){Q&&Q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return fr("action",F)}},{key:"defaultTarget",value:function(F){var Q=fr("target",F);if(Q)return document.querySelector(Q)}},{key:"defaultText",value:function(F){return fr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var Q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return G(F,Q)}},{key:"cut",value:function(F){return v(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],Q=typeof F=="string"?[F]:F,_e=!!document.queryCommandSupported;return Q.forEach(function(Ct){_e=_e&&!!document.queryCommandSupported(Ct)}),_e}}]),S}(a()),Ei=yi},828:function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s},438:function(o,n,i){var s=i(828);function a(l,f,u,d,v){var b=p.apply(this,arguments);return l.addEventListener(u,b,v),{destroy:function(){l.removeEventListener(u,b,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(b){return a(b,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c},879:function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}},370:function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(b){b.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(b){b.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c},817:function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n},279:function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var Ha=/["'&<>]/;Un.exports=$a;function $a(e){var t=""+e,r=Ha.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function U(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||a(u,d)})})}function a(u,d){try{c(o[u](d))}catch(v){f(i[0][3],v)}}function c(u){u.value instanceof Ze?Promise.resolve(u.value.v).then(p,l):f(i[0][2],u)}function p(u){a("next",u)}function l(u){a("throw",u)}function f(u,d){u(d),i.shift(),i.length&&a(i[0][0],i[0][1])}}function io(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof we=="function"?we(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function k(e){return typeof e=="function"}function at(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Rt=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Ie=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=we(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(b){t={error:b}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(k(l))try{l()}catch(b){i=b instanceof Rt?b.errors:[b]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=we(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{ao(v)}catch(b){i=i!=null?i:[],b instanceof Rt?i=D(D([],U(i)),U(b.errors)):i.push(b)}}}catch(b){o={error:b}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Rt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)ao(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var gr=Ie.EMPTY;function Pt(e){return e instanceof Ie||e&&"closed"in e&&k(e.remove)&&k(e.add)&&k(e.unsubscribe)}function ao(e){k(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?gr:(this.currentObservers=null,a.push(r),new Ie(function(){o.currentObservers=null,De(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new P;return r.source=this,r},t.create=function(r,o){return new ho(r,o)},t}(P);var ho=function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:gr},t}(x);var yt={now:function(){return(yt.delegate||Date).now()},delegate:void 0};var Et=function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=yt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=lt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(lt.cancelAnimationFrame(o),r._scheduled=void 0)},t}(jt);var go=function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o=this._scheduled;this._scheduled=void 0;var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t}(Wt);var Oe=new go(vo);var L=new P(function(e){return e.complete()});function Ut(e){return e&&k(e.schedule)}function Or(e){return e[e.length-1]}function Qe(e){return k(Or(e))?e.pop():void 0}function Me(e){return Ut(Or(e))?e.pop():void 0}function Nt(e,t){return typeof Or(e)=="number"?e.pop():t}var mt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function Dt(e){return k(e==null?void 0:e.then)}function Vt(e){return k(e[pt])}function zt(e){return Symbol.asyncIterator&&k(e==null?void 0:e[Symbol.asyncIterator])}function qt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Pi(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Kt=Pi();function Qt(e){return k(e==null?void 0:e[Kt])}function Yt(e){return no(this,arguments,function(){var r,o,n,i;return $t(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,Ze(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,Ze(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,Ze(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return k(e==null?void 0:e.getReader)}function I(e){if(e instanceof P)return e;if(e!=null){if(Vt(e))return Ii(e);if(mt(e))return Fi(e);if(Dt(e))return ji(e);if(zt(e))return xo(e);if(Qt(e))return Wi(e);if(Bt(e))return Ui(e)}throw qt(e)}function Ii(e){return new P(function(t){var r=e[pt]();if(k(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Fi(e){return new P(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?M(function(n,i){return e(n,i,o)}):ue,xe(1),r?He(t):Io(function(){return new Jt}))}}function Fo(){for(var e=[],t=0;t=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new x}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,b=!1,z=function(){f==null||f.unsubscribe(),f=void 0},K=function(){z(),l=u=void 0,v=b=!1},G=function(){var C=l;K(),C==null||C.unsubscribe()};return g(function(C,it){d++,!b&&!v&&z();var Ne=u=u!=null?u:r();it.add(function(){d--,d===0&&!b&&!v&&(f=Hr(G,c))}),Ne.subscribe(it),!l&&d>0&&(l=new tt({next:function(Pe){return Ne.next(Pe)},error:function(Pe){b=!0,z(),f=Hr(K,n,Pe),Ne.error(Pe)},complete:function(){v=!0,z(),f=Hr(K,s),Ne.complete()}}),I(C).subscribe(l))})(p)}}function Hr(e,t){for(var r=[],o=2;oe.next(document)),e}function q(e,t=document){return Array.from(t.querySelectorAll(e))}function N(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function Re(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}var na=_(h(document.body,"focusin"),h(document.body,"focusout")).pipe(ke(1),V(void 0),m(()=>Re()||document.body),J(1));function Zt(e){return na.pipe(m(t=>e.contains(t)),X())}function Je(e){return{x:e.offsetLeft,y:e.offsetTop}}function No(e){return _(h(window,"load"),h(window,"resize")).pipe(Ce(0,Oe),m(()=>Je(e)),V(Je(e)))}function er(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return _(h(e,"scroll"),h(window,"resize")).pipe(Ce(0,Oe),m(()=>er(e)),V(er(e)))}function Do(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)Do(e,r)}function T(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)Do(o,n);return o}function tr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function ht(e){let t=T("script",{src:e});return H(()=>(document.head.appendChild(t),_(h(t,"load"),h(t,"error").pipe(E(()=>Mr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),xe(1))))}var Vo=new x,ia=H(()=>typeof ResizeObserver=="undefined"?ht("https://unpkg.com/resize-observer-polyfill"):j(void 0)).pipe(m(()=>new ResizeObserver(e=>{for(let t of e)Vo.next(t)})),E(e=>_(Ve,j(e)).pipe(A(()=>e.disconnect()))),J(1));function he(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ye(e){return ia.pipe(w(t=>t.observe(e)),E(t=>Vo.pipe(M(({target:r})=>r===e),A(()=>t.unobserve(e)),m(()=>he(e)))),V(he(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function zo(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var qo=new x,aa=H(()=>j(new IntersectionObserver(e=>{for(let t of e)qo.next(t)},{threshold:0}))).pipe(E(e=>_(Ve,j(e)).pipe(A(()=>e.disconnect()))),J(1));function rr(e){return aa.pipe(w(t=>t.observe(e)),E(t=>qo.pipe(M(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function Ko(e,t=16){return dt(e).pipe(m(({y:r})=>{let o=he(e),n=bt(e);return r>=n.height-o.height-t}),X())}var or={drawer:N("[data-md-toggle=drawer]"),search:N("[data-md-toggle=search]")};function Qo(e){return or[e].checked}function Ke(e,t){or[e].checked!==t&&or[e].click()}function We(e){let t=or[e];return h(t,"change").pipe(m(()=>t.checked),V(t.checked))}function sa(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function ca(){return _(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(V(!1))}function Yo(){let e=h(window,"keydown").pipe(M(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:Qo("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),M(({mode:t,type:r})=>{if(t==="global"){let o=Re();if(typeof o!="undefined")return!sa(o,r)}return!0}),le());return ca().pipe(E(t=>t?L:e))}function pe(){return new URL(location.href)}function ot(e,t=!1){if(te("navigation.instant")&&!t){let r=T("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function Bo(){return new x}function Go(){return location.hash.slice(1)}function nr(e){let t=T("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function pa(e){return _(h(window,"hashchange"),e).pipe(m(Go),V(Go()),M(t=>t.length>0),J(1))}function Jo(e){return pa(e).pipe(m(t=>se(`[id="${t}"]`)),M(t=>typeof t!="undefined"))}function Fr(e){let t=matchMedia(e);return Xt(r=>t.addListener(()=>r(t.matches))).pipe(V(t.matches))}function Xo(){let e=matchMedia("print");return _(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(V(e.matches))}function jr(e,t){return e.pipe(E(r=>r?t():L))}function ir(e,t){return new P(r=>{let o=new XMLHttpRequest;o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network Error"))}),o.addEventListener("abort",()=>{r.error(new Error("Request aborted"))}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{t.progress$.next(n.loaded/n.total*100)}),t.progress$.next(5)),o.send()})}function Ue(e,t){return ir(e,t).pipe(E(r=>r.text()),m(r=>JSON.parse(r)),J(1))}function Zo(e,t){let r=new DOMParser;return ir(e,t).pipe(E(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),J(1))}function en(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function tn(){return _(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(en),V(en()))}function rn(){return{width:innerWidth,height:innerHeight}}function on(){return h(window,"resize",{passive:!0}).pipe(m(rn),V(rn()))}function nn(){return B([tn(),on()]).pipe(m(([e,t])=>({offset:e,size:t})),J(1))}function ar(e,{viewport$:t,header$:r}){let o=t.pipe(ee("size")),n=B([o,r]).pipe(m(()=>Je(e)));return B([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function la(e){return h(e,"message",t=>t.data)}function ma(e){let t=new x;return t.subscribe(r=>e.postMessage(r)),t}function an(e,t=new Worker(e)){let r=la(t),o=ma(t),n=new x;n.subscribe(o);let i=o.pipe(Z(),re(!0));return n.pipe(Z(),qe(r.pipe(Y(i))),le())}var fa=N("#__config"),vt=JSON.parse(fa.textContent);vt.base=`${new URL(vt.base,pe())}`;function me(){return vt}function te(e){return vt.features.includes(e)}function be(e,t){return typeof t!="undefined"?vt.translations[e].replace("#",t.toString()):vt.translations[e]}function Ee(e,t=document){return N(`[data-md-component=${e}]`,t)}function oe(e,t=document){return q(`[data-md-component=${e}]`,t)}function ua(e){let t=N(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>N(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function sn(e){if(!te("announce.dismiss")||!e.childElementCount)return L;if(!e.hidden){let t=N(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new x;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),ua(e).pipe(w(r=>t.next(r)),A(()=>t.complete()),m(r=>R({ref:e},r)))})}function da(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function cn(e,t){let r=new x;return r.subscribe(({hidden:o})=>{e.hidden=o}),da(e,t).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))}function ha(e,t){let r=H(()=>B([No(e),dt(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=he(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Zt(e).pipe(E(o=>r.pipe(m(n=>({active:o,offset:n})),xe(+!o||1/0))))}function pn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new x,s=i.pipe(Z(),re(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),rr(e).pipe(Y(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),_(i.pipe(M(({active:a})=>a)),i.pipe(ke(250),M(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe(Ce(16,Oe)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(Pr(125,Oe),M(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(Y(s),M(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(Y(s),ne(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Re())==null||p.blur()}}),r.pipe(Y(s),M(a=>a===o),ze(125)).subscribe(()=>e.focus()),ha(e,t).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))})}function Wr(e){return T("div",{class:"md-tooltip",id:e},T("div",{class:"md-tooltip__inner md-typeset"}))}function ln(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return T("aside",{class:"md-annotation",tabIndex:0},Wr(t),T("a",{href:r,class:"md-annotation__index",tabIndex:-1},T("span",{"data-md-annotation-id":e})))}else return T("aside",{class:"md-annotation",tabIndex:0},Wr(t),T("span",{class:"md-annotation__index",tabIndex:-1},T("span",{"data-md-annotation-id":e})))}function mn(e){return T("button",{class:"md-clipboard md-icon",title:be("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function Ur(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,T("del",null,p)," "],[]).slice(0,-1),i=me(),s=new URL(e.location,i.base);te("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=me();return T("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},T("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&T("div",{class:"md-search-result__icon md-icon"}),r>0&&T("h1",null,e.title),r<=0&&T("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return T("span",{class:`md-tag ${p}`},c)}),o>0&&n.length>0&&T("p",{class:"md-search-result__terms"},be("search.result.term.missing"),": ",...n)))}function fn(e){let t=e[0].score,r=[...e],o=me(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreUr(l,1)),...c.length?[T("details",{class:"md-search-result__more"},T("summary",{tabIndex:-1},T("div",null,c.length>0&&c.length===1?be("search.result.more.one"):be("search.result.more.other",c.length))),...c.map(l=>Ur(l,1)))]:[]];return T("li",{class:"md-search-result__item"},p)}function un(e){return T("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>T("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?tr(r):r)))}function Nr(e){let t=`tabbed-control tabbed-control--${e}`;return T("div",{class:t,hidden:!0},T("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function dn(e){return T("div",{class:"md-typeset__scrollwrap"},T("div",{class:"md-typeset__table"},e))}function ba(e){let t=me(),r=new URL(`../${e.version}/`,t.base);return T("li",{class:"md-version__item"},T("a",{href:`${r}`,class:"md-version__link"},e.title))}function hn(e,t){return T("div",{class:"md-version"},T("button",{class:"md-version__current","aria-label":be("select.version")},t.title),T("ul",{class:"md-version__list"},e.map(ba)))}function va(e){return e.tagName==="CODE"?q(".c, .c1, .cm",e):[e]}function ga(e){let t=[];for(let r of va(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function bn(e,t){t.append(...Array.from(e.childNodes))}function sr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of ga(t)){let[,c]=a.textContent.match(/\((\d+)\)/);se(`:scope > li:nth-child(${c})`,e)&&(s.set(c,ln(c,i)),a.replaceWith(s.get(c)))}return s.size===0?L:H(()=>{let a=new x,c=a.pipe(Z(),re(!0)),p=[];for(let[l,f]of s)p.push([N(".md-typeset",f),N(`:scope > li:nth-child(${l})`,e)]);return o.pipe(Y(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?bn(f,u):bn(u,f)}),_(...[...s].map(([,l])=>pn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function vn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return vn(t)}}function gn(e,t){return H(()=>{let r=vn(e);return typeof r!="undefined"?sr(r,e,t):L})}var yn=Ht(Vr());var xa=0;function En(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return En(t)}}function xn(e){return ye(e).pipe(m(({width:t})=>({scrollable:bt(e).width>t})),ee("scrollable"))}function wn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new x;if(n.subscribe(({scrollable:s})=>{s&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")}),yn.default.isSupported()&&(e.closest(".copy")||te("content.code.copy")&&!e.closest(".no-copy"))){let s=e.closest("pre");s.id=`__code_${xa++}`,s.insertBefore(mn(s.id),e)}let i=e.closest(".highlight");if(i instanceof HTMLElement){let s=En(i);if(typeof s!="undefined"&&(i.classList.contains("annotate")||te("content.code.annotate"))){let a=sr(s,e,t);return xn(e).pipe(w(c=>n.next(c)),A(()=>n.complete()),m(c=>R({ref:e},c)),qe(ye(i).pipe(m(({width:c,height:p})=>c&&p),X(),E(c=>c?a:L))))}}return xn(e).pipe(w(s=>n.next(s)),A(()=>n.complete()),m(s=>R({ref:e},s)))});return te("content.lazy")?rr(e).pipe(M(n=>n),xe(1),E(()=>o)):o}function ya(e,{target$:t,print$:r}){let o=!0;return _(t.pipe(m(n=>n.closest("details:not([open])")),M(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(M(n=>n||!o),w(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Sn(e,t){return H(()=>{let r=new x;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ya(e,t).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}var Tn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel rect,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel rect{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color);stroke-width:.05rem}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs #classDiagram-compositionEnd,defs #classDiagram-compositionStart,defs #classDiagram-dependencyEnd,defs #classDiagram-dependencyStart,defs #classDiagram-extensionEnd,defs #classDiagram-extensionStart{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs #classDiagram-aggregationEnd,defs #classDiagram-aggregationStart{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}.attributeBoxEven,.attributeBoxOdd{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityBox{fill:var(--md-mermaid-label-bg-color);stroke:var(--md-mermaid-node-fg-color)}.entityLabel{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.relationshipLabelBox{fill:var(--md-mermaid-label-bg-color);fill-opacity:1;background-color:var(--md-mermaid-label-bg-color);opacity:1}.relationshipLabel{fill:var(--md-mermaid-label-fg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs #ONE_OR_MORE_END *,defs #ONE_OR_MORE_START *,defs #ONLY_ONE_END *,defs #ONLY_ONE_START *,defs #ZERO_OR_MORE_END *,defs #ZERO_OR_MORE_START *,defs #ZERO_OR_ONE_END *,defs #ZERO_OR_ONE_START *{stroke:var(--md-mermaid-edge-color)!important}defs #ZERO_OR_MORE_END circle,defs #ZERO_OR_MORE_START circle{fill:var(--md-mermaid-label-bg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var zr,wa=0;function Sa(){return typeof mermaid=="undefined"||mermaid instanceof Element?ht("https://unpkg.com/mermaid@9.4.3/dist/mermaid.min.js"):j(void 0)}function On(e){return e.classList.remove("mermaid"),zr||(zr=Sa().pipe(w(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Tn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),J(1))),zr.subscribe(()=>{e.classList.add("mermaid");let t=`__mermaid_${wa++}`,r=T("div",{class:"mermaid"}),o=e.textContent;mermaid.mermaidAPI.render(t,o,(n,i)=>{let s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})}),zr.pipe(m(()=>({ref:e})))}var Mn=T("table");function Ln(e){return e.replaceWith(Mn),Mn.replaceWith(dn(e)),j({ref:e})}function Ta(e){let t=q(":scope > input",e),r=t.find(o=>o.checked)||t[0];return _(...t.map(o=>h(o,"change").pipe(m(()=>N(`label[for="${o.id}"]`))))).pipe(V(N(`label[for="${r.id}"]`)),m(o=>({active:o})))}function _n(e,{viewport$:t}){let r=Nr("prev");e.append(r);let o=Nr("next");e.append(o);let n=N(".tabbed-labels",e);return H(()=>{let i=new x,s=i.pipe(Z(),re(!0));return B([i,ye(e)]).pipe(Ce(1,Oe),Y(s)).subscribe({next([{active:a},c]){let p=Je(a),{width:l}=he(a);e.style.setProperty("--md-indicator-x",`${p.x}px`),e.style.setProperty("--md-indicator-width",`${l}px`);let f=er(n);(p.xf.x+c.width)&&n.scrollTo({left:Math.max(0,p.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),B([dt(n),ye(n)]).pipe(Y(s)).subscribe(([a,c])=>{let p=bt(n);r.hidden=a.x<16,o.hidden=a.x>p.width-c.width-16}),_(h(r,"click").pipe(m(()=>-1)),h(o,"click").pipe(m(()=>1))).pipe(Y(s)).subscribe(a=>{let{width:c}=he(n);n.scrollBy({left:c*a,behavior:"smooth"})}),te("content.tabs.link")&&i.pipe(je(1),ne(t)).subscribe(([{active:a},{offset:c}])=>{let p=a.innerText.trim();if(a.hasAttribute("data-md-switching"))a.removeAttribute("data-md-switching");else{let l=e.offsetTop-c.y;for(let u of q("[data-tabs]"))for(let d of q(":scope > input",u)){let v=N(`label[for="${d.id}"]`);if(v!==a&&v.innerText.trim()===p){v.setAttribute("data-md-switching",""),d.click();break}}window.scrollTo({top:e.offsetTop-l});let f=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([p,...f])])}}),i.pipe(Y(s)).subscribe(()=>{for(let a of q("audio, video",e))a.pause()}),Ta(e).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))}).pipe(rt(ae))}function An(e,{viewport$:t,target$:r,print$:o}){return _(...q(".annotate:not(.highlight)",e).map(n=>gn(n,{target$:r,print$:o})),...q("pre:not(.mermaid) > code",e).map(n=>wn(n,{target$:r,print$:o})),...q("pre.mermaid",e).map(n=>On(n)),...q("table:not([class])",e).map(n=>Ln(n)),...q("details",e).map(n=>Sn(n,{target$:r,print$:o})),...q("[data-tabs]",e).map(n=>_n(n,{viewport$:t})))}function Oa(e,{alert$:t}){return t.pipe(E(r=>_(j(!0),j(!1).pipe(ze(2e3))).pipe(m(o=>({message:r,active:o})))))}function Cn(e,t){let r=N(".md-typeset",e);return H(()=>{let o=new x;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),Oa(e,t).pipe(w(n=>o.next(n)),A(()=>o.complete()),m(n=>R({ref:e},n)))})}function Ma({viewport$:e}){if(!te("header.autohide"))return j(!1);let t=e.pipe(m(({offset:{y:n}})=>n),Le(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),X()),o=We("search");return B([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),X(),E(n=>n?r:j(!1)),V(!1))}function kn(e,t){return H(()=>B([ye(e),Ma(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),X((r,o)=>r.height===o.height&&r.hidden===o.hidden),J(1))}function Hn(e,{header$:t,main$:r}){return H(()=>{let o=new x,n=o.pipe(Z(),re(!0));return o.pipe(ee("active"),Ge(t)).subscribe(([{active:i},{hidden:s}])=>{e.classList.toggle("md-header--shadow",i&&!s),e.hidden=s}),r.subscribe(o),t.pipe(Y(n),m(i=>R({ref:e},i)))})}function La(e,{viewport$:t,header$:r}){return ar(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=he(e);return{active:o>=n}}),ee("active"))}function $n(e,t){return H(()=>{let r=new x;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=se(".md-content h1");return typeof o=="undefined"?L:La(o,t).pipe(w(n=>r.next(n)),A(()=>r.complete()),m(n=>R({ref:e},n)))})}function Rn(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),X()),n=o.pipe(E(()=>ye(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ee("bottom"))));return B([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),X((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function _a(e){let t=__md_get("__palette")||{index:e.findIndex(r=>matchMedia(r.getAttribute("data-md-color-media")).matches)};return j(...e).pipe(ce(r=>h(r,"change").pipe(m(()=>r))),V(e[Math.max(0,t.index)]),m(r=>({index:e.indexOf(r),color:{scheme:r.getAttribute("data-md-color-scheme"),primary:r.getAttribute("data-md-color-primary"),accent:r.getAttribute("data-md-color-accent")}})),J(1))}function Pn(e){let t=T("meta",{name:"theme-color"});document.head.appendChild(t);let r=T("meta",{name:"color-scheme"});return document.head.appendChild(r),H(()=>{let o=new x;o.subscribe(i=>{document.body.setAttribute("data-md-color-switching","");for(let[s,a]of Object.entries(i.color))document.body.setAttribute(`data-md-color-${s}`,a);for(let s=0;s{let i=Ee("header"),s=window.getComputedStyle(i);return r.content=s.colorScheme,s.backgroundColor.match(/\d+/g).map(a=>(+a).toString(16).padStart(2,"0")).join("")})).subscribe(i=>t.content=`#${i}`),o.pipe(Se(ae)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")});let n=q("input",e);return _a(n).pipe(w(i=>o.next(i)),A(()=>o.complete()),m(i=>R({ref:e},i)))})}function In(e,{progress$:t}){return H(()=>{let r=new x;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(w(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}var qr=Ht(Vr());function Aa(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r}function Fn({alert$:e}){qr.default.isSupported()&&new P(t=>{new qr.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||Aa(N(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(w(t=>{t.trigger.focus()}),m(()=>be("clipboard.copied"))).subscribe(e)}function Ca(e){if(e.length<2)return[""];let[t,r]=[...e].sort((n,i)=>n.length-i.length).map(n=>n.replace(/[^/]+$/,"")),o=0;if(t===r)o=t.length;else for(;t.charCodeAt(o)===r.charCodeAt(o);)o++;return e.map(n=>n.replace(t.slice(0,o),""))}function cr(e){let t=__md_get("__sitemap",sessionStorage,e);if(t)return j(t);{let r=me();return Zo(new URL("sitemap.xml",e||r.base)).pipe(m(o=>Ca(q("loc",o).map(n=>n.textContent))),de(()=>L),He([]),w(o=>__md_set("__sitemap",o,sessionStorage,e)))}}function jn(e){let t=se("[rel=canonical]",e);typeof t!="undefined"&&(t.href=t.href.replace("//localhost:","//127.0.0.1:"));let r=new Map;for(let o of q(":scope > *",e)){let n=o.outerHTML;for(let i of["href","src"]){let s=o.getAttribute(i);if(s===null)continue;let a=new URL(s,t==null?void 0:t.href),c=o.cloneNode();c.setAttribute(i,`${a}`),n=c.outerHTML;break}r.set(n,o)}return r}function Wn({location$:e,viewport$:t,progress$:r}){let o=me();if(location.protocol==="file:")return L;let n=cr().pipe(m(l=>l.map(f=>`${new URL(f,o.base)}`))),i=h(document.body,"click").pipe(ne(n),E(([l,f])=>{if(!(l.target instanceof Element))return L;let u=l.target.closest("a");if(u===null)return L;if(u.target||l.metaKey||l.ctrlKey)return L;let d=new URL(u.href);return d.search=d.hash="",f.includes(`${d}`)?(l.preventDefault(),j(new URL(u.href))):L}),le());i.pipe(xe(1)).subscribe(()=>{let l=se("link[rel=icon]");typeof l!="undefined"&&(l.href=l.href)}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),i.pipe(ne(t)).subscribe(([l,{offset:f}])=>{history.scrollRestoration="manual",history.replaceState(f,""),history.pushState(null,"",l)}),i.subscribe(e);let s=e.pipe(V(pe()),ee("pathname"),je(1),E(l=>ir(l,{progress$:r}).pipe(de(()=>(ot(l,!0),L))))),a=new DOMParser,c=s.pipe(E(l=>l.text()),E(l=>{let f=a.parseFromString(l,"text/html");for(let b of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...te("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let z=se(b),K=se(b,f);typeof z!="undefined"&&typeof K!="undefined"&&z.replaceWith(K)}let u=jn(document.head),d=jn(f.head);for(let[b,z]of d)z.getAttribute("rel")==="stylesheet"||z.hasAttribute("src")||(u.has(b)?u.delete(b):document.head.appendChild(z));for(let b of u.values())b.getAttribute("rel")==="stylesheet"||b.hasAttribute("src")||b.remove();let v=Ee("container");return Fe(q("script",v)).pipe(E(b=>{let z=f.createElement("script");if(b.src){for(let K of b.getAttributeNames())z.setAttribute(K,b.getAttribute(K));return b.replaceWith(z),new P(K=>{z.onload=()=>K.complete()})}else return z.textContent=b.textContent,b.replaceWith(z),L}),Z(),re(f))}),le());return h(window,"popstate").pipe(m(pe)).subscribe(e),e.pipe(V(pe()),Le(2,1),M(([l,f])=>l.pathname===f.pathname&&l.hash!==f.hash),m(([,l])=>l)).subscribe(l=>{var f,u;history.state!==null||!l.hash?window.scrollTo(0,(u=(f=history.state)==null?void 0:f.y)!=null?u:0):(history.scrollRestoration="auto",nr(l.hash),history.scrollRestoration="manual")}),e.pipe(Cr(i),V(pe()),Le(2,1),M(([l,f])=>l.pathname===f.pathname&&l.hash===f.hash),m(([,l])=>l)).subscribe(l=>{history.scrollRestoration="auto",nr(l.hash),history.scrollRestoration="manual",history.back()}),c.pipe(ne(e)).subscribe(([,l])=>{var f,u;history.state!==null||!l.hash?window.scrollTo(0,(u=(f=history.state)==null?void 0:f.y)!=null?u:0):nr(l.hash)}),t.pipe(ee("offset"),ke(100)).subscribe(({offset:l})=>{history.replaceState(l,"")}),c}var Dn=Ht(Nn());function Vn(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,Dn.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function Mt(e){return e.type===1}function pr(e){return e.type===3}function zn(e,t){let r=an(e);return _(j(location.protocol!=="file:"),We("search")).pipe($e(o=>o),E(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:te("search.suggest")}}})),r}function qn({document$:e}){let t=me(),r=Ue(new URL("../versions.json",t.base)).pipe(de(()=>L)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),E(n=>h(document.body,"click").pipe(M(i=>!i.metaKey&&!i.ctrlKey),ne(o),E(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?L:(i.preventDefault(),j(c))}}return L}),E(i=>{let{version:s}=n.get(i);return cr(new URL(i)).pipe(m(a=>{let p=pe().href.replace(t.base,"");return a.includes(p.split("#")[0])?new URL(`../${s}/${p}`,t.base):new URL(i)}))})))).subscribe(n=>ot(n,!0)),B([r,o]).subscribe(([n,i])=>{N(".md-header__topic").appendChild(hn(n,i))}),e.pipe(E(()=>o)).subscribe(n=>{var s;let i=__md_get("__outdated",sessionStorage);if(i===null){i=!0;let a=((s=t.version)==null?void 0:s.default)||"latest";Array.isArray(a)||(a=[a]);e:for(let c of a)for(let p of n.aliases)if(new RegExp(c,"i").test(p)){i=!1;break e}__md_set("__outdated",i,sessionStorage)}if(i)for(let a of oe("outdated"))a.hidden=!1})}function Pa(e,{worker$:t}){let{searchParams:r}=pe();r.has("q")&&(Ke("search",!0),e.value=r.get("q"),e.focus(),We("search").pipe($e(i=>!i)).subscribe(()=>{let i=pe();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Zt(e),n=_(t.pipe($e(Mt)),h(e,"keyup"),o).pipe(m(()=>e.value),X());return B([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),J(1))}function Kn(e,{worker$:t}){let r=new x,o=r.pipe(Z(),re(!0));B([t.pipe($e(Mt)),r],(i,s)=>s).pipe(ee("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ee("focus")).subscribe(({focus:i})=>{i&&Ke("search",i)}),h(e.form,"reset").pipe(Y(o)).subscribe(()=>e.focus());let n=N("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),Pa(e,{worker$:t}).pipe(w(i=>r.next(i)),A(()=>r.complete()),m(i=>R({ref:e},i)),J(1))}function Qn(e,{worker$:t,query$:r}){let o=new x,n=Ko(e.parentElement).pipe(M(Boolean)),i=e.parentElement,s=N(":scope > :first-child",e),a=N(":scope > :last-child",e);We("search").subscribe(l=>a.setAttribute("role",l?"list":"presentation")),o.pipe(ne(r),$r(t.pipe($e(Mt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?be("search.result.none"):be("search.result.placeholder");break;case 1:s.textContent=be("search.result.one");break;default:let u=tr(l.length);s.textContent=be("search.result.other",u)}});let c=o.pipe(w(()=>a.innerHTML=""),E(({items:l})=>_(j(...l.slice(0,10)),j(...l.slice(10)).pipe(Le(4),Ir(n),E(([f])=>f)))),m(fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(ce(l=>{let f=se("details",l);return typeof f=="undefined"?L:h(f,"toggle").pipe(Y(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(M(pr),m(({data:l})=>l)).pipe(w(l=>o.next(l)),A(()=>o.complete()),m(l=>R({ref:e},l)))}function Ia(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=pe();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Yn(e,t){let r=new x,o=r.pipe(Z(),re(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(Y(o)).subscribe(n=>n.preventDefault()),Ia(e,t).pipe(w(n=>r.next(n)),A(()=>r.complete()),m(n=>R({ref:e},n)))}function Bn(e,{worker$:t,keyboard$:r}){let o=new x,n=Ee("search-query"),i=_(h(n,"keydown"),h(n,"focus")).pipe(Se(ae),m(()=>n.value),X());return o.pipe(Ge(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(M(({mode:a})=>a==="search")).subscribe(a=>{switch(a.type){case"ArrowRight":e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText);break}}),t.pipe(M(pr),m(({data:a})=>a)).pipe(w(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function Gn(e,{index$:t,keyboard$:r}){let o=me();try{let n=zn(o.search,t),i=Ee("search-query",e),s=Ee("search-result",e);h(e,"click").pipe(M(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>Ke("search",!1)),r.pipe(M(({mode:c})=>c==="search")).subscribe(c=>{let p=Re();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of q(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":Ke("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...q(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Re()&&i.focus()}}),r.pipe(M(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Kn(i,{worker$:n});return _(a,Qn(s,{worker$:n,query$:a})).pipe(qe(...oe("search-share",e).map(c=>Yn(c,{query$:a})),...oe("search-suggest",e).map(c=>Bn(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,Ve}}function Jn(e,{index$:t,location$:r}){return B([t,r.pipe(V(pe()),M(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>Vn(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=T("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Fa(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return B([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),X((i,s)=>i.height===s.height&&i.locked===s.locked))}function Kr(e,o){var n=o,{header$:t}=n,r=eo(n,["header$"]);let i=N(".md-sidebar__scrollwrap",e),{y:s}=Je(i);return H(()=>{let a=new x,c=a.pipe(Z(),re(!0)),p=a.pipe(Ce(0,Oe));return p.pipe(ne(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe($e()).subscribe(()=>{for(let l of q(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=he(f);f.scrollTo({top:u-d/2})}}}),ge(q("label[tabindex]",e)).pipe(ce(l=>h(l,"click").pipe(Se(ae),m(()=>l),Y(c)))).subscribe(l=>{let f=N(`[id="${l.htmlFor}"]`);N(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),Fa(e,r).pipe(w(l=>a.next(l)),A(()=>a.complete()),m(l=>R({ref:e},l)))})}function Xn(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return St(Ue(`${r}/releases/latest`).pipe(de(()=>L),m(o=>({version:o.tag_name})),He({})),Ue(r).pipe(de(()=>L),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),He({}))).pipe(m(([o,n])=>R(R({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return Ue(r).pipe(m(o=>({repositories:o.public_repos})),He({}))}}function Zn(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Ue(r).pipe(de(()=>L),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),He({}))}function ei(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Xn(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return Zn(r,o)}return L}var ja;function Wa(e){return ja||(ja=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return j(t);if(oe("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return L}return ei(e.href).pipe(w(o=>__md_set("__source",o,sessionStorage)))}).pipe(de(()=>L),M(t=>Object.keys(t).length>0),m(t=>({facts:t})),J(1)))}function ti(e){let t=N(":scope > :last-child",e);return H(()=>{let r=new x;return r.subscribe(({facts:o})=>{t.appendChild(un(o)),t.classList.add("md-source__repository--active")}),Wa(e).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}function Ua(e,{viewport$:t,header$:r}){return ye(document.body).pipe(E(()=>ar(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ee("hidden"))}function ri(e,t){return H(()=>{let r=new x;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(te("navigation.tabs.sticky")?j({hidden:!1}):Ua(e,t)).pipe(w(o=>r.next(o)),A(()=>r.complete()),m(o=>R({ref:e},o)))})}function Na(e,{viewport$:t,header$:r}){let o=new Map,n=q("[href^=\\#]",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=se(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ee("height"),m(({height:a})=>{let c=Ee("main"),p=N(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return ye(document.body).pipe(ee("height"),E(a=>H(()=>{let c=[];return j([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Ge(i),E(([c,p])=>t.pipe(kr(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,b]=f[0];if(b-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),X((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),V({prev:[],next:[]}),Le(2,1),m(([a,c])=>a.prev.length{let i=new x,s=i.pipe(Z(),re(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),te("toc.follow")){let a=_(t.pipe(ke(1),m(()=>{})),t.pipe(ke(250),m(()=>"smooth")));i.pipe(M(({prev:c})=>c.length>0),Ge(o.pipe(Se(ae))),ne(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=zo(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=he(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return te("navigation.tracking")&&t.pipe(Y(s),ee("offset"),ke(250),je(1),Y(n.pipe(je(1))),Tt({delay:250}),ne(i)).subscribe(([,{prev:a}])=>{let c=pe(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),Na(e,{viewport$:t,header$:r}).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))})}function Da(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),Le(2,1),m(([s,a])=>s>a&&a>0),X()),i=r.pipe(m(({active:s})=>s));return B([i,n]).pipe(m(([s,a])=>!(s&&a)),X(),Y(o.pipe(je(1))),re(!0),Tt({delay:250}),m(s=>({hidden:s})))}function ni(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new x,s=i.pipe(Z(),re(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(Y(s),ee("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),Da(e,{viewport$:t,main$:o,target$:n}).pipe(w(a=>i.next(a)),A(()=>i.complete()),m(a=>R({ref:e},a)))}function ii({document$:e,tablet$:t}){e.pipe(E(()=>q(".md-toggle--indeterminate")),w(r=>{r.indeterminate=!0,r.checked=!1}),ce(r=>h(r,"change").pipe(Rr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),ne(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Va(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function ai({document$:e}){e.pipe(E(()=>q("[data-md-scrollfix]")),w(t=>t.removeAttribute("data-md-scrollfix")),M(Va),ce(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function si({viewport$:e,tablet$:t}){B([We("search"),t]).pipe(m(([r,o])=>r&&!o),E(r=>j(r).pipe(ze(r?400:100))),ne(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function za(){return location.protocol==="file:"?ht(`${new URL("search/search_index.js",Qr.base)}`).pipe(m(()=>__index),J(1)):Ue(new URL("search/search_index.json",Qr.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var nt=Uo(),_t=Bo(),gt=Jo(_t),Yr=Yo(),Te=nn(),lr=Fr("(min-width: 960px)"),pi=Fr("(min-width: 1220px)"),li=Xo(),Qr=me(),mi=document.forms.namedItem("search")?za():Ve,Br=new x;Fn({alert$:Br});var Gr=new x;te("navigation.instant")&&Wn({location$:_t,viewport$:Te,progress$:Gr}).subscribe(nt);var ci;((ci=Qr.version)==null?void 0:ci.provider)==="mike"&&qn({document$:nt});_(_t,gt).pipe(ze(125)).subscribe(()=>{Ke("drawer",!1),Ke("search",!1)});Yr.pipe(M(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=se("link[rel=prev]");typeof t!="undefined"&&ot(t);break;case"n":case".":let r=se("link[rel=next]");typeof r!="undefined"&&ot(r);break;case"Enter":let o=Re();o instanceof HTMLLabelElement&&o.click()}});ii({document$:nt,tablet$:lr});ai({document$:nt});si({viewport$:Te,tablet$:lr});var Xe=kn(Ee("header"),{viewport$:Te}),Lt=nt.pipe(m(()=>Ee("main")),E(e=>Rn(e,{viewport$:Te,header$:Xe})),J(1)),qa=_(...oe("consent").map(e=>cn(e,{target$:gt})),...oe("dialog").map(e=>Cn(e,{alert$:Br})),...oe("header").map(e=>Hn(e,{viewport$:Te,header$:Xe,main$:Lt})),...oe("palette").map(e=>Pn(e)),...oe("progress").map(e=>In(e,{progress$:Gr})),...oe("search").map(e=>Gn(e,{index$:mi,keyboard$:Yr})),...oe("source").map(e=>ti(e))),Ka=H(()=>_(...oe("announce").map(e=>sn(e)),...oe("content").map(e=>An(e,{viewport$:Te,target$:gt,print$:li})),...oe("content").map(e=>te("search.highlight")?Jn(e,{index$:mi,location$:_t}):L),...oe("header-title").map(e=>$n(e,{viewport$:Te,header$:Xe})),...oe("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?jr(pi,()=>Kr(e,{viewport$:Te,header$:Xe,main$:Lt})):jr(lr,()=>Kr(e,{viewport$:Te,header$:Xe,main$:Lt}))),...oe("tabs").map(e=>ri(e,{viewport$:Te,header$:Xe})),...oe("toc").map(e=>oi(e,{viewport$:Te,header$:Xe,main$:Lt,target$:gt})),...oe("top").map(e=>ni(e,{viewport$:Te,header$:Xe,main$:Lt,target$:gt})))),fi=nt.pipe(E(()=>Ka),qe(qa),J(1));fi.subscribe();window.document$=nt;window.location$=_t;window.target$=gt;window.keyboard$=Yr;window.viewport$=Te;window.tablet$=lr;window.screen$=pi;window.print$=li;window.alert$=Br;window.progress$=Gr;window.component$=fi;})(); +//# sourceMappingURL=bundle.81fa17fe.min.js.map + diff --git a/assets/javascripts/bundle.81fa17fe.min.js.map b/assets/javascripts/bundle.81fa17fe.min.js.map new file mode 100644 index 0000000..582e525 --- /dev/null +++ b/assets/javascripts/bundle.81fa17fe.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/clipboard/dist/clipboard.js", "node_modules/escape-html/index.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/rxjs/node_modules/tslib/tslib.es6.js", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/sample.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*\n * Copyright (c) 2016-2023 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 960px)\")\nconst screen$ = watchMedia(\"(min-width: 1220px)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n *\n * @class Subscription\n */\nexport class Subscription implements SubscriptionLike {\n /** @nocollapse */\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n * @return {void}\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n *\n * @class Subscriber\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @nocollapse\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param {T} [value] The `next` value.\n * @return {void}\n */\n next(value?: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param {any} [err] The `error` exception.\n * @return {void}\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n * @return {void}\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as (((value: T) => void) | undefined),\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent\n * @param subscriber The stopped subscriber\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n *\n * @class Observable\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @constructor\n * @param {Function} subscribe the function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @owner Observable\n * @method create\n * @param {Function} subscribe? the subscriber function to be passed to the Observable constructor\n * @return {Observable} a new observable\n * @nocollapse\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @method lift\n * @param operator the operator defining the operation to take on the observable\n * @return a new observable with the Operator applied\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param {Observer|Function} observerOrNext (optional) Either an observer with methods to be called,\n * or the first of three possible handlers, which is the handler for each value emitted from the subscribed\n * Observable.\n * @param {Function} error (optional) A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param {Function} complete (optional) A handler for a terminal event resulting from successful completion.\n * @return {Subscription} a subscription reference to the registered handlers\n * @method subscribe\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next a handler for each value emitted by the observable\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @method Symbol.observable\n * @return {Observable} this instance of the observable\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n * @method pipe\n * @return {Observable} the Observable result of all of the operators having\n * been called in the order they were passed in.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @method toPromise\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @nocollapse\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return {Observable} Observable that the Subject casts to\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\n/**\n * @class AnonymousSubject\n */\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param bufferSize The size of the buffer to replay on subscription\n * @param windowTime The amount of time the buffered items will stay buffered\n * @param timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n *\n * @class Action\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler.\n * @return {void}\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n * @return {any}\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @class Scheduler\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return {number} A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param {function(state: ?T): ?Subscription} work A function representing a\n * task, or some unit of work to be executed by the Scheduler.\n * @param {number} [delay] Time to wait before executing the work, where the\n * time unit is implicit and defined by the Scheduler itself.\n * @param {T} [state] Some contextual data that the `work` function uses when\n * called by the Scheduler.\n * @return {Subscription} A subscription in order to be able to unsubscribe\n * the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @type {boolean}\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @type {any}\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n const flushId = this._scheduled;\n this._scheduled = undefined;\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:

\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an