From 9aee1bd620873c41c03722fd7e5017897bf8ec0e Mon Sep 17 00:00:00 2001 From: Jed Sundwall Date: Fri, 15 Sep 2023 16:16:18 -0700 Subject: [PATCH] Built site for gh-pages --- .nojekyll | 1 + .../cogs-examples.html | 0 .../figure-html/cell-15-output-2.png | Bin .../figure-html/cell-15-output-3.png | Bin .../figure-html/cell-15-output-4.png | Bin .../figure-html/cell-19-output-2.png | Bin .../figure-html/cell-19-output-3.png | Bin .../figure-html/cell-19-output-4.png | Bin .../figure-html/cell-19-output-5.png | Bin .../environment.yml | 0 .../intro.html | 0 .../index.html | 0 .../contributing.html => contributing.html | 0 .../pr-55/cookbooks => cookbooks}/index.html | 2 +- {pr-preview/pr-55/copc => copc}/index.html | 0 .../flatgeobuf => flatgeobuf}/environment.yml | 0 .../flatgeobuf => flatgeobuf}/flatgeobuf.html | 0 .../hilbert-r-tree.html | 0 .../flatgeobuf => flatgeobuf}/intro.html | 0 .../geoparquet => geoparquet}/environment.yml | 0 .../geoparquet-example.html | 0 .../geoparquet => geoparquet}/index.html | 0 .../2019-points-lines-polygons.png | Bin .../analytical_vs_tiled_geoms.png | Bin .../pr-55/images => images}/cog-diagram-1.png | Bin .../pr-55/images => images}/cog-overviews.png | Bin .../images => images}/cogeo-formats-table.png | Bin .../copc-vlr-chunk-table-illustration.png | Bin .../pr-55/images => images}/fgb_diagram_2.png | Bin .../flatgeobuf_file_layout.png | Bin .../images => images}/geoparquet_layout.png | Bin .../images => images}/gpq_query_window.png | Bin .../higher-level-libraries.png | Bin .../pr-55/images => images}/multi_refs.png | Bin .../images => images}/pmtiles-viewer.png | Bin .../pr-55/images => images}/r-tree.svg | 0 .../images => images}/thankyous-cng-logo.png | Bin .../thankyous-devseed-logo.png | Bin .../thankyous-impact-logo.png | Bin .../pr-55/images => images}/tile-diagram.png | Bin .../type-format-support-matrix.png | Bin .../xarray-datastructure.png | Bin pr-preview/pr-55/index.html => index.html | 0 .../kerchunk => kerchunk}/environment.yml | 0 .../pr-55/kerchunk => kerchunk}/intro.html | 0 .../kerchunk-in-practice.html | 0 overview.html | 1050 ++++++++++++++++ .../pr-55/pmtiles => pmtiles}/environment.yml | 0 .../pr-55/pmtiles => pmtiles}/intro.html | 0 .../pmtiles => pmtiles}/pmtiles-example.html | 0 pr-preview/pr-55/.nojekyll | 0 pr-preview/pr-55/overview.html | 1081 ----------------- robots.txt | 1 + pr-preview/pr-55/search.json => search.json | 769 ++++++------ .../bootstrap/bootstrap-dark.min.css | 2 +- .../bootstrap/bootstrap-icons.css | 0 .../bootstrap/bootstrap-icons.woff | Bin .../bootstrap/bootstrap.min.css | 2 +- .../bootstrap/bootstrap.min.js | 0 .../clipboard/clipboard.min.js | 0 .../quarto-html/anchor.min.js | 0 site_libs/quarto-html/light-border.css | 1 + .../quarto-html/popper.min.js | 0 site_libs/quarto-html/quarto-html.min.css | 1 + .../quarto-syntax-highlighting-dark.css | 0 .../quarto-syntax-highlighting.css | 0 .../quarto-html/quarto.js | 0 site_libs/quarto-html/tabby.min.js | 418 +++++++ .../quarto-html/tippy.css | 0 .../quarto-html/tippy.umd.min.js | 0 .../quarto-nav/quarto-nav.js | 0 .../quarto-search/autocomplete.umd.js | 0 .../quarto-search/fuse.min.js | 0 .../quarto-search/quarto-search.js | 0 site_libs/revealjs/dist/reset.css | 30 + site_libs/revealjs/dist/reveal.css | 8 + site_libs/revealjs/dist/reveal.esm.js | 9 + site_libs/revealjs/dist/reveal.esm.js.map | 1 + site_libs/revealjs/dist/reveal.js | 9 + site_libs/revealjs/dist/reveal.js.map | 1 + .../dist/theme/fonts/league-gothic/LICENSE | 2 + .../fonts/league-gothic/league-gothic.css | 10 + .../fonts/league-gothic/league-gothic.eot | Bin 0 -> 25696 bytes .../fonts/league-gothic/league-gothic.ttf | Bin 0 -> 64256 bytes .../fonts/league-gothic/league-gothic.woff | Bin 0 -> 30764 bytes .../dist/theme/fonts/source-sans-pro/LICENSE | 45 + .../source-sans-pro-italic.eot | Bin 0 -> 75720 bytes .../source-sans-pro-italic.ttf | Bin 0 -> 238084 bytes .../source-sans-pro-italic.woff | Bin 0 -> 98556 bytes .../source-sans-pro-regular.eot | Bin 0 -> 88070 bytes .../source-sans-pro-regular.ttf | Bin 0 -> 288008 bytes .../source-sans-pro-regular.woff | Bin 0 -> 114324 bytes .../source-sans-pro-semibold.eot | Bin 0 -> 89897 bytes .../source-sans-pro-semibold.ttf | Bin 0 -> 284640 bytes .../source-sans-pro-semibold.woff | Bin 0 -> 115648 bytes .../source-sans-pro-semibolditalic.eot | Bin 0 -> 75706 bytes .../source-sans-pro-semibolditalic.ttf | Bin 0 -> 240944 bytes .../source-sans-pro-semibolditalic.woff | Bin 0 -> 98816 bytes .../fonts/source-sans-pro/source-sans-pro.css | 39 + site_libs/revealjs/dist/theme/quarto.css | 5 + .../plugin/highlight/highlight.esm.js | 5 + .../revealjs/plugin/highlight/highlight.js | 5 + .../revealjs/plugin/highlight/monokai.css | 71 ++ site_libs/revealjs/plugin/highlight/plugin.js | 439 +++++++ .../revealjs/plugin/highlight/zenburn.css | 80 ++ .../revealjs/plugin/markdown/markdown.esm.js | 1 + .../revealjs/plugin/markdown/markdown.js | 1 + site_libs/revealjs/plugin/markdown/plugin.js | 475 ++++++++ site_libs/revealjs/plugin/math/katex.js | 96 ++ site_libs/revealjs/plugin/math/math.esm.js | 1 + site_libs/revealjs/plugin/math/math.js | 1 + site_libs/revealjs/plugin/math/mathjax2.js | 89 ++ site_libs/revealjs/plugin/math/mathjax3.js | 77 ++ site_libs/revealjs/plugin/math/plugin.js | 15 + site_libs/revealjs/plugin/notes/notes.esm.js | 1 + site_libs/revealjs/plugin/notes/notes.js | 1 + site_libs/revealjs/plugin/notes/plugin.js | 236 ++++ .../revealjs/plugin/notes/speaker-view.html | 884 ++++++++++++++ .../revealjs/plugin/pdf-export/pdfexport.js | 111 ++ .../revealjs/plugin/pdf-export/plugin.yml | 2 + .../quarto-line-highlight/line-highlight.css | 31 + .../quarto-line-highlight/line-highlight.js | 351 ++++++ .../plugin/quarto-line-highlight/plugin.yml | 4 + .../revealjs/plugin/quarto-support/footer.css | 110 ++ .../revealjs/plugin/quarto-support/plugin.yml | 5 + .../revealjs/plugin/quarto-support/support.js | 290 +++++ .../revealjs/plugin/reveal-menu/menu.css | 346 ++++++ site_libs/revealjs/plugin/reveal-menu/menu.js | 1 + .../revealjs/plugin/reveal-menu/plugin.yml | 9 + .../plugin/reveal-menu/quarto-menu.css | 68 ++ .../plugin/reveal-menu/quarto-menu.js | 43 + site_libs/revealjs/plugin/search/plugin.js | 243 ++++ .../revealjs/plugin/search/search.esm.js | 7 + site_libs/revealjs/plugin/search/search.js | 7 + site_libs/revealjs/plugin/zoom/plugin.js | 264 ++++ site_libs/revealjs/plugin/zoom/zoom.esm.js | 4 + site_libs/revealjs/plugin/zoom/zoom.js | 4 + sitemap.xml | 87 ++ pr-preview/pr-55/styles.css => styles.css | 0 .../pr-55/template.html => template.html | 0 ...own.html => transition-from-rmarkdown.html | 0 {pr-preview/pr-55/zarr => zarr}/intro.html | 0 .../pr-55/zarr => zarr}/zarr-in-practice.html | 0 143 files changed, 6480 insertions(+), 1472 deletions(-) create mode 100644 .nojekyll rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples.html (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-15-output-2.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-15-output-3.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-15-output-4.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-19-output-2.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-19-output-3.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-19-output-4.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/cogs-examples_files/figure-html/cell-19-output-5.png (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/environment.yml (100%) rename {pr-preview/pr-55/cloud-optimized-geotiffs => cloud-optimized-geotiffs}/intro.html (100%) rename {pr-preview/pr-55/cloud-optimized-netcdf4-hdf5 => cloud-optimized-netcdf4-hdf5}/index.html (100%) rename pr-preview/pr-55/contributing.html => contributing.html (100%) rename {pr-preview/pr-55/cookbooks => cookbooks}/index.html (99%) rename {pr-preview/pr-55/copc => copc}/index.html (100%) rename {pr-preview/pr-55/flatgeobuf => flatgeobuf}/environment.yml (100%) rename {pr-preview/pr-55/flatgeobuf => flatgeobuf}/flatgeobuf.html (100%) rename {pr-preview/pr-55/flatgeobuf => flatgeobuf}/hilbert-r-tree.html (100%) rename {pr-preview/pr-55/flatgeobuf => flatgeobuf}/intro.html (100%) rename {pr-preview/pr-55/geoparquet => geoparquet}/environment.yml (100%) rename {pr-preview/pr-55/geoparquet => geoparquet}/geoparquet-example.html (100%) rename {pr-preview/pr-55/geoparquet => geoparquet}/index.html (100%) rename {pr-preview/pr-55/images => images}/2019-points-lines-polygons.png (100%) rename {pr-preview/pr-55/images => images}/analytical_vs_tiled_geoms.png (100%) rename {pr-preview/pr-55/images => images}/cog-diagram-1.png (100%) rename {pr-preview/pr-55/images => images}/cog-overviews.png (100%) rename {pr-preview/pr-55/images => images}/cogeo-formats-table.png (100%) rename {pr-preview/pr-55/images => images}/copc-vlr-chunk-table-illustration.png (100%) rename {pr-preview/pr-55/images => images}/fgb_diagram_2.png (100%) rename {pr-preview/pr-55/images => images}/flatgeobuf_file_layout.png (100%) rename {pr-preview/pr-55/images => images}/geoparquet_layout.png (100%) rename {pr-preview/pr-55/images => images}/gpq_query_window.png (100%) rename {pr-preview/pr-55/images => images}/higher-level-libraries.png (100%) rename {pr-preview/pr-55/images => images}/multi_refs.png (100%) rename {pr-preview/pr-55/images => images}/pmtiles-viewer.png (100%) rename {pr-preview/pr-55/images => images}/r-tree.svg (100%) rename {pr-preview/pr-55/images => images}/thankyous-cng-logo.png (100%) rename {pr-preview/pr-55/images => images}/thankyous-devseed-logo.png (100%) rename {pr-preview/pr-55/images => images}/thankyous-impact-logo.png (100%) rename {pr-preview/pr-55/images => images}/tile-diagram.png (100%) rename {pr-preview/pr-55/images => images}/type-format-support-matrix.png (100%) rename {pr-preview/pr-55/images => images}/xarray-datastructure.png (100%) rename pr-preview/pr-55/index.html => index.html (100%) rename {pr-preview/pr-55/kerchunk => kerchunk}/environment.yml (100%) rename {pr-preview/pr-55/kerchunk => kerchunk}/intro.html (100%) rename {pr-preview/pr-55/kerchunk => kerchunk}/kerchunk-in-practice.html (100%) create mode 100644 overview.html rename {pr-preview/pr-55/pmtiles => pmtiles}/environment.yml (100%) rename {pr-preview/pr-55/pmtiles => pmtiles}/intro.html (100%) rename {pr-preview/pr-55/pmtiles => pmtiles}/pmtiles-example.html (100%) delete mode 100644 pr-preview/pr-55/.nojekyll delete mode 100644 pr-preview/pr-55/overview.html create mode 100644 robots.txt rename pr-preview/pr-55/search.json => search.json (99%) rename {pr-preview/pr-55/site_libs => site_libs}/bootstrap/bootstrap-dark.min.css (99%) rename {pr-preview/pr-55/site_libs => site_libs}/bootstrap/bootstrap-icons.css (100%) rename {pr-preview/pr-55/site_libs => site_libs}/bootstrap/bootstrap-icons.woff (100%) rename {pr-preview/pr-55/site_libs => site_libs}/bootstrap/bootstrap.min.css (99%) rename {pr-preview/pr-55/site_libs => site_libs}/bootstrap/bootstrap.min.js (100%) rename {pr-preview/pr-55/site_libs => site_libs}/clipboard/clipboard.min.js (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/anchor.min.js (100%) create mode 100644 site_libs/quarto-html/light-border.css rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/popper.min.js (100%) create mode 100644 site_libs/quarto-html/quarto-html.min.css rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/quarto-syntax-highlighting-dark.css (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/quarto-syntax-highlighting.css (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/quarto.js (100%) create mode 100644 site_libs/quarto-html/tabby.min.js rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/tippy.css (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-html/tippy.umd.min.js (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-nav/quarto-nav.js (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-search/autocomplete.umd.js (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-search/fuse.min.js (100%) rename {pr-preview/pr-55/site_libs => site_libs}/quarto-search/quarto-search.js (100%) create mode 100644 site_libs/revealjs/dist/reset.css create mode 100644 site_libs/revealjs/dist/reveal.css create mode 100644 site_libs/revealjs/dist/reveal.esm.js create mode 100644 site_libs/revealjs/dist/reveal.esm.js.map create mode 100644 site_libs/revealjs/dist/reveal.js create mode 100644 site_libs/revealjs/dist/reveal.js.map create mode 100644 site_libs/revealjs/dist/theme/fonts/league-gothic/LICENSE create mode 100644 site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.css create mode 100644 site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.eot create mode 100644 site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.ttf create mode 100644 site_libs/revealjs/dist/theme/fonts/league-gothic/league-gothic.woff create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/LICENSE create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.eot create mode 100755 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.ttf create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-italic.woff create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.eot create mode 100755 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.ttf create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-regular.woff create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.eot create mode 100755 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.ttf create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibold.woff create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.eot create mode 100755 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.ttf create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro-semibolditalic.woff create mode 100644 site_libs/revealjs/dist/theme/fonts/source-sans-pro/source-sans-pro.css create mode 100644 site_libs/revealjs/dist/theme/quarto.css create mode 100644 site_libs/revealjs/plugin/highlight/highlight.esm.js create mode 100644 site_libs/revealjs/plugin/highlight/highlight.js create mode 100644 site_libs/revealjs/plugin/highlight/monokai.css create mode 100644 site_libs/revealjs/plugin/highlight/plugin.js create mode 100644 site_libs/revealjs/plugin/highlight/zenburn.css create mode 100644 site_libs/revealjs/plugin/markdown/markdown.esm.js create mode 100644 site_libs/revealjs/plugin/markdown/markdown.js create mode 100644 site_libs/revealjs/plugin/markdown/plugin.js create mode 100644 site_libs/revealjs/plugin/math/katex.js create mode 100644 site_libs/revealjs/plugin/math/math.esm.js create mode 100644 site_libs/revealjs/plugin/math/math.js create mode 100644 site_libs/revealjs/plugin/math/mathjax2.js create mode 100644 site_libs/revealjs/plugin/math/mathjax3.js create mode 100644 site_libs/revealjs/plugin/math/plugin.js create mode 100644 site_libs/revealjs/plugin/notes/notes.esm.js create mode 100644 site_libs/revealjs/plugin/notes/notes.js create mode 100644 site_libs/revealjs/plugin/notes/plugin.js create mode 100644 site_libs/revealjs/plugin/notes/speaker-view.html create mode 100644 site_libs/revealjs/plugin/pdf-export/pdfexport.js create mode 100644 site_libs/revealjs/plugin/pdf-export/plugin.yml create mode 100644 site_libs/revealjs/plugin/quarto-line-highlight/line-highlight.css create mode 100644 site_libs/revealjs/plugin/quarto-line-highlight/line-highlight.js create mode 100644 site_libs/revealjs/plugin/quarto-line-highlight/plugin.yml create mode 100644 site_libs/revealjs/plugin/quarto-support/footer.css create mode 100644 site_libs/revealjs/plugin/quarto-support/plugin.yml create mode 100644 site_libs/revealjs/plugin/quarto-support/support.js create mode 100644 site_libs/revealjs/plugin/reveal-menu/menu.css create mode 100644 site_libs/revealjs/plugin/reveal-menu/menu.js create mode 100644 site_libs/revealjs/plugin/reveal-menu/plugin.yml create mode 100644 site_libs/revealjs/plugin/reveal-menu/quarto-menu.css create mode 100644 site_libs/revealjs/plugin/reveal-menu/quarto-menu.js create mode 100644 site_libs/revealjs/plugin/search/plugin.js create mode 100644 site_libs/revealjs/plugin/search/search.esm.js create mode 100644 site_libs/revealjs/plugin/search/search.js create mode 100644 site_libs/revealjs/plugin/zoom/plugin.js create mode 100644 site_libs/revealjs/plugin/zoom/zoom.esm.js create mode 100644 site_libs/revealjs/plugin/zoom/zoom.js create mode 100644 sitemap.xml rename pr-preview/pr-55/styles.css => styles.css (100%) rename pr-preview/pr-55/template.html => template.html (100%) rename pr-preview/pr-55/transition-from-rmarkdown.html => transition-from-rmarkdown.html (100%) rename {pr-preview/pr-55/zarr => zarr}/intro.html (100%) rename {pr-preview/pr-55/zarr => zarr}/zarr-in-practice.html (100%) diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..afd600f --- /dev/null +++ b/.nojekyll @@ -0,0 +1 @@ +fa517d98 \ No newline at end of file diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples.html b/cloud-optimized-geotiffs/cogs-examples.html similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples.html rename to cloud-optimized-geotiffs/cogs-examples.html diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-2.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-2.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-2.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-2.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-3.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-3.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-3.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-3.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-4.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-4.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-4.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-15-output-4.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-2.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-2.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-2.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-2.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-3.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-3.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-3.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-3.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-4.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-4.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-4.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-4.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-5.png b/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-5.png similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-5.png rename to cloud-optimized-geotiffs/cogs-examples_files/figure-html/cell-19-output-5.png diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/environment.yml b/cloud-optimized-geotiffs/environment.yml similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/environment.yml rename to cloud-optimized-geotiffs/environment.yml diff --git a/pr-preview/pr-55/cloud-optimized-geotiffs/intro.html b/cloud-optimized-geotiffs/intro.html similarity index 100% rename from pr-preview/pr-55/cloud-optimized-geotiffs/intro.html rename to cloud-optimized-geotiffs/intro.html diff --git a/pr-preview/pr-55/cloud-optimized-netcdf4-hdf5/index.html b/cloud-optimized-netcdf4-hdf5/index.html similarity index 100% rename from pr-preview/pr-55/cloud-optimized-netcdf4-hdf5/index.html rename to cloud-optimized-netcdf4-hdf5/index.html diff --git a/pr-preview/pr-55/contributing.html b/contributing.html similarity index 100% rename from pr-preview/pr-55/contributing.html rename to contributing.html diff --git a/pr-preview/pr-55/cookbooks/index.html b/cookbooks/index.html similarity index 99% rename from pr-preview/pr-55/cookbooks/index.html rename to cookbooks/index.html index 0bb5c53..0d6317b 100644 --- a/pr-preview/pr-55/cookbooks/index.html +++ b/cookbooks/index.html @@ -696,7 +696,7 @@

Cloud-Optimized Cookbooks

} } var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//); - var filterRegex = new RegExp('/' + window.location.host + '/'); + var filterRegex = new RegExp("https:\/\/github\.com\/cloudnativegeo\/cloud-optimized-geospatial-formats-guide"); var isInternal = (href) => { return filterRegex.test(href) || localhostRegex.test(href); } diff --git a/pr-preview/pr-55/copc/index.html b/copc/index.html similarity index 100% rename from pr-preview/pr-55/copc/index.html rename to copc/index.html diff --git a/pr-preview/pr-55/flatgeobuf/environment.yml b/flatgeobuf/environment.yml similarity index 100% rename from pr-preview/pr-55/flatgeobuf/environment.yml rename to flatgeobuf/environment.yml diff --git a/pr-preview/pr-55/flatgeobuf/flatgeobuf.html b/flatgeobuf/flatgeobuf.html similarity index 100% rename from pr-preview/pr-55/flatgeobuf/flatgeobuf.html rename to flatgeobuf/flatgeobuf.html diff --git a/pr-preview/pr-55/flatgeobuf/hilbert-r-tree.html b/flatgeobuf/hilbert-r-tree.html similarity index 100% rename from pr-preview/pr-55/flatgeobuf/hilbert-r-tree.html rename to flatgeobuf/hilbert-r-tree.html diff --git a/pr-preview/pr-55/flatgeobuf/intro.html b/flatgeobuf/intro.html similarity index 100% rename from pr-preview/pr-55/flatgeobuf/intro.html rename to flatgeobuf/intro.html diff --git a/pr-preview/pr-55/geoparquet/environment.yml b/geoparquet/environment.yml similarity index 100% rename from pr-preview/pr-55/geoparquet/environment.yml rename to geoparquet/environment.yml diff --git a/pr-preview/pr-55/geoparquet/geoparquet-example.html b/geoparquet/geoparquet-example.html similarity index 100% rename from pr-preview/pr-55/geoparquet/geoparquet-example.html rename to geoparquet/geoparquet-example.html diff --git a/pr-preview/pr-55/geoparquet/index.html b/geoparquet/index.html similarity index 100% rename from pr-preview/pr-55/geoparquet/index.html rename to geoparquet/index.html diff --git a/pr-preview/pr-55/images/2019-points-lines-polygons.png b/images/2019-points-lines-polygons.png similarity index 100% rename from pr-preview/pr-55/images/2019-points-lines-polygons.png rename to images/2019-points-lines-polygons.png diff --git a/pr-preview/pr-55/images/analytical_vs_tiled_geoms.png b/images/analytical_vs_tiled_geoms.png similarity index 100% rename from pr-preview/pr-55/images/analytical_vs_tiled_geoms.png rename to images/analytical_vs_tiled_geoms.png diff --git a/pr-preview/pr-55/images/cog-diagram-1.png b/images/cog-diagram-1.png similarity index 100% rename from pr-preview/pr-55/images/cog-diagram-1.png rename to images/cog-diagram-1.png diff --git a/pr-preview/pr-55/images/cog-overviews.png b/images/cog-overviews.png similarity index 100% rename from pr-preview/pr-55/images/cog-overviews.png rename to images/cog-overviews.png diff --git a/pr-preview/pr-55/images/cogeo-formats-table.png b/images/cogeo-formats-table.png similarity index 100% rename from pr-preview/pr-55/images/cogeo-formats-table.png rename to images/cogeo-formats-table.png diff --git a/pr-preview/pr-55/images/copc-vlr-chunk-table-illustration.png b/images/copc-vlr-chunk-table-illustration.png similarity index 100% rename from pr-preview/pr-55/images/copc-vlr-chunk-table-illustration.png rename to images/copc-vlr-chunk-table-illustration.png diff --git a/pr-preview/pr-55/images/fgb_diagram_2.png b/images/fgb_diagram_2.png similarity index 100% rename from pr-preview/pr-55/images/fgb_diagram_2.png rename to images/fgb_diagram_2.png diff --git a/pr-preview/pr-55/images/flatgeobuf_file_layout.png b/images/flatgeobuf_file_layout.png similarity index 100% rename from pr-preview/pr-55/images/flatgeobuf_file_layout.png rename to images/flatgeobuf_file_layout.png diff --git a/pr-preview/pr-55/images/geoparquet_layout.png b/images/geoparquet_layout.png similarity index 100% rename from pr-preview/pr-55/images/geoparquet_layout.png rename to images/geoparquet_layout.png diff --git a/pr-preview/pr-55/images/gpq_query_window.png b/images/gpq_query_window.png similarity index 100% rename from pr-preview/pr-55/images/gpq_query_window.png rename to images/gpq_query_window.png diff --git a/pr-preview/pr-55/images/higher-level-libraries.png b/images/higher-level-libraries.png similarity index 100% rename from pr-preview/pr-55/images/higher-level-libraries.png rename to images/higher-level-libraries.png diff --git a/pr-preview/pr-55/images/multi_refs.png b/images/multi_refs.png similarity index 100% rename from pr-preview/pr-55/images/multi_refs.png rename to images/multi_refs.png diff --git a/pr-preview/pr-55/images/pmtiles-viewer.png b/images/pmtiles-viewer.png similarity index 100% rename from pr-preview/pr-55/images/pmtiles-viewer.png rename to images/pmtiles-viewer.png diff --git a/pr-preview/pr-55/images/r-tree.svg b/images/r-tree.svg similarity index 100% rename from pr-preview/pr-55/images/r-tree.svg rename to images/r-tree.svg diff --git a/pr-preview/pr-55/images/thankyous-cng-logo.png b/images/thankyous-cng-logo.png similarity index 100% rename from pr-preview/pr-55/images/thankyous-cng-logo.png rename to images/thankyous-cng-logo.png diff --git a/pr-preview/pr-55/images/thankyous-devseed-logo.png b/images/thankyous-devseed-logo.png similarity index 100% rename from pr-preview/pr-55/images/thankyous-devseed-logo.png rename to images/thankyous-devseed-logo.png diff --git a/pr-preview/pr-55/images/thankyous-impact-logo.png b/images/thankyous-impact-logo.png similarity index 100% rename from pr-preview/pr-55/images/thankyous-impact-logo.png rename to images/thankyous-impact-logo.png diff --git a/pr-preview/pr-55/images/tile-diagram.png b/images/tile-diagram.png similarity index 100% rename from pr-preview/pr-55/images/tile-diagram.png rename to images/tile-diagram.png diff --git a/pr-preview/pr-55/images/type-format-support-matrix.png b/images/type-format-support-matrix.png similarity index 100% rename from pr-preview/pr-55/images/type-format-support-matrix.png rename to images/type-format-support-matrix.png diff --git a/pr-preview/pr-55/images/xarray-datastructure.png b/images/xarray-datastructure.png similarity index 100% rename from pr-preview/pr-55/images/xarray-datastructure.png rename to images/xarray-datastructure.png diff --git a/pr-preview/pr-55/index.html b/index.html similarity index 100% rename from pr-preview/pr-55/index.html rename to index.html diff --git a/pr-preview/pr-55/kerchunk/environment.yml b/kerchunk/environment.yml similarity index 100% rename from pr-preview/pr-55/kerchunk/environment.yml rename to kerchunk/environment.yml diff --git a/pr-preview/pr-55/kerchunk/intro.html b/kerchunk/intro.html similarity index 100% rename from pr-preview/pr-55/kerchunk/intro.html rename to kerchunk/intro.html diff --git a/pr-preview/pr-55/kerchunk/kerchunk-in-practice.html b/kerchunk/kerchunk-in-practice.html similarity index 100% rename from pr-preview/pr-55/kerchunk/kerchunk-in-practice.html rename to kerchunk/kerchunk-in-practice.html diff --git a/overview.html b/overview.html new file mode 100644 index 0000000..4651a8c --- /dev/null +++ b/overview.html @@ -0,0 +1,1050 @@ + + + + + + + + + + + + + + Cloud-Optimized Geospatial Formats Guide - Cloud-Optimized Geospatial Formats Overview + + + + + + + + + + + + + + + +
+
+ +
+

Cloud-Optimized Geospatial Formats Overview

+

These slides are a summarization of Cloud-Optimized Geospatial Formats Guide to support presentations.

+ +
+
+
+Authors + Credits: Aimee Barciauskas, Alex Mandel, Brianna Pagán, Vincent Sarago, Chris Holmes, Patrick Quinn, Matt Hanson, Ryan Abernathey +
+
+
+ +
+
+ + +
+
+

Cloud-Optimized Geospatial Formats Overview

+

Google Slides version of this content: Cloud-Optimized Geospatial Formats.

+
+ +
+

What makes cloud-optimized challenging?

+
    +
  • No one size fits all approach
  • +
  • Earth observation data may be processed into raster, vector and point cloud data types and stored in a long list of data formats and structures.
  • +
  • Optimization depends on the user.
  • +
  • Users must learn new tools and which data is accessed and how may differ depending on the user.
  • +
  • … hopefully only a few new methods and concepts are necessary.
  • +
+
+ +
+

What makes cloud-optimized challenging?

+ +

image source: ui.josiahparry.com/spatial-analysis.html

+
+ +
+

What makes cloud-optimized challenging?

+
+
+
+

There is no one-size-fits-all packaging for data, as the optimal packaging is highly use-case dependent.

+
+

Task 51 - Cloud-Optimized Format Study

+

Authors: Chris Durbin, Patrick Quinn, Dana Shum

+
+

+
+
+
+ +
+
+

What does cloud-optimized mean?

+

File formats are read-oriented to support:

+
    +
  • Partial reads
  • +
  • Parallel reads
  • +
+
+
+

What does cloud-optimized mean?

+
    +
  • File metadata in one read
  • +
  • When accessing data over the internet, such as when data is in cloud storage, latency is high when compared with local storage so it is preferable to fetch lots of data in fewer reads.
  • +
  • An easy win is metadata in one read, which can be used to read a cloud-native dataset.
  • +
  • A cloud-native dataset is one with small addressable chunks via files, internal tiles, or both.
  • +
+
+
+

What does cloud-optimized mean?

+
+
+
    +
  • Accessible over HTTP using range requests.
  • +
  • This makes it compatible with object storage (a file storage alternative to local disk) and thus accessible via HTTP, from many compute instances.
  • +
  • Supports lazy access and intelligent subsetting.
  • +
  • Integrates with high-level analysis libraries and distributed frameworks.
  • +
+
+

higher level libraries

+
+
+ +
+
+

Formats by Data Type

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FormatData TypeStandard Status
Cloud-Optimized GeoTIFF (COG)RasterOGC standard for comment
Zarr, KerchunkMulti-dimensional rasterESDIS and OGC standards in development
Cloud-Optimized Point Cloud (COPC), Entwine Point Tiles (EPT)Point Clouds*no known ESDIS or OGC standard
FlatGeobuf, GeoParquet,Vectorno known ESDIS, draft OGC standard
+
+ +
+

Formats by Adoption

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FormatAdoptionStandard Status
Cloud-Optimized GeoTIFF (COG)Widely adoptedOGC standard for comment
Zarr, Kerchunk(Less) widely adopted, especially in specific communitiesESDIS and OGC standards in development
Entwine Point Tiles (EPT), Cloud-Optimized Point Cloud (COPC)Less common (PDAL Supported)no known ESDIS or OGC standard
GeoParquet, FlatGeobufLess common (OGR Supported)no known ESDIS, draft OGC standard
+
+ +
+
+

What are COGs?

+
+
+
    +
  • COGs are raster data representing a snapshot in time of gridded data, for example digital elevation models (DEMs).
  • +
  • COGs are a de facto standard, with an Open Geospatial Consortium (OGC) standard under review.
  • +
  • The standard specifies conformance to how the GeoTIFF is formatted, with additional requirements of tiling and overviews.
  • +
+
+

+
+
+ +
+
+

What are COGs?

+
+
+
    +
  • COGs have internal file directories (IFDs) which are used to tell clients where to find different overview levels and data within the file.
  • +
  • Clients can use this metadata to read only the data they need to visualize or calculate.
  • +
  • This internal organization is friendly for consumption by clients issuing HTTP GET range request (“bytes: start_offset-end_offset” HTTP header)
  • +
+
+

+
+
+ +
+
+

What is Zarr?

+
+
+
    +
  • Zarr is used to represent multidimensional raster data or “data cubes”. For example, weather data and climate models.
  • +
  • Chunked, compressed, N-dimensional arrays.
  • +
  • The metadata is stored external to the data files themselves. The data itself is often reorganized and compressed into many files which can be accessed according to which chunks the user is interested in.
  • +
+
+

+
+
+ +
+ +
+
+

What is Kerchunk?

+
    +
  • Kerchunk is a way to create Zarr metadata for archival formats, so that you can leverage the benefits of partial and parallel reads for archives in NetCDF4, HDF5, GRIB2, TIFF and FITS.
  • +
+
+

+ +
+
+
+

Zarr specs in development

+
    +
  • V2 and older specs exist, however,
  • +
  • A cross-organization working group has just formed to establish a GeoZarr standards working group, organized by Brianna Pagán (NASA) and includes representatives from many other orgs in the industry.
  • +
  • The GeoZarr spec defines conventions for how geospatial data should be organized in a Zarr store. The spec details how the Zarr DataArray and DataSet metadata, and subsequent organization of data, must be in order to be conformant as GeoZarr archive.
  • +
  • There is a proposal for Zarr v3 which will address challenges in language support, and storage organization to address the issues of high-latency reads and volume of reads for the many objects stored.
  • +
  • There is recent work on a parquet alternative to JSON for indexing.
  • +
+
+
+

COPC (Cloud-Optimized Point Clouds)

+

+ +
    +
  • Point clouds are a set of data points in space, such as gathered from LiDAR measurements.
  • +
  • COPC is a valid LAZ file.
  • +
  • Similar to COGs but for point clouds: COPC is just one file, but data is reorganized into a clustered octree instead of regularly gridded overviews.
  • +
  • 2 key features: +
      +
    • Support for partial decompression via storage of data in a series of chunks
    • +
    • Variable-length records (VLRs) can store application-specific metadata of any kind. VLRs describe the octree structure.
    • +
  • +
  • Limitation: Not all attribute types are compatible.
  • +
+
+
+

FlatGeoBuf

+
+
+

+
+
    +
  • Vector data is traditionally stored as rows representing points, lines, or polygons with an attribute table.
  • +
  • FlatGeobuf is a binary encoding format for geographic data. Flatbuffers that hold a collection of Simple Features. Single-File.
  • +
  • A row-based streamable-spatial index optimizes for remote reading.
  • +
  • Developed with OGR compatibility in mind. Works with existing OGR APIs, e.g. python and R.
  • +
  • Works with HTTP range requests, and has CDN compatibility.
  • +
  • Limitation: Not compressed specifically to allow random reads.
  • +
  • Learn more: https://github.com/flatgeobuf/flatgeobuf, Kicking the Tires: Flatgeobuf
  • +
+
+
+ +
+ +
+

Geoparquet

+
+
+

+
+
    +
  • Vector data is traditionally stored as rows representing points, lines, or polygons with an attribute table
  • +
  • GeoParquet defines how to store vector data in Apache Parquet, which is a columnar storage format (like many cloud data warehouses). “Give me all points with height greater than 10m”.
  • +
  • Highly compressed
  • +
  • Single-file or multi-file
  • +
  • Recent support added to geopandas as a distinct function, R support with geoarrow
  • +
  • Potential for cross language in-memory shared access
  • +
  • Specifications for spatial-indexing, projection handling, etc. are still in discussion
  • +
  • Learn more: https://github.com/opengeospatial/geoparquet
  • +
+
+
+


+ +
+ +
+
+

The End?

+

Return to Cloud-Optimized Geospatial Formats Guide or …

+
+
+

Not quite

+
    +
  • These formats and their tooling are in active development
  • +
  • Some formats were not mentioned, such as EPT, geopkg, tiledb, Cloud-Optimized HDF5. This presentation was scoped to those known best by the authors.
  • +
  • This site will continue to be updated with new content.
  • +
+
+
+

References

+
+

Prior presentations and studies discussing multiple formats

+ +

Format Homepages and Explainers

+ +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pr-preview/pr-55/pmtiles/environment.yml b/pmtiles/environment.yml similarity index 100% rename from pr-preview/pr-55/pmtiles/environment.yml rename to pmtiles/environment.yml diff --git a/pr-preview/pr-55/pmtiles/intro.html b/pmtiles/intro.html similarity index 100% rename from pr-preview/pr-55/pmtiles/intro.html rename to pmtiles/intro.html diff --git a/pr-preview/pr-55/pmtiles/pmtiles-example.html b/pmtiles/pmtiles-example.html similarity index 100% rename from pr-preview/pr-55/pmtiles/pmtiles-example.html rename to pmtiles/pmtiles-example.html diff --git a/pr-preview/pr-55/.nojekyll b/pr-preview/pr-55/.nojekyll deleted file mode 100644 index e69de29..0000000 diff --git a/pr-preview/pr-55/overview.html b/pr-preview/pr-55/overview.html deleted file mode 100644 index d91e212..0000000 --- a/pr-preview/pr-55/overview.html +++ /dev/null @@ -1,1081 +0,0 @@ - - - - - - - - - - -Cloud-Optimized Geospatial Formats Guide - Cloud-Optimized Geospatial Formats Overview - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- -
- -
- - -
- - - -
- -
-
-

Cloud-Optimized Geospatial Formats Overview

-

These slides are a summarization of Cloud-Optimized Geospatial Formats Guide to support presentations.

-
- - - -
- -
-
Author
-
-

Authors + Credits: Aimee Barciauskas, Alex Mandel, Brianna Pagán, Vincent Sarago, Chris Holmes, Patrick Quinn, Matt Hanson, Ryan Abernathey

-
-
- - - -
- - -
- -
-

These slides were generated with https://quarto.org/docs/presentations/revealjs. Source: https://github.com/cloudnativegeo/cloud-optimized-geospatial-formats-guide.

-
-
-

Cloud-Optimized Geospatial Formats Overview

-

Google Slides version of this content: Cloud-Optimized Geospatial Formats.

-
-
-

What makes cloud-optimized challenging?

-
    -
  • No one size fits all approach
  • -
  • Earth observation data may be processed into raster, vector and point cloud data types and stored in a long list of data formats and structures.
  • -
  • Optimization depends on the user.
  • -
  • Users must learn new tools and which data is accessed and how may differ depending on the user.
  • -
  • … hopefully only a few new methods and concepts are necessary.
  • -
-
-
-

What makes cloud-optimized challenging?

-

-

image source: ui.josiahparry.com/spatial-analysis.html

-
-
-

What makes cloud-optimized challenging?

-
-
-
-

There is no one-size-fits-all packaging for data, as the optimal packaging is highly use-case dependent.

-
-

Task 51 - Cloud-Optimized Format Study

-

Authors: Chris Durbin, Patrick Quinn, Dana Shum

-
-

-
-
-
-
-

What does cloud-optimized mean?

-

File formats are read-oriented to support:

-
    -
  • Partial reads
  • -
  • Parallel reads
  • -
-
-

What does cloud-optimized mean?

-
    -
  • File metadata in one read
  • -
  • When accessing data over the internet, such as when data is in cloud storage, latency is high when compared with local storage so it is preferable to fetch lots of data in fewer reads.
  • -
  • An easy win is metadata in one read, which can be used to read a cloud-native dataset.
  • -
  • A cloud-native dataset is one with small addressable chunks via files, internal tiles, or both.
  • -
-
-
-

What does cloud-optimized mean?

-
-
-
    -
  • Accessible over HTTP using range requests.
  • -
  • This makes it compatible with object storage (a file storage alternative to local disk) and thus accessible via HTTP, from many compute instances.
  • -
  • Supports lazy access and intelligent subsetting.
  • -
  • Integrates with high-level analysis libraries and distributed frameworks.
  • -
-
-

higher level libraries

-
-
- -
-

image credit: Ryan Abernathey

-
-
-
-

Formats by Data Type

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FormatData TypeStandard Status
Cloud-Optimized GeoTIFF (COG)RasterOGC standard for comment
Zarr, KerchunkMulti-dimensional rasterESDIS and OGC standards in development
Cloud-Optimized Point Cloud (COPC), Entwine Point Tiles (EPT)Point Clouds*no known ESDIS or OGC standard
FlatGeobuf, GeoParquet,Vectorno known ESDIS, draft OGC standard
-
-
-

Formats by Adoption

- ----- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FormatAdoptionStandard Status
Cloud-Optimized GeoTIFF (COG)Widely adoptedOGC standard for comment
Zarr, Kerchunk(Less) widely adopted, especially in specific communitiesESDIS and OGC standards in development
Entwine Point Tiles (EPT), Cloud-Optimized Point Cloud (COPC)Less common (PDAL Supported)no known ESDIS or OGC standard
GeoParquet, FlatGeobufLess common (OGR Supported)no known ESDIS, draft OGC standard
-
-
-

What are COGs?

-
-
-
    -
  • COGs are raster data representing a snapshot in time of gridded data, for example digital elevation models (DEMs).
  • -
  • COGs are a de facto standard, with an Open Geospatial Consortium (OGC) standard under review.
  • -
  • The standard specifies conformance to how the GeoTIFF is formatted, with additional requirements of tiling and overviews.
  • -
-
-

-
-
- -
-

image source: https://www.kitware.com/deciphering-cloud-optimized-geotiffs/

-
-

What are COGs?

-
-
-
    -
  • COGs have internal file directories (IFDs) which are used to tell clients where to find different overview levels and data within the file.
  • -
  • Clients can use this metadata to read only the data they need to visualize or calculate.
  • -
  • This internal organization is friendly for consumption by clients issuing HTTP GET range request (“bytes: start_offset-end_offset” HTTP header)
  • -
-
-

-
-
- -
-

image source: https://medium.com/devseed/cog-talk-part-1-whats-new-941facbcd3d1

-
-
-
-

What is Zarr?

-
-
-
    -
  • Zarr is used to represent multidimensional raster data or “data cubes”. For example, weather data and climate models.
  • -
  • Chunked, compressed, N-dimensional arrays.
  • -
  • The metadata is stored external to the data files themselves. The data itself is often reorganized and compressed into many files which can be accessed according to which chunks the user is interested in.
  • -
-
-

-
-
- -
-

image source: https://xarray.dev/

-
-
-

What is Kerchunk?

-
    -
  • Kerchunk is a way to create Zarr metadata for archival formats, so that you can leverage the benefits of partial and parallel reads for archives in NetCDF4, HDF5, GRIB2, TIFF and FITS.
  • -
-

. . .

-

- -
-

image source: https://fsspec.github.io/kerchunk/detail.html

-
-

Zarr specs in development

-
    -
  • V2 and older specs exist, however,
  • -
  • A cross-organization working group has just formed to establish a GeoZarr standards working group, organized by Brianna Pagán (NASA) and includes representatives from many other orgs in the industry.
  • -
  • The GeoZarr spec defines conventions for how geospatial data should be organized in a Zarr store. The spec details how the Zarr DataArray and DataSet metadata, and subsequent organization of data, must be in order to be conformant as GeoZarr archive.
  • -
  • There is a proposal for Zarr v3 which will address challenges in language support, and storage organization to address the issues of high-latency reads and volume of reads for the many objects stored.
  • -
  • There is recent work on a parquet alternative to JSON for indexing.
  • -
-
-
-

COPC (Cloud-Optimized Point Clouds)

-

- -
-

image source: https://copc.io/

-
    -
  • Point clouds are a set of data points in space, such as gathered from LiDAR measurements.
  • -
  • COPC is a valid LAZ file.
  • -
  • Similar to COGs but for point clouds: COPC is just one file, but data is reorganized into a clustered octree instead of regularly gridded overviews.
  • -
  • 2 key features: -
      -
    • Support for partial decompression via storage of data in a series of chunks
    • -
    • Variable-length records (VLRs) can store application-specific metadata of any kind. VLRs describe the octree structure.
    • -
  • -
  • Limitation: Not all attribute types are compatible.
  • -
-
-
-
-

FlatGeoBuf

-
-
-

-
-
    -
  • Vector data is traditionally stored as rows representing points, lines, or polygons with an attribute table.
  • -
  • FlatGeobuf is a binary encoding format for geographic data. Flatbuffers that hold a collection of Simple Features. Single-File.
  • -
  • A row-based streamable-spatial index optimizes for remote reading.
  • -
  • Developed with OGR compatibility in mind. Works with existing OGR APIs, e.g. python and R.
  • -
  • Works with HTTP range requests, and has CDN compatibility.
  • -
  • Limitation: Not compressed specifically to allow random reads.
  • -
  • Learn more: https://github.com/flatgeobuf/flatgeobuf, Kicking the Tires: Flatgeobuf
  • -
-
-
- -
-

image source: https://worace.works/2022/02/23/kicking-the-tires-flatgeobuf/

-
-
-

Geoparquet

-
-
-

-
-
    -
  • Vector data is traditionally stored as rows representing points, lines, or polygons with an attribute table
  • -
  • GeoParquet defines how to store vector data in Apache Parquet, which is a columnar storage format (like many cloud data warehouses). “Give me all points with height greater than 10m”.
  • -
  • Highly compressed
  • -
  • Single-file or multi-file
  • -
  • Recent support added to geopandas as a distinct function, R support with geoarrow
  • -
  • Potential for cross language in-memory shared access
  • -
  • Specifications for spatial-indexing, projection handling, etc. are still in discussion
  • -
  • Learn more: https://github.com/opengeospatial/geoparquet
  • -
-
-
-


- -
-

image source: https://www.wherobots.ai/post/spatial-data-parquet-and-apache-sedona

-
-
-

The End?

-

Return to Cloud-Optimized Geospatial Formats Guide or …

-
-

Not quite

-
    -
  • These formats and their tooling are in active development
  • -
  • Some formats were not mentioned, such as EPT, geopkg, tiledb, Cloud-Optimized HDF5. This presentation was scoped to those known best by the authors.
  • -
  • This site will continue to be updated with new content.
  • -
-
-
-
-

References

-
-

Prior presentations and studies discussing multiple formats

- -

Format Homepages and Explainers

- -
- - -
- -
- - -
- - - - - \ No newline at end of file diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..45f96be --- /dev/null +++ b/robots.txt @@ -0,0 +1 @@ +Sitemap: https://github.com/cloudnativegeo/cloud-optimized-geospatial-formats-guide/sitemap.xml diff --git a/pr-preview/pr-55/search.json b/search.json similarity index 99% rename from pr-preview/pr-55/search.json rename to search.json index 1290710..bc281c3 100644 --- a/pr-preview/pr-55/search.json +++ b/search.json @@ -1,4 +1,179 @@ [ + { + "objectID": "geoparquet/geoparquet-example.html", + "href": "geoparquet/geoparquet-example.html", + "title": "GeoParquet Example", + "section": "", + "text": "This notebook will give an overview of how to read and write GeoParquet files with GeoPandas, putting an emphasis on cloud-native operations where possible.\nThe easiest way to read and write GeoParquet files is to use GeoPandas’ read_parquet and to_parquet functions." + }, + { + "objectID": "geoparquet/geoparquet-example.html#environment", + "href": "geoparquet/geoparquet-example.html#environment", + "title": "GeoParquet Example", + "section": "Environment", + "text": "Environment\nThe packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment. If you don’t want to use Conda or Mamba, install the latest versions of geopandas, fsspec, and pyarrow with pip. Note that you’ll also need the GDAL CLI with Parquet driver. If you’re on MacOS, you can install that via brew install gdal." + }, + { + "objectID": "geoparquet/geoparquet-example.html#imports", + "href": "geoparquet/geoparquet-example.html#imports", + "title": "GeoParquet Example", + "section": "Imports", + "text": "Imports\n\nfrom urllib.request import urlretrieve\n\nimport fsspec\nimport geopandas as gpd\nfrom fsspec.implementations.http import HTTPFileSystem" + }, + { + "objectID": "geoparquet/geoparquet-example.html#comparison-with-flatgeobuf", + "href": "geoparquet/geoparquet-example.html#comparison-with-flatgeobuf", + "title": "GeoParquet Example", + "section": "Comparison with FlatGeobuf", + "text": "Comparison with FlatGeobuf\nIn order to compare reading GeoParquet with FlatGeobuf, we’ll cover reading and writing GeoParquet files on local disk storage. To be consistent with the FlatGeobuf example, we’ll fetch the same US counties FlatGeobuf file (13 MB) and convert it to GeoParquet using ogr2ogr.\n\n# URL to download\nurl = \"https://flatgeobuf.org/test/data/UScounties.fgb\"\n\n# Download, saving to the current directory\nlocal_fgb_path, _ = urlretrieve(url, \"countries.fgb\")\n\n\n!ogr2ogr countries.parquet countries.fgb\n\nLoading this GeoParquet file is really fast! 13% faster than loading the same data via FlatGeobuf (shown in the FlatGeobuf example notebook).\n\n%time gdf = gpd.read_parquet(\"countries.parquet\")\n\nCPU times: user 23.8 ms, sys: 11.8 ms, total: 35.6 ms\nWall time: 34.1 ms\n\n\n\ngdf\n\n\n\n\n\n\n\n\nSTATE_FIPS\nCOUNTY_FIP\nFIPS\nSTATE\nNAME\nLSAD\ngeometry\n\n\n\n\n0\n23\n009\n23009\nME\nHancock\nCounty\nMULTIPOLYGON (((-68.53108 44.33278, -68.53348 ...\n\n\n1\n33\n007\n33007\nNH\nCoos\nCounty\nMULTIPOLYGON (((-71.05975 45.01485, -71.06939 ...\n\n\n2\n50\n009\n50009\nVT\nEssex\nCounty\nMULTIPOLYGON (((-71.49463 44.90874, -71.49392 ...\n\n\n3\n50\n019\n50019\nVT\nOrleans\nCounty\nMULTIPOLYGON (((-72.14193 45.00600, -72.16051 ...\n\n\n4\n23\n007\n23007\nME\nFranklin\nCounty\nMULTIPOLYGON (((-70.83471 45.27514, -70.77984 ...\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n3216\n15\n003\n15003\nHI\nHonolulu\nCounty\nMULTIPOLYGON (((-171.73761 25.79210, -171.7513...\n\n\n3217\n15\n007\n15007\nHI\nKauai\nCounty\nMULTIPOLYGON (((-160.55535 21.66345, -160.5541...\n\n\n3218\n15\n009\n15009\nHI\nMaui\nCounty\nMULTIPOLYGON (((-157.06121 20.89150, -157.0611...\n\n\n3219\n15\n001\n15001\nHI\nHawaii\nCounty\nMULTIPOLYGON (((-155.08767 19.72887, -155.0909...\n\n\n3220\n15\n005\n15005\nHI\nKalawao\nCounty\nMULTIPOLYGON (((-157.01455 21.18550, -157.0145...\n\n\n\n\n3221 rows × 7 columns" + }, + { + "objectID": "geoparquet/geoparquet-example.html#writing-to-local-disk", + "href": "geoparquet/geoparquet-example.html#writing-to-local-disk", + "title": "GeoParquet Example", + "section": "Writing to local disk", + "text": "Writing to local disk\nWe can use GeoDataFrame.to_parquet to write out this data to GeoParquet files locally. This is about 3x faster than writing the same dataset to FlatGeobuf, but note that FlatGeobuf’s writing is also calculating a spatial index.\n\n%time gdf.to_parquet(\"countries_written.parquet\")\n\nCPU times: user 42.3 ms, sys: 12.6 ms, total: 55 ms\nWall time: 53.9 ms" + }, + { + "objectID": "geoparquet/geoparquet-example.html#reading-from-the-cloud", + "href": "geoparquet/geoparquet-example.html#reading-from-the-cloud", + "title": "GeoParquet Example", + "section": "Reading from the cloud", + "text": "Reading from the cloud\nAs of GeoParquet version 1.0.0-rc.1, spatial indexing has not yet been implemented. Therefore, there is not yet an API in GeoPandas to read data given a specific bounding box.\nWhat is already efficient in GeoParquet is reading only specified columns from a dataset.\n\nurl = \"https://data.source.coop/cholmes/eurocrops/unprojected/geoparquet/FR_2018_EC21.parquet\"\n\nNote that since we’re fetching this data directly from the cloud, we need to pass in an fsspec filesystem object. Otherwise GeoPandas will attempt to load a local file.\n\nfilesystem = HTTPFileSystem()\n\nBy default, calling read_parquet will fetch the entire file and parse it all into a single GeoDataFrame. Since this is a 3GB file, downloading the file takes a long time:\n\n# This cell will take a few minutes to run, because it downloads the entire file\n# %time gdf = gpd.read_parquet(url, filesystem=filesystem)\n\nWe can make this faster by only fetching specific columns. Because GeoParquet stores data in a columnar fashion, when selecting only specific columns we can download a lot less data.\n\n# This cell will take a few minutes to run, because it downloads the entire file for these columns\n# %time gdf = gpd.read_parquet(url, columns=[\"ID_PARCEL\", \"geometry\"], filesystem=filesystem)" + }, + { + "objectID": "geoparquet/geoparquet-example.html#working-with-geoparquet-row-groups-advanced", + "href": "geoparquet/geoparquet-example.html#working-with-geoparquet-row-groups-advanced", + "title": "GeoParquet Example", + "section": "Working with GeoParquet row groups (Advanced)", + "text": "Working with GeoParquet row groups (Advanced)\nAs described in the intro document, GeoParquet is a chunked format, which allows you to access one of the chunks of rows very efficiently. This can allow you to stream a dataset — loading and operating on one chunk at a time — if the dataset is larger than your memory.\nGeoPandas does not yet have built-in support for working with row groups, so this section will use the underlying pyarrow library directly.\n\nimport pyarrow.parquet as pq\nfrom geopandas.io.arrow import _arrow_to_geopandas\n\nFirst, we’ll create a ParquetFile object from the remote URL. All this does is load the metadata from the file, allowing you to inspect the schema and number of columns, rows, and row groups. Because this doesn’t load any actual data, it’s nearly instant to complete.\n\nparquet_file = pq.ParquetFile(url, filesystem=filesystem)\n\nWe can access the column names in the dataset:\n\nparquet_file.schema_arrow.names\n\n['ID_PARCEL',\n 'SURF_PARC',\n 'CODE_CULTU',\n 'CODE_GROUP',\n 'CULTURE_D1',\n 'CULTURE_D2',\n 'EC_org_n',\n 'EC_trans_n',\n 'EC_hcat_n',\n 'EC_hcat_c',\n 'geometry']\n\n\nThis Parquet file includes 9.5 million rows:\n\nparquet_file.metadata.num_rows\n\n9517874\n\n\nAnd 146 row groups. Given that each row group is about the same number of rows, each one contains around 65,000 rows.\n\nparquet_file.num_row_groups\n\n146\n\n\nThen to load one of the row groups by numeric index, we can call ParquetFile.read_row_group.\n\npyarrow_table = parquet_file.read_row_group(0)\n\nNote that this returns a pyarrow.Table, not a geopandas.GeoDataFrame. To convert between the two, we can use _arrow_to_geopandas. This conversion is very fast.\n\ngeopandas_gdf = _arrow_to_geopandas(pyarrow_table, parquet_file.metadata.metadata)\n\nAs expected, this row group contains right around 65,000 rows\n\ngeopandas_gdf.shape\n\n(65536, 11)\n\n\n\ngeopandas_gdf.head()\n\n\n\n\n\n\n\n\nID_PARCEL\nSURF_PARC\nCODE_CULTU\nCODE_GROUP\nCULTURE_D1\nCULTURE_D2\nEC_org_n\nEC_trans_n\nEC_hcat_n\nEC_hcat_c\ngeometry\n\n\n\n\n0\n123563\n6.38\nCZH\n5\nNone\nNone\nColza d’hiver\nWinter rapeseed\nwinter_rapeseed_rape\n3301060401\nMULTIPOLYGON (((3.33896 49.84122, 3.33948 49.8...\n\n\n1\n5527076\n2.30\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((-1.44483 49.61280, -1.44467 49...\n\n\n2\n11479241\n6.33\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((2.87821 46.53674, 2.87820 46.5...\n\n\n3\n12928442\n5.10\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((-0.19026 48.28723, -0.19025 48...\n\n\n4\n318389\n0.92\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((5.72084 44.03576, 5.72081 44.0...\n\n\n\n\n\n\n\nAs before, we can speed up the data fetching by requesting only specific columns in the read_row_group call.:\n\npyarrow_table = parquet_file.read_row_group(0, columns=[\"ID_PARCEL\", \"geometry\"])\n\nThen the resulting GeoDataFrame will only have those two columns:\n\n_arrow_to_geopandas(pyarrow_table, parquet_file.metadata.metadata).head()\n\n\n\n\n\n\n\n\nID_PARCEL\ngeometry\n\n\n\n\n0\n123563\nMULTIPOLYGON (((3.33896 49.84122, 3.33948 49.8...\n\n\n1\n5527076\nMULTIPOLYGON (((-1.44483 49.61280, -1.44467 49...\n\n\n2\n11479241\nMULTIPOLYGON (((2.87821 46.53674, 2.87820 46.5...\n\n\n3\n12928442\nMULTIPOLYGON (((-0.19026 48.28723, -0.19025 48...\n\n\n4\n318389\nMULTIPOLYGON (((5.72084 44.03576, 5.72081 44.0..." + }, + { + "objectID": "cookbooks/index.html", + "href": "cookbooks/index.html", + "title": "Cloud-Optimized Cookbooks", + "section": "", + "text": "Cookbooks should address common questions and present solutions for cloud-optimized access and visualization.\nCookbooks:\n\nZarr Visualization Cookbook (in development)" + }, + { + "objectID": "zarr/zarr-in-practice.html", + "href": "zarr/zarr-in-practice.html", + "title": "Zarr in Practice", + "section": "", + "text": "This notebook demonstrates how to create, explore and modify a Zarr store.\nThese concepts are explored in more detail in the official Zarr Tutorial.\nIt also shows the use of public Zarr stores for geospatial data." + }, + { + "objectID": "zarr/zarr-in-practice.html#how-to-create-a-zarr-store", + "href": "zarr/zarr-in-practice.html#how-to-create-a-zarr-store", + "title": "Zarr in Practice", + "section": "How to create a Zarr store", + "text": "How to create a Zarr store\n\nimport sys\nimport numpy as np\nimport xarray as xr\nimport zarr\n\n# Here we create a simple Zarr store.\nzstore = zarr.array(np.arange(10))\n\nThis is an in-memory Zarr store. To persist it to disk, we can use .save.\n\nzarr.save(\"test.zarr\", zstore)\n\nWe can open the metadata about this dataset, which gives us some interesting information. The dataset has a shape of 10 chunks of 10, so we know all the data was stored in 1 chunk, and was compressed with the blosc compressor.\n\n!cat test.zarr/.zarray \n\n{\n \"chunks\": [\n 10\n ],\n \"compressor\": {\n \"blocksize\": 0,\n \"clevel\": 5,\n \"cname\": \"lz4\",\n \"id\": \"blosc\",\n \"shuffle\": 1\n },\n \"dtype\": \"<i8\",\n \"fill_value\": 0,\n \"filters\": null,\n \"order\": \"C\",\n \"shape\": [\n 10\n ],\n \"zarr_format\": 2\n}\n\n\nThis was a pretty basic example. Let’s explore the other things we might want to do when creating Zarr." + }, + { + "objectID": "zarr/zarr-in-practice.html#how-to-create-a-group", + "href": "zarr/zarr-in-practice.html#how-to-create-a-group", + "title": "Zarr in Practice", + "section": "How to create a group", + "text": "How to create a group\n\nroot = zarr.group()\ngroup1 = root.create_group('group1')\ngroup2 = root.create_group('group2')\nz1 = group1.create_dataset('ds_in_group', shape=(100,100), chunks=(10,10), dtype='i4')\nz2 = group2.create_dataset('ds_in_group', shape=(1000,1000), chunks=(10,10), dtype='i4')\nroot.tree(expand=True)" + }, + { + "objectID": "zarr/zarr-in-practice.html#how-to-examine-and-modify-the-chunk-shape", + "href": "zarr/zarr-in-practice.html#how-to-examine-and-modify-the-chunk-shape", + "title": "Zarr in Practice", + "section": "How to Examine and Modify the Chunk Shape", + "text": "How to Examine and Modify the Chunk Shape\nIf your data is sufficiently large, Zarr will chose a chunksize for you.\n\nzarr_no_chunks = zarr.array(np.arange(100), chunks=True)\nzarr_no_chunks.chunks, zarr_no_chunks.shape\n\n((100,), (100,))\n\n\n\nzarr_with_chunks = zarr.array(np.arange(10000000), chunks=True)\nzarr_with_chunks.chunks, zarr_with_chunks.shape\n\n((156250,), (10000000,))\n\n\nFor zarr_with_chunks we see the chunks are smaller than the shape, so we know the data has been chunked. Other ways to examine the chunk structure are zarr.info and zarr.cdata_shape.\n\n?zarr_no_chunks.cdata_shape\n\n\nType: property\nString form: <property object at 0x7efde6ecfb00>\nDocstring: \nA tuple of integers describing the number of chunks along each\ndimension of the array.\n\n\n\n\nzarr_no_chunks.cdata_shape, zarr_with_chunks.cdata_shape\n\n((1,), (64,))\n\n\nThe zarr store with chunks has 64 chunks. The number of chunks multiplied by the chunk size equals the length of the whole array.\n\nzarr_with_chunks.cdata_shape[0] * zarr_with_chunks.chunks[0] == zarr_with_chunks.shape[0]\n\nTrue\n\n\n\nWhat’s the storage size of these chunks?\nThe default chunks are pretty small.\n\nsys.getsizeof(zarr_with_chunks.chunk_store['0']) # this is in bytes\n\n8049\n\n\n\nzarr_with_big_chunks = zarr.array(np.arange(10000000), chunks=(500000))\n\n\nzarr_with_big_chunks.chunks, zarr_with_big_chunks.shape, zarr_with_big_chunks.cdata_shape\n\n((500000,), (10000000,), (20,))\n\n\nThis Zarr store has 10 million values, stored in 20 chunks of 500,000 data values.\n\nsys.getsizeof(zarr_with_big_chunks.chunk_store['0'])\n\n24941\n\n\nThese chunks are still pretty small, but this is just a silly example. In the real world, you will likely want to deal in Zarr chunks of 1MB or greater, especially when dealing with remote storatge options where data is read over a network and the number of requests should be minimized." + }, + { + "objectID": "zarr/zarr-in-practice.html#exploring-and-modifying-data-compression", + "href": "zarr/zarr-in-practice.html#exploring-and-modifying-data-compression", + "title": "Zarr in Practice", + "section": "Exploring and Modifying Data Compression", + "text": "Exploring and Modifying Data Compression\nContinuing with data from the example above, we can tell that Zarr has also compressed the data for us using zarr.info or zarr.compressor.\n\nzarr_with_chunks.compressor\n\nBlosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)\n\n\nThe Blosc compressor is actually a meta compressor so actually implements multiple different internal compressors. In this case, it has implemented lz4 compression. We can also explore how much space was saved by using this compression method.\n\nzarr_with_chunks.info\n\n\n\n\nType\nzarr.core.Array\n\n\nData type\nint64\n\n\nShape\n(10000000,)\n\n\nChunk shape\n(156250,)\n\n\nOrder\nC\n\n\nRead-only\nFalse\n\n\nCompressor\nBlosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)\n\n\nStore type\nzarr.storage.KVStore\n\n\nNo. bytes\n80000000 (76.3M)\n\n\nNo. bytes stored\n514193 (502.1K)\n\n\nStorage ratio\n155.6\n\n\nChunks initialized\n64/64\n\n\n\n\n\nWe can see, from the storage ratio above, that compression has made our data 155 times smaller 😱 .\nYou can set compression=None when creating a Zarr array to turn off this behavior, but I’m not sure why you would do that.\nLet’s see what happens when we use a different compression method. We can checkout a full list of numcodecs compressors here: https://numcodecs.readthedocs.io/.\n\nfrom numcodecs import GZip\ncompressor = GZip()\nzstore_gzip_compressed = zarr.array(np.arange(10000000), chunks=True, compressor=compressor)\nzstore_gzip_compressed.info\n\n\n\n\nType\nzarr.core.Array\n\n\nData type\nint64\n\n\nShape\n(10000000,)\n\n\nChunk shape\n(156250,)\n\n\nOrder\nC\n\n\nRead-only\nFalse\n\n\nCompressor\nGZip(level=1)\n\n\nStore type\nzarr.storage.KVStore\n\n\nNo. bytes\n80000000 (76.3M)\n\n\nNo. bytes stored\n15086009 (14.4M)\n\n\nStorage ratio\n5.3\n\n\nChunks initialized\n64/64\n\n\n\n\n\nIn this case, the storage ratio is 5.3 - so not as good! How to chose a compression algorithm is a topic for future investigation." + }, + { + "objectID": "zarr/zarr-in-practice.html#consolidating-metadata", + "href": "zarr/zarr-in-practice.html#consolidating-metadata", + "title": "Zarr in Practice", + "section": "Consolidating metadata", + "text": "Consolidating metadata\nIt’s important to consolidate metadata to minimize requests. Each group and array will have a metadata file, so in order to limit requests to read the whole tree of metadata files, Zarr provides the ability to consolidate metdata into a metadata file at the of the store.\nSo far we have only been dealing in single array Zarr data stores. In this next example, we will create a zarr store with multiple arrays and then consolidate metadata. The speed up with local storage is insignificant, but becomes significant when dealing in remote storage options, which we will see in the following example on accessing cloud storage.\n\nroot = zarr.group()\nzarr_store = 'example.zarr'\n# Let's create many groups and many arrays\nnum_groups, num_arrays_per_group = 100, 100\nfor i in range(num_groups):\n group = root.create_group(f'group-{i}')\n for j in range(num_arrays_per_group):\n group.create_dataset(f'array-{j}', shape=(1000,1000), dtype='i4')\n\nstore = zarr.DirectoryStore(zarr_store)\nzarr.save(store, root)\n\n\n# We don't expect it to exist yet!\n!cat {zarr_store}/.zmetadata\n\ncat: {zarr_store}/.zmetadata: No such file or directory\n\n\n\nzarr.consolidate_metadata(zarr_store)\n\n<zarr.core.Array (100,) <U8>\n\n\n\nzarr.open_consolidated(zarr_store)\n\n<zarr.core.Array (100,) <U8>\n\n\n\n!cat {zarr_store}/.zmetadata\n\n{\n \"metadata\": {\n \".zarray\": {\n \"chunks\": [\n 100\n ],\n \"compressor\": {\n \"blocksize\": 0,\n \"clevel\": 5,\n \"cname\": \"lz4\",\n \"id\": \"blosc\",\n \"shuffle\": 1\n },\n \"dtype\": \"<U8\",\n \"fill_value\": \"\",\n \"filters\": null,\n \"order\": \"C\",\n \"shape\": [\n 100\n ],\n \"zarr_format\": 2\n }\n },\n \"zarr_consolidated_format\": 1\n}" + }, + { + "objectID": "kerchunk/kerchunk-in-practice.html", + "href": "kerchunk/kerchunk-in-practice.html", + "title": "Kerchunk in Practice", + "section": "", + "text": "In this notebook, we demonstrate how to create a kerchunk reference file for one and then multiple publicly available NetCDF files and how to open a kerchunk store with xarray.\nGenerally, NetCDF should work with kerchunk. Some nested data structures and data types, such as those that can exist in HDF5, won’t work with kerchunk. A future release of this guide will provide further information and/or resources on limitations of kerchunk." + }, + { + "objectID": "kerchunk/kerchunk-in-practice.html#environment", + "href": "kerchunk/kerchunk-in-practice.html#environment", + "title": "Kerchunk in Practice", + "section": "Environment", + "text": "Environment\nThe packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment." + }, + { + "objectID": "kerchunk/kerchunk-in-practice.html#how-to-create-a-kerchunk-store", + "href": "kerchunk/kerchunk-in-practice.html#how-to-create-a-kerchunk-store", + "title": "Kerchunk in Practice", + "section": "How to create a kerchunk store", + "text": "How to create a kerchunk store\nWe can use the publicly available NEX GDDP CMIP6 dataset for this example. This dataset is provided by NASA and publicly available on AWS S3. You can browse that data in the nex-gddp-cmip6 file browser.\n\nimport json\nimport os\nfrom tempfile import TemporaryDirectory\n\nimport fsspec\nimport imagecodecs.numcodecs\nimport xarray as xr\nfrom kerchunk.combine import MultiZarrToZarr\nfrom kerchunk.hdf import SingleHdf5ToZarr\n\nimagecodecs.numcodecs.register_codecs() \n\n\n# Set variables\n## Since there are a number of CMIP6 models and variables to chose from, we make the model and variable selections variables.\nmodel = \"ACCESS-CM2\"\n# `tasmax` is daily-maximum near-surface air temperature, see https://pcmdi.llnl.gov/mips/cmip3/variableList.html.\nvariable = \"tasmax\"\n## Note we are only reading historical data here, but model data is available for SSPs (Shared Socio-economic Pathways) as well.\n## SSPs are scenarios are used to model the future, so SSP files predict environment variables into the future.\ns3_path = f\"s3://nex-gddp-cmip6/NEX-GDDP-CMIP6/{model}/historical/r1i1p1*/{variable}/*\"\n\n# Initiate fsspec filesystem for reading.\n## We set anon=True because this specific dataset on AWS does not require users to be logged in to access.\nfs_read = fsspec.filesystem(\"s3\", anon=True)\n\n# Retrieve list of available data.\nfile_paths = fs_read.glob(s3_path)\nprint(f\"{len(file_paths)} discovered from {s3_path}\")\n\n65 discovered from s3://nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1*/tasmax/*\n\n\nTo start, we are just going to create a single reference file for a single NetCDF file.\n\nnetcdf_file = file_paths[0]\nnetcdf_file\n\n'nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc'\n\n\n\n# Define a function to generate the kerchunk file so we can use it again for other files.\ndef generate_json_reference(input_file, temp_dir: str):\n \"\"\"\n Use Kerchunk's `SingleHdf5ToZarr` method to create a `Kerchunk` index from a NetCDF file.\n \"\"\"\n with fs_read.open(input_file, **dict(mode=\"rb\")) as infile:\n print(f\"Running kerchunk generation for {input_file}...\")\n # Chunks smaller than `inline_threshold` will be stored directly in the reference file as data (as opposed to a URL and byte range).\n h5chunks = SingleHdf5ToZarr(infile, input_file, inline_threshold=300)\n fname = input_file.split(\"/\")[-1].strip(\".nc\")\n outf = os.path.join(temp_dir, f\"{fname}.json\")\n with open(outf, \"wb\") as f:\n f.write(json.dumps(h5chunks.translate()).encode())\n return outf\n\n\n# Create a temporary directory to store the .json reference files.\n# Alternately, you could write these to cloud storage.\ntd = TemporaryDirectory()\ntemp_dir = td.name\nprint(f\"Writing single file references to {temp_dir}\")\n\nWriting single file references to /var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo\n\n\n\nsingle_kerchunk_reference = generate_json_reference(netcdf_file, temp_dir)\n\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc...\n\n\nWe might also want to inspect what was just created. Below we load just the first few keys and values of the “refs” part of the kerchunk reference file.\n\n# Read and load the JSON file\nwith open(single_kerchunk_reference, 'r') as file:\n data = json.load(file)\nkeys_to_select = ['.zgroup', 'tasmax/.zarray', 'tasmax/0.0.0']\n\n# Pretty print JSON data\ndata_to_print = {}\nfor key, value in data['refs'].items():\n if key in keys_to_select:\n if isinstance(value, str):\n data_to_print[key] = json.loads(value)\n else:\n data_to_print[key] = value\nprint(json.dumps(data_to_print, indent=4))\n\n{\n \".zgroup\": {\n \"zarr_format\": 2\n },\n \"tasmax/.zarray\": {\n \"chunks\": [\n 1,\n 600,\n 1440\n ],\n \"compressor\": {\n \"id\": \"zlib\",\n \"level\": 5\n },\n \"dtype\": \"<f4\",\n \"fill_value\": 1.0000000200408773e+20,\n \"filters\": [\n {\n \"elementsize\": 4,\n \"id\": \"shuffle\"\n }\n ],\n \"order\": \"C\",\n \"shape\": [\n 365,\n 600,\n 1440\n ],\n \"zarr_format\": 2\n },\n \"tasmax/0.0.0\": [\n \"nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc\",\n 18097,\n 674483\n ]\n}\n\n\nWe can also check that our reference file works with xarray.\n\n# Open dataset as zarr object using fsspec reference file system and Xarray\nfs_single = fsspec.filesystem(\n \"reference\", fo=single_kerchunk_reference, remote_protocol=\"https\"\n)\nsingle_map = fs_single.get_mapper(\"\")\n\n\nds_single = xr.open_dataset(single_map, engine=\"zarr\", backend_kwargs=dict(consolidated=False))\nds_single\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (lat: 600, lon: 1440, time: 365)\nCoordinates:\n * lat (lat) float64 0.0 1.23e-321 0.0 ... -3.218e+163 -3.218e+163\n * lon (lon) float64 0.0 2.164e-314 0.0 ... -2.022e-53 -1.699e+282\n * time (time) datetime64[ns] 1950-01-01T12:00:00 ... 1950-12-31T12:00:00\nData variables:\n tasmax (time, lat, lon) float32 ...\nAttributes: (12/22)\n Conventions: CF-1.7\n activity: NEX-GDDP-CMIP6\n cmip6_institution_id: CSIRO-ARCCSS\n cmip6_license: CC-BY-SA 4.0\n cmip6_source_id: ACCESS-CM2\n contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...\n ... ...\n scenario: historical\n source: BCSD\n title: ACCESS-CM2, r1i1p1f1, historical, global downscale...\n tracking_id: f85d4c2e-48e4-484f-aad4-6a3f30a04326\n variant_label: r1i1p1f1\n version: 1.0xarray.DatasetDimensions:lat: 600lon: 1440time: 365Coordinates: (3)lat(lat)float640.0 1.23e-321 ... -3.218e+163axis :Ylong_name :latitudestandard_name :latitudeunits :degrees_northarray([ 0.000000e+000, 1.230223e-321, 0.000000e+000, ..., -3.218047e+163,\n -3.218047e+163, -3.218047e+163])lon(lon)float640.0 2.164e-314 ... -1.699e+282axis :Xlong_name :longitudestandard_name :longitudeunits :degrees_eastarray([ 0.000000e+000, 2.163912e-314, 0.000000e+000, ..., 1.902013e-242,\n -2.022208e-053, -1.698612e+282])time(time)datetime64[ns]1950-01-01T12:00:00 ... 1950-12-...axis :Tlong_name :timestandard_name :timearray(['1950-01-01T12:00:00.000000000', '1950-01-02T12:00:00.000000000',\n '1950-01-03T12:00:00.000000000', ..., '1950-12-29T12:00:00.000000000',\n '1950-12-30T12:00:00.000000000', '1950-12-31T12:00:00.000000000'],\n dtype='datetime64[ns]')Data variables: (1)tasmax(time, lat, lon)float32...cell_measures :area: areacellacell_methods :area: mean time: maximumcomment :maximum near-surface (usually, 2 meter) air temperature (add cell_method attribute 'time: max')long_name :Daily Maximum Near-Surface Air Temperaturestandard_name :air_temperatureunits :K[315360000 values with dtype=float32]Indexes: (3)latPandasIndexPandasIndex(Index([ 0.0, 1.23e-321,\n 0.0, 1.23e-321,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n ...\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163],\n dtype='float64', name='lat', length=600))lonPandasIndexPandasIndex(Index([ 0.0, 2.163911906e-314,\n 0.0, nan,\n 0.0, 0.0,\n 0.0, 0.0,\n 0.0, 0.0,\n ...\n 0.0, 0.0,\n 1.5390572997222847e+73, 1.0494093556865241e-86,\n 7.328222560480262e-213, 3.493934932025909e-195,\n 7.981962361089973e-296, 1.90201295465319e-242,\n -2.022208454662242e-53, -1.698612219286841e+282],\n dtype='float64', name='lon', length=1440))timePandasIndexPandasIndex(DatetimeIndex(['1950-01-01 12:00:00', '1950-01-02 12:00:00',\n '1950-01-03 12:00:00', '1950-01-04 12:00:00',\n '1950-01-05 12:00:00', '1950-01-06 12:00:00',\n '1950-01-07 12:00:00', '1950-01-08 12:00:00',\n '1950-01-09 12:00:00', '1950-01-10 12:00:00',\n ...\n '1950-12-22 12:00:00', '1950-12-23 12:00:00',\n '1950-12-24 12:00:00', '1950-12-25 12:00:00',\n '1950-12-26 12:00:00', '1950-12-27 12:00:00',\n '1950-12-28 12:00:00', '1950-12-29 12:00:00',\n '1950-12-30 12:00:00', '1950-12-31 12:00:00'],\n dtype='datetime64[ns]', name='time', length=365, freq=None))Attributes: (22)Conventions :CF-1.7activity :NEX-GDDP-CMIP6cmip6_institution_id :CSIRO-ARCCSScmip6_license :CC-BY-SA 4.0cmip6_source_id :ACCESS-CM2contact :Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget Thrasher: bridget@climateanalyticsgroup.orgcreation_date :2021-10-04T14:00:55.510838+00:00disclaimer :This data is considered provisional and subject to change. This data is provided as is without any warranty of any kind, either express or implied, arising by law or otherwise, including but not limited to warranties of completeness, non-infringement, accuracy, merchantability, or fitness for a particular purpose. The user assumes all risk associated with the use of, or inability to use, this data.external_variables :areacellafrequency :dayhistory :2021-10-04T14:00:55.510838+00:00: install global attributesinstitution :NASA Earth Exchange, NASA Ames Research Center, Moffett Field, CA 94035product :outputrealm :atmosreferences :BCSD method: Thrasher et al., 2012, Hydrol. Earth Syst. Sci.,16, 3309-3314. Ref period obs: latest version of the Princeton Global Meteorological Forcings (http://hydrology.princeton.edu/data.php), based on Sheffield et al., 2006, J. Climate, 19 (13), 3088-3111.resolution_id :0.25 degreescenario :historicalsource :BCSDtitle :ACCESS-CM2, r1i1p1f1, historical, global downscaled CMIP6 climate projection datatracking_id :f85d4c2e-48e4-484f-aad4-6a3f30a04326variant_label :r1i1p1f1version :1.0\n\n\nIt worked! But we can do even better. What if you want to open multiple NetCDF files with xarray? Let’s create kerchunk references for 3 files and then combine them.\n\nsubset_files = file_paths[0:3]\nsubset_files\n\n['nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc',\n 'nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1951.nc',\n 'nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1952.nc']\n\n\n\n# Iterate through filelist to generate Kerchunked files. Good use for `dask.bag`, see: https://docs.dask.org/en/stable/bag.html.\noutput_files = []\nfor single_file in subset_files:\n out_file = generate_json_reference(single_file, temp_dir)\n output_files.append(out_file)\n\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc...\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1951.nc...\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1952.nc...\n\n\n\noutput_files\n\n['/var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.json',\n '/var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1951.json',\n '/var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1952.json']\n\n\n\n# combine individual references into single consolidated reference\nmzz = MultiZarrToZarr(\n output_files,\n remote_protocol='s3',\n remote_options={'anon': True},\n concat_dims=['time'],\n coo_map={'time': 'cf:time'},\n # inline_threshold=0 means don't story any raw data in the kerchunk reference file.\n inline_threshold=0\n)\nmulti_kerchunk = mzz.translate()\n\n\n# Write kerchunk .json record\noutput_fname = os.path.join(temp_dir, f\"combined_CMIP6_daily_{model}_{variable}_kerchunk.json\")\nwith open(f\"{output_fname}\", \"wb\") as f:\n print(f\"Writing combined kerchunk reference file {output_fname}\")\n f.write(json.dumps(multi_kerchunk).encode())\n\nWriting combined kerchunk reference file /var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/combined_CMIP6_daily_ACCESS-CM2_tasmax_kerchunk.json\n\n\n\n# open dataset as zarr object using fsspec reference file system and Xarray\nfs_multi = fsspec.filesystem(\n \"reference\",\n fo=multi_kerchunk,\n remote_protocol=\"s3\"\n)\nmulti_map = fs_multi.get_mapper(\"\")\n\n\nds_multi = xr.open_dataset(multi_map, engine=\"zarr\", backend_kwargs=dict(consolidated=False))\nds_multi\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (lat: 600, lon: 1440, time: 1096)\nCoordinates:\n * lat (lat) float64 0.0 2.164e-314 0.0 ... 2.961e-314 2.961e-314\n * lon (lon) float64 0.0 2.164e-314 0.0 ... -6.915e+193 -4.603e+95\n * time (time) datetime64[ns] 1950-01-01T12:00:00 ... 1952-12-31T12:00:00\nData variables:\n tasmax (time, lat, lon) float32 ...\nAttributes: (12/22)\n Conventions: CF-1.7\n activity: NEX-GDDP-CMIP6\n cmip6_institution_id: CSIRO-ARCCSS\n cmip6_license: CC-BY-SA 4.0\n cmip6_source_id: ACCESS-CM2\n contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...\n ... ...\n scenario: historical\n source: BCSD\n title: ACCESS-CM2, r1i1p1f1, historical, global downscale...\n tracking_id: f85d4c2e-48e4-484f-aad4-6a3f30a04326\n variant_label: r1i1p1f1\n version: 1.0xarray.DatasetDimensions:lat: 600lon: 1440time: 1096Coordinates: (3)lat(lat)float640.0 2.164e-314 ... 2.961e-314axis :Ylong_name :latitudestandard_name :latitudeunits :degrees_northarray([0.000000e+000, 2.163912e-314, 0.000000e+000, ..., 2.961067e-314,\n 2.960919e-314, 2.961067e-314])lon(lon)float640.0 2.164e-314 ... -4.603e+95axis :Xlong_name :longitudestandard_name :longitudeunits :degrees_eastarray([ 0.000000e+000, 2.163912e-314, 0.000000e+000, ..., 2.334981e+006,\n -6.914611e+193, -4.603478e+095])time(time)datetime64[ns]1950-01-01T12:00:00 ... 1952-12-...axis :Tlong_name :timestandard_name :timearray(['1950-01-01T12:00:00.000000000', '1950-01-02T12:00:00.000000000',\n '1950-01-03T12:00:00.000000000', ..., '1952-12-29T12:00:00.000000000',\n '1952-12-30T12:00:00.000000000', '1952-12-31T12:00:00.000000000'],\n dtype='datetime64[ns]')Data variables: (1)tasmax(time, lat, lon)float32...cell_measures :area: areacellacell_methods :area: mean time: maximumcomment :maximum near-surface (usually, 2 meter) air temperature (add cell_method attribute 'time: max')long_name :Daily Maximum Near-Surface Air Temperaturestandard_name :air_temperatureunits :K[946944000 values with dtype=float32]Indexes: (3)latPandasIndexPandasIndex(Index([ 0.0, 2.163911906e-314, 0.0,\n nan, 0.0, 2.847840319e-314,\n 2.847840477e-314, 5e-324, 2.8478403307e-314,\n 2.8478403347e-314,\n ...\n 2.960919408e-314, 2.9610663864e-314, 2.960919313e-314,\n 2.9610664496e-314, 2.9609193446e-314, 2.961066513e-314,\n 2.9609192497e-314, 2.961066576e-314, 2.9609192813e-314,\n 2.9610666394e-314],\n dtype='float64', name='lat', length=600))lonPandasIndexPandasIndex(Index([ 0.0, 2.163911906e-314,\n 0.0, nan,\n 1.8178640317427325e+185, 1.0640025030406259e+248,\n 6.01334685394558e-154, 9.363931581572749e+252,\n 1.2064976717019484e+285, 2.582765705848744e-144,\n ...\n 2.7454590140292026e+40, -3.255930979178767e-308,\n 1.5281971544072024e-111, -7.088607689435405e+42,\n 1.1472324330854862e+22, 3.6014577529949115e+106,\n 9.851096278175061e+67, 2334981.4421286285,\n -6.9146108782833415e+193, -4.603477998061419e+95],\n dtype='float64', name='lon', length=1440))timePandasIndexPandasIndex(DatetimeIndex(['1950-01-01 12:00:00', '1950-01-02 12:00:00',\n '1950-01-03 12:00:00', '1950-01-04 12:00:00',\n '1950-01-05 12:00:00', '1950-01-06 12:00:00',\n '1950-01-07 12:00:00', '1950-01-08 12:00:00',\n '1950-01-09 12:00:00', '1950-01-10 12:00:00',\n ...\n '1952-12-22 12:00:00', '1952-12-23 12:00:00',\n '1952-12-24 12:00:00', '1952-12-25 12:00:00',\n '1952-12-26 12:00:00', '1952-12-27 12:00:00',\n '1952-12-28 12:00:00', '1952-12-29 12:00:00',\n '1952-12-30 12:00:00', '1952-12-31 12:00:00'],\n dtype='datetime64[ns]', name='time', length=1096, freq=None))Attributes: (22)Conventions :CF-1.7activity :NEX-GDDP-CMIP6cmip6_institution_id :CSIRO-ARCCSScmip6_license :CC-BY-SA 4.0cmip6_source_id :ACCESS-CM2contact :Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget Thrasher: bridget@climateanalyticsgroup.orgcreation_date :2021-10-04T14:00:55.510838+00:00disclaimer :This data is considered provisional and subject to change. This data is provided as is without any warranty of any kind, either express or implied, arising by law or otherwise, including but not limited to warranties of completeness, non-infringement, accuracy, merchantability, or fitness for a particular purpose. The user assumes all risk associated with the use of, or inability to use, this data.external_variables :areacellafrequency :dayhistory :2021-10-04T14:00:55.510838+00:00: install global attributesinstitution :NASA Earth Exchange, NASA Ames Research Center, Moffett Field, CA 94035product :outputrealm :atmosreferences :BCSD method: Thrasher et al., 2012, Hydrol. Earth Syst. Sci.,16, 3309-3314. Ref period obs: latest version of the Princeton Global Meteorological Forcings (http://hydrology.princeton.edu/data.php), based on Sheffield et al., 2006, J. Climate, 19 (13), 3088-3111.resolution_id :0.25 degreescenario :historicalsource :BCSDtitle :ACCESS-CM2, r1i1p1f1, historical, global downscaled CMIP6 climate projection datatracking_id :f85d4c2e-48e4-484f-aad4-6a3f30a04326variant_label :r1i1p1f1version :1.0\n\n\nCool! Now we have 1096 days (3 years) of data." + }, + { + "objectID": "kerchunk/kerchunk-in-practice.html#how-to-read-a-kerchunk-store", + "href": "kerchunk/kerchunk-in-practice.html#how-to-read-a-kerchunk-store", + "title": "Kerchunk in Practice", + "section": "How to read a Kerchunk Store", + "text": "How to read a Kerchunk Store\nWe’ve already demonstrated how to open the datasets with Xarray:\nfs_multi = fsspec.filesystem(\n \"reference\",\n fo=multi_kerchunk,\n remote_protocol=\"s3\"\n)\nLet’s take it line by line to understand what’s happening.\n\nfsspec.filesystem is used to open the kerchunk reference. It is not necessary to have kerchunk installed to read data.\nThe first argument to fsspec.filesystem is the protocol. In the case of a kerchunk reference the protocol is the string \"reference\".\nThe fo argument is the set of reference files used to create a ReferenceFileSystem instance.\nThe remote_protocol argument is the protocol of the filesystem on which the references will be evaluated (unless fs is provided). If not given, will be derived from the first URL that has a protocol in the templates or in the references.\n\nNotice how the fs_multi object we’ve created is a fsspec.implementations.reference.ReferenceFileSystem.\n\ntype(fs_multi)\n\nfsspec.implementations.reference.ReferenceFileSystem\n\n\nRead about all the options for a fsspec.ReferenceFileSystem in the fsspec docs.\nOne other common situation is to load data over HTTP (as opposed to a local filesystem or via the S3 protocol). Here’s an example from the kerchunk case studies that loads a reference file and data files over HTTP:\n\nzarr_all_url='https://sentinel-1-global-coherence-earthbigdata.s3.us-west-2.amazonaws.com/data/wrappers/zarr-all.json'\n\nmapper = fsspec.get_mapper(\n 'reference://',\n fo=zarr_all_url,\n target_protocol='http',\n remote_protocol='http'\n)\ndataset = xr.open_dataset(\n mapper, engine='zarr', backend_kwargs={'consolidated': False}\n)\ndataset\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (season: 4, polarization: 4, latitude: 193200,\n longitude: 432000, coherence: 6, flightdirection: 2,\n orbit: 175)\nCoordinates:\n * coherence (coherence) float32 6.0 12.0 18.0 24.0 36.0 48.0\n * flightdirection (flightdirection) object 'A' 'D'\n * latitude (latitude) float32 82.0 82.0 82.0 ... -79.0 -79.0 -79.0\n * longitude (longitude) float32 -180.0 -180.0 -180.0 ... 180.0 180.0\n * orbit (orbit) float64 1.0 2.0 3.0 4.0 ... 172.0 173.0 174.0 175.0\n * polarization (polarization) object 'vv' 'vh' 'hv' 'hh'\n * season (season) object 'winter' 'spring' 'summer' 'fall'\nData variables:\n AMP (season, polarization, latitude, longitude) float32 ...\n COH (season, polarization, coherence, latitude, longitude) float32 ...\n inc (orbit, flightdirection, latitude, longitude) float32 ...\n lsmap (orbit, flightdirection, latitude, longitude) float32 ...\n rho (season, polarization, latitude, longitude) float32 ...\n rmse (season, polarization, latitude, longitude) float32 ...\n tau (season, polarization, latitude, longitude) float32 ...xarray.DatasetDimensions:season: 4polarization: 4latitude: 193200longitude: 432000coherence: 6flightdirection: 2orbit: 175Coordinates: (7)coherence(coherence)float326.0 12.0 18.0 24.0 36.0 48.0array([ 6., 12., 18., 24., 36., 48.], dtype=float32)flightdirection(flightdirection)object'A' 'D'array(['A', 'D'], dtype=object)latitude(latitude)float3282.0 82.0 82.0 ... -79.0 -79.0array([ 81.99958, 81.99875, 81.99792, ..., -78.99792, -78.99875, -78.99958],\n dtype=float32)longitude(longitude)float32-180.0 -180.0 ... 180.0 180.0array([-179.99959, -179.99875, -179.99791, ..., 179.99791, 179.99875,\n 179.99959], dtype=float32)orbit(orbit)float641.0 2.0 3.0 ... 173.0 174.0 175.0array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.,\n 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24.,\n 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36.,\n 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48.,\n 49., 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60.,\n 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72.,\n 73., 74., 75., 76., 77., 78., 79., 80., 81., 82., 83., 84.,\n 85., 86., 87., 88., 89., 90., 91., 92., 93., 94., 95., 96.,\n 97., 98., 99., 100., 101., 102., 103., 104., 105., 106., 107., 108.,\n 109., 110., 111., 112., 113., 114., 115., 116., 117., 118., 119., 120.,\n 121., 122., 123., 124., 125., 126., 127., 128., 129., 130., 131., 132.,\n 133., 134., 135., 136., 137., 138., 139., 140., 141., 142., 143., 144.,\n 145., 146., 147., 148., 149., 150., 151., 152., 153., 154., 155., 156.,\n 157., 158., 159., 160., 161., 162., 163., 164., 165., 166., 167., 168.,\n 169., 170., 171., 172., 173., 174., 175.])polarization(polarization)object'vv' 'vh' 'hv' 'hh'array(['vv', 'vh', 'hv', 'hh'], dtype=object)season(season)object'winter' 'spring' 'summer' 'fall'array(['winter', 'spring', 'summer', 'fall'], dtype=object)Data variables: (7)AMP(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]COH(season, polarization, coherence, latitude, longitude)float32...[8012390400000 values with dtype=float32]inc(orbit, flightdirection, latitude, longitude)float32...[29211840000000 values with dtype=float32]lsmap(orbit, flightdirection, latitude, longitude)float32...[29211840000000 values with dtype=float32]rho(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]rmse(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]tau(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]Indexes: (7)coherencePandasIndexPandasIndex(Index([6.0, 12.0, 18.0, 24.0, 36.0, 48.0], dtype='float32', name='coherence'))flightdirectionPandasIndexPandasIndex(Index(['A', 'D'], dtype='object', name='flightdirection'))latitudePandasIndexPandasIndex(Index([ 81.99958038330078, 81.99874877929688, 81.99791717529297,\n 81.99708557128906, 81.99624633789062, 81.99541473388672,\n 81.99458312988281, 81.9937515258789, 81.992919921875,\n 81.99208068847656,\n ...\n -78.99208068847656, -78.992919921875, -78.9937515258789,\n -78.99458312988281, -78.99541473388672, -78.99624633789062,\n -78.99708557128906, -78.99791717529297, -78.99874877929688,\n -78.99958038330078],\n dtype='float32', name='latitude', length=193200))longitudePandasIndexPandasIndex(Index([ -179.9995880126953, -179.99874877929688, -179.99790954589844,\n -179.99708557128906, -179.99624633789062, -179.99542236328125,\n -179.9945831298828, -179.99374389648438, -179.992919921875,\n -179.99208068847656,\n ...\n 179.99208068847656, 179.992919921875, 179.99374389648438,\n 179.9945831298828, 179.99542236328125, 179.99624633789062,\n 179.99708557128906, 179.99790954589844, 179.99874877929688,\n 179.9995880126953],\n dtype='float32', name='longitude', length=432000))orbitPandasIndexPandasIndex(Index([ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,\n ...\n 166.0, 167.0, 168.0, 169.0, 170.0, 171.0, 172.0, 173.0, 174.0, 175.0],\n dtype='float64', name='orbit', length=175))polarizationPandasIndexPandasIndex(Index(['vv', 'vh', 'hv', 'hh'], dtype='object', name='polarization'))seasonPandasIndexPandasIndex(Index(['winter', 'spring', 'summer', 'fall'], dtype='object', name='season'))Attributes: (0)\n\n\nBecause xarray uses fsspec to read data, you can also bypass creating a fsspec object explicitly. Here’s an example using of opening a kerchunk reference generated with pangeo-forge for the NOAA 1/4° daily Optimum Interpolation Sea Surface Temperature (or daily OISST) Climate Data Record (CDR).\n\nurl = \"https://ncsa.osn.xsede.org/Pangeo/pangeo-forge/pangeo-forge/aws-noaa-oisst-feedstock/aws-noaa-oisst-avhrr-only.zarr/reference.json\"\nds = xr.open_dataset(\n \"reference://\",\n engine='zarr',\n backend_kwargs={\n 'consolidated': False,\n 'storage_options': {\n 'fo': url,\n 'remote_options': {'anon': True},\n 'remote_protocol': 's3'}},\n chunks={})\nds\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (time: 15044, zlev: 1, lat: 720, lon: 1440)\nCoordinates:\n * lat (lat) float32 -89.88 -89.62 -89.38 -89.12 ... 89.38 89.62 89.88\n * lon (lon) float32 0.125 0.375 0.625 0.875 ... 359.1 359.4 359.6 359.9\n * time (time) datetime64[ns] 1981-09-01T12:00:00 ... 2022-11-08T12:00:00\n * zlev (zlev) float32 0.0\nData variables:\n anom (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n err (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n ice (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n sst (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\nAttributes: (12/37)\n Conventions: CF-1.6, ACDD-1.3\n cdm_data_type: Grid\n comment: Data was converted from NetCDF-3 to NetCDF-4 ...\n creator_email: oisst-help@noaa.gov\n creator_url: https://www.ncei.noaa.gov/\n date_created: 2020-05-08T19:05:13Z\n ... ...\n source: ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfin...\n standard_name_vocabulary: CF Standard Name Table (v40, 25 January 2017)\n summary: NOAAs 1/4-degree Daily Optimum Interpolation ...\n time_coverage_end: 1981-09-01T23:59:59Z\n time_coverage_start: 1981-09-01T00:00:00Z\n title: NOAA/NCEI 1/4 Degree Daily Optimum Interpolat...xarray.DatasetDimensions:time: 15044zlev: 1lat: 720lon: 1440Coordinates: (4)lat(lat)float32-89.88 -89.62 ... 89.62 89.88grids :Uniform grid from -89.875 to 89.875 by 0.25long_name :Latitudeunits :degrees_northarray([-89.875, -89.625, -89.375, ..., 89.375, 89.625, 89.875],\n dtype=float32)lon(lon)float320.125 0.375 0.625 ... 359.6 359.9grids :Uniform grid from 0.125 to 359.875 by 0.25long_name :Longitudeunits :degrees_eastarray([1.25000e-01, 3.75000e-01, 6.25000e-01, ..., 3.59375e+02, 3.59625e+02,\n 3.59875e+02], dtype=float32)time(time)datetime64[ns]1981-09-01T12:00:00 ... 2022-11-...long_name :Center time of the dayarray(['1981-09-01T12:00:00.000000000', '1981-09-02T12:00:00.000000000',\n '1981-09-03T12:00:00.000000000', ..., '2022-11-06T12:00:00.000000000',\n '2022-11-07T12:00:00.000000000', '2022-11-08T12:00:00.000000000'],\n dtype='datetime64[ns]')zlev(zlev)float320.0actual_range :0, 0long_name :Sea surface heightpositive :downunits :metersarray([0.], dtype=float32)Data variables: (4)anom(time, zlev, lat, lon)float32dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>long_name :Daily sea surface temperature anomaliesunits :Celsiusvalid_max :1200valid_min :-1200\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\nerr\n\n\n(time, zlev, lat, lon)\n\n\nfloat32\n\n\ndask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n\n\n\n\nlong_name :\n\nEstimated error standard deviation of analysed_sst\n\nunits :\n\nCelsius\n\nvalid_max :\n\n1000\n\nvalid_min :\n\n0\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\n\nice\n\n\n(time, zlev, lat, lon)\n\n\nfloat32\n\n\ndask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n\n\n\n\nlong_name :\n\nSea ice concentration\n\nunits :\n\n%\n\nvalid_max :\n\n100\n\nvalid_min :\n\n0\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\n\nsst\n\n\n(time, zlev, lat, lon)\n\n\nfloat32\n\n\ndask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n\n\n\n\nlong_name :\n\nDaily sea surface temperature\n\nunits :\n\nCelsius\n\nvalid_max :\n\n4500\n\nvalid_min :\n\n-300\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\n\nIndexes: (4)latPandasIndexPandasIndex(Index([-89.875, -89.625, -89.375, -89.125, -88.875, -88.625, -88.375, -88.125,\n -87.875, -87.625,\n ...\n 87.625, 87.875, 88.125, 88.375, 88.625, 88.875, 89.125, 89.375,\n 89.625, 89.875],\n dtype='float32', name='lat', length=720))lonPandasIndexPandasIndex(Index([ 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875,\n 2.125, 2.375,\n ...\n 357.625, 357.875, 358.125, 358.375, 358.625, 358.875, 359.125, 359.375,\n 359.625, 359.875],\n dtype='float32', name='lon', length=1440))timePandasIndexPandasIndex(DatetimeIndex(['1981-09-01 12:00:00', '1981-09-02 12:00:00',\n '1981-09-03 12:00:00', '1981-09-04 12:00:00',\n '1981-09-05 12:00:00', '1981-09-06 12:00:00',\n '1981-09-07 12:00:00', '1981-09-08 12:00:00',\n '1981-09-09 12:00:00', '1981-09-10 12:00:00',\n ...\n '2022-10-30 12:00:00', '2022-10-31 12:00:00',\n '2022-11-01 12:00:00', '2022-11-02 12:00:00',\n '2022-11-03 12:00:00', '2022-11-04 12:00:00',\n '2022-11-05 12:00:00', '2022-11-06 12:00:00',\n '2022-11-07 12:00:00', '2022-11-08 12:00:00'],\n dtype='datetime64[ns]', name='time', length=15044, freq=None))zlevPandasIndexPandasIndex(Index([0.0], dtype='float32', name='zlev'))Attributes: (37)Conventions :CF-1.6, ACDD-1.3cdm_data_type :Gridcomment :Data was converted from NetCDF-3 to NetCDF-4 format with metadata updates in November 2017.creator_email :oisst-help@noaa.govcreator_url :https://www.ncei.noaa.gov/date_created :2020-05-08T19:05:13Zdate_modified :2020-05-08T19:05:13Zgeospatial_lat_max :90.0geospatial_lat_min :-90.0geospatial_lat_resolution :0.25geospatial_lat_units :degrees_northgeospatial_lon_max :360.0geospatial_lon_min :0.0geospatial_lon_resolution :0.25geospatial_lon_units :degrees_easthistory :Final file created using preliminary as first guess, and 3 days of AVHRR data. Preliminary uses only 1 day of AVHRR data.id :oisst-avhrr-v02r01.19810901.ncinstitution :NOAA/National Centers for Environmental Informationinstrument :Earth Remote Sensing Instruments > Passive Remote Sensing > Spectrometers/Radiometers > Imaging Spectrometers/Radiometers > AVHRR > Advanced Very High Resolution Radiometerinstrument_vocabulary :Global Change Master Directory (GCMD) Instrument Keywordskeywords :Earth Science > Oceans > Ocean Temperature > Sea Surface Temperaturekeywords_vocabulary :Global Change Master Directory (GCMD) Earth Science Keywordsmetadata_link :https://doi.org/10.25921/RE9P-PT57naming_authority :gov.noaa.nceincei_template_version :NCEI_NetCDF_Grid_Template_v2.0platform :Ships, buoys, Argo floats, MetOp-A, MetOp-Bplatform_vocabulary :Global Change Master Directory (GCMD) Platform Keywordsprocessing_level :NOAA Level 4product_version :Version v02r01references :Reynolds, et al.(2007) Daily High-Resolution-Blended Analyses for Sea Surface Temperature (available at https://doi.org/10.1175/2007JCLI1824.1). Banzon, et al.(2016) A long-term record of blended satellite and in situ sea-surface temperature for climate monitoring, modeling and environmental studies (available at https://doi.org/10.5194/essd-8-165-2016). Huang et al. (2020) Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version v02r01, submitted.Climatology is based on 1971-2000 OI.v2 SST. Satellite data: Pathfinder AVHRR SST and Navy AVHRR SST. Ice data: NCEP Ice and GSFC Ice.sensor :Thermometer, AVHRRsource :ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfinder_AVHRR, Navy_AVHRRstandard_name_vocabulary :CF Standard Name Table (v40, 25 January 2017)summary :NOAAs 1/4-degree Daily Optimum Interpolation Sea Surface Temperature (OISST) (sometimes referred to as Reynolds SST, which however also refers to earlier products at different resolution), currently available as version v02r01, is created by interpolating and extrapolating SST observations from different sources, resulting in a smoothed complete field. The sources of data are satellite (AVHRR) and in situ platforms (i.e., ships and buoys), and the specific datasets employed may change over time. At the marginal ice zone, sea ice concentrations are used to generate proxy SSTs. A preliminary version of this file is produced in near-real time (1-day latency), and then replaced with a final version after 2 weeks. Note that this is the AVHRR-ONLY DOISST, available from Oct 1981, but there is a companion DOISST product that includes microwave satellite data, available from June 2002time_coverage_end :1981-09-01T23:59:59Ztime_coverage_start :1981-09-01T00:00:00Ztitle :NOAA/NCEI 1/4 Degree Daily Optimum Interpolation Sea Surface Temperature (OISST) Analysis, Version 2.1 - Final" + }, + { + "objectID": "kerchunk/kerchunk-in-practice.html#other-examples-of-existing-kerchunk-data", + "href": "kerchunk/kerchunk-in-practice.html#other-examples-of-existing-kerchunk-data", + "title": "Kerchunk in Practice", + "section": "Other examples of existing kerchunk data", + "text": "Other examples of existing kerchunk data\n\nCase Studies on kerchunk Docs page" + }, + { + "objectID": "template.html", + "href": "template.html", + "title": "Template", + "section": "", + "text": "Format Basics (or What is a XX?)\n\n\nExample of Creating this Format\n\n\nExample of Cloud-Optimized Access for this Format" + }, + { + "objectID": "transition-from-rmarkdown.html", + "href": "transition-from-rmarkdown.html", + "title": "Transition from RMarkdown", + "section": "", + "text": "You may already have workflows in RMarkdown and are interested in transitioning to Quarto. There’s no hurry to migrate to Quarto. Keep using Rmarkdown and when you’re ready the migration will be fine.\nHere are some notes as we migrate RMarkdown sites and books.\nTODO: translating R code chunks" + }, + { + "objectID": "transition-from-rmarkdown.html#bookdown-to-quarto", + "href": "transition-from-rmarkdown.html#bookdown-to-quarto", + "title": "Transition from RMarkdown", + "section": "Bookdown to Quarto", + "text": "Bookdown to Quarto\nConverting a Bookdown book to Quarto is slightly more involved than converting a website. A book has chapters whose order must be defined, and likely has citations and cross-refs. Still, conversion is not that hard.\nWe got some practice converting from Bookdown to Quarto by helping Gavin Fay convert his lab’s fantastic onboarding documentation, the Faylab Lab Manual. Here’s the GitHub view before and after.\nOur best first reference material for this was Nick Tierney’s Notes on Changing from Rmarkdown/Bookdown to Quarto. Nick shares some scripts in that post to automate some changes. In our case, the book was small enough that we made all changes manually. Quarto documentation was indispensable.\n\nExperimenting in a low-risk environment\nWe forked a copy of the Faylab Lab manual to the Openscapes organization, and worked in a branch so we could make changes relatively risk-free. We could always fork a new copy of the original if we “broke” something. (Caution: the default when making a pull request from a fork is to push changes to the original upstream repo, not your fork and it does this without warning if you have write-access to that repo.) With local previews it’s easy to test / play with settings to see what they do. We tended to make a change, Preview, then compare the look and functionality of the book to the original. It was helpful to comment out some elements of the configuration file _output.yml after their counterparts had been added to the Quarto configuration file _quarto.yml, or to confirm they were no longer needed, before making the drastic move of deleting them.\n\n\nThe conversion\nHere are the main steps to convert the Faylab Lab manual from Bookdown to Quarto.\nCreate new empty file called _quarto.yml and add book metadata there. The screenshots below\nSet project type as book.\nMove metadata out of index.qmd and into _quarto.yml. Title, author, and publication date were in index.qmd with date set using date: \"Last updated:r Sys.Date()\". Now these are in _quarto.yml with date set using date: last-modified. Note that having R code would require you to adjust code chunk options in the Quarto style (#|). This tripped us up a bit; see GitHub Actions.\nMove chapters listing out of _bookdown.yml and into _quarto.yml.\nAdd page footer to _quarto.yml.\nHere’s what ours looked like when we finished the steps above (_quarto.yml).\n\n\n\n\n\n\n_quarto.yml contents\n\n\n\n\n\n\n\nFaylab Lab Manual\n\n\n\n\n\nChange insertion of images from html style to Quarto style. (Note Quarto calls them “figures”, not “images”.) The following snippet will insert the GitHub octocat logo in a page:\n![](https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png){fig-align=\"left\" width=\"35px\"}\nChange all filename extensions .Rmd -> .qmd (you could Preview after this change and see that the book looks the same). Note that Quarto works with .Rmd files just as well as it does .qmd, so this change is not urgent. In fact, if you have a lot of R code in your .Rmds (unlike the Faylab Lab Manual), there will be additional tinkering needed to make the code chunks happy.\n\n\nCitations\nThe Faylab Lab Manual cited two papers, presenting us with an opportunity to see how easy it is to add references to a Quarto book. Briefly, in the Visual Editor, Insert > Citation > DOI. Pasting the DOI or its full URL, we can insert the citation. This automatically creates a references.bib file and adds the full citations at the bottom of the chapter page (watch demo). In July 2022, we had to manually add a ## References heading, but this may not be necessary in future Quarto updates.\n\n\n\n\n\n\nInsert citation via its DOI using RStudio Visual Editor\n\n\n\n\n\n\n\n\n\n\nPublishing notes\nIf the book’s output is strictly html, there’s no need to specify output-dir in _quarto.yml. The output directory default is _book/, which is what we’d like. If we wanted other types of output like like PDF or EPUB, etc. those single file outputs are also written to the output-dir (Quarto docs).\nIf you currently have a docs/ folder, delete it.\nUpdate .gitignore to ignore _book/. At the same time, we have it ignore caches and a .quarto file:\n/.quarto/\n*_cache/\n_book/\nOnce all is settled, delete _output.yml.\nOnce the Openscapes fork was fully reviewed, we made a pull request from that to the main branch of the book’s repo. Once that was merged, we set up GitHub Actions to render the book. (TODO: instructions for GitHub Actions)\n\n\nGitHub Actions\nThis book was mostly prose and screenshots without any R code. This made the conversion from RMarkdown to Quarto likely more straightforward than if you also needed to adjust code chunk options in the quarto style (#|). Our initial GitHub Action to render the converted Faylab Lab Manual failed because we had a piece of R code - even though the code was commented out! This was resolved when we deleted the line." + }, + { + "objectID": "transition-from-rmarkdown.html#distill-to-quarto", + "href": "transition-from-rmarkdown.html#distill-to-quarto", + "title": "Transition from RMarkdown", + "section": "Distill to quarto", + "text": "Distill to quarto\nWe transitioned our events site from distill to quarto in May 2022 (github view before and after). We followed excellent notes and examples from Nick Tierney and Danielle Navarro.\nAfter we had changed all the files, the Build tab in the RStudio IDE still showed “Build website” rather then “Render Website” and “Preview Website”, and would error when we pushed them (because that button was expecting a distill site, not a quarto site). To fix this, we updated the .Rproj file. Clicking on the .Rproj file in the RStudio IDE will open a dialog box where you can click things you want (you can also open these in a text editor or from the GitHub website to see the actual text). To fix this situation with the Build tab: Project Options > Build Tools > Project Build Tools > None.\nLooking at files /posts/_metadata.yml and _quarto.yml helps see where things are defined. For example, to make event post citations appear, we added citation: true to /posts/_metadata.yml and in _quarto.yml under the website key we set site-url: https://openscapes.github.io/events. We deleted footer.html used with distill because footer is now defined in quarto.yml.\n\nPublishing notes\n\nBackground: Our distill site had been set up to output to a docs folder, and had GitHub Settings > Pages set to look there rather gh-pages branch. (Julie note: this was a new-to-me capability when we set up the events distill site in Spring 2021 so I had forgotten that was an option). We’ve inititally kept this same set-up for now with our events page in _quarto.yml: output-dir: docs. However, this is sub-optimal; better to not have to commit and push these files but to instead have a GitHub Action generate them upon a commit. So the following is what we did -\n\nDon’t specify output-dir in _quarto.yml. The output directory default is _site/, which is what we’d like.\nIf you currently have a docs/ folder (like we did as we were experimenting), delete it.\nUpdate .gitignore to ignore _site/. At the same time, we have it ignore caches and a .quarto file:\n/.quarto/\n*_cache/\n_site/\nPush these changes, merge into main.\nOn GitHub.com, in your repo, set up GitHub publishing\nFollow instructions from the explore and setup chapter." + }, + { + "objectID": "transition-from-rmarkdown.html#troubleshooting", + "href": "transition-from-rmarkdown.html#troubleshooting", + "title": "Transition from RMarkdown", + "section": "Troubleshooting", + "text": "Troubleshooting\n\nGitHub Action fails, says you need RMarkdown but you don’t have R code!\nAnd you changed all .Rmds to .qmds!\nYou likely have a few setup code chunks from RMarkdown, that look like this:\n{r setup, include=FALSE}\nknitr::opts_chunk$set(echo = FALSE)\nYou can find them by opening each of your files and having a look, or use GitHub’s search for the keyword knitr" + }, + { + "objectID": "flatgeobuf/intro.html", + "href": "flatgeobuf/intro.html", + "title": "FlatGeobuf", + "section": "", + "text": "FlatGeobuf\nFlatGeobuf is a binary file format for geographic vector data, such as points, lines, and polygons.\nUnlike some formats like Cloud-Optimized GeoTIFF, which builds on the previous success of TIFF and GeoTIFF, FlatGeobuf is a new format, designed from the ground up to be faster for geospatial data.\nFlatGeobuf is widely supported — via its GDAL implementation — in many programming languages as well as applications like QGIS.\nFlatGeobuf supports any vector geometry type defined in the OGC Simple Features specification. This includes the standard building blocks of Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection, but also includes more obscure types such as CircularString, Surface, and TIN (Triangulated irregular network). A best practice is to store only geometries with the same type, as that allows readers to know which geometry type is stored without scanning the entire file.\nAn optional row-based spatial index optimizes for remote reading.\n\nFile layout\nThe internal layout of the file has four sections: magic bytes (aka signature), header, index, and data (aka features).\n\n\nImage source: Horace Williams, Kicking the Tires: Flatgeobuf\n\n\nThe file signature is 8 “magic bytes” indicating the file type and specification version, which allows readers to know a file is FlatGeobuf, even if it’s missing a file extension.\nNext comes the header, which stores the bounding box of the dataset, the geometry type of the features (if known and unique), the attribute schema, the number of features, and coordinate reference system information.\nAfter the file header is an optional spatial index. If included, this lets a reader skip reading features that are not within a provided spatial query.\nLast come the individual features. The rest of the file is a sequence of feature records, placed end to end in a row-wise fashion.\n\n\n\nRow based\nInternally, features are laid out in a row-oriented fashion rather than a column-oriented fasion. This means that it’s relatively cheap to select specific records from the file, but relatively expensive to select a specific column. This is ideal for a small spatial query (assuming an index exists in the file) but to load all geometries requires loading all attribute information as well.\n\n\nNo internal compression\nFlatGeobuf does not support compression while maintaining the ability to seek within the file. In particular, FlatGeobuf’s spatial index describes the byte ranges in the uncompressed file. Those byte ranges will be incorrect if the file is compressed.\nA compression like gzip can be applied to the FlatGeobuf file in full, but keep in mind that storing the compressed file will eliminate random access support.\n\n\nNo append support\nFlatGeobuf is a write-only format, and doesn’t support appending, as that would invalidate the spatial index in the file.\n\n\nRandom access supported via spatial index\nFlatGeobuf optionally supports a spatial index at the beginning of the file, which can speed up reading portions of a file based on a spatial query. For more information on how this spatial index works, refer to the Hilbert R Tree page.\n\n\n\nStreaming features is supported\nFlatGeobuf supports streaming, meaning that you can use part of the file before the entire file has finished downloading. This is different than random access, because you have no ability to skip around in the file.\nStreaming can be valuable because it makes an application seem more responsive; you can have something happen without having to wait for the full download to complete. A good example of this is this example by FlatGeobuf’s author Björn Harrtell. As the file is downloaded to the browser, portions of the file get rendered progressively in parts.\nThis works even with full-file compression like gzip or deflate because those compression algorithms support streaming decompression.\n\n\nBroad type system\nFlatGeobuf supports attributes with a range of types:\n\nByte: Signed 8-bit integer\nUByte: Unsigned 8-bit integer\nBool: Boolean\nShort: Signed 16-bit integer\nUShort: Unsigned 16-bit integer\nInt: Signed 32-bit integer\nUInt: Unsigned 32-bit integer\nLong: Signed 64-bit integer\nULong: Unsigned 64-bit integer\nFloat: Single precision floating point number\nDouble: Double precision floating point number\nString: UTF8 string\nJson: General JSON type intended to be application specific\nDateTime: ISO 8601 date time\nBinary: General binary type intended to be application specific\n\n\n\n\n\n\n\nNote\n\n\n\nNote that FlatGeobuf is unable to store nested types without overhead. It doesn’t support a “list” or “dict” type apart from JSON, which has a parsing overhead.\nIn some situations, having strong nested type support can be useful. For example STAC stored as GeoParquet has columns that are nested, such as the assets column that needs to store a dictionary-like mapping from asset names to their information. FlatGeobuf is able to store such data by serializing it to JSON, but it’s not possible to see the nested schema before parsing the full dataset.\n\n\n\n\nKnown table schema\nFlatGeobuf declares the schema of properties at the beginning of the file. This makes it much easier to read the file — compared to a fully schemaless format like GeoJSON — because the reader knows what data type each attribute has in advance.\n\n\nReferences\n\nflatgeobuf.org: Official project website.\nFlatgeobuf: Implementer’s Guide" + }, { "objectID": "contributing.html", "href": "contributing.html", @@ -49,46 +224,39 @@ "text": "Thank you to our supporters\nThis guide has been made possible through the support of:" }, { - "objectID": "pmtiles/pmtiles-example.html", - "href": "pmtiles/pmtiles-example.html", - "title": "PMTiles example", + "objectID": "cloud-optimized-geotiffs/intro.html", + "href": "cloud-optimized-geotiffs/intro.html", + "title": "Cloud-Optimized GeoTIFFs", "section": "", - "text": "This notebook will give an overview of how to create and visualize PMTiles archives." - }, - { - "objectID": "pmtiles/pmtiles-example.html#environment", - "href": "pmtiles/pmtiles-example.html#environment", - "title": "PMTiles example", - "section": "Environment", - "text": "Environment\nThe packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml \nor\nmamba create -f environment.yml \nAlternatively, you can install pmtiles and mapbox-vector-tile through pip, and tippecanoe through Homebrew (brew install tippecanoe) if on MacOS." + "text": "Cloud-Optimized GeoTIFF (the COG) is a variant of the TIFF image format that specifies a particular layout of internal data in the GeoTIFF specification to allow for optimized (subsetted or aggregated) access over a network for display or data reading. The key components are overviews, and internal tiling.\nFor more details see https://www.cogeo.org/\n\n\n\nThis attribute is also sometimes called chunks or internal tiles.\nDimensions are the number of bands, rows and columns stored in a GeoTIFF. There is a tradeoff between storing lots of data in one GeoTIFF and storing less data in many GeoTIFFs. The larger a single file, the larger the GeoTIFF header and the multiple requests may be required just to read the spatial index before data retrieval. The opposite problem occurs if you make too many small files, then it takes many reads to retrieve data, and when rendering a combined visualization can greatly impact load time.\nIf you plan to pan and zoom a large amount of data through a tiling service in a web browser, there is a tradeoff between 1 large file, or many smaller files. The current recommendation is to meet somewhere in the middle, a moderate amount of medium files.\n\n\n\nInternal blocks are required if the dimensions of data are over 512x512. However you can control the size of the internal blocks. 256x256 or 512x512 are recommended. When displaying data at full resolution, or doing partial reading of data this size will impact the number of reads required. A size of 256 will take less time to read, and read less data outside the desired bounding box, however for reading large parts of a file, it may take more total read requests. Some clients will aggregate neighboring block reads to reduce the total number of requests.\n\n\n\nOverviews are downsampled (aggregated) data intended for visualization. The best resampling algorithm depends on the range, type, and distribution of the data.\nThe smallest size overview should match the tiling components’ fetch size, typically 256x256. Due to aspect ratio variation just aim to have at least one dimension at or slightly less than 256. The COG driver in GDAL, or rio cogeo tools should do this.\nThere are many resampling algorithms for generating overviews. When creating overviews several options should be compared before deciding which resampling method to apply." }, { - "objectID": "pmtiles/pmtiles-example.html#creating-pmtiles", - "href": "pmtiles/pmtiles-example.html#creating-pmtiles", - "title": "PMTiles example", - "section": "Creating PMTiles", - "text": "Creating PMTiles\nFor this example, we’ll use the same file as used in the FlatGeobuf and GeoParquet example notebooks: a 13MB file of US counties.\nWe’ll use Tippecanoe to convert this file into tiles.\nFirst we’ll download the file to our local directory:\n\n!wget https://flatgeobuf.org/test/data/UScounties.fgb\n\n--2023-08-23 15:54:58-- https://flatgeobuf.org/test/data/UScounties.fgb\nResolving flatgeobuf.org (flatgeobuf.org)... 185.199.108.153\nConnecting to flatgeobuf.org (flatgeobuf.org)|185.199.108.153|:443... connected.\nHTTP request sent, awaiting response... 200 OK\nLength: 14100008 (13M) [application/octet-stream]\nSaving to: ‘UScounties.fgb’\n\nUScounties.fgb 100%[===================>] 13.45M 7.94MB/s in 1.7s \n\n2023-08-23 15:55:02 (7.94 MB/s) - ‘UScounties.fgb’ saved [14100008/14100008]\n\n\n\nTippecanoe has many options to customize its behavior. Here we’ll use the -zg flag to tell Tippecanoe to deduce appropriate minimum and maximum zoom levels for the dataset. The -o counties.pmtiles flag tells Tippecanoe to save the output with that name.\nTippecanoe also works especially well with FlatGeobuf files. When a FlatGeobuf file is used as input, Tippecanoe will reuse the spatial index stored in the FlatGeobuf file instead of creating its own.\n\n!tippecanoe UScounties.fgb -o counties.pmtiles -zg\n\nFor layer 0, using name \"UScountiesfgb\"\ndetected indexed FlatGeobuf: assigning feature IDs by sequence\n3221 features, 5580299 bytes of geometry, 53296 bytes of string pool\nChoosing a maxzoom of -z1 for features typically 141427 feet (43107 meters) apart, and at least 33249 feet (10135 meters) apart\nChoosing a maxzoom of -z7 for resolution of about 3195 feet (973 meters) within features\n 99.9% 7/36/49 \n 100.0% 7/127/42 \n\n\nNow we have a file named counties.pmtiles with our data:\n\n!ls -lh counties.pmtiles\n\n-rw-r--r--@ 1 kyle staff 2.8M Aug 25 13:09 counties.pmtiles" + "objectID": "cloud-optimized-geotiffs/intro.html#what-is-a-cloud-optimized-geotiff", + "href": "cloud-optimized-geotiffs/intro.html#what-is-a-cloud-optimized-geotiff", + "title": "Cloud-Optimized GeoTIFFs", + "section": "", + "text": "Cloud-Optimized GeoTIFF (the COG) is a variant of the TIFF image format that specifies a particular layout of internal data in the GeoTIFF specification to allow for optimized (subsetted or aggregated) access over a network for display or data reading. The key components are overviews, and internal tiling.\nFor more details see https://www.cogeo.org/\n\n\n\nThis attribute is also sometimes called chunks or internal tiles.\nDimensions are the number of bands, rows and columns stored in a GeoTIFF. There is a tradeoff between storing lots of data in one GeoTIFF and storing less data in many GeoTIFFs. The larger a single file, the larger the GeoTIFF header and the multiple requests may be required just to read the spatial index before data retrieval. The opposite problem occurs if you make too many small files, then it takes many reads to retrieve data, and when rendering a combined visualization can greatly impact load time.\nIf you plan to pan and zoom a large amount of data through a tiling service in a web browser, there is a tradeoff between 1 large file, or many smaller files. The current recommendation is to meet somewhere in the middle, a moderate amount of medium files.\n\n\n\nInternal blocks are required if the dimensions of data are over 512x512. However you can control the size of the internal blocks. 256x256 or 512x512 are recommended. When displaying data at full resolution, or doing partial reading of data this size will impact the number of reads required. A size of 256 will take less time to read, and read less data outside the desired bounding box, however for reading large parts of a file, it may take more total read requests. Some clients will aggregate neighboring block reads to reduce the total number of requests.\n\n\n\nOverviews are downsampled (aggregated) data intended for visualization. The best resampling algorithm depends on the range, type, and distribution of the data.\nThe smallest size overview should match the tiling components’ fetch size, typically 256x256. Due to aspect ratio variation just aim to have at least one dimension at or slightly less than 256. The COG driver in GDAL, or rio cogeo tools should do this.\nThere are many resampling algorithms for generating overviews. When creating overviews several options should be compared before deciding which resampling method to apply." }, { - "objectID": "pmtiles/pmtiles-example.html#visualization", - "href": "pmtiles/pmtiles-example.html#visualization", - "title": "PMTiles example", - "section": "Visualization", - "text": "Visualization\nThe easiest way to interpret this data is to load it into the PMTiles Viewer. Drag the counties.pmtiles file into that website, and you’ll be able to hover over areas" + "objectID": "cloud-optimized-geotiffs/intro.html#how-to-create-and-validate-cogs", + "href": "cloud-optimized-geotiffs/intro.html#how-to-create-and-validate-cogs", + "title": "Cloud-Optimized GeoTIFFs", + "section": "How to create and validate COGs", + "text": "How to create and validate COGs\n\nRio-cogeo: GitHub - cogeotiff/rio-cogeo: Cloud Optimized GeoTIFF creation and validation plugin for rasterio\nGdal: COG – Cloud Optimized GeoTIFF generator — GDAL documentation" }, { - "objectID": "pmtiles/pmtiles-example.html#reading-from-python", - "href": "pmtiles/pmtiles-example.html#reading-from-python", - "title": "PMTiles example", - "section": "Reading from Python", - "text": "Reading from Python\nIt’s possible to open and read a PMTiles file from python using the pmtiles and mapbox-vector-tile libraries. The pmtiles library is used to open the archive and fetch a specific tile, while mapbox-vector-tile is used to decode the MVT vector tile data contained within that tile.\n\nfrom pmtiles.reader import Reader, MmapSource\n\nOpen the file and create a pmtiles Reader object\n\nfile = open(\"counties.pmtiles\")\nreader = Reader(MmapSource(file))\n\nFetch a specific tile. This tile’s coordinates were found from the PMTiles viewer above, and is located over the east coast.\n\nx, y, z = 37, 48, 7\ntile_data = reader.get(z, x, y)\n\ntile_data is now a bytes object, representing the data contained in the PMTiles archive for that specific XYZ tile.\n\ntype(tile_data)\n\nbytes\n\n\n\nlen(tile_data)\n\n11878\n\n\nIn our case, the PMTiles archive contains MVT data, so we can decode the buffer using mapbox_vector_tile. It’s also possible for the archive to contain raster images (e.g. PNG files), in which case a different decoding process would be necessary.\n\nimport mapbox_vector_tile\nimport gzip\n\nWe’ll decode the tile and print the output from mapbox_vector_tile. MVT data are encoded with “quantization”, meaning reduced precision so the data can be compressed better. So the coordinates printed out have a range of 0-4096, where those are the integer steps within the local coordinate reference system within the tile. Refer to the mapbox_vector_tile docs for how to read to GeoJSON.\n\nmapbox_vector_tile.decode(gzip.decompress(tile_data))\n\n{'UScountiesfgb': {'extent': 4096,\n 'version': 2,\n 'features': [{'geometry': {'type': 'Polygon',\n 'coordinates': [[[289, 4176],\n [290, 4168],\n [299, 4151],\n [198, 4102],\n [172, 4100],\n [163, 4096],\n [128, 4080],\n [130, 4070],\n [0, 4009],\n [-71, 3976],\n [-80, 3970],\n [-80, 4176],\n [289, 4176]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '079',\n 'FIPS': '42079',\n 'STATE': 'PA',\n 'NAME': 'Luzerne',\n 'LSAD': 'County'},\n 'id': 2224,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1272, 4176],\n [1256, 4168],\n [1247, 4167],\n [1235, 4163],\n [1226, 4152],\n [1206, 4143],\n [1204, 4139],\n [1180, 4123],\n [1175, 4118],\n [1174, 4113],\n [1174, 4106],\n [1171, 4096],\n [1168, 4090],\n [1168, 4084],\n [1171, 4079],\n [1174, 4076],\n [1177, 4075],\n [1187, 4077],\n [1190, 4074],\n [1177, 4056],\n [1154, 4041],\n [1143, 4037],\n [1119, 4035],\n [1108, 4030],\n [1106, 4020],\n [1092, 4014],\n [1081, 4012],\n [1048, 3996],\n [1042, 3980],\n [1014, 3960],\n [1027, 3941],\n [976, 3908],\n [967, 3890],\n [952, 3877],\n [928, 3864],\n [898, 3857],\n [868, 3837],\n [807, 3809],\n [758, 3800],\n [753, 3795],\n [721, 3785],\n [675, 3778],\n [663, 3807],\n [648, 3835],\n [529, 4041],\n [620, 4096],\n [643, 4110],\n [590, 4176],\n [1272, 4176]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '089',\n 'FIPS': '42089',\n 'STATE': 'PA',\n 'NAME': 'Monroe',\n 'LSAD': 'County'},\n 'id': 2227,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[590, 4176],\n [643, 4110],\n [620, 4096],\n [529, 4041],\n [598, 3923],\n [655, 3822],\n [675, 3778],\n [668, 3775],\n [625, 3767],\n [609, 3766],\n [596, 3762],\n [577, 3763],\n [562, 3758],\n [553, 3752],\n [524, 3748],\n [498, 3737],\n [494, 3730],\n [488, 3726],\n [478, 3725],\n [399, 3706],\n [363, 3696],\n [354, 3692],\n [342, 3677],\n [304, 3661],\n [299, 3656],\n [289, 3642],\n [282, 3641],\n [281, 3635],\n [262, 3625],\n [75, 3781],\n [0, 3867],\n [-80, 3958],\n [-80, 3970],\n [0, 4009],\n [130, 4070],\n [128, 4080],\n [163, 4096],\n [172, 4100],\n [198, 4102],\n [299, 4151],\n [290, 4168],\n [289, 4176],\n [590, 4176]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '025',\n 'FIPS': '42025',\n 'STATE': 'PA',\n 'NAME': 'Carbon',\n 'LSAD': 'County'},\n 'id': 2217,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1529, 4176],\n [1594, 4096],\n [1703, 3965],\n [1691, 3952],\n [1687, 3954],\n [1681, 3943],\n [1671, 3935],\n [1671, 3928],\n [1668, 3923],\n [1651, 3914],\n [1647, 3908],\n [1648, 3894],\n [1645, 3883],\n [1646, 3880],\n [1654, 3874],\n [1652, 3869],\n [1643, 3865],\n [1642, 3855],\n [1637, 3849],\n [1627, 3845],\n [1625, 3843],\n [1622, 3833],\n [1627, 3829],\n [1630, 3821],\n [1617, 3812],\n [1611, 3804],\n [1613, 3799],\n [1606, 3792],\n [1606, 3786],\n [1598, 3780],\n [1592, 3771],\n [1589, 3771],\n [1588, 3765],\n [1583, 3757],\n [1576, 3756],\n [1569, 3752],\n [1567, 3756],\n [1562, 3755],\n [1558, 3742],\n [1546, 3737],\n [1539, 3731],\n [1535, 3733],\n [1532, 3732],\n [1517, 3719],\n [1505, 3712],\n [1507, 3706],\n [1506, 3698],\n [1507, 3695],\n [1494, 3686],\n [1489, 3680],\n [1489, 3677],\n [1485, 3672],\n [1473, 3663],\n [1470, 3658],\n [1467, 3656],\n [1465, 3651],\n [1452, 3636],\n [1451, 3631],\n [1443, 3624],\n [1435, 3610],\n [1426, 3606],\n [1424, 3602],\n [1419, 3597],\n [1410, 3577],\n [1396, 3578],\n [1393, 3569],\n [1378, 3563],\n [1361, 3545],\n [1356, 3544],\n [1351, 3547],\n [1345, 3543],\n [1341, 3547],\n [1337, 3542],\n [1333, 3542],\n [1315, 3526],\n [1310, 3526],\n [1308, 3524],\n [1303, 3517],\n [1301, 3516],\n [1295, 3518],\n [1288, 3512],\n [1282, 3510],\n [1278, 3505],\n [1275, 3499],\n [1267, 3497],\n [1264, 3494],\n [1254, 3488],\n [1247, 3476],\n [1236, 3472],\n [1235, 3468],\n [1226, 3458],\n [1211, 3451],\n [1207, 3447],\n [1208, 3442],\n [1205, 3440],\n [1180, 3431],\n [1168, 3424],\n [1153, 3420],\n [1150, 3416],\n [1150, 3408],\n [1147, 3405],\n [1141, 3407],\n [1129, 3399],\n [1130, 3393],\n [1128, 3387],\n [1124, 3385],\n [1120, 3378],\n [1114, 3375],\n [1111, 3363],\n [1104, 3361],\n [1102, 3355],\n [1096, 3351],\n [1088, 3349],\n [1085, 3370],\n [1072, 3393],\n [1071, 3398],\n [1077, 3402],\n [1088, 3404],\n [1091, 3412],\n [1085, 3444],\n [1073, 3455],\n [1073, 3460],\n [1087, 3484],\n [1099, 3492],\n [1107, 3505],\n [1108, 3511],\n [1107, 3514],\n [1102, 3518],\n [1097, 3519],\n [1088, 3518],\n [1079, 3521],\n [1073, 3529],\n [1068, 3541],\n [1082, 3584],\n [1089, 3592],\n [1100, 3618],\n [1095, 3629],\n [1081, 3645],\n [1079, 3656],\n [1086, 3664],\n [1099, 3671],\n [1105, 3676],\n [1107, 3681],\n [1110, 3697],\n [1115, 3707],\n [1118, 3708],\n [1127, 3708],\n [1148, 3701],\n [1170, 3699],\n [1174, 3703],\n [1186, 3724],\n [1195, 3729],\n [1203, 3730],\n [1207, 3732],\n [1219, 3749],\n [1219, 3764],\n [1225, 3773],\n [1234, 3780],\n [1243, 3797],\n [1243, 3803],\n [1241, 3807],\n [1224, 3825],\n [1223, 3832],\n [1226, 3840],\n [1233, 3844],\n [1254, 3845],\n [1262, 3841],\n [1269, 3841],\n [1272, 3842],\n [1288, 3865],\n [1291, 3876],\n [1290, 3885],\n [1287, 3891],\n [1280, 3899],\n [1270, 3914],\n [1256, 3931],\n [1255, 3956],\n [1250, 3969],\n [1226, 3989],\n [1212, 4012],\n [1211, 4018],\n [1203, 4035],\n [1194, 4044],\n [1190, 4065],\n [1191, 4070],\n [1190, 4074],\n [1187, 4077],\n [1177, 4075],\n [1174, 4076],\n [1171, 4079],\n [1168, 4084],\n [1168, 4090],\n [1171, 4096],\n [1174, 4106],\n [1174, 4113],\n [1175, 4118],\n [1180, 4123],\n [1204, 4139],\n [1206, 4143],\n [1226, 4152],\n [1235, 4163],\n [1247, 4167],\n [1256, 4168],\n [1272, 4176],\n [1529, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '041',\n 'FIPS': '34041',\n 'STATE': 'NJ',\n 'NAME': 'Warren',\n 'LSAD': 'County'},\n 'id': 2207,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[2427, 4176],\n [2425, 4170],\n [2437, 4158],\n [2460, 4120],\n [2494, 4099],\n [2518, 4096],\n [2573, 4090],\n [2573, 4096],\n [2574, 4104],\n [2583, 4096],\n [2616, 4070],\n [2606, 4022],\n [2612, 4020],\n [2619, 4007],\n [2615, 3999],\n [2616, 3992],\n [2619, 3987],\n [2630, 3985],\n [2633, 3981],\n [2628, 3967],\n [2630, 3956],\n [2627, 3941],\n [2634, 3930],\n [2632, 3915],\n [2646, 3904],\n [2654, 3892],\n [2656, 3885],\n [2661, 3874],\n [2665, 3871],\n [2665, 3860],\n [2662, 3856],\n [2659, 3857],\n [2656, 3864],\n [2649, 3864],\n [2647, 3855],\n [2648, 3849],\n [2644, 3833],\n [2641, 3826],\n [2645, 3808],\n [2641, 3793],\n [2638, 3790],\n [2632, 3788],\n [2552, 3846],\n [2534, 3860],\n [2536, 3865],\n [2529, 3868],\n [2527, 3864],\n [2472, 3904],\n [2453, 3906],\n [2451, 3909],\n [2443, 3911],\n [2437, 3917],\n [2433, 3931],\n [2425, 3938],\n [2424, 3942],\n [2427, 3948],\n [2424, 3953],\n [2426, 3956],\n [2426, 3962],\n [2430, 3957],\n [2432, 3957],\n [2433, 3959],\n [2426, 3972],\n [2416, 3973],\n [2416, 3978],\n [2408, 3975],\n [2408, 3980],\n [2405, 3984],\n [2407, 3986],\n [2402, 3989],\n [2402, 3995],\n [2401, 3997],\n [2407, 4002],\n [2411, 4013],\n [2415, 4025],\n [2418, 4047],\n [2414, 4053],\n [2415, 4069],\n [2413, 4071],\n [2412, 4075],\n [2413, 4081],\n [2417, 4079],\n [2418, 4085],\n [2412, 4090],\n [2412, 4096],\n [2407, 4094],\n [2399, 4096],\n [2392, 4094],\n [2389, 4096],\n [2389, 4103],\n [2396, 4111],\n [2391, 4117],\n [2391, 4125],\n [2388, 4128],\n [2388, 4136],\n [2387, 4140],\n [2384, 4140],\n [2382, 4133],\n [2370, 4132],\n [2363, 4139],\n [2344, 4137],\n [2341, 4140],\n [2334, 4142],\n [2334, 4147],\n [2329, 4148],\n [2325, 4146],\n [2324, 4148],\n [2318, 4144],\n [2310, 4144],\n [2310, 4156],\n [2306, 4160],\n [2298, 4162],\n [2293, 4160],\n [2292, 4157],\n [2281, 4157],\n [2274, 4161],\n [2265, 4162],\n [2259, 4157],\n [2255, 4159],\n [2251, 4165],\n [2240, 4168],\n [2235, 4173],\n [2234, 4176],\n [2427, 4176]]],\n [[[2203, 4176],\n [2202, 4172],\n [2196, 4170],\n [2189, 4174],\n [2190, 4176],\n [2203, 4176]]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '031',\n 'FIPS': '34031',\n 'STATE': 'NJ',\n 'NAME': 'Passaic',\n 'LSAD': 'County'},\n 'id': 2230,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2981, 4176],\n [2977, 4145],\n [2976, 4129],\n [2960, 4130],\n [2885, 4176],\n [2981, 4176]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '087',\n 'FIPS': '36087',\n 'STATE': 'NY',\n 'NAME': 'Rockland',\n 'LSAD': 'County'},\n 'id': 2226,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[3469, 4176],\n [3460, 4167],\n [3451, 4156],\n [3448, 4138],\n [3428, 4137],\n [3426, 4155],\n [3410, 4166],\n [3398, 4164],\n [3341, 4139],\n [3330, 4126],\n [3321, 4106],\n [3317, 4111],\n [3319, 4122],\n [3318, 4134],\n [3325, 4157],\n [3312, 4176],\n [3469, 4176]]],\n [[[3519, 4176],\n [3517, 4172],\n [3508, 4166],\n [3505, 4176],\n [3519, 4176]]]]},\n 'properties': {'STATE_FIPS': '09',\n 'COUNTY_FIP': '001',\n 'FIPS': '09001',\n 'STATE': 'CT',\n 'NAME': 'Fairfield',\n 'LSAD': 'County'},\n 'id': 1713,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[3156, 3920],\n [3161, 3917],\n [3162, 3906],\n [3161, 3904],\n [3155, 3902],\n [3149, 3908],\n [3149, 3912],\n [3153, 3912],\n [3153, 3918],\n [3156, 3920]]],\n [[[3312, 4176],\n [3325, 4157],\n [3318, 4134],\n [3319, 4122],\n [3317, 4111],\n [3321, 4106],\n [3323, 4096],\n [3317, 4074],\n [3314, 4070],\n [3310, 4072],\n [3291, 4063],\n [3283, 4036],\n [3278, 4029],\n [3262, 4018],\n [3227, 4004],\n [3212, 3990],\n [3176, 3966],\n [3140, 3914],\n [3137, 3905],\n [3079, 3925],\n [3078, 3922],\n [3057, 3931],\n [3053, 3950],\n [3048, 3950],\n [3048, 3952],\n [3042, 3955],\n [3036, 3956],\n [3039, 3961],\n [3036, 3961],\n [3033, 3958],\n [3034, 3955],\n [3031, 3954],\n [3026, 3943],\n [2941, 3976],\n [2972, 4096],\n [2981, 4176],\n [3312, 4176]]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '119',\n 'FIPS': '36119',\n 'STATE': 'NY',\n 'NAME': 'Westchester',\n 'LSAD': 'County'},\n 'id': 2232,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[-80, 3958],\n [0, 3867],\n [75, 3781],\n [219, 3660],\n [262, 3625],\n [250, 3617],\n [232, 3613],\n [230, 3605],\n [205, 3590],\n [189, 3572],\n [135, 3544],\n [120, 3544],\n [67, 3514],\n [0, 3481],\n [-80, 3442],\n [-80, 3958]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '107',\n 'FIPS': '42107',\n 'STATE': 'PA',\n 'NAME': 'Schuylkill',\n 'LSAD': 'County'},\n 'id': 2216,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[-80, 2134],\n [-80, 2627],\n [0, 2560],\n [93, 2480],\n [69, 2466],\n [0, 2431],\n [-8, 2427],\n [0, 2380],\n [2, 2369],\n [2, 2342],\n [5, 2310],\n [3, 2293],\n [0, 2287],\n [-72, 2157],\n [-71, 2150],\n [-77, 2142],\n [-80, 2134]]],\n [[[-80, 2117],\n [-77, 2115],\n [-74, 2105],\n [-65, 2097],\n [-63, 2093],\n [-64, 2086],\n [-69, 2090],\n [-73, 2086],\n [-79, 2085],\n [-78, 2081],\n [-80, 2080],\n [-80, 2117]]],\n [[[-80, 1996],\n [-74, 1988],\n [-72, 1979],\n [-77, 1974],\n [-79, 1970],\n [-80, 1971],\n [-80, 1996]]],\n [[[-80, 2026],\n [-73, 2020],\n [-72, 2015],\n [-76, 2012],\n [-78, 2006],\n [-79, 2001],\n [-77, 2000],\n [-80, 1996],\n [-80, 2026]]],\n [[[-80, 2123], [-78, 2120], [-80, 2117], [-80, 2123]]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '071',\n 'FIPS': '42071',\n 'STATE': 'PA',\n 'NAME': 'Lancaster',\n 'LSAD': 'County'},\n 'id': 2185,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[223, 1051],\n [235, 1047],\n [235, 1044],\n [240, 1045],\n [249, 1041],\n [264, 794],\n [262, 796],\n [255, 795],\n [247, 797],\n [241, 794],\n [236, 796],\n [233, 793],\n [232, 795],\n [223, 792],\n [222, 791],\n [210, 788],\n [209, 790],\n [205, 785],\n [197, 787],\n [195, 790],\n [191, 786],\n [185, 792],\n [171, 794],\n [163, 799],\n [162, 802],\n [157, 803],\n [148, 808],\n [143, 809],\n [142, 816],\n [136, 816],\n [122, 820],\n [120, 824],\n [113, 821],\n [106, 823],\n [92, 809],\n [92, 801],\n [86, 802],\n [84, 804],\n [75, 800],\n [71, 807],\n [67, 809],\n [63, 806],\n [63, 797],\n [29, 789],\n [18, 790],\n [0, 782],\n [-5, 780],\n [-10, 781],\n [-16, 787],\n [-21, 788],\n [-27, 784],\n [-30, 784],\n [-37, 786],\n [-43, 793],\n [-48, 795],\n [-58, 792],\n [-70, 794],\n [-71, 783],\n [-75, 781],\n [-80, 780],\n [-80, 1037],\n [-75, 1034],\n [-64, 1021],\n [-60, 1021],\n [-56, 1022],\n [-48, 1033],\n [-39, 1036],\n [-21, 1034],\n [-17, 1032],\n [-11, 1025],\n [-5, 1023],\n [0, 1025],\n [8, 1030],\n [14, 1030],\n [21, 1022],\n [49, 1017],\n [63, 1019],\n [71, 1012],\n [76, 1010],\n [81, 1017],\n [102, 1023],\n [107, 1021],\n [111, 1023],\n [120, 1017],\n [130, 1024],\n [134, 1023],\n [139, 1029],\n [155, 1035],\n [166, 1049],\n [173, 1050],\n [176, 1047],\n [186, 1046],\n [191, 1038],\n [193, 1037],\n [198, 1042],\n [206, 1041],\n [209, 1046],\n [213, 1046],\n [216, 1048],\n [222, 1046],\n [223, 1051]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '029',\n 'FIPS': '24029',\n 'STATE': 'MD',\n 'NAME': 'Kent',\n 'LSAD': 'County'},\n 'id': 2126,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[120, 824],\n [122, 820],\n [136, 816],\n [142, 816],\n [143, 809],\n [148, 808],\n [157, 803],\n [162, 802],\n [163, 799],\n [171, 794],\n [185, 792],\n [191, 786],\n [195, 790],\n [197, 787],\n [205, 785],\n [209, 790],\n [210, 788],\n [222, 791],\n [223, 792],\n [232, 795],\n [233, 793],\n [236, 796],\n [241, 794],\n [247, 797],\n [255, 795],\n [262, 796],\n [264, 794],\n [276, 601],\n [262, 592],\n [236, 584],\n [213, 581],\n [209, 575],\n [202, 573],\n [197, 566],\n [186, 558],\n [167, 548],\n [153, 536],\n [149, 535],\n [143, 520],\n [137, 502],\n [133, 500],\n [130, 495],\n [132, 473],\n [129, 466],\n [116, 452],\n [112, 447],\n [108, 443],\n [99, 440],\n [96, 432],\n [89, 422],\n [86, 421],\n [86, 418],\n [88, 417],\n [89, 414],\n [87, 405],\n [84, 404],\n [82, 396],\n [79, 393],\n [79, 389],\n [78, 387],\n [80, 384],\n [73, 382],\n [71, 377],\n [68, 374],\n [68, 366],\n [57, 359],\n [49, 364],\n [47, 361],\n [49, 354],\n [49, 352],\n [42, 352],\n [42, 348],\n [39, 340],\n [35, 339],\n [33, 335],\n [26, 332],\n [23, 322],\n [11, 315],\n [13, 311],\n [7, 307],\n [4, 299],\n [4, 295],\n [0, 292],\n [-5, 289],\n [-3, 285],\n [0, 284],\n [1, 283],\n [0, 278],\n [-8, 270],\n [-1, 257],\n [-2, 253],\n [-4, 254],\n [-7, 250],\n [-7, 238],\n [-10, 234],\n [-20, 231],\n [-19, 222],\n [-14, 222],\n [-14, 220],\n [-23, 217],\n [-21, 214],\n [-17, 213],\n [-15, 210],\n [-16, 204],\n [-17, 202],\n [-22, 201],\n [-22, 200],\n [-18, 190],\n [-17, 179],\n [-45, 186],\n [-50, 186],\n [-59, 190],\n [-80, 194],\n [-80, 780],\n [-75, 781],\n [-71, 783],\n [-70, 794],\n [-58, 792],\n [-48, 795],\n [-43, 793],\n [-37, 786],\n [-30, 784],\n [-27, 784],\n [-21, 788],\n [-16, 787],\n [-10, 781],\n [-5, 780],\n [0, 782],\n [18, 790],\n [29, 789],\n [63, 797],\n [63, 806],\n [67, 809],\n [71, 807],\n [75, 800],\n [84, 804],\n [86, 802],\n [92, 801],\n [92, 809],\n [106, 823],\n [113, 821],\n [120, 824]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '035',\n 'FIPS': '24035',\n 'STATE': 'MD',\n 'NAME': \"Queen Anne's\",\n 'LSAD': 'County'},\n 'id': 2137,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[-80, 194],\n [-59, 190],\n [-50, 186],\n [-45, 186],\n [-17, 179],\n [-10, 176],\n [-9, 172],\n [-16, 172],\n [-16, 167],\n [-8, 158],\n [-9, 156],\n [-15, 153],\n [-16, 149],\n [-11, 145],\n [-10, 140],\n [-12, 133],\n [-16, 130],\n [-16, 129],\n [-10, 126],\n [-4, 117],\n [-6, 115],\n [-18, 116],\n [-20, 115],\n [-20, 108],\n [-16, 104],\n [-3, 110],\n [0, 107],\n [0, 104],\n [-2, 101],\n [-9, 101],\n [-10, 98],\n [-6, 85],\n [-3, 82],\n [0, 83],\n [4, 84],\n [11, 72],\n [19, 71],\n [22, 65],\n [22, 62],\n [10, 53],\n [16, 46],\n [26, 45],\n [27, 44],\n [26, 42],\n [14, 35],\n [14, 30],\n [26, 24],\n [33, 23],\n [36, 13],\n [53, 4],\n [51, 0],\n [47, -2],\n [45, -5],\n [50, -11],\n [59, -14],\n [60, -19],\n [55, -20],\n [49, -29],\n [38, -30],\n [14, -44],\n [7, -49],\n [0, -66],\n [-15, -80],\n [-80, -80],\n [-80, 194]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '041',\n 'FIPS': '24041',\n 'STATE': 'MD',\n 'NAME': 'Talbot',\n 'LSAD': 'County'},\n 'id': 2156,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[478, 3725],\n [484, 3712],\n [485, 3703],\n [482, 3694],\n [487, 3684],\n [486, 3666],\n [489, 3653],\n [496, 3641],\n [502, 3627],\n [509, 3622],\n [528, 3625],\n [533, 3621],\n [538, 3607],\n [543, 3602],\n [552, 3597],\n [558, 3598],\n [571, 3604],\n [576, 3611],\n [578, 3626],\n [583, 3630],\n [593, 3627],\n [598, 3623],\n [599, 3618],\n [600, 3595],\n [605, 3582],\n [622, 3548],\n [633, 3537],\n [640, 3517],\n [643, 3515],\n [653, 3511],\n [659, 3502],\n [661, 3494],\n [661, 3481],\n [664, 3472],\n [739, 3508],\n [739, 3497],\n [783, 3442],\n [795, 3430],\n [801, 3429],\n [800, 3418],\n [807, 3411],\n [806, 3404],\n [808, 3398],\n [811, 3398],\n [813, 3394],\n [799, 3393],\n [795, 3359],\n [777, 3330],\n [777, 3328],\n [880, 3245],\n [769, 3151],\n [660, 3017],\n [165, 3428],\n [67, 3514],\n [120, 3544],\n [135, 3544],\n [189, 3572],\n [205, 3590],\n [230, 3605],\n [232, 3613],\n [250, 3617],\n [262, 3625],\n [281, 3635],\n [282, 3641],\n [289, 3642],\n [299, 3656],\n [304, 3661],\n [342, 3677],\n [354, 3692],\n [380, 3701],\n [478, 3725]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '077',\n 'FIPS': '42077',\n 'STATE': 'PA',\n 'NAME': 'Lehigh',\n 'LSAD': 'County'},\n 'id': 2212,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[67, 3514],\n [165, 3428],\n [594, 3072],\n [351, 2680],\n [283, 2632],\n [93, 2480],\n [0, 2560],\n [-25, 2581],\n [-80, 2627],\n [-80, 3442],\n [0, 3481],\n [67, 3514]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '011',\n 'FIPS': '42011',\n 'STATE': 'PA',\n 'NAME': 'Berks',\n 'LSAD': 'County'},\n 'id': 2193,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[594, 3072],\n [660, 3017],\n [745, 2952],\n [937, 2799],\n [1053, 2710],\n [1176, 2612],\n [1343, 2482],\n [1335, 2471],\n [1280, 2421],\n [1252, 2383],\n [1245, 2377],\n [1239, 2369],\n [1224, 2351],\n [1238, 2340],\n [1228, 2327],\n [1210, 2313],\n [1206, 2307],\n [1108, 2381],\n [1091, 2358],\n [1040, 2396],\n [980, 2322],\n [987, 2313],\n [1002, 2299],\n [1007, 2292],\n [1018, 2281],\n [1027, 2273],\n [1048, 2259],\n [1056, 2249],\n [1065, 2242],\n [1063, 2239],\n [963, 2176],\n [952, 2195],\n [912, 2257],\n [898, 2250],\n [846, 2340],\n [841, 2345],\n [839, 2344],\n [831, 2357],\n [794, 2335],\n [754, 2404],\n [728, 2390],\n [723, 2399],\n [701, 2386],\n [700, 2400],\n [691, 2405],\n [693, 2416],\n [690, 2416],\n [682, 2423],\n [679, 2432],\n [682, 2441],\n [692, 2447],\n [694, 2452],\n [693, 2457],\n [677, 2466],\n [665, 2461],\n [648, 2460],\n [629, 2472],\n [624, 2479],\n [621, 2488],\n [625, 2498],\n [635, 2512],\n [636, 2519],\n [633, 2522],\n [624, 2524],\n [620, 2522],\n [614, 2514],\n [610, 2502],\n [607, 2500],\n [602, 2501],\n [598, 2504],\n [597, 2511],\n [598, 2521],\n [595, 2538],\n [591, 2544],\n [580, 2553],\n [576, 2558],\n [566, 2580],\n [555, 2613],\n [549, 2619],\n [544, 2618],\n [543, 2612],\n [546, 2604],\n [546, 2596],\n [539, 2589],\n [535, 2589],\n [530, 2591],\n [522, 2602],\n [519, 2612],\n [510, 2627],\n [496, 2670],\n [491, 2673],\n [487, 2673],\n [483, 2671],\n [481, 2669],\n [481, 2664],\n [487, 2654],\n [487, 2652],\n [483, 2648],\n [474, 2644],\n [468, 2648],\n [463, 2658],\n [428, 2674],\n [419, 2680],\n [411, 2679],\n [406, 2680],\n [398, 2686],\n [388, 2685],\n [381, 2681],\n [367, 2670],\n [360, 2670],\n [351, 2680],\n [594, 3072]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '091',\n 'FIPS': '42091',\n 'STATE': 'PA',\n 'NAME': 'Montgomery',\n 'LSAD': 'County'},\n 'id': 2198,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[398, 2686],\n [406, 2680],\n [411, 2679],\n [419, 2680],\n [428, 2674],\n [463, 2658],\n [468, 2648],\n [474, 2644],\n [483, 2648],\n [487, 2652],\n [487, 2654],\n [481, 2664],\n [481, 2669],\n [483, 2671],\n [487, 2673],\n [491, 2673],\n [496, 2670],\n [510, 2627],\n [519, 2612],\n [521, 2604],\n [525, 2597],\n [535, 2589],\n [539, 2589],\n [546, 2596],\n [546, 2604],\n [543, 2612],\n [544, 2618],\n [549, 2619],\n [555, 2613],\n [566, 2580],\n [576, 2558],\n [580, 2553],\n [591, 2544],\n [595, 2538],\n [598, 2521],\n [597, 2511],\n [598, 2504],\n [602, 2501],\n [607, 2500],\n [610, 2502],\n [614, 2514],\n [620, 2522],\n [624, 2524],\n [633, 2522],\n [636, 2519],\n [635, 2512],\n [625, 2498],\n [621, 2488],\n [624, 2479],\n [629, 2472],\n [648, 2460],\n [665, 2461],\n [677, 2466],\n [693, 2457],\n [694, 2452],\n [692, 2447],\n [682, 2441],\n [679, 2432],\n [682, 2423],\n [690, 2416],\n [693, 2416],\n [691, 2405],\n [700, 2400],\n [701, 2386],\n [723, 2399],\n [728, 2390],\n [754, 2404],\n [794, 2335],\n [831, 2357],\n [839, 2344],\n [761, 2299],\n [776, 2272],\n [705, 2207],\n [720, 2208],\n [724, 2206],\n [728, 2208],\n [727, 2204],\n [731, 2198],\n [730, 2191],\n [602, 2113],\n [609, 2103],\n [610, 2095],\n [601, 2090],\n [599, 2096],\n [593, 2093],\n [594, 2091],\n [593, 2090],\n [603, 2073],\n [600, 2071],\n [598, 2074],\n [593, 2072],\n [585, 2086],\n [575, 2081],\n [577, 2077],\n [573, 2074],\n [573, 2068],\n [575, 2064],\n [564, 2058],\n [570, 2048],\n [555, 2040],\n [552, 2046],\n [548, 2044],\n [551, 2035],\n [530, 2023],\n [532, 2017],\n [529, 2015],\n [534, 2007],\n [520, 1999],\n [517, 2002],\n [510, 2000],\n [514, 1993],\n [495, 1981],\n [501, 1972],\n [502, 1967],\n [498, 1966],\n [496, 1962],\n [497, 1959],\n [489, 1947],\n [490, 1945],\n [493, 1943],\n [503, 1939],\n [503, 1937],\n [500, 1935],\n [499, 1925],\n [495, 1918],\n [495, 1915],\n [499, 1911],\n [466, 1904],\n [431, 1894],\n [400, 1880],\n [366, 1861],\n [344, 1845],\n [306, 1810],\n [281, 1779],\n [258, 1740],\n [239, 1693],\n [217, 1692],\n [0, 1691],\n [-80, 1691],\n [-80, 1971],\n [-79, 1970],\n [-77, 1974],\n [-72, 1979],\n [-72, 1982],\n [-74, 1988],\n [-80, 1996],\n [-77, 2000],\n [-79, 2001],\n [-78, 2006],\n [-76, 2012],\n [-72, 2015],\n [-73, 2020],\n [-80, 2026],\n [-80, 2080],\n [-78, 2081],\n [-79, 2085],\n [-73, 2086],\n [-69, 2090],\n [-64, 2086],\n [-63, 2093],\n [-65, 2097],\n [-74, 2105],\n [-77, 2115],\n [-80, 2117],\n [-78, 2120],\n [-80, 2123],\n [-80, 2134],\n [-77, 2142],\n [-71, 2150],\n [-72, 2157],\n [0, 2287],\n [3, 2293],\n [5, 2310],\n [2, 2342],\n [2, 2369],\n [0, 2380],\n [-8, 2427],\n [0, 2431],\n [69, 2466],\n [93, 2480],\n [283, 2632],\n [351, 2680],\n [360, 2670],\n [367, 2670],\n [381, 2681],\n [388, 2685],\n [398, 2686]]],\n [[[512, 1924],\n [518, 1918],\n [521, 1913],\n [502, 1911],\n [506, 1921],\n [512, 1924]]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '029',\n 'FIPS': '42029',\n 'STATE': 'PA',\n 'NAME': 'Chester',\n 'LSAD': 'County'},\n 'id': 2187,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[841, 2345],\n [846, 2340],\n [898, 2250],\n [912, 2257],\n [952, 2195],\n [963, 2176],\n [957, 2172],\n [959, 2167],\n [964, 2161],\n [972, 2160],\n [979, 2153],\n [986, 2155],\n [991, 2151],\n [995, 2154],\n [1004, 2151],\n [1004, 2146],\n [1000, 2142],\n [1003, 2138],\n [1002, 2135],\n [999, 2128],\n [1001, 2118],\n [1006, 2112],\n [1016, 2109],\n [1016, 2102],\n [1020, 2102],\n [1023, 2101],\n [1020, 2092],\n [1013, 2093],\n [1009, 2087],\n [1011, 2086],\n [1011, 2082],\n [1005, 2071],\n [1006, 2056],\n [999, 2049],\n [1001, 2045],\n [996, 2040],\n [999, 2038],\n [993, 2029],\n [994, 2027],\n [990, 2021],\n [992, 2020],\n [988, 2015],\n [984, 2014],\n [983, 2008],\n [980, 2007],\n [983, 2002],\n [980, 2001],\n [983, 1996],\n [980, 1996],\n [983, 1985],\n [1004, 1985],\n [1015, 1977],\n [1023, 1986],\n [1045, 1983],\n [1051, 1984],\n [1051, 1975],\n [1058, 1964],\n [1043, 1956],\n [1023, 1947],\n [970, 1934],\n [938, 1932],\n [914, 1935],\n [895, 1934],\n [884, 1933],\n [868, 1927],\n [849, 1916],\n [797, 1869],\n [761, 1843],\n [742, 1857],\n [705, 1878],\n [664, 1895],\n [639, 1903],\n [610, 1909],\n [580, 1912],\n [535, 1914],\n [521, 1913],\n [518, 1918],\n [512, 1924],\n [508, 1922],\n [502, 1911],\n [499, 1911],\n [495, 1915],\n [495, 1918],\n [499, 1925],\n [500, 1935],\n [503, 1937],\n [503, 1939],\n [493, 1943],\n [490, 1945],\n [489, 1947],\n [497, 1959],\n [496, 1962],\n [498, 1966],\n [502, 1967],\n [501, 1972],\n [495, 1981],\n [514, 1993],\n [510, 2000],\n [517, 2002],\n [520, 1999],\n [534, 2007],\n [529, 2015],\n [532, 2017],\n [530, 2023],\n [551, 2035],\n [548, 2044],\n [552, 2046],\n [555, 2040],\n [570, 2048],\n [564, 2058],\n [575, 2064],\n [573, 2068],\n [573, 2074],\n [577, 2077],\n [575, 2081],\n [585, 2086],\n [593, 2072],\n [598, 2074],\n [600, 2071],\n [603, 2073],\n [593, 2090],\n [594, 2091],\n [593, 2093],\n [599, 2096],\n [601, 2090],\n [610, 2095],\n [609, 2103],\n [602, 2113],\n [730, 2191],\n [731, 2198],\n [727, 2204],\n [728, 2208],\n [724, 2206],\n [720, 2208],\n [705, 2207],\n [776, 2272],\n [761, 2299],\n [841, 2345]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '045',\n 'FIPS': '42045',\n 'STATE': 'PA',\n 'NAME': 'Delaware',\n 'LSAD': 'County'},\n 'id': 2186,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1190, 4074],\n [1191, 4070],\n [1190, 4065],\n [1194, 4044],\n [1203, 4035],\n [1211, 4018],\n [1212, 4012],\n [1226, 3989],\n [1250, 3969],\n [1255, 3956],\n [1256, 3931],\n [1270, 3914],\n [1280, 3899],\n [1287, 3891],\n [1290, 3885],\n [1291, 3876],\n [1288, 3865],\n [1272, 3842],\n [1269, 3841],\n [1262, 3841],\n [1254, 3845],\n [1233, 3844],\n [1226, 3840],\n [1223, 3832],\n [1224, 3825],\n [1241, 3807],\n [1243, 3803],\n [1243, 3797],\n [1234, 3780],\n [1225, 3773],\n [1219, 3764],\n [1219, 3749],\n [1207, 3732],\n [1203, 3730],\n [1195, 3729],\n [1186, 3724],\n [1174, 3703],\n [1170, 3699],\n [1148, 3701],\n [1127, 3708],\n [1118, 3708],\n [1115, 3707],\n [1110, 3697],\n [1107, 3681],\n [1105, 3676],\n [1099, 3671],\n [1086, 3664],\n [1079, 3656],\n [1081, 3645],\n [1095, 3629],\n [1100, 3618],\n [1089, 3592],\n [1082, 3584],\n [1068, 3541],\n [1073, 3529],\n [1079, 3521],\n [1088, 3518],\n [1097, 3519],\n [1102, 3518],\n [1107, 3514],\n [1108, 3511],\n [1107, 3505],\n [1099, 3492],\n [1087, 3484],\n [1073, 3460],\n [1073, 3455],\n [1085, 3444],\n [1091, 3412],\n [1088, 3404],\n [1077, 3402],\n [1071, 3398],\n [1072, 3393],\n [1079, 3382],\n [992, 3332],\n [977, 3322],\n [938, 3292],\n [880, 3244],\n [777, 3328],\n [777, 3330],\n [795, 3359],\n [799, 3393],\n [813, 3394],\n [811, 3398],\n [808, 3398],\n [806, 3404],\n [807, 3411],\n [800, 3418],\n [801, 3429],\n [795, 3430],\n [783, 3442],\n [739, 3497],\n [739, 3508],\n [664, 3472],\n [661, 3481],\n [661, 3494],\n [659, 3502],\n [653, 3511],\n [643, 3515],\n [640, 3517],\n [633, 3537],\n [622, 3548],\n [605, 3582],\n [600, 3595],\n [599, 3618],\n [598, 3623],\n [593, 3627],\n [583, 3630],\n [578, 3626],\n [576, 3611],\n [571, 3604],\n [558, 3598],\n [552, 3597],\n [543, 3602],\n [538, 3607],\n [533, 3621],\n [528, 3625],\n [509, 3622],\n [502, 3627],\n [496, 3641],\n [489, 3653],\n [486, 3666],\n [487, 3684],\n [482, 3694],\n [485, 3703],\n [484, 3712],\n [478, 3725],\n [488, 3726],\n [494, 3730],\n [498, 3737],\n [524, 3748],\n [553, 3752],\n [562, 3758],\n [577, 3763],\n [596, 3762],\n [609, 3766],\n [625, 3767],\n [668, 3775],\n [675, 3778],\n [721, 3785],\n [753, 3795],\n [758, 3800],\n [807, 3809],\n [868, 3837],\n [898, 3857],\n [928, 3864],\n [952, 3877],\n [967, 3890],\n [976, 3908],\n [1027, 3941],\n [1014, 3960],\n [1042, 3980],\n [1048, 3996],\n [1081, 4012],\n [1092, 4014],\n [1106, 4020],\n [1108, 4030],\n [1119, 4035],\n [1143, 4037],\n [1154, 4041],\n [1177, 4056],\n [1190, 4074]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '095',\n 'FIPS': '42095',\n 'STATE': 'PA',\n 'NAME': 'Northampton',\n 'LSAD': 'County'},\n 'id': 2208,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1978, 4176],\n [1938, 4131],\n [1910, 4096],\n [1889, 4068],\n [1888, 4056],\n [1897, 4040],\n [1897, 4029],\n [1889, 4014],\n [1873, 4001],\n [1871, 3998],\n [1870, 3992],\n [1872, 3987],\n [1871, 3984],\n [1855, 3977],\n [1846, 3969],\n [1832, 3960],\n [1827, 3961],\n [1820, 3959],\n [1818, 3952],\n [1814, 3951],\n [1808, 3954],\n [1796, 3946],\n [1792, 3945],\n [1786, 3940],\n [1782, 3942],\n [1785, 3951],\n [1783, 3958],\n [1769, 3970],\n [1761, 3974],\n [1756, 3982],\n [1749, 3979],\n [1734, 3980],\n [1727, 3976],\n [1727, 3972],\n [1722, 3968],\n [1715, 3968],\n [1713, 3965],\n [1711, 3968],\n [1708, 3965],\n [1706, 3966],\n [1703, 3965],\n [1594, 4096],\n [1570, 4125],\n [1529, 4176],\n [1978, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '037',\n 'FIPS': '34037',\n 'STATE': 'NJ',\n 'NAME': 'Sussex',\n 'LSAD': 'County'},\n 'id': 2225,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1526, 3726],\n [1592, 3670],\n [1594, 3665],\n [1617, 3642],\n [1622, 3639],\n [1633, 3639],\n [1638, 3638],\n [1638, 3636],\n [1644, 3636],\n [1659, 3638],\n [1666, 3634],\n [1704, 3625],\n [1725, 3617],\n [1740, 3607],\n [1740, 3603],\n [1755, 3595],\n [1767, 3595],\n [1768, 3590],\n [1773, 3586],\n [1772, 3582],\n [1774, 3569],\n [1777, 3567],\n [1778, 3565],\n [1776, 3564],\n [1779, 3558],\n [1778, 3555],\n [1772, 3550],\n [1771, 3541],\n [1768, 3533],\n [1776, 3526],\n [1775, 3519],\n [1771, 3515],\n [1769, 3500],\n [1764, 3494],\n [1757, 3491],\n [1762, 3478],\n [1769, 3474],\n [1774, 3465],\n [1768, 3455],\n [1767, 3450],\n [1770, 3446],\n [1769, 3440],\n [1762, 3434],\n [1765, 3426],\n [1774, 3425],\n [1778, 3426],\n [1783, 3424],\n [1786, 3418],\n [1786, 3413],\n [1797, 3406],\n [1786, 3387],\n [1783, 3389],\n [1771, 3366],\n [1775, 3364],\n [1766, 3344],\n [1783, 3335],\n [1766, 3300],\n [1758, 3295],\n [1751, 3286],\n [1740, 3277],\n [1693, 3182],\n [1690, 3180],\n [1685, 3182],\n [1678, 3190],\n [1678, 3193],\n [1674, 3197],\n [1660, 3203],\n [1732, 3028],\n [1644, 3014],\n [1654, 2953],\n [1555, 2940],\n [1575, 2880],\n [1464, 2866],\n [1448, 2871],\n [1447, 2872],\n [1444, 2901],\n [1440, 2915],\n [1433, 2936],\n [1416, 2977],\n [1410, 2982],\n [1390, 2991],\n [1381, 2999],\n [1371, 3002],\n [1360, 2997],\n [1340, 2991],\n [1329, 2989],\n [1324, 2990],\n [1312, 2995],\n [1298, 3009],\n [1284, 3013],\n [1280, 3017],\n [1276, 3026],\n [1274, 3046],\n [1267, 3075],\n [1263, 3088],\n [1267, 3106],\n [1267, 3122],\n [1272, 3129],\n [1275, 3138],\n [1275, 3147],\n [1269, 3194],\n [1271, 3224],\n [1269, 3244],\n [1265, 3254],\n [1251, 3266],\n [1219, 3304],\n [1195, 3314],\n [1166, 3319],\n [1159, 3318],\n [1151, 3314],\n [1135, 3299],\n [1128, 3296],\n [1120, 3296],\n [1110, 3298],\n [1099, 3303],\n [1093, 3307],\n [1083, 3320],\n [1081, 3326],\n [1082, 3331],\n [1087, 3340],\n [1088, 3349],\n [1096, 3351],\n [1102, 3355],\n [1104, 3361],\n [1111, 3363],\n [1114, 3375],\n [1120, 3378],\n [1124, 3385],\n [1128, 3387],\n [1130, 3393],\n [1129, 3399],\n [1141, 3407],\n [1147, 3405],\n [1150, 3408],\n [1150, 3416],\n [1153, 3420],\n [1168, 3424],\n [1180, 3431],\n [1205, 3440],\n [1208, 3442],\n [1207, 3447],\n [1211, 3451],\n [1226, 3458],\n [1235, 3468],\n [1236, 3472],\n [1247, 3476],\n [1254, 3488],\n [1264, 3494],\n [1267, 3497],\n [1275, 3499],\n [1278, 3505],\n [1282, 3510],\n [1288, 3512],\n [1295, 3518],\n [1301, 3516],\n [1303, 3517],\n [1308, 3524],\n [1310, 3526],\n [1315, 3526],\n [1333, 3542],\n [1337, 3542],\n [1341, 3547],\n [1345, 3543],\n [1351, 3547],\n [1356, 3544],\n [1361, 3545],\n [1378, 3563],\n [1393, 3569],\n [1396, 3578],\n [1410, 3577],\n [1419, 3597],\n [1424, 3602],\n [1426, 3606],\n [1435, 3610],\n [1443, 3624],\n [1451, 3631],\n [1452, 3636],\n [1465, 3651],\n [1467, 3656],\n [1470, 3658],\n [1473, 3663],\n [1485, 3672],\n [1489, 3677],\n [1489, 3680],\n [1494, 3686],\n [1507, 3695],\n [1506, 3698],\n [1507, 3706],\n [1505, 3712],\n [1517, 3719],\n [1526, 3726]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '019',\n 'FIPS': '34019',\n 'STATE': 'NJ',\n 'NAME': 'Hunterdon',\n 'LSAD': 'County'},\n 'id': 2209,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2012, 3669],\n [2018, 3663],\n [2019, 3655],\n [2017, 3652],\n [2023, 3646],\n [2022, 3622],\n [2026, 3623],\n [2028, 3621],\n [2035, 3622],\n [2043, 3616],\n [2046, 3617],\n [2052, 3611],\n [2048, 3598],\n [2048, 3595],\n [2051, 3594],\n [2052, 3591],\n [2058, 3587],\n [2057, 3581],\n [2054, 3578],\n [2056, 3575],\n [2061, 3574],\n [2068, 3568],\n [2069, 3561],\n [2071, 3561],\n [2073, 3559],\n [2073, 3547],\n [2070, 3544],\n [2067, 3545],\n [2066, 3541],\n [2069, 3539],\n [2069, 3537],\n [2062, 3535],\n [2058, 3537],\n [2055, 3529],\n [2053, 3527],\n [2052, 3521],\n [2056, 3501],\n [2051, 3498],\n [2050, 3495],\n [2043, 3494],\n [2044, 3491],\n [2050, 3490],\n [2051, 3486],\n [2056, 3482],\n [2054, 3479],\n [2056, 3469],\n [2059, 3469],\n [2055, 3465],\n [2060, 3464],\n [2061, 3461],\n [2070, 3462],\n [2071, 3463],\n [2075, 3460],\n [2082, 3466],\n [2086, 3475],\n [2085, 3477],\n [2088, 3480],\n [2091, 3478],\n [2095, 3479],\n [2094, 3483],\n [2100, 3484],\n [2102, 3482],\n [2106, 3484],\n [2106, 3487],\n [2112, 3488],\n [2114, 3485],\n [2117, 3487],\n [2116, 3491],\n [2128, 3493],\n [2133, 3498],\n [2137, 3496],\n [2149, 3503],\n [2150, 3506],\n [2164, 3469],\n [2175, 3475],\n [2185, 3486],\n [2196, 3491],\n [2198, 3487],\n [2202, 3485],\n [2208, 3477],\n [2211, 3478],\n [2214, 3483],\n [2220, 3485],\n [2224, 3491],\n [2231, 3495],\n [2234, 3493],\n [2236, 3487],\n [2235, 3481],\n [2235, 3473],\n [2229, 3462],\n [2229, 3455],\n [2226, 3447],\n [2220, 3446],\n [2219, 3444],\n [2217, 3429],\n [2213, 3418],\n [2206, 3414],\n [2205, 3405],\n [2199, 3397],\n [2193, 3396],\n [2184, 3385],\n [2173, 3383],\n [2169, 3381],\n [2164, 3373],\n [2159, 3373],\n [2152, 3365],\n [2143, 3362],\n [2137, 3362],\n [2126, 3354],\n [2118, 3345],\n [2108, 3340],\n [2104, 3341],\n [2097, 3338],\n [2094, 3340],\n [2091, 3337],\n [2091, 3334],\n [2086, 3339],\n [2076, 3331],\n [2070, 3329],\n [2071, 3326],\n [2063, 3315],\n [2059, 3307],\n [2056, 3305],\n [2057, 3292],\n [2054, 3288],\n [2059, 3286],\n [2065, 3280],\n [2068, 3273],\n [2072, 3255],\n [2083, 3244],\n [2100, 3230],\n [2119, 3197],\n [2122, 3195],\n [2141, 3190],\n [2143, 3178],\n [2151, 3171],\n [2153, 3166],\n [2152, 3156],\n [2140, 3148],\n [2125, 3143],\n [2119, 3139],\n [2102, 3115],\n [2082, 3099],\n [2075, 3091],\n [2064, 3080],\n [2055, 3068],\n [2041, 3056],\n [2013, 3040],\n [1971, 3018],\n [1968, 3014],\n [1969, 2989],\n [1966, 2981],\n [1966, 2971],\n [1960, 2967],\n [1955, 2956],\n [1944, 2945],\n [1928, 2936],\n [1919, 2933],\n [1922, 2940],\n [1921, 2944],\n [1915, 2952],\n [1870, 2965],\n [1839, 2952],\n [1791, 2943],\n [1770, 2935],\n [1660, 3203],\n [1674, 3197],\n [1678, 3193],\n [1678, 3190],\n [1685, 3182],\n [1692, 3180],\n [1740, 3277],\n [1751, 3286],\n [1758, 3295],\n [1766, 3300],\n [1783, 3335],\n [1766, 3344],\n [1775, 3364],\n [1771, 3366],\n [1783, 3389],\n [1786, 3387],\n [1797, 3406],\n [1786, 3413],\n [1786, 3418],\n [1783, 3424],\n [1778, 3426],\n [1774, 3425],\n [1765, 3426],\n [1762, 3434],\n [1769, 3440],\n [1770, 3446],\n [1767, 3450],\n [1768, 3455],\n [1774, 3465],\n [1769, 3474],\n [1762, 3478],\n [1757, 3491],\n [1764, 3494],\n [1769, 3500],\n [1771, 3515],\n [1775, 3519],\n [1776, 3526],\n [1768, 3533],\n [1771, 3541],\n [1772, 3550],\n [1778, 3555],\n [1779, 3558],\n [1776, 3564],\n [1778, 3565],\n [1777, 3567],\n [1774, 3569],\n [1772, 3582],\n [1773, 3586],\n [1768, 3590],\n [1767, 3595],\n [1848, 3621],\n [2012, 3669]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '035',\n 'FIPS': '34035',\n 'STATE': 'NJ',\n 'NAME': 'Somerset',\n 'LSAD': 'County'},\n 'id': 2211,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1079, 3382],\n [1085, 3370],\n [1088, 3347],\n [1087, 3340],\n [1082, 3331],\n [1082, 3324],\n [1085, 3316],\n [1099, 3303],\n [1110, 3298],\n [1128, 3296],\n [1135, 3299],\n [1151, 3314],\n [1159, 3318],\n [1166, 3319],\n [1195, 3314],\n [1219, 3304],\n [1251, 3266],\n [1265, 3254],\n [1269, 3244],\n [1271, 3224],\n [1269, 3194],\n [1275, 3147],\n [1275, 3138],\n [1272, 3129],\n [1267, 3122],\n [1267, 3106],\n [1263, 3088],\n [1267, 3075],\n [1274, 3046],\n [1276, 3026],\n [1280, 3017],\n [1284, 3013],\n [1298, 3009],\n [1312, 2995],\n [1324, 2990],\n [1329, 2989],\n [1340, 2991],\n [1360, 2997],\n [1371, 3002],\n [1381, 2999],\n [1390, 2991],\n [1410, 2982],\n [1416, 2977],\n [1433, 2936],\n [1440, 2915],\n [1444, 2901],\n [1447, 2872],\n [1453, 2864],\n [1499, 2823],\n [1506, 2821],\n [1516, 2820],\n [1523, 2816],\n [1539, 2802],\n [1557, 2782],\n [1569, 2762],\n [1595, 2697],\n [1604, 2688],\n [1622, 2679],\n [1628, 2674],\n [1663, 2656],\n [1698, 2630],\n [1714, 2598],\n [1723, 2572],\n [1727, 2568],\n [1738, 2564],\n [1748, 2558],\n [1770, 2525],\n [1771, 2512],\n [1767, 2499],\n [1743, 2477],\n [1735, 2475],\n [1722, 2476],\n [1716, 2475],\n [1701, 2465],\n [1683, 2449],\n [1673, 2449],\n [1656, 2453],\n [1633, 2462],\n [1624, 2461],\n [1619, 2455],\n [1615, 2448],\n [1609, 2432],\n [1605, 2417],\n [1601, 2411],\n [1593, 2405],\n [1577, 2397],\n [1572, 2388],\n [1570, 2381],\n [1564, 2376],\n [1540, 2369],\n [1529, 2364],\n [1498, 2353],\n [1481, 2355],\n [1474, 2354],\n [1446, 2340],\n [1403, 2312],\n [1394, 2322],\n [1389, 2323],\n [1388, 2329],\n [1394, 2334],\n [1394, 2345],\n [1405, 2356],\n [1408, 2365],\n [1420, 2368],\n [1426, 2380],\n [1426, 2390],\n [1429, 2397],\n [1429, 2402],\n [1421, 2406],\n [1422, 2416],\n [1416, 2425],\n [1418, 2428],\n [1419, 2438],\n [1418, 2443],\n [1414, 2444],\n [1409, 2442],\n [1404, 2448],\n [1395, 2447],\n [1390, 2450],\n [1390, 2454],\n [1384, 2454],\n [1379, 2460],\n [1374, 2460],\n [1377, 2469],\n [1375, 2471],\n [1368, 2463],\n [1184, 2606],\n [1065, 2701],\n [937, 2799],\n [745, 2952],\n [660, 3017],\n [769, 3151],\n [872, 3239],\n [880, 3244],\n [938, 3292],\n [977, 3322],\n [992, 3332],\n [1079, 3382]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '017',\n 'FIPS': '42017',\n 'STATE': 'PA',\n 'NAME': 'Bucks',\n 'LSAD': 'County'},\n 'id': 2197,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1343, 2482],\n [1368, 2463],\n [1375, 2471],\n [1377, 2469],\n [1374, 2460],\n [1379, 2460],\n [1384, 2454],\n [1390, 2454],\n [1390, 2450],\n [1395, 2447],\n [1404, 2448],\n [1409, 2442],\n [1414, 2444],\n [1418, 2443],\n [1419, 2438],\n [1418, 2428],\n [1416, 2425],\n [1422, 2416],\n [1421, 2406],\n [1429, 2402],\n [1429, 2397],\n [1426, 2390],\n [1426, 2380],\n [1420, 2368],\n [1408, 2365],\n [1405, 2356],\n [1394, 2345],\n [1394, 2334],\n [1388, 2329],\n [1389, 2323],\n [1394, 2322],\n [1403, 2312],\n [1389, 2301],\n [1349, 2260],\n [1308, 2244],\n [1297, 2236],\n [1291, 2228],\n [1279, 2205],\n [1260, 2183],\n [1236, 2172],\n [1208, 2163],\n [1180, 2146],\n [1171, 2133],\n [1168, 2119],\n [1167, 2094],\n [1169, 2081],\n [1179, 2052],\n [1175, 2030],\n [1158, 2004],\n [1154, 2000],\n [1146, 1997],\n [1099, 1995],\n [1090, 1993],\n [1058, 1964],\n [1051, 1975],\n [1051, 1984],\n [1045, 1983],\n [1023, 1986],\n [1015, 1977],\n [1004, 1985],\n [983, 1985],\n [980, 1996],\n [983, 1996],\n [980, 2001],\n [983, 2002],\n [980, 2007],\n [983, 2008],\n [984, 2014],\n [988, 2015],\n [992, 2020],\n [990, 2021],\n [994, 2027],\n [993, 2029],\n [999, 2038],\n [996, 2040],\n [1001, 2045],\n [999, 2049],\n [1006, 2056],\n [1005, 2071],\n [1011, 2082],\n [1011, 2086],\n [1009, 2087],\n [1013, 2093],\n [1020, 2092],\n [1023, 2101],\n [1020, 2102],\n [1016, 2102],\n [1016, 2109],\n [1006, 2112],\n [1001, 2118],\n [999, 2128],\n [1002, 2135],\n [1003, 2138],\n [1000, 2142],\n [1004, 2146],\n [1004, 2151],\n [995, 2154],\n [991, 2151],\n [986, 2155],\n [979, 2153],\n [972, 2160],\n [964, 2161],\n [957, 2172],\n [982, 2187],\n [993, 2196],\n [1063, 2239],\n [1065, 2242],\n [1056, 2249],\n [1048, 2259],\n [1027, 2273],\n [1018, 2281],\n [1007, 2292],\n [1002, 2299],\n [987, 2313],\n [980, 2322],\n [1040, 2396],\n [1091, 2358],\n [1108, 2381],\n [1206, 2307],\n [1210, 2313],\n [1228, 2327],\n [1238, 2340],\n [1224, 2351],\n [1239, 2369],\n [1245, 2377],\n [1252, 2383],\n [1280, 2421],\n [1335, 2471],\n [1343, 2482]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '101',\n 'FIPS': '42101',\n 'STATE': 'PA',\n 'NAME': 'Philadelphia',\n 'LSAD': 'County'},\n 'id': 2177,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1732, 3028],\n [1770, 2935],\n [1791, 2943],\n [1839, 2952],\n [1870, 2965],\n [1915, 2952],\n [1921, 2944],\n [1922, 2940],\n [1916, 2919],\n [1912, 2916],\n [1904, 2878],\n [1904, 2869],\n [1914, 2857],\n [1912, 2847],\n [1915, 2845],\n [1915, 2840],\n [1916, 2838],\n [1925, 2839],\n [1936, 2833],\n [1943, 2832],\n [1947, 2821],\n [1953, 2819],\n [1957, 2820],\n [1969, 2816],\n [1972, 2803],\n [1976, 2799],\n [1983, 2797],\n [1991, 2790],\n [1995, 2791],\n [1999, 2785],\n [2007, 2783],\n [2010, 2776],\n [2016, 2773],\n [2021, 2773],\n [2027, 2775],\n [2028, 2777],\n [2032, 2775],\n [2036, 2769],\n [2040, 2771],\n [2045, 2776],\n [2052, 2780],\n [2061, 2772],\n [2061, 2765],\n [2072, 2766],\n [2077, 2759],\n [2086, 2754],\n [2088, 2754],\n [2091, 2751],\n [2095, 2754],\n [2119, 2741],\n [2116, 2702],\n [2112, 2702],\n [2112, 2700],\n [2119, 2689],\n [2121, 2683],\n [2069, 2652],\n [2031, 2632],\n [2025, 2624],\n [2023, 2615],\n [2011, 2600],\n [2002, 2581],\n [1991, 2573],\n [1985, 2576],\n [1968, 2569],\n [1957, 2562],\n [1954, 2562],\n [1952, 2574],\n [1948, 2575],\n [1927, 2567],\n [1966, 2483],\n [1945, 2480],\n [1937, 2485],\n [1938, 2487],\n [1933, 2485],\n [1926, 2487],\n [1924, 2489],\n [1920, 2487],\n [1916, 2488],\n [1914, 2490],\n [1915, 2495],\n [1914, 2497],\n [1913, 2503],\n [1908, 2504],\n [1908, 2503],\n [1904, 2503],\n [1902, 2506],\n [1899, 2503],\n [1897, 2508],\n [1893, 2509],\n [1892, 2513],\n [1887, 2515],\n [1885, 2513],\n [1880, 2517],\n [1878, 2516],\n [1874, 2521],\n [1868, 2521],\n [1867, 2524],\n [1862, 2528],\n [1859, 2528],\n [1857, 2534],\n [1853, 2533],\n [1852, 2536],\n [1849, 2537],\n [1847, 2537],\n [1846, 2534],\n [1845, 2537],\n [1841, 2538],\n [1838, 2535],\n [1835, 2536],\n [1840, 2543],\n [1838, 2548],\n [1840, 2550],\n [1832, 2550],\n [1831, 2554],\n [1824, 2559],\n [1823, 2553],\n [1822, 2557],\n [1816, 2556],\n [1813, 2561],\n [1808, 2560],\n [1810, 2567],\n [1806, 2567],\n [1799, 2570],\n [1796, 2566],\n [1790, 2566],\n [1786, 2563],\n [1788, 2560],\n [1783, 2554],\n [1786, 2549],\n [1790, 2547],\n [1787, 2539],\n [1788, 2537],\n [1796, 2541],\n [1793, 2527],\n [1783, 2517],\n [1782, 2510],\n [1774, 2501],\n [1767, 2500],\n [1771, 2512],\n [1770, 2525],\n [1748, 2558],\n [1738, 2564],\n [1727, 2568],\n [1723, 2572],\n [1714, 2598],\n [1698, 2630],\n [1663, 2656],\n [1628, 2674],\n [1622, 2679],\n [1604, 2688],\n [1595, 2697],\n [1569, 2762],\n [1557, 2782],\n [1539, 2802],\n [1523, 2816],\n [1516, 2820],\n [1506, 2821],\n [1499, 2823],\n [1486, 2834],\n [1453, 2864],\n [1448, 2871],\n [1464, 2866],\n [1575, 2880],\n [1555, 2940],\n [1654, 2953],\n [1644, 3014],\n [1732, 3028]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '021',\n 'FIPS': '34021',\n 'STATE': 'NJ',\n 'NAME': 'Mercer',\n 'LSAD': 'County'},\n 'id': 2196,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1799, 2570],\n [1806, 2567],\n [1810, 2567],\n [1808, 2560],\n [1813, 2561],\n [1816, 2556],\n [1822, 2557],\n [1823, 2553],\n [1824, 2559],\n [1831, 2554],\n [1832, 2550],\n [1840, 2550],\n [1838, 2548],\n [1840, 2543],\n [1835, 2536],\n [1838, 2535],\n [1841, 2538],\n [1845, 2537],\n [1846, 2534],\n [1847, 2537],\n [1849, 2537],\n [1852, 2536],\n [1853, 2533],\n [1857, 2534],\n [1859, 2528],\n [1862, 2528],\n [1867, 2524],\n [1868, 2521],\n [1874, 2521],\n [1878, 2516],\n [1880, 2517],\n [1885, 2513],\n [1887, 2515],\n [1892, 2513],\n [1893, 2509],\n [1897, 2508],\n [1899, 2503],\n [1902, 2506],\n [1904, 2503],\n [1908, 2503],\n [1908, 2504],\n [1913, 2503],\n [1914, 2497],\n [1915, 2495],\n [1914, 2490],\n [1916, 2488],\n [1920, 2487],\n [1924, 2489],\n [1926, 2487],\n [1933, 2485],\n [1938, 2487],\n [1937, 2485],\n [1945, 2480],\n [1966, 2483],\n [2052, 2289],\n [2084, 2203],\n [2167, 1994],\n [2254, 1789],\n [2253, 1454],\n [2250, 1450],\n [2244, 1432],\n [2242, 1417],\n [2238, 1409],\n [2235, 1408],\n [2232, 1404],\n [2226, 1404],\n [2224, 1406],\n [2221, 1405],\n [2216, 1405],\n [2212, 1402],\n [2214, 1393],\n [2219, 1390],\n [2214, 1380],\n [2209, 1380],\n [2201, 1373],\n [2199, 1373],\n [2191, 1377],\n [2187, 1376],\n [2184, 1374],\n [2184, 1369],\n [2190, 1359],\n [2190, 1353],\n [2188, 1350],\n [2182, 1350],\n [2175, 1357],\n [2172, 1363],\n [2176, 1378],\n [2172, 1383],\n [2169, 1384],\n [2164, 1382],\n [2162, 1378],\n [2162, 1367],\n [2158, 1361],\n [2154, 1362],\n [2152, 1366],\n [2150, 1376],\n [2148, 1379],\n [2144, 1379],\n [2139, 1377],\n [2134, 1371],\n [2130, 1369],\n [2126, 1372],\n [2120, 1382],\n [2113, 1383],\n [2108, 1379],\n [2103, 1379],\n [2101, 1381],\n [2099, 1391],\n [2092, 1395],\n [2088, 1403],\n [2080, 1402],\n [2076, 1404],\n [2072, 1412],\n [2067, 1411],\n [2059, 1403],\n [2054, 1403],\n [2051, 1405],\n [2045, 1411],\n [2037, 1413],\n [2034, 1417],\n [2031, 1425],\n [2033, 1434],\n [2022, 1442],\n [2015, 1457],\n [2000, 1455],\n [1992, 1459],\n [1983, 1458],\n [1975, 1468],\n [1969, 1471],\n [1966, 1481],\n [1964, 1484],\n [1954, 1488],\n [1936, 1502],\n [1929, 1506],\n [1926, 1505],\n [1916, 1512],\n [1914, 1512],\n [1911, 1509],\n [1910, 1503],\n [1906, 1499],\n [1902, 1501],\n [1902, 1507],\n [1900, 1513],\n [1896, 1513],\n [1893, 1505],\n [1890, 1504],\n [1889, 1505],\n [1888, 1510],\n [1878, 1509],\n [1878, 1514],\n [1874, 1515],\n [1871, 1511],\n [1868, 1512],\n [1868, 1518],\n [1866, 1520],\n [1861, 1521],\n [1863, 1530],\n [1861, 1538],\n [1865, 1546],\n [1862, 1550],\n [1863, 1554],\n [1863, 1559],\n [1860, 1560],\n [1862, 1563],\n [1861, 1566],\n [1863, 1569],\n [1860, 1591],\n [1854, 1601],\n [1854, 1606],\n [1851, 1615],\n [1846, 1623],\n [1842, 1625],\n [1837, 1634],\n [1718, 1733],\n [1716, 1731],\n [1713, 1735],\n [1706, 1738],\n [1702, 1745],\n [1696, 1747],\n [1693, 1758],\n [1684, 1761],\n [1680, 1769],\n [1681, 1771],\n [1678, 1775],\n [1676, 1776],\n [1669, 1787],\n [1659, 1794],\n [1646, 1809],\n [1632, 1813],\n [1628, 1816],\n [1603, 1822],\n [1591, 1821],\n [1587, 1819],\n [1584, 1819],\n [1579, 1815],\n [1560, 1811],\n [1545, 1805],\n [1526, 1806],\n [1517, 1810],\n [1513, 1819],\n [1506, 1823],\n [1472, 1980],\n [1464, 2006],\n [1451, 2015],\n [1447, 2016],\n [1443, 2024],\n [1439, 2027],\n [1435, 2032],\n [1435, 2041],\n [1430, 2055],\n [1421, 2065],\n [1418, 2069],\n [1402, 2070],\n [1392, 2080],\n [1389, 2081],\n [1387, 2086],\n [1388, 2089],\n [1393, 2089],\n [1393, 2094],\n [1397, 2095],\n [1402, 2102],\n [1400, 2105],\n [1392, 2106],\n [1385, 2105],\n [1381, 2107],\n [1376, 2106],\n [1368, 2106],\n [1364, 2109],\n [1359, 2108],\n [1352, 2115],\n [1345, 2116],\n [1344, 2118],\n [1342, 2125],\n [1346, 2132],\n [1346, 2137],\n [1340, 2142],\n [1340, 2149],\n [1338, 2150],\n [1337, 2161],\n [1342, 2163],\n [1342, 2167],\n [1340, 2170],\n [1343, 2172],\n [1343, 2175],\n [1351, 2181],\n [1345, 2182],\n [1341, 2189],\n [1344, 2192],\n [1340, 2191],\n [1338, 2199],\n [1336, 2201],\n [1335, 2198],\n [1328, 2202],\n [1325, 2207],\n [1324, 2204],\n [1321, 2204],\n [1314, 2212],\n [1305, 2210],\n [1303, 2207],\n [1300, 2209],\n [1296, 2208],\n [1293, 2203],\n [1278, 2204],\n [1291, 2228],\n [1297, 2236],\n [1308, 2244],\n [1349, 2260],\n [1389, 2301],\n [1402, 2312],\n [1446, 2340],\n [1474, 2354],\n [1481, 2355],\n [1498, 2353],\n [1529, 2364],\n [1540, 2369],\n [1564, 2376],\n [1568, 2379],\n [1574, 2393],\n [1577, 2397],\n [1593, 2405],\n [1601, 2411],\n [1605, 2417],\n [1609, 2432],\n [1615, 2448],\n [1619, 2455],\n [1624, 2461],\n [1633, 2462],\n [1656, 2453],\n [1678, 2448],\n [1683, 2449],\n [1701, 2465],\n [1716, 2475],\n [1722, 2476],\n [1735, 2475],\n [1743, 2477],\n [1767, 2500],\n [1774, 2501],\n [1782, 2510],\n [1783, 2517],\n [1793, 2527],\n [1796, 2541],\n [1788, 2537],\n [1787, 2539],\n [1790, 2547],\n [1786, 2549],\n [1783, 2554],\n [1788, 2560],\n [1786, 2563],\n [1790, 2566],\n [1796, 2566],\n [1799, 2570]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '005',\n 'FIPS': '34005',\n 'STATE': 'NJ',\n 'NAME': 'Burlington',\n 'LSAD': 'County'},\n 'id': 2178,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[217, 1692],\n [217, 1571],\n [219, 1532],\n [249, 1041],\n [240, 1045],\n [235, 1044],\n [235, 1047],\n [223, 1051],\n [222, 1046],\n [216, 1048],\n [213, 1046],\n [209, 1046],\n [206, 1041],\n [198, 1042],\n [193, 1037],\n [191, 1038],\n [186, 1046],\n [176, 1047],\n [173, 1050],\n [166, 1049],\n [155, 1035],\n [139, 1029],\n [134, 1023],\n [130, 1024],\n [120, 1017],\n [111, 1023],\n [107, 1021],\n [102, 1023],\n [81, 1017],\n [76, 1010],\n [71, 1012],\n [63, 1019],\n [49, 1017],\n [21, 1022],\n [14, 1030],\n [8, 1030],\n [0, 1025],\n [-5, 1023],\n [-11, 1025],\n [-17, 1032],\n [-21, 1034],\n [-39, 1036],\n [-48, 1033],\n [-56, 1022],\n [-60, 1021],\n [-64, 1021],\n [-75, 1034],\n [-80, 1037],\n [-80, 1144],\n [-66, 1150],\n [-57, 1168],\n [-57, 1174],\n [-76, 1194],\n [-80, 1194],\n [-80, 1260],\n [-71, 1292],\n [-56, 1329],\n [-43, 1345],\n [-43, 1367],\n [-48, 1381],\n [-80, 1391],\n [-80, 1691],\n [0, 1691],\n [217, 1692]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '015',\n 'FIPS': '24015',\n 'STATE': 'MD',\n 'NAME': 'Cecil',\n 'LSAD': 'County'},\n 'id': 2129,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1272, 1811],\n [1283, 1808],\n [1285, 1809],\n [1287, 1804],\n [1294, 1799],\n [1292, 1795],\n [1294, 1786],\n [1300, 1777],\n [1303, 1772],\n [1302, 1767],\n [1308, 1764],\n [1313, 1759],\n [1315, 1754],\n [1333, 1738],\n [1334, 1735],\n [1332, 1727],\n [1335, 1722],\n [1334, 1714],\n [1331, 1708],\n [1373, 1681],\n [1374, 1670],\n [1381, 1667],\n [1384, 1663],\n [1397, 1657],\n [1407, 1655],\n [1413, 1656],\n [1417, 1655],\n [1425, 1656],\n [1434, 1649],\n [1447, 1647],\n [1461, 1637],\n [1463, 1634],\n [1466, 1633],\n [1467, 1629],\n [1472, 1626],\n [1472, 1621],\n [1476, 1618],\n [1480, 1611],\n [1484, 1611],\n [1486, 1600],\n [1498, 1585],\n [1496, 1582],\n [1498, 1574],\n [1497, 1569],\n [1499, 1567],\n [1496, 1561],\n [1498, 1559],\n [1501, 1539],\n [1507, 1522],\n [1522, 1508],\n [1521, 1502],\n [1523, 1495],\n [1536, 1486],\n [1537, 1482],\n [1544, 1477],\n [1387, 1300],\n [1075, 1588],\n [1044, 1578],\n [1039, 1581],\n [1026, 1578],\n [1004, 1584],\n [1000, 1592],\n [999, 1606],\n [996, 1610],\n [991, 1611],\n [984, 1616],\n [972, 1617],\n [963, 1613],\n [961, 1615],\n [948, 1612],\n [938, 1621],\n [918, 1631],\n [914, 1630],\n [912, 1627],\n [907, 1629],\n [909, 1632],\n [907, 1634],\n [898, 1634],\n [895, 1631],\n [890, 1631],\n [889, 1639],\n [891, 1643],\n [884, 1644],\n [883, 1648],\n [878, 1651],\n [876, 1658],\n [870, 1660],\n [868, 1659],\n [859, 1664],\n [856, 1671],\n [840, 1675],\n [839, 1682],\n [836, 1681],\n [831, 1684],\n [832, 1689],\n [831, 1692],\n [828, 1692],\n [823, 1697],\n [822, 1696],\n [817, 1700],\n [817, 1704],\n [821, 1717],\n [816, 1725],\n [819, 1727],\n [815, 1727],\n [817, 1729],\n [815, 1732],\n [817, 1735],\n [812, 1742],\n [803, 1739],\n [801, 1742],\n [802, 1747],\n [800, 1746],\n [800, 1749],\n [796, 1749],\n [796, 1745],\n [793, 1747],\n [794, 1749],\n [789, 1753],\n [791, 1756],\n [794, 1754],\n [797, 1756],\n [797, 1759],\n [794, 1760],\n [798, 1765],\n [788, 1767],\n [787, 1762],\n [785, 1759],\n [783, 1762],\n [784, 1766],\n [778, 1766],\n [774, 1771],\n [776, 1782],\n [779, 1783],\n [782, 1779],\n [785, 1781],\n [790, 1790],\n [788, 1791],\n [785, 1788],\n [783, 1789],\n [781, 1794],\n [782, 1799],\n [779, 1807],\n [777, 1807],\n [775, 1803],\n [771, 1804],\n [774, 1810],\n [772, 1816],\n [769, 1817],\n [764, 1812],\n [757, 1813],\n [748, 1809],\n [734, 1808],\n [723, 1803],\n [728, 1808],\n [775, 1833],\n [761, 1843],\n [797, 1869],\n [849, 1916],\n [868, 1927],\n [884, 1933],\n [895, 1934],\n [914, 1935],\n [938, 1932],\n [970, 1934],\n [1011, 1943],\n [1043, 1956],\n [1059, 1965],\n [1090, 1993],\n [1099, 1995],\n [1146, 1997],\n [1154, 2000],\n [1161, 2008],\n [1177, 1998],\n [1180, 1993],\n [1173, 1989],\n [1174, 1987],\n [1190, 1978],\n [1184, 1976],\n [1187, 1971],\n [1188, 1965],\n [1192, 1959],\n [1195, 1960],\n [1193, 1968],\n [1201, 1959],\n [1196, 1959],\n [1201, 1952],\n [1208, 1953],\n [1205, 1956],\n [1209, 1960],\n [1211, 1960],\n [1213, 1957],\n [1211, 1955],\n [1213, 1943],\n [1216, 1942],\n [1215, 1947],\n [1228, 1938],\n [1229, 1931],\n [1235, 1930],\n [1238, 1924],\n [1241, 1924],\n [1244, 1919],\n [1239, 1912],\n [1241, 1910],\n [1242, 1906],\n [1245, 1903],\n [1242, 1898],\n [1245, 1891],\n [1241, 1887],\n [1241, 1884],\n [1237, 1879],\n [1240, 1870],\n [1236, 1868],\n [1238, 1866],\n [1242, 1865],\n [1243, 1861],\n [1248, 1855],\n [1247, 1853],\n [1256, 1849],\n [1259, 1845],\n [1255, 1839],\n [1255, 1836],\n [1257, 1835],\n [1259, 1827],\n [1249, 1820],\n [1246, 1823],\n [1241, 1813],\n [1239, 1801],\n [1252, 1804],\n [1257, 1790],\n [1264, 1795],\n [1261, 1802],\n [1269, 1806],\n [1268, 1807],\n [1272, 1811]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '015',\n 'FIPS': '34015',\n 'STATE': 'NJ',\n 'NAME': 'Gloucester',\n 'LSAD': 'County'},\n 'id': 2180,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[535, 1914],\n [580, 1912],\n [610, 1909],\n [639, 1903],\n [691, 1885],\n [742, 1857],\n [775, 1833],\n [728, 1808],\n [712, 1791],\n [714, 1789],\n [707, 1781],\n [696, 1775],\n [691, 1766],\n [691, 1761],\n [682, 1733],\n [675, 1718],\n [670, 1697],\n [670, 1679],\n [668, 1677],\n [659, 1680],\n [654, 1678],\n [643, 1653],\n [627, 1645],\n [624, 1640],\n [623, 1624],\n [594, 1636],\n [547, 1568],\n [510, 1558],\n [472, 1501],\n [470, 1491],\n [472, 1486],\n [471, 1473],\n [474, 1456],\n [485, 1442],\n [509, 1425],\n [511, 1421],\n [545, 1390],\n [542, 1383],\n [536, 1349],\n [535, 1324],\n [549, 1311],\n [540, 1288],\n [526, 1290],\n [509, 1265],\n [502, 1233],\n [507, 1198],\n [520, 1180],\n [522, 1174],\n [534, 1164],\n [533, 1157],\n [556, 1141],\n [581, 1115],\n [585, 1101],\n [603, 1068],\n [619, 1019],\n [614, 1017],\n [613, 1010],\n [606, 1006],\n [603, 1008],\n [597, 1006],\n [595, 1002],\n [597, 998],\n [595, 996],\n [591, 998],\n [587, 996],\n [587, 992],\n [585, 991],\n [580, 993],\n [579, 997],\n [577, 998],\n [573, 992],\n [570, 991],\n [567, 992],\n [567, 995],\n [565, 997],\n [562, 997],\n [558, 993],\n [559, 989],\n [562, 989],\n [563, 981],\n [558, 981],\n [554, 988],\n [549, 980],\n [550, 977],\n [553, 976],\n [552, 969],\n [555, 967],\n [555, 962],\n [551, 958],\n [543, 956],\n [537, 950],\n [535, 945],\n [537, 940],\n [528, 921],\n [519, 916],\n [514, 912],\n [510, 914],\n [510, 916],\n [504, 917],\n [500, 911],\n [496, 914],\n [489, 911],\n [486, 916],\n [482, 911],\n [474, 912],\n [471, 911],\n [468, 914],\n [463, 914],\n [458, 911],\n [454, 903],\n [451, 904],\n [445, 898],\n [430, 897],\n [430, 894],\n [422, 888],\n [417, 880],\n [403, 880],\n [396, 878],\n [391, 879],\n [383, 878],\n [375, 881],\n [368, 890],\n [324, 894],\n [258, 889],\n [219, 1532],\n [217, 1571],\n [217, 1692],\n [239, 1693],\n [250, 1722],\n [269, 1760],\n [293, 1794],\n [321, 1825],\n [344, 1845],\n [366, 1861],\n [400, 1880],\n [431, 1894],\n [466, 1904],\n [501, 1911],\n [535, 1914]]],\n [[[551, 1518],\n [550, 1507],\n [554, 1484],\n [553, 1479],\n [556, 1472],\n [547, 1471],\n [539, 1487],\n [533, 1506],\n [534, 1512],\n [551, 1518]]],\n [[[521, 1459],\n [541, 1443],\n [543, 1430],\n [526, 1439],\n [523, 1445],\n [521, 1459]]]]},\n 'properties': {'STATE_FIPS': '10',\n 'COUNTY_FIP': '003',\n 'FIPS': '10003',\n 'STATE': 'DE',\n 'NAME': 'New Castle',\n 'LSAD': 'County'},\n 'id': 2130,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[276, 601],\n [314, 0],\n [314, -1],\n [319, -80],\n [-15, -80],\n [0, -66],\n [7, -49],\n [14, -44],\n [38, -30],\n [49, -29],\n [55, -20],\n [60, -19],\n [59, -14],\n [50, -11],\n [45, -5],\n [47, -2],\n [51, 0],\n [53, 4],\n [36, 13],\n [33, 23],\n [26, 24],\n [14, 30],\n [14, 35],\n [26, 42],\n [27, 44],\n [26, 45],\n [16, 46],\n [10, 53],\n [22, 62],\n [22, 65],\n [19, 71],\n [11, 72],\n [4, 84],\n [0, 83],\n [-3, 82],\n [-6, 85],\n [-10, 98],\n [-9, 101],\n [-2, 101],\n [0, 104],\n [0, 107],\n [-3, 110],\n [-16, 104],\n [-20, 108],\n [-20, 115],\n [-18, 116],\n [-6, 115],\n [-4, 117],\n [-10, 126],\n [-16, 129],\n [-16, 130],\n [-12, 133],\n [-10, 140],\n [-11, 145],\n [-16, 149],\n [-15, 153],\n [-9, 156],\n [-8, 158],\n [-16, 167],\n [-16, 172],\n [-9, 172],\n [-10, 176],\n [-17, 179],\n [-18, 190],\n [-22, 200],\n [-22, 201],\n [-17, 202],\n [-16, 204],\n [-15, 210],\n [-17, 213],\n [-21, 214],\n [-23, 217],\n [-14, 220],\n [-14, 222],\n [-19, 222],\n [-20, 231],\n [-10, 234],\n [-7, 238],\n [-7, 250],\n [-4, 254],\n [-2, 253],\n [-1, 257],\n [-8, 270],\n [0, 278],\n [1, 283],\n [0, 284],\n [-3, 285],\n [-5, 289],\n [0, 292],\n [4, 295],\n [4, 299],\n [7, 307],\n [13, 311],\n [11, 315],\n [23, 322],\n [26, 332],\n [33, 335],\n [35, 339],\n [39, 340],\n [42, 348],\n [42, 352],\n [49, 352],\n [49, 354],\n [47, 361],\n [49, 364],\n [57, 359],\n [68, 366],\n [68, 374],\n [71, 377],\n [73, 382],\n [80, 384],\n [78, 387],\n [79, 389],\n [79, 393],\n [82, 396],\n [84, 404],\n [87, 405],\n [89, 414],\n [88, 417],\n [86, 418],\n [86, 421],\n [89, 422],\n [96, 432],\n [99, 440],\n [108, 443],\n [112, 447],\n [116, 452],\n [129, 466],\n [132, 473],\n [130, 495],\n [133, 500],\n [137, 502],\n [143, 520],\n [149, 535],\n [153, 536],\n [167, 548],\n [186, 558],\n [197, 566],\n [202, 573],\n [209, 575],\n [213, 581],\n [236, 584],\n [262, 592],\n [276, 601]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '011',\n 'FIPS': '24011',\n 'STATE': 'MD',\n 'NAME': 'Caroline',\n 'LSAD': 'County'},\n 'id': 2136,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[619, 1019],\n [629, 1007],\n [646, 998],\n [649, 993],\n [646, 983],\n [651, 976],\n [682, 953],\n [695, 949],\n [726, 921],\n [729, 913],\n [731, 889],\n [742, 867],\n [771, 829],\n [778, 810],\n [774, 789],\n [774, 752],\n [787, 738],\n [793, 716],\n [790, 685],\n [783, 682],\n [771, 660],\n [767, 625],\n [781, 498],\n [780, 457],\n [789, 443],\n [788, 441],\n [800, 428],\n [812, 424],\n [862, 379],\n [869, 369],\n [902, 310],\n [907, 296],\n [912, 271],\n [910, 240],\n [911, 231],\n [908, 233],\n [909, 239],\n [904, 244],\n [900, 245],\n [897, 245],\n [893, 238],\n [887, 237],\n [884, 239],\n [881, 240],\n [877, 233],\n [872, 231],\n [865, 234],\n [867, 241],\n [864, 248],\n [863, 248],\n [855, 234],\n [852, 234],\n [841, 253],\n [832, 246],\n [829, 253],\n [830, 256],\n [823, 260],\n [817, 258],\n [810, 260],\n [808, 249],\n [799, 247],\n [796, 251],\n [793, 250],\n [792, 244],\n [785, 238],\n [781, 233],\n [785, 221],\n [783, 218],\n [779, 215],\n [783, 211],\n [786, 213],\n [781, 200],\n [784, 195],\n [783, 193],\n [780, 192],\n [778, 187],\n [781, 180],\n [779, 178],\n [776, 177],\n [775, 181],\n [770, 182],\n [770, 176],\n [759, 179],\n [757, 171],\n [753, 174],\n [751, 171],\n [741, 169],\n [732, 165],\n [724, 169],\n [716, 169],\n [695, 162],\n [688, 166],\n [670, 156],\n [659, 152],\n [654, 143],\n [655, 131],\n [651, 125],\n [645, 122],\n [642, 113],\n [640, 111],\n [635, 109],\n [632, 110],\n [625, 105],\n [607, 62],\n [584, 51],\n [585, 47],\n [580, 39],\n [577, 38],\n [573, 39],\n [566, 35],\n [557, 24],\n [313, 14],\n [258, 889],\n [324, 894],\n [368, 890],\n [375, 881],\n [383, 878],\n [391, 879],\n [396, 878],\n [403, 880],\n [417, 880],\n [422, 888],\n [430, 894],\n [430, 897],\n [445, 898],\n [451, 904],\n [454, 903],\n [458, 911],\n [463, 914],\n [468, 914],\n [471, 911],\n [474, 912],\n [482, 911],\n [486, 916],\n [489, 911],\n [496, 914],\n [500, 911],\n [504, 917],\n [510, 916],\n [510, 914],\n [514, 912],\n [519, 916],\n [528, 921],\n [537, 940],\n [535, 945],\n [537, 950],\n [543, 956],\n [551, 958],\n [555, 962],\n [555, 967],\n [552, 969],\n [553, 976],\n [550, 977],\n [549, 980],\n [554, 988],\n [558, 981],\n [563, 981],\n [562, 989],\n [559, 989],\n [558, 993],\n [562, 997],\n [565, 997],\n [567, 995],\n [567, 992],\n [570, 991],\n [573, 992],\n [577, 998],\n [579, 997],\n [580, 993],\n [585, 991],\n [587, 992],\n [587, 996],\n [591, 998],\n [595, 996],\n [597, 998],\n [595, 1002],\n [597, 1006],\n [603, 1008],\n [606, 1006],\n [613, 1010],\n [614, 1017],\n [619, 1019]]]},\n 'properties': {'STATE_FIPS': '10',\n 'COUNTY_FIP': '001',\n 'FIPS': '10001',\n 'STATE': 'DE',\n 'NAME': 'Kent',\n 'LSAD': 'County'},\n 'id': 2138,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1272, 1811],\n [1268, 1807],\n [1269, 1806],\n [1261, 1802],\n [1264, 1795],\n [1257, 1790],\n [1252, 1804],\n [1239, 1801],\n [1241, 1813],\n [1246, 1823],\n [1249, 1820],\n [1259, 1827],\n [1257, 1835],\n [1255, 1836],\n [1255, 1839],\n [1259, 1845],\n [1256, 1849],\n [1247, 1853],\n [1248, 1855],\n [1243, 1861],\n [1242, 1865],\n [1238, 1866],\n [1236, 1868],\n [1240, 1870],\n [1237, 1879],\n [1241, 1884],\n [1241, 1887],\n [1245, 1891],\n [1242, 1898],\n [1245, 1903],\n [1242, 1906],\n [1241, 1910],\n [1239, 1912],\n [1244, 1919],\n [1241, 1924],\n [1238, 1924],\n [1235, 1930],\n [1229, 1931],\n [1228, 1938],\n [1215, 1947],\n [1216, 1942],\n [1213, 1943],\n [1211, 1955],\n [1213, 1957],\n [1211, 1960],\n [1209, 1960],\n [1205, 1956],\n [1208, 1953],\n [1201, 1952],\n [1196, 1959],\n [1201, 1959],\n [1193, 1968],\n [1195, 1960],\n [1192, 1959],\n [1188, 1965],\n [1187, 1971],\n [1184, 1976],\n [1190, 1978],\n [1174, 1987],\n [1173, 1989],\n [1180, 1993],\n [1177, 1998],\n [1161, 2008],\n [1175, 2030],\n [1179, 2052],\n [1169, 2081],\n [1167, 2094],\n [1168, 2119],\n [1171, 2133],\n [1180, 2146],\n [1208, 2163],\n [1236, 2172],\n [1260, 2183],\n [1278, 2204],\n [1293, 2203],\n [1296, 2208],\n [1300, 2209],\n [1303, 2207],\n [1305, 2210],\n [1314, 2212],\n [1321, 2204],\n [1324, 2204],\n [1325, 2207],\n [1328, 2202],\n [1335, 2198],\n [1336, 2201],\n [1338, 2199],\n [1340, 2191],\n [1344, 2192],\n [1341, 2189],\n [1345, 2182],\n [1351, 2181],\n [1343, 2175],\n [1343, 2172],\n [1340, 2170],\n [1342, 2167],\n [1342, 2163],\n [1337, 2161],\n [1338, 2150],\n [1340, 2149],\n [1340, 2142],\n [1346, 2137],\n [1346, 2132],\n [1342, 2125],\n [1344, 2118],\n [1345, 2116],\n [1352, 2115],\n [1359, 2108],\n [1364, 2109],\n [1368, 2106],\n [1376, 2106],\n [1381, 2107],\n [1385, 2105],\n [1392, 2106],\n [1400, 2105],\n [1402, 2102],\n [1397, 2095],\n [1393, 2094],\n [1393, 2089],\n [1388, 2089],\n [1387, 2086],\n [1389, 2081],\n [1392, 2080],\n [1402, 2070],\n [1418, 2069],\n [1421, 2065],\n [1430, 2055],\n [1435, 2041],\n [1435, 2032],\n [1439, 2027],\n [1443, 2024],\n [1447, 2016],\n [1451, 2015],\n [1464, 2006],\n [1472, 1980],\n [1506, 1823],\n [1513, 1819],\n [1517, 1810],\n [1526, 1806],\n [1545, 1805],\n [1560, 1811],\n [1579, 1815],\n [1584, 1819],\n [1587, 1819],\n [1591, 1821],\n [1603, 1822],\n [1628, 1816],\n [1632, 1813],\n [1646, 1809],\n [1659, 1794],\n [1669, 1787],\n [1676, 1776],\n [1678, 1775],\n [1681, 1771],\n [1680, 1769],\n [1684, 1761],\n [1693, 1758],\n [1696, 1747],\n [1702, 1745],\n [1706, 1738],\n [1713, 1735],\n [1716, 1731],\n [1718, 1733],\n [1749, 1707],\n [1544, 1477],\n [1537, 1482],\n [1536, 1486],\n [1523, 1495],\n [1521, 1502],\n [1522, 1508],\n [1507, 1522],\n [1501, 1539],\n [1498, 1559],\n [1496, 1561],\n [1499, 1567],\n [1497, 1569],\n [1498, 1574],\n [1496, 1582],\n [1498, 1585],\n [1486, 1600],\n [1484, 1611],\n [1480, 1611],\n [1476, 1618],\n [1472, 1621],\n [1472, 1626],\n [1467, 1629],\n [1466, 1633],\n [1463, 1634],\n [1461, 1637],\n [1447, 1647],\n [1434, 1649],\n [1425, 1656],\n [1417, 1655],\n [1413, 1656],\n [1407, 1655],\n [1397, 1657],\n [1384, 1663],\n [1381, 1667],\n [1374, 1670],\n [1373, 1681],\n [1331, 1708],\n [1334, 1714],\n [1335, 1722],\n [1332, 1727],\n [1334, 1735],\n [1333, 1738],\n [1315, 1754],\n [1313, 1759],\n [1308, 1764],\n [1302, 1767],\n [1303, 1772],\n [1300, 1777],\n [1294, 1786],\n [1292, 1795],\n [1294, 1799],\n [1287, 1804],\n [1285, 1809],\n [1283, 1808],\n [1272, 1811]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '007',\n 'FIPS': '34007',\n 'STATE': 'NJ',\n 'NAME': 'Camden',\n 'LSAD': 'County'},\n 'id': 2179,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[763, 1054],\n [751, 1058],\n [736, 1068],\n [721, 1088],\n [688, 1157],\n [672, 1156],\n [661, 1161],\n [629, 1184],\n [625, 1195],\n [584, 1198],\n [575, 1217],\n [573, 1234],\n [575, 1266],\n [596, 1269],\n [598, 1273],\n [594, 1275],\n [593, 1283],\n [593, 1293],\n [598, 1323],\n [598, 1332],\n [597, 1339],\n [591, 1342],\n [588, 1349],\n [590, 1354],\n [609, 1377],\n [616, 1391],\n [620, 1400],\n [619, 1420],\n [615, 1425],\n [609, 1430],\n [600, 1431],\n [583, 1448],\n [571, 1455],\n [559, 1465],\n [553, 1479],\n [554, 1484],\n [550, 1507],\n [551, 1518],\n [555, 1527],\n [568, 1538],\n [576, 1548],\n [586, 1551],\n [616, 1591],\n [620, 1602],\n [624, 1623],\n [624, 1640],\n [627, 1645],\n [643, 1653],\n [654, 1678],\n [659, 1680],\n [668, 1677],\n [670, 1679],\n [670, 1697],\n [675, 1718],\n [682, 1733],\n [691, 1761],\n [691, 1766],\n [696, 1775],\n [707, 1781],\n [714, 1789],\n [712, 1791],\n [723, 1803],\n [734, 1808],\n [748, 1809],\n [757, 1813],\n [764, 1812],\n [769, 1817],\n [772, 1816],\n [774, 1810],\n [771, 1804],\n [775, 1803],\n [777, 1807],\n [779, 1807],\n [782, 1799],\n [781, 1794],\n [783, 1789],\n [785, 1788],\n [788, 1791],\n [790, 1790],\n [785, 1781],\n [782, 1779],\n [779, 1783],\n [776, 1782],\n [774, 1771],\n [778, 1766],\n [784, 1766],\n [783, 1762],\n [785, 1759],\n [787, 1762],\n [788, 1767],\n [798, 1765],\n [794, 1760],\n [797, 1759],\n [797, 1756],\n [794, 1754],\n [791, 1756],\n [789, 1753],\n [794, 1749],\n [793, 1747],\n [796, 1745],\n [796, 1749],\n [800, 1749],\n [800, 1746],\n [802, 1747],\n [801, 1742],\n [803, 1739],\n [812, 1742],\n [817, 1735],\n [815, 1732],\n [817, 1729],\n [815, 1727],\n [819, 1727],\n [816, 1725],\n [821, 1717],\n [817, 1704],\n [817, 1700],\n [822, 1696],\n [823, 1697],\n [828, 1692],\n [831, 1692],\n [832, 1689],\n [831, 1684],\n [836, 1681],\n [839, 1682],\n [840, 1675],\n [856, 1671],\n [859, 1664],\n [868, 1659],\n [870, 1660],\n [876, 1658],\n [878, 1651],\n [883, 1648],\n [884, 1644],\n [891, 1643],\n [889, 1639],\n [890, 1631],\n [895, 1631],\n [898, 1634],\n [907, 1634],\n [909, 1632],\n [907, 1629],\n [912, 1627],\n [914, 1630],\n [918, 1631],\n [938, 1621],\n [948, 1612],\n [961, 1615],\n [963, 1613],\n [972, 1617],\n [984, 1616],\n [991, 1611],\n [996, 1610],\n [999, 1606],\n [1000, 1592],\n [1004, 1584],\n [1026, 1578],\n [1039, 1581],\n [1044, 1578],\n [1075, 1588],\n [1275, 1402],\n [1269, 1387],\n [1268, 1378],\n [1264, 1373],\n [1257, 1357],\n [1262, 1348],\n [1260, 1340],\n [1260, 1334],\n [1262, 1334],\n [1258, 1324],\n [1260, 1318],\n [1256, 1309],\n [1256, 1303],\n [1253, 1299],\n [1252, 1291],\n [1253, 1289],\n [1252, 1283],\n [1253, 1282],\n [1249, 1275],\n [1251, 1267],\n [1254, 1264],\n [1257, 1257],\n [1256, 1251],\n [1259, 1245],\n [1257, 1239],\n [1254, 1237],\n [1253, 1229],\n [1251, 1229],\n [1252, 1227],\n [1250, 1224],\n [1251, 1222],\n [1248, 1219],\n [1247, 1215],\n [1249, 1212],\n [1247, 1211],\n [1246, 1206],\n [1248, 1202],\n [1243, 1192],\n [1178, 1249],\n [1016, 1384],\n [1003, 1367],\n [902, 1257],\n [891, 1251],\n [867, 1255],\n [855, 1230],\n [854, 1219],\n [851, 1216],\n [845, 1216],\n [844, 1212],\n [836, 1205],\n [836, 1201],\n [829, 1191],\n [820, 1188],\n [813, 1192],\n [813, 1194],\n [802, 1193],\n [783, 1201],\n [771, 1194],\n [761, 1182],\n [762, 1179],\n [768, 1178],\n [766, 1169],\n [769, 1165],\n [765, 1159],\n [760, 1160],\n [759, 1156],\n [762, 1153],\n [768, 1157],\n [769, 1153],\n [766, 1147],\n [764, 1147],\n [760, 1149],\n [755, 1145],\n [757, 1140],\n [762, 1138],\n [765, 1142],\n [768, 1141],\n [768, 1135],\n [771, 1131],\n [770, 1129],\n [766, 1128],\n [765, 1126],\n [766, 1122],\n [761, 1121],\n [762, 1114],\n [756, 1113],\n [755, 1105],\n [760, 1100],\n [762, 1093],\n [764, 1092],\n [766, 1094],\n [767, 1100],\n [775, 1098],\n [775, 1094],\n [773, 1091],\n [767, 1086],\n [767, 1084],\n [772, 1080],\n [772, 1076],\n [769, 1074],\n [763, 1074],\n [758, 1076],\n [757, 1071],\n [761, 1066],\n [758, 1062],\n [758, 1060],\n [763, 1054]]],\n [[[763, 1054],\n [765, 1055],\n [767, 1060],\n [770, 1060],\n [772, 1050],\n [763, 1054]]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '033',\n 'FIPS': '34033',\n 'STATE': 'NJ',\n 'NAME': 'Salem',\n 'LSAD': 'County'},\n 'id': 2131,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1275, 1402],\n [1575, 1130],\n [1573, 1127],\n [1574, 1123],\n [1579, 1112],\n [1580, 1106],\n [1577, 1095],\n [1572, 1086],\n [1573, 1080],\n [1572, 1075],\n [1580, 1055],\n [1581, 1041],\n [1579, 1031],\n [1569, 1012],\n [1570, 1000],\n [1564, 981],\n [1564, 974],\n [1562, 968],\n [1563, 948],\n [1568, 940],\n [1563, 941],\n [1557, 929],\n [1549, 919],\n [1537, 911],\n [1515, 773],\n [1490, 757],\n [1479, 754],\n [1476, 743],\n [1474, 742],\n [1476, 740],\n [1474, 739],\n [1473, 735],\n [1475, 732],\n [1474, 730],\n [1476, 727],\n [1473, 727],\n [1473, 724],\n [1477, 722],\n [1476, 719],\n [1479, 717],\n [1474, 710],\n [1473, 701],\n [1475, 698],\n [1483, 700],\n [1484, 697],\n [1478, 691],\n [1478, 686],\n [1480, 685],\n [1481, 687],\n [1491, 685],\n [1488, 679],\n [1490, 673],\n [1493, 672],\n [1492, 669],\n [1493, 667],\n [1489, 665],\n [1420, 689],\n [1400, 693],\n [1368, 691],\n [1327, 695],\n [1323, 697],\n [1325, 706],\n [1331, 712],\n [1331, 716],\n [1327, 726],\n [1313, 736],\n [1305, 737],\n [1296, 729],\n [1289, 733],\n [1274, 733],\n [1240, 723],\n [1218, 729],\n [1209, 729],\n [1198, 722],\n [1180, 688],\n [1166, 669],\n [1163, 670],\n [1124, 711],\n [1125, 739],\n [1117, 772],\n [1107, 788],\n [1099, 795],\n [1086, 793],\n [1079, 813],\n [1066, 825],\n [1056, 825],\n [1013, 847],\n [1010, 853],\n [1012, 859],\n [1009, 869],\n [999, 895],\n [970, 903],\n [954, 894],\n [950, 881],\n [945, 876],\n [920, 899],\n [906, 915],\n [889, 957],\n [888, 969],\n [879, 981],\n [867, 987],\n [848, 985],\n [834, 973],\n [797, 1005],\n [791, 1015],\n [790, 1030],\n [784, 1045],\n [772, 1050],\n [770, 1060],\n [767, 1060],\n [765, 1055],\n [763, 1054],\n [758, 1060],\n [758, 1062],\n [761, 1066],\n [757, 1071],\n [757, 1075],\n [758, 1076],\n [763, 1074],\n [769, 1074],\n [772, 1076],\n [772, 1080],\n [767, 1084],\n [767, 1086],\n [773, 1091],\n [775, 1094],\n [775, 1098],\n [767, 1100],\n [766, 1094],\n [764, 1092],\n [762, 1093],\n [760, 1100],\n [755, 1103],\n [754, 1109],\n [756, 1113],\n [762, 1114],\n [761, 1121],\n [766, 1122],\n [765, 1126],\n [766, 1128],\n [770, 1129],\n [771, 1131],\n [768, 1135],\n [768, 1141],\n [765, 1142],\n [762, 1138],\n [757, 1140],\n [755, 1145],\n [760, 1149],\n [764, 1147],\n [766, 1147],\n [769, 1153],\n [768, 1157],\n [762, 1153],\n [759, 1156],\n [760, 1160],\n [765, 1159],\n [769, 1165],\n [766, 1169],\n [768, 1178],\n [762, 1179],\n [761, 1182],\n [771, 1194],\n [783, 1201],\n [802, 1193],\n [813, 1194],\n [813, 1192],\n [820, 1188],\n [829, 1191],\n [836, 1201],\n [836, 1205],\n [844, 1212],\n [845, 1216],\n [851, 1216],\n [854, 1219],\n [855, 1230],\n [867, 1255],\n [891, 1251],\n [902, 1257],\n [1003, 1367],\n [1016, 1384],\n [1178, 1249],\n [1243, 1192],\n [1248, 1202],\n [1246, 1206],\n [1247, 1211],\n [1249, 1212],\n [1247, 1215],\n [1248, 1219],\n [1251, 1222],\n [1250, 1224],\n [1252, 1227],\n [1251, 1229],\n [1253, 1229],\n [1254, 1237],\n [1257, 1239],\n [1259, 1245],\n [1256, 1251],\n [1257, 1257],\n [1254, 1264],\n [1251, 1267],\n [1249, 1275],\n [1253, 1282],\n [1252, 1283],\n [1253, 1289],\n [1252, 1291],\n [1253, 1299],\n [1256, 1303],\n [1256, 1309],\n [1260, 1318],\n [1258, 1324],\n [1262, 1334],\n [1260, 1334],\n [1260, 1340],\n [1262, 1348],\n [1257, 1357],\n [1264, 1373],\n [1268, 1378],\n [1269, 1387],\n [1275, 1402]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '011',\n 'FIPS': '34011',\n 'STATE': 'NJ',\n 'NAME': 'Cumberland',\n 'LSAD': 'County'},\n 'id': 2133,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1563, 941],\n [1568, 940],\n [1568, 932],\n [1576, 930],\n [1582, 926],\n [1602, 924],\n [1611, 916],\n [1620, 905],\n [1627, 909],\n [1629, 907],\n [1643, 907],\n [1646, 904],\n [1651, 909],\n [1656, 911],\n [1659, 908],\n [1664, 908],\n [1666, 905],\n [1671, 909],\n [1676, 908],\n [1679, 909],\n [1689, 902],\n [1687, 894],\n [1688, 894],\n [1696, 897],\n [1699, 894],\n [1706, 893],\n [1709, 889],\n [1720, 894],\n [1725, 891],\n [1726, 886],\n [1735, 885],\n [1744, 888],\n [1748, 893],\n [1758, 892],\n [1764, 888],\n [1773, 893],\n [1775, 892],\n [1773, 886],\n [1778, 881],\n [1782, 880],\n [1785, 886],\n [1788, 886],\n [1789, 880],\n [1791, 878],\n [1794, 884],\n [1798, 885],\n [1802, 878],\n [1805, 875],\n [1810, 879],\n [1816, 878],\n [1820, 883],\n [1824, 885],\n [1828, 883],\n [1831, 884],\n [1835, 890],\n [1837, 890],\n [1840, 887],\n [1840, 883],\n [1838, 877],\n [1840, 875],\n [1843, 876],\n [1845, 881],\n [1845, 889],\n [1849, 891],\n [1864, 890],\n [1862, 879],\n [1863, 877],\n [1870, 874],\n [1875, 875],\n [1879, 882],\n [1885, 887],\n [1889, 888],\n [1900, 886],\n [1913, 881],\n [1918, 882],\n [1960, 913],\n [1967, 914],\n [1994, 894],\n [2011, 893],\n [2023, 900],\n [2031, 899],\n [2033, 896],\n [2019, 883],\n [2016, 871],\n [2005, 855],\n [1976, 841],\n [1951, 818],\n [1927, 791],\n [1895, 747],\n [1880, 730],\n [1873, 705],\n [1844, 669],\n [1781, 557],\n [1780, 551],\n [1782, 547],\n [1796, 534],\n [1794, 525],\n [1746, 472],\n [1688, 375],\n [1676, 332],\n [1667, 317],\n [1645, 306],\n [1628, 293],\n [1583, 247],\n [1563, 220],\n [1562, 222],\n [1554, 226],\n [1537, 223],\n [1501, 205],\n [1481, 199],\n [1462, 198],\n [1419, 203],\n [1413, 207],\n [1406, 220],\n [1430, 334],\n [1439, 361],\n [1455, 398],\n [1506, 496],\n [1514, 517],\n [1522, 544],\n [1531, 601],\n [1530, 630],\n [1503, 660],\n [1489, 665],\n [1493, 667],\n [1492, 669],\n [1493, 672],\n [1490, 673],\n [1488, 679],\n [1491, 685],\n [1481, 687],\n [1480, 685],\n [1478, 686],\n [1478, 691],\n [1484, 697],\n [1482, 701],\n [1475, 698],\n [1473, 701],\n [1474, 710],\n [1479, 717],\n [1476, 719],\n [1477, 722],\n [1473, 724],\n [1473, 727],\n [1476, 727],\n [1474, 730],\n [1475, 732],\n [1473, 735],\n [1474, 739],\n [1476, 740],\n [1474, 742],\n [1476, 743],\n [1479, 754],\n [1490, 757],\n [1515, 773],\n [1537, 911],\n [1549, 919],\n [1557, 929],\n [1563, 941]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '009',\n 'FIPS': '34009',\n 'STATE': 'NJ',\n 'NAME': 'Cape May',\n 'LSAD': 'County'},\n 'id': 2134,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2885, 4176],\n [2960, 4130],\n [2976, 4129],\n [2972, 4096],\n [2941, 3976],\n [2925, 3922],\n [2889, 3842],\n [2875, 3801],\n [2844, 3745],\n [2831, 3752],\n [2832, 3760],\n [2803, 3780],\n [2803, 3784],\n [2806, 3789],\n [2802, 3794],\n [2797, 3795],\n [2792, 3789],\n [2792, 3785],\n [2788, 3785],\n [2787, 3781],\n [2785, 3780],\n [2786, 3778],\n [2781, 3781],\n [2780, 3774],\n [2775, 3774],\n [2769, 3765],\n [2742, 3763],\n [2733, 3760],\n [2727, 3755],\n [2722, 3747],\n [2718, 3744],\n [2710, 3742],\n [2708, 3736],\n [2692, 3711],\n [2690, 3703],\n [2693, 3687],\n [2690, 3677],\n [2686, 3679],\n [2683, 3677],\n [2677, 3687],\n [2679, 3692],\n [2675, 3703],\n [2672, 3702],\n [2669, 3705],\n [2662, 3706],\n [2657, 3703],\n [2653, 3694],\n [2650, 3692],\n [2640, 3696],\n [2636, 3695],\n [2633, 3699],\n [2632, 3707],\n [2607, 3723],\n [2619, 3744],\n [2620, 3749],\n [2618, 3760],\n [2623, 3784],\n [2638, 3790],\n [2641, 3793],\n [2643, 3799],\n [2645, 3808],\n [2641, 3826],\n [2644, 3833],\n [2648, 3849],\n [2647, 3855],\n [2649, 3864],\n [2656, 3864],\n [2659, 3857],\n [2661, 3856],\n [2665, 3860],\n [2666, 3864],\n [2665, 3871],\n [2661, 3874],\n [2656, 3885],\n [2654, 3892],\n [2646, 3904],\n [2632, 3915],\n [2634, 3930],\n [2627, 3941],\n [2630, 3956],\n [2628, 3967],\n [2633, 3981],\n [2630, 3985],\n [2619, 3987],\n [2616, 3992],\n [2615, 3999],\n [2619, 4007],\n [2612, 4020],\n [2606, 4022],\n [2616, 4070],\n [2583, 4096],\n [2574, 4104],\n [2573, 4096],\n [2573, 4090],\n [2518, 4096],\n [2494, 4099],\n [2460, 4120],\n [2437, 4158],\n [2425, 4170],\n [2427, 4176],\n [2885, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '003',\n 'FIPS': '34003',\n 'STATE': 'NJ',\n 'NAME': 'Bergen',\n 'LSAD': 'County'},\n 'id': 2229,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2234, 4176],\n [2235, 4173],\n [2240, 4168],\n [2251, 4165],\n [2255, 4159],\n [2259, 4157],\n [2265, 4162],\n [2274, 4161],\n [2281, 4157],\n [2292, 4157],\n [2293, 4160],\n [2298, 4162],\n [2306, 4160],\n [2310, 4156],\n [2310, 4144],\n [2318, 4144],\n [2324, 4148],\n [2325, 4146],\n [2329, 4148],\n [2334, 4147],\n [2334, 4142],\n [2341, 4140],\n [2344, 4137],\n [2363, 4139],\n [2370, 4132],\n [2382, 4133],\n [2384, 4140],\n [2387, 4140],\n [2388, 4136],\n [2388, 4128],\n [2391, 4125],\n [2391, 4117],\n [2396, 4111],\n [2389, 4103],\n [2389, 4096],\n [2392, 4094],\n [2399, 4096],\n [2407, 4094],\n [2412, 4096],\n [2412, 4090],\n [2418, 4085],\n [2417, 4079],\n [2413, 4081],\n [2412, 4075],\n [2413, 4071],\n [2415, 4069],\n [2414, 4053],\n [2418, 4047],\n [2415, 4025],\n [2407, 4002],\n [2401, 3997],\n [2402, 3995],\n [2402, 3989],\n [2407, 3986],\n [2405, 3984],\n [2408, 3980],\n [2408, 3975],\n [2416, 3978],\n [2416, 3973],\n [2426, 3972],\n [2433, 3959],\n [2432, 3957],\n [2430, 3957],\n [2426, 3962],\n [2426, 3956],\n [2424, 3953],\n [2427, 3948],\n [2424, 3942],\n [2425, 3938],\n [2427, 3936],\n [2413, 3937],\n [2411, 3934],\n [2407, 3933],\n [2408, 3938],\n [2404, 3940],\n [2411, 3942],\n [2411, 3944],\n [2406, 3945],\n [2400, 3941],\n [2397, 3942],\n [2396, 3944],\n [2397, 3945],\n [2396, 3948],\n [2397, 3953],\n [2390, 3954],\n [2380, 3950],\n [2381, 3956],\n [2376, 3952],\n [2370, 3951],\n [2367, 3952],\n [2364, 3949],\n [2358, 3948],\n [2358, 3957],\n [2355, 3957],\n [2353, 3955],\n [2351, 3956],\n [2352, 3959],\n [2347, 3958],\n [2345, 3956],\n [2348, 3956],\n [2348, 3953],\n [2344, 3953],\n [2342, 3959],\n [2340, 3955],\n [2335, 3952],\n [2330, 3939],\n [2335, 3937],\n [2335, 3935],\n [2331, 3935],\n [2328, 3931],\n [2333, 3929],\n [2328, 3927],\n [2327, 3923],\n [2329, 3918],\n [2326, 3902],\n [2330, 3902],\n [2330, 3900],\n [2334, 3898],\n [2330, 3894],\n [2335, 3891],\n [2330, 3888],\n [2332, 3885],\n [2329, 3884],\n [2331, 3878],\n [2337, 3883],\n [2339, 3878],\n [2347, 3877],\n [2353, 3873],\n [2356, 3876],\n [2359, 3874],\n [2358, 3871],\n [2354, 3869],\n [2357, 3861],\n [2349, 3861],\n [2347, 3858],\n [2348, 3854],\n [2346, 3851],\n [2345, 3847],\n [2341, 3844],\n [2343, 3835],\n [2347, 3834],\n [2345, 3830],\n [2342, 3829],\n [2341, 3816],\n [2343, 3811],\n [2334, 3803],\n [2338, 3792],\n [2335, 3785],\n [2327, 3774],\n [2320, 3776],\n [2313, 3774],\n [2311, 3763],\n [2303, 3757],\n [2298, 3749],\n [2291, 3748],\n [2291, 3742],\n [2288, 3739],\n [2287, 3711],\n [2279, 3701],\n [2282, 3700],\n [2284, 3694],\n [2280, 3687],\n [2283, 3686],\n [2282, 3680],\n [2278, 3678],\n [2274, 3678],\n [2272, 3675],\n [2275, 3674],\n [2277, 3669],\n [2287, 3675],\n [2286, 3669],\n [2291, 3668],\n [2295, 3665],\n [2301, 3665],\n [2301, 3662],\n [2298, 3655],\n [2287, 3652],\n [2286, 3650],\n [2291, 3646],\n [2288, 3642],\n [2279, 3637],\n [2282, 3626],\n [2280, 3622],\n [2276, 3622],\n [2272, 3623],\n [2268, 3621],\n [2272, 3615],\n [2262, 3604],\n [2258, 3607],\n [2253, 3606],\n [2253, 3592],\n [2246, 3590],\n [2236, 3592],\n [2228, 3583],\n [2227, 3577],\n [2222, 3577],\n [2217, 3568],\n [2200, 3560],\n [2197, 3555],\n [2194, 3545],\n [2189, 3545],\n [2184, 3542],\n [2181, 3537],\n [2169, 3529],\n [2164, 3529],\n [2162, 3524],\n [2164, 3518],\n [2162, 3514],\n [2163, 3512],\n [2160, 3507],\n [2154, 3508],\n [2150, 3506],\n [2149, 3503],\n [2137, 3496],\n [2133, 3498],\n [2128, 3493],\n [2116, 3491],\n [2117, 3487],\n [2114, 3485],\n [2112, 3488],\n [2106, 3487],\n [2106, 3484],\n [2102, 3482],\n [2100, 3484],\n [2094, 3483],\n [2095, 3479],\n [2091, 3478],\n [2088, 3480],\n [2085, 3477],\n [2086, 3475],\n [2082, 3466],\n [2075, 3460],\n [2071, 3463],\n [2070, 3462],\n [2061, 3461],\n [2060, 3464],\n [2055, 3465],\n [2059, 3469],\n [2056, 3469],\n [2054, 3479],\n [2056, 3482],\n [2051, 3486],\n [2050, 3490],\n [2044, 3491],\n [2043, 3494],\n [2050, 3495],\n [2051, 3498],\n [2056, 3501],\n [2052, 3521],\n [2053, 3527],\n [2055, 3529],\n [2058, 3537],\n [2062, 3535],\n [2069, 3537],\n [2069, 3539],\n [2066, 3541],\n [2067, 3545],\n [2070, 3544],\n [2073, 3547],\n [2073, 3559],\n [2071, 3561],\n [2069, 3561],\n [2068, 3568],\n [2061, 3574],\n [2056, 3575],\n [2054, 3578],\n [2057, 3581],\n [2058, 3587],\n [2052, 3591],\n [2051, 3594],\n [2048, 3595],\n [2048, 3598],\n [2052, 3611],\n [2046, 3617],\n [2043, 3616],\n [2035, 3622],\n [2028, 3621],\n [2026, 3623],\n [2022, 3622],\n [2023, 3646],\n [2017, 3652],\n [2019, 3655],\n [2018, 3663],\n [2012, 3669],\n [1848, 3621],\n [1765, 3594],\n [1755, 3595],\n [1740, 3603],\n [1740, 3607],\n [1725, 3617],\n [1704, 3625],\n [1666, 3634],\n [1659, 3638],\n [1644, 3636],\n [1638, 3636],\n [1638, 3638],\n [1633, 3639],\n [1622, 3639],\n [1617, 3642],\n [1594, 3665],\n [1592, 3670],\n [1526, 3726],\n [1532, 3732],\n [1535, 3733],\n [1539, 3731],\n [1546, 3737],\n [1558, 3742],\n [1562, 3755],\n [1567, 3756],\n [1569, 3752],\n [1576, 3756],\n [1583, 3757],\n [1588, 3765],\n [1589, 3771],\n [1592, 3771],\n [1598, 3780],\n [1606, 3786],\n [1606, 3792],\n [1613, 3799],\n [1611, 3804],\n [1617, 3812],\n [1630, 3821],\n [1627, 3829],\n [1622, 3833],\n [1625, 3843],\n [1627, 3845],\n [1637, 3849],\n [1642, 3855],\n [1643, 3865],\n [1652, 3869],\n [1654, 3874],\n [1646, 3880],\n [1645, 3883],\n [1648, 3894],\n [1647, 3908],\n [1651, 3914],\n [1668, 3923],\n [1671, 3928],\n [1671, 3935],\n [1681, 3943],\n [1687, 3954],\n [1691, 3952],\n [1706, 3966],\n [1708, 3965],\n [1711, 3968],\n [1713, 3965],\n [1715, 3968],\n [1722, 3968],\n [1727, 3972],\n [1727, 3976],\n [1734, 3980],\n [1749, 3979],\n [1756, 3982],\n [1761, 3974],\n [1769, 3970],\n [1783, 3958],\n [1785, 3951],\n [1782, 3942],\n [1786, 3940],\n [1792, 3945],\n [1796, 3946],\n [1808, 3954],\n [1814, 3951],\n [1818, 3952],\n [1820, 3959],\n [1827, 3961],\n [1832, 3960],\n [1846, 3969],\n [1855, 3977],\n [1871, 3984],\n [1872, 3987],\n [1870, 3992],\n [1871, 3998],\n [1873, 4001],\n [1889, 4014],\n [1897, 4029],\n [1897, 4040],\n [1888, 4056],\n [1889, 4068],\n [1910, 4096],\n [1938, 4131],\n [1978, 4176],\n [2190, 4176],\n [2189, 4174],\n [2196, 4170],\n [2202, 4172],\n [2203, 4176],\n [2234, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '027',\n 'FIPS': '34027',\n 'STATE': 'NJ',\n 'NAME': 'Morris',\n 'LSAD': 'County'},\n 'id': 2228,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2352, 3959],\n [2351, 3956],\n [2353, 3955],\n [2355, 3957],\n [2358, 3957],\n [2358, 3948],\n [2364, 3949],\n [2367, 3952],\n [2370, 3951],\n [2376, 3952],\n [2381, 3956],\n [2380, 3950],\n [2390, 3954],\n [2397, 3953],\n [2396, 3948],\n [2397, 3945],\n [2396, 3944],\n [2397, 3942],\n [2400, 3941],\n [2406, 3945],\n [2411, 3944],\n [2411, 3942],\n [2404, 3940],\n [2408, 3938],\n [2407, 3933],\n [2411, 3934],\n [2413, 3937],\n [2427, 3936],\n [2433, 3931],\n [2437, 3917],\n [2443, 3911],\n [2451, 3909],\n [2453, 3906],\n [2472, 3904],\n [2527, 3864],\n [2529, 3868],\n [2536, 3865],\n [2534, 3860],\n [2632, 3788],\n [2623, 3784],\n [2618, 3760],\n [2620, 3749],\n [2619, 3744],\n [2607, 3723],\n [2606, 3710],\n [2596, 3695],\n [2592, 3684],\n [2584, 3671],\n [2580, 3648],\n [2586, 3626],\n [2589, 3623],\n [2596, 3622],\n [2606, 3623],\n [2610, 3626],\n [2614, 3633],\n [2621, 3637],\n [2643, 3638],\n [2650, 3633],\n [2651, 3629],\n [2645, 3598],\n [2647, 3588],\n [2657, 3563],\n [2623, 3508],\n [2620, 3507],\n [2588, 3532],\n [2587, 3536],\n [2585, 3537],\n [2577, 3538],\n [2575, 3535],\n [2528, 3532],\n [2516, 3540],\n [2511, 3558],\n [2498, 3571],\n [2448, 3583],\n [2406, 3600],\n [2398, 3598],\n [2393, 3595],\n [2392, 3591],\n [2386, 3584],\n [2382, 3583],\n [2368, 3587],\n [2361, 3594],\n [2354, 3594],\n [2348, 3590],\n [2323, 3606],\n [2304, 3624],\n [2280, 3633],\n [2279, 3637],\n [2288, 3642],\n [2291, 3646],\n [2286, 3650],\n [2287, 3652],\n [2298, 3655],\n [2301, 3662],\n [2301, 3665],\n [2295, 3665],\n [2291, 3668],\n [2286, 3669],\n [2287, 3675],\n [2277, 3669],\n [2275, 3674],\n [2272, 3675],\n [2274, 3678],\n [2278, 3678],\n [2282, 3680],\n [2283, 3686],\n [2280, 3687],\n [2284, 3694],\n [2282, 3700],\n [2279, 3701],\n [2287, 3711],\n [2288, 3739],\n [2291, 3742],\n [2291, 3748],\n [2298, 3749],\n [2303, 3757],\n [2311, 3763],\n [2313, 3774],\n [2320, 3776],\n [2327, 3774],\n [2335, 3785],\n [2338, 3792],\n [2334, 3803],\n [2343, 3811],\n [2341, 3816],\n [2342, 3829],\n [2345, 3830],\n [2347, 3834],\n [2343, 3835],\n [2341, 3844],\n [2345, 3847],\n [2346, 3851],\n [2348, 3854],\n [2347, 3858],\n [2349, 3861],\n [2357, 3861],\n [2354, 3869],\n [2358, 3871],\n [2359, 3874],\n [2356, 3876],\n [2353, 3873],\n [2347, 3877],\n [2339, 3878],\n [2337, 3883],\n [2331, 3878],\n [2329, 3884],\n [2332, 3885],\n [2330, 3888],\n [2335, 3891],\n [2330, 3894],\n [2334, 3898],\n [2330, 3900],\n [2330, 3902],\n [2326, 3902],\n [2329, 3918],\n [2327, 3923],\n [2328, 3927],\n [2333, 3929],\n [2328, 3931],\n [2331, 3935],\n [2335, 3935],\n [2335, 3937],\n [2330, 3939],\n [2331, 3942],\n [2335, 3952],\n [2340, 3955],\n [2342, 3959],\n [2344, 3953],\n [2348, 3953],\n [2348, 3956],\n [2345, 3956],\n [2347, 3958],\n [2352, 3959]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '013',\n 'FIPS': '34013',\n 'STATE': 'NJ',\n 'NAME': 'Essex',\n 'LSAD': 'County'},\n 'id': 2206,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2380, 3382],\n [2380, 3379],\n [2378, 3378],\n [2380, 3376],\n [2377, 3366],\n [2385, 3361],\n [2397, 3350],\n [2443, 3360],\n [2446, 3370],\n [2467, 3365],\n [2478, 3370],\n [2484, 3363],\n [2498, 3366],\n [2504, 3368],\n [2508, 3373],\n [2512, 3369],\n [2513, 3362],\n [2521, 3355],\n [2517, 3320],\n [2509, 3290],\n [2504, 3283],\n [2484, 3287],\n [2460, 3269],\n [2456, 3255],\n [2463, 3214],\n [2431, 3172],\n [2425, 3151],\n [2432, 3120],\n [2440, 3106],\n [2477, 3093],\n [2494, 3084],\n [2494, 3083],\n [2497, 3080],\n [2490, 3067],\n [2483, 3068],\n [2481, 3065],\n [2479, 3067],\n [2461, 3042],\n [2461, 3006],\n [2458, 3003],\n [2460, 2996],\n [2455, 2990],\n [2271, 2799],\n [2248, 2769],\n [2249, 2752],\n [2150, 2698],\n [2146, 2700],\n [2116, 2702],\n [2119, 2741],\n [2095, 2754],\n [2091, 2751],\n [2088, 2754],\n [2086, 2754],\n [2077, 2759],\n [2072, 2766],\n [2061, 2765],\n [2061, 2772],\n [2052, 2780],\n [2045, 2776],\n [2040, 2771],\n [2036, 2769],\n [2032, 2775],\n [2028, 2777],\n [2027, 2775],\n [2021, 2773],\n [2016, 2773],\n [2010, 2776],\n [2007, 2783],\n [1999, 2785],\n [1995, 2791],\n [1991, 2790],\n [1983, 2797],\n [1976, 2799],\n [1972, 2803],\n [1969, 2816],\n [1957, 2820],\n [1953, 2819],\n [1947, 2821],\n [1943, 2832],\n [1936, 2833],\n [1925, 2839],\n [1916, 2838],\n [1915, 2840],\n [1915, 2845],\n [1912, 2847],\n [1914, 2857],\n [1904, 2869],\n [1904, 2878],\n [1912, 2916],\n [1916, 2919],\n [1919, 2933],\n [1928, 2936],\n [1944, 2945],\n [1955, 2956],\n [1960, 2967],\n [1966, 2971],\n [1966, 2981],\n [1969, 2989],\n [1969, 3016],\n [1983, 3025],\n [2013, 3040],\n [2041, 3056],\n [2055, 3068],\n [2064, 3080],\n [2075, 3091],\n [2082, 3099],\n [2102, 3115],\n [2119, 3139],\n [2125, 3143],\n [2140, 3148],\n [2152, 3156],\n [2153, 3166],\n [2151, 3171],\n [2143, 3178],\n [2141, 3190],\n [2122, 3195],\n [2119, 3197],\n [2100, 3230],\n [2083, 3244],\n [2072, 3255],\n [2068, 3273],\n [2065, 3280],\n [2059, 3286],\n [2054, 3288],\n [2057, 3292],\n [2056, 3305],\n [2059, 3307],\n [2063, 3315],\n [2071, 3326],\n [2070, 3329],\n [2076, 3331],\n [2086, 3339],\n [2091, 3334],\n [2091, 3337],\n [2094, 3340],\n [2097, 3338],\n [2104, 3341],\n [2108, 3340],\n [2118, 3345],\n [2126, 3354],\n [2137, 3362],\n [2143, 3362],\n [2147, 3364],\n [2154, 3355],\n [2156, 3357],\n [2203, 3362],\n [2279, 3370],\n [2380, 3382]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '023',\n 'FIPS': '34023',\n 'STATE': 'NJ',\n 'NAME': 'Middlesex',\n 'LSAD': 'County'},\n 'id': 2194,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2280, 3633],\n [2304, 3624],\n [2323, 3606],\n [2348, 3590],\n [2354, 3594],\n [2361, 3594],\n [2368, 3587],\n [2382, 3583],\n [2386, 3584],\n [2392, 3591],\n [2393, 3595],\n [2398, 3598],\n [2406, 3600],\n [2448, 3583],\n [2498, 3571],\n [2511, 3558],\n [2516, 3540],\n [2528, 3532],\n [2575, 3535],\n [2577, 3538],\n [2585, 3537],\n [2587, 3536],\n [2588, 3532],\n [2620, 3507],\n [2623, 3508],\n [2587, 3450],\n [2558, 3454],\n [2551, 3454],\n [2527, 3425],\n [2521, 3355],\n [2513, 3362],\n [2512, 3369],\n [2508, 3373],\n [2504, 3368],\n [2498, 3366],\n [2484, 3363],\n [2478, 3370],\n [2467, 3365],\n [2446, 3370],\n [2443, 3360],\n [2397, 3350],\n [2385, 3361],\n [2377, 3366],\n [2380, 3376],\n [2378, 3378],\n [2380, 3379],\n [2380, 3382],\n [2279, 3370],\n [2203, 3362],\n [2156, 3357],\n [2154, 3355],\n [2147, 3364],\n [2152, 3365],\n [2159, 3373],\n [2164, 3373],\n [2169, 3381],\n [2173, 3383],\n [2184, 3385],\n [2193, 3396],\n [2199, 3397],\n [2203, 3402],\n [2206, 3414],\n [2213, 3418],\n [2217, 3429],\n [2219, 3444],\n [2220, 3446],\n [2226, 3447],\n [2229, 3455],\n [2229, 3462],\n [2235, 3473],\n [2235, 3481],\n [2236, 3487],\n [2234, 3493],\n [2231, 3495],\n [2224, 3491],\n [2220, 3485],\n [2214, 3483],\n [2211, 3478],\n [2208, 3477],\n [2202, 3485],\n [2198, 3487],\n [2196, 3491],\n [2185, 3486],\n [2175, 3475],\n [2164, 3469],\n [2150, 3506],\n [2154, 3508],\n [2160, 3507],\n [2163, 3512],\n [2162, 3514],\n [2164, 3518],\n [2162, 3524],\n [2164, 3529],\n [2169, 3529],\n [2181, 3537],\n [2184, 3542],\n [2189, 3545],\n [2194, 3545],\n [2197, 3555],\n [2200, 3560],\n [2217, 3568],\n [2222, 3577],\n [2227, 3577],\n [2228, 3583],\n [2236, 3592],\n [2246, 3590],\n [2253, 3592],\n [2253, 3606],\n [2258, 3607],\n [2262, 3604],\n [2272, 3615],\n [2268, 3621],\n [2272, 3623],\n [2276, 3622],\n [2280, 3622],\n [2282, 3626],\n [2280, 3633]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '039',\n 'FIPS': '34039',\n 'STATE': 'NJ',\n 'NAME': 'Union',\n 'LSAD': 'County'},\n 'id': 2204,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[2941, 3976],\n [3026, 3943],\n [3031, 3954],\n [3034, 3955],\n [3033, 3958],\n [3036, 3961],\n [3039, 3961],\n [3036, 3956],\n [3042, 3955],\n [3048, 3952],\n [3048, 3950],\n [3053, 3950],\n [3057, 3931],\n [3078, 3922],\n [3079, 3925],\n [3137, 3905],\n [3135, 3901],\n [3134, 3882],\n [3129, 3862],\n [3130, 3854],\n [3135, 3849],\n [3139, 3841],\n [3140, 3824],\n [3136, 3820],\n [3130, 3830],\n [3126, 3839],\n [3128, 3848],\n [3124, 3857],\n [3122, 3856],\n [3117, 3850],\n [3114, 3842],\n [3103, 3845],\n [3094, 3839],\n [3090, 3817],\n [3091, 3809],\n [3096, 3798],\n [3106, 3785],\n [3117, 3779],\n [3133, 3751],\n [3131, 3749],\n [3122, 3749],\n [3097, 3759],\n [3088, 3760],\n [3074, 3755],\n [3038, 3749],\n [3022, 3744],\n [3011, 3732],\n [3010, 3724],\n [2998, 3722],\n [2982, 3725],\n [2981, 3730],\n [2949, 3742],\n [2940, 3748],\n [2935, 3754],\n [2928, 3754],\n [2921, 3765],\n [2920, 3772],\n [2919, 3818],\n [2926, 3837],\n [2938, 3859],\n [2952, 3877],\n [2957, 3891],\n [2957, 3896],\n [2953, 3901],\n [2946, 3897],\n [2945, 3895],\n [2938, 3897],\n [2936, 3900],\n [2919, 3907],\n [2925, 3922],\n [2941, 3976]]],\n [[[3156, 3866],\n [3162, 3860],\n [3164, 3854],\n [3162, 3836],\n [3157, 3835],\n [3152, 3842],\n [3152, 3864],\n [3156, 3866]]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '005',\n 'FIPS': '36005',\n 'STATE': 'NY',\n 'NAME': 'Bronx',\n 'LSAD': 'County'},\n 'id': 2199,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2797, 3795],\n [2802, 3794],\n [2806, 3789],\n [2803, 3784],\n [2803, 3780],\n [2832, 3760],\n [2831, 3752],\n [2844, 3745],\n [2802, 3666],\n [2786, 3575],\n [2766, 3578],\n [2747, 3549],\n [2722, 3481],\n [2694, 3468],\n [2685, 3461],\n [2613, 3446],\n [2587, 3450],\n [2657, 3563],\n [2647, 3588],\n [2645, 3598],\n [2651, 3629],\n [2650, 3633],\n [2643, 3638],\n [2621, 3637],\n [2614, 3633],\n [2610, 3626],\n [2606, 3623],\n [2596, 3622],\n [2589, 3623],\n [2583, 3633],\n [2580, 3648],\n [2582, 3665],\n [2586, 3675],\n [2592, 3684],\n [2596, 3695],\n [2606, 3710],\n [2607, 3723],\n [2632, 3707],\n [2633, 3699],\n [2636, 3695],\n [2640, 3696],\n [2650, 3692],\n [2653, 3694],\n [2657, 3703],\n [2662, 3706],\n [2669, 3705],\n [2672, 3702],\n [2675, 3703],\n [2679, 3692],\n [2677, 3687],\n [2683, 3677],\n [2686, 3679],\n [2690, 3677],\n [2693, 3687],\n [2690, 3703],\n [2692, 3711],\n [2708, 3736],\n [2710, 3742],\n [2718, 3744],\n [2722, 3747],\n [2727, 3755],\n [2733, 3760],\n [2742, 3763],\n [2769, 3765],\n [2775, 3774],\n [2780, 3774],\n [2781, 3781],\n [2786, 3778],\n [2785, 3780],\n [2787, 3781],\n [2788, 3785],\n [2792, 3785],\n [2792, 3789],\n [2797, 3795]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '017',\n 'FIPS': '34017',\n 'STATE': 'NJ',\n 'NAME': 'Hudson',\n 'LSAD': 'County'},\n 'id': 2205,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2711, 3458],\n [2720, 3444],\n [2723, 3411],\n [2734, 3388],\n [2744, 3372],\n [2735, 3353],\n [2722, 3335],\n [2689, 3301],\n [2659, 3263],\n [2658, 3265],\n [2644, 3255],\n [2622, 3231],\n [2617, 3238],\n [2611, 3241],\n [2605, 3240],\n [2587, 3225],\n [2562, 3211],\n [2556, 3213],\n [2531, 3196],\n [2515, 3192],\n [2502, 3179],\n [2482, 3176],\n [2462, 3166],\n [2457, 3167],\n [2451, 3178],\n [2449, 3189],\n [2454, 3200],\n [2468, 3214],\n [2470, 3233],\n [2461, 3257],\n [2488, 3279],\n [2506, 3279],\n [2515, 3290],\n [2525, 3345],\n [2535, 3360],\n [2537, 3369],\n [2536, 3396],\n [2529, 3398],\n [2528, 3403],\n [2530, 3426],\n [2546, 3446],\n [2559, 3453],\n [2568, 3452],\n [2574, 3446],\n [2599, 3440],\n [2647, 3446],\n [2696, 3458],\n [2711, 3458]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '085',\n 'FIPS': '36085',\n 'STATE': 'NY',\n 'NAME': 'Richmond',\n 'LSAD': 'County'},\n 'id': 2210,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2891, 3633],\n [2906, 3626],\n [2913, 3615],\n [2925, 3611],\n [2933, 3596],\n [2934, 3590],\n [2932, 3587],\n [2937, 3577],\n [2936, 3574],\n [2950, 3564],\n [2949, 3562],\n [2952, 3559],\n [2950, 3557],\n [2961, 3549],\n [2959, 3546],\n [2966, 3541],\n [2966, 3533],\n [2972, 3523],\n [2976, 3529],\n [2978, 3525],\n [2982, 3528],\n [2983, 3527],\n [3005, 3546],\n [3013, 3548],\n [3017, 3522],\n [3020, 3523],\n [3025, 3502],\n [3029, 3503],\n [3032, 3488],\n [3028, 3487],\n [3029, 3480],\n [3021, 3477],\n [3031, 3460],\n [3033, 3447],\n [3043, 3449],\n [3064, 3420],\n [3063, 3379],\n [3040, 3344],\n [2998, 3317],\n [2911, 3300],\n [2902, 3305],\n [2894, 3314],\n [2834, 3308],\n [2819, 3309],\n [2804, 3316],\n [2803, 3323],\n [2819, 3347],\n [2817, 3357],\n [2806, 3367],\n [2774, 3374],\n [2764, 3390],\n [2760, 3413],\n [2766, 3436],\n [2775, 3454],\n [2795, 3478],\n [2792, 3516],\n [2788, 3521],\n [2793, 3518],\n [2798, 3520],\n [2799, 3522],\n [2816, 3536],\n [2825, 3557],\n [2831, 3566],\n [2852, 3568],\n [2862, 3574],\n [2866, 3570],\n [2866, 3575],\n [2869, 3589],\n [2878, 3603],\n [2876, 3630],\n [2878, 3628],\n [2889, 3633],\n [2891, 3633]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '047',\n 'FIPS': '36047',\n 'STATE': 'NY',\n 'NAME': 'Kings',\n 'LSAD': 'County'},\n 'id': 2203,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[2919, 3907],\n [2936, 3900],\n [2938, 3897],\n [2945, 3895],\n [2946, 3897],\n [2953, 3901],\n [2957, 3896],\n [2957, 3891],\n [2952, 3877],\n [2938, 3859],\n [2926, 3837],\n [2919, 3813],\n [2921, 3765],\n [2928, 3754],\n [2935, 3754],\n [2940, 3748],\n [2949, 3742],\n [2952, 3732],\n [2945, 3727],\n [2941, 3721],\n [2927, 3705],\n [2921, 3708],\n [2918, 3706],\n [2916, 3709],\n [2915, 3705],\n [2911, 3701],\n [2912, 3697],\n [2916, 3695],\n [2915, 3693],\n [2903, 3681],\n [2887, 3656],\n [2882, 3645],\n [2878, 3640],\n [2876, 3630],\n [2878, 3603],\n [2869, 3589],\n [2866, 3575],\n [2866, 3570],\n [2862, 3574],\n [2852, 3568],\n [2830, 3565],\n [2816, 3536],\n [2799, 3522],\n [2798, 3520],\n [2793, 3518],\n [2787, 3523],\n [2786, 3532],\n [2790, 3545],\n [2795, 3548],\n [2797, 3561],\n [2793, 3571],\n [2786, 3575],\n [2802, 3666],\n [2875, 3801],\n [2889, 3842],\n [2919, 3907]]],\n [[[2763, 3558],\n [2766, 3555],\n [2757, 3535],\n [2753, 3539],\n [2763, 3558]]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '061',\n 'FIPS': '36061',\n 'STATE': 'NY',\n 'NAME': 'New York',\n 'LSAD': 'County'},\n 'id': 2200,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[3088, 3760],\n [3097, 3759],\n [3122, 3749],\n [3131, 3749],\n [3133, 3751],\n [3140, 3740],\n [3148, 3740],\n [3159, 3751],\n [3190, 3709],\n [3190, 3712],\n [3228, 3682],\n [3255, 3660],\n [3257, 3655],\n [3258, 3633],\n [3247, 3611],\n [3228, 3606],\n [3214, 3600],\n [3219, 3578],\n [3222, 3558],\n [3220, 3551],\n [3221, 3519],\n [3217, 3493],\n [3222, 3468],\n [3217, 3463],\n [3199, 3457],\n [3198, 3446],\n [3201, 3434],\n [3197, 3432],\n [3198, 3437],\n [3198, 3438],\n [3194, 3438],\n [3193, 3431],\n [3162, 3415],\n [3163, 3394],\n [3171, 3387],\n [3178, 3386],\n [3178, 3384],\n [3189, 3389],\n [3190, 3387],\n [3192, 3388],\n [3203, 3370],\n [3203, 3354],\n [3204, 3352],\n [3195, 3352],\n [3189, 3349],\n [3179, 3339],\n [3181, 3347],\n [3149, 3348],\n [3103, 3336],\n [3063, 3322],\n [2998, 3290],\n [2908, 3256],\n [2909, 3278],\n [2920, 3289],\n [2915, 3298],\n [2911, 3300],\n [2998, 3317],\n [3040, 3344],\n [3063, 3379],\n [3064, 3420],\n [3043, 3449],\n [3033, 3447],\n [3031, 3460],\n [3021, 3477],\n [3029, 3480],\n [3028, 3487],\n [3032, 3488],\n [3029, 3503],\n [3025, 3502],\n [3020, 3523],\n [3017, 3522],\n [3013, 3548],\n [3005, 3546],\n [2983, 3527],\n [2982, 3528],\n [2978, 3525],\n [2976, 3529],\n [2972, 3523],\n [2966, 3533],\n [2966, 3541],\n [2959, 3546],\n [2961, 3549],\n [2950, 3557],\n [2952, 3559],\n [2949, 3562],\n [2950, 3564],\n [2936, 3574],\n [2937, 3577],\n [2932, 3587],\n [2934, 3590],\n [2933, 3596],\n [2925, 3611],\n [2913, 3615],\n [2906, 3626],\n [2891, 3633],\n [2889, 3633],\n [2878, 3628],\n [2876, 3630],\n [2878, 3640],\n [2882, 3645],\n [2887, 3656],\n [2903, 3681],\n [2915, 3693],\n [2916, 3695],\n [2912, 3697],\n [2911, 3701],\n [2915, 3705],\n [2916, 3709],\n [2918, 3706],\n [2921, 3708],\n [2927, 3705],\n [2941, 3721],\n [2945, 3727],\n [2952, 3732],\n [2949, 3742],\n [2981, 3730],\n [2982, 3725],\n [2998, 3722],\n [3010, 3724],\n [3011, 3732],\n [3022, 3744],\n [3038, 3749],\n [3074, 3755],\n [3088, 3760]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '081',\n 'FIPS': '36081',\n 'STATE': 'NY',\n 'NAME': 'Queens',\n 'LSAD': 'County'},\n 'id': 2202,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2801, 3129],\n [2811, 3126],\n [2828, 3114],\n [2853, 3059],\n [2855, 2998],\n [2863, 2928],\n [2863, 2883],\n [2855, 2790],\n [2848, 2752],\n [2831, 2672],\n [2778, 2453],\n [2772, 2416],\n [2775, 2412],\n [2765, 2418],\n [2763, 2417],\n [2757, 2420],\n [2749, 2418],\n [2740, 2412],\n [2738, 2405],\n [2733, 2403],\n [2718, 2389],\n [2700, 2402],\n [2684, 2424],\n [2685, 2431],\n [2683, 2435],\n [2685, 2438],\n [2681, 2448],\n [2682, 2464],\n [2680, 2465],\n [2677, 2463],\n [2670, 2472],\n [2671, 2475],\n [2665, 2480],\n [2603, 2407],\n [2597, 2409],\n [2596, 2413],\n [2592, 2416],\n [2591, 2419],\n [2588, 2423],\n [2571, 2424],\n [2561, 2430],\n [2556, 2429],\n [2551, 2431],\n [2546, 2430],\n [2539, 2433],\n [2533, 2432],\n [2525, 2434],\n [2513, 2429],\n [2503, 2429],\n [2495, 2427],\n [2483, 2429],\n [2480, 2432],\n [2473, 2433],\n [2465, 2445],\n [2467, 2453],\n [2469, 2454],\n [2470, 2466],\n [2458, 2494],\n [2453, 2500],\n [2447, 2503],\n [2443, 2503],\n [2445, 2509],\n [2443, 2518],\n [2446, 2524],\n [2442, 2535],\n [2438, 2540],\n [2229, 2548],\n [2138, 2464],\n [2054, 2391],\n [2016, 2370],\n [1927, 2567],\n [1948, 2575],\n [1952, 2574],\n [1954, 2562],\n [1957, 2562],\n [1968, 2569],\n [1985, 2576],\n [1991, 2573],\n [2002, 2581],\n [2011, 2600],\n [2023, 2615],\n [2025, 2624],\n [2031, 2632],\n [2069, 2652],\n [2120, 2682],\n [2119, 2689],\n [2114, 2695],\n [2112, 2702],\n [2146, 2700],\n [2150, 2698],\n [2249, 2752],\n [2248, 2769],\n [2271, 2799],\n [2460, 2996],\n [2458, 3003],\n [2461, 3006],\n [2461, 3042],\n [2479, 3067],\n [2481, 3065],\n [2483, 3068],\n [2490, 3067],\n [2497, 3080],\n [2494, 3083],\n [2494, 3084],\n [2516, 3073],\n [2521, 3060],\n [2521, 3056],\n [2518, 3054],\n [2520, 3050],\n [2527, 3057],\n [2539, 3061],\n [2548, 3073],\n [2567, 3089],\n [2567, 3076],\n [2564, 3074],\n [2566, 3071],\n [2574, 3079],\n [2578, 3075],\n [2580, 3072],\n [2584, 3075],\n [2592, 3071],\n [2599, 3073],\n [2614, 3079],\n [2620, 3086],\n [2624, 3088],\n [2651, 3070],\n [2693, 3056],\n [2711, 3047],\n [2736, 3026],\n [2752, 3018],\n [2812, 3003],\n [2824, 3003],\n [2828, 3019],\n [2834, 3064],\n [2813, 3106],\n [2796, 3120],\n [2796, 3124],\n [2801, 3129]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '025',\n 'FIPS': '34025',\n 'STATE': 'NJ',\n 'NAME': 'Monmouth',\n 'LSAD': 'County'},\n 'id': 2195,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2229, 2548],\n [2438, 2540],\n [2442, 2535],\n [2446, 2524],\n [2443, 2518],\n [2445, 2509],\n [2443, 2503],\n [2447, 2503],\n [2453, 2500],\n [2458, 2494],\n [2470, 2466],\n [2469, 2454],\n [2467, 2453],\n [2465, 2445],\n [2473, 2433],\n [2480, 2432],\n [2483, 2429],\n [2495, 2427],\n [2503, 2429],\n [2513, 2429],\n [2525, 2434],\n [2533, 2432],\n [2539, 2433],\n [2546, 2430],\n [2551, 2431],\n [2556, 2429],\n [2561, 2430],\n [2571, 2424],\n [2588, 2423],\n [2591, 2419],\n [2592, 2416],\n [2596, 2413],\n [2597, 2409],\n [2603, 2407],\n [2665, 2480],\n [2671, 2475],\n [2670, 2472],\n [2677, 2463],\n [2680, 2465],\n [2682, 2464],\n [2681, 2448],\n [2685, 2438],\n [2683, 2435],\n [2685, 2431],\n [2684, 2424],\n [2700, 2402],\n [2718, 2389],\n [2733, 2403],\n [2738, 2405],\n [2740, 2412],\n [2749, 2418],\n [2757, 2420],\n [2763, 2417],\n [2765, 2418],\n [2776, 2411],\n [2773, 2409],\n [2764, 2374],\n [2728, 2180],\n [2709, 2050],\n [2689, 1840],\n [2680, 1779],\n [2681, 1770],\n [2678, 1763],\n [2656, 1727],\n [2615, 1630],\n [2544, 1509],\n [2471, 1376],\n [2459, 1363],\n [2418, 1299],\n [2396, 1277],\n [2381, 1273],\n [2378, 1286],\n [2369, 1285],\n [2367, 1272],\n [2353, 1282],\n [2346, 1284],\n [2330, 1285],\n [2306, 1283],\n [2296, 1276],\n [2267, 1270],\n [2234, 1349],\n [2224, 1363],\n [2216, 1366],\n [2215, 1368],\n [2216, 1378],\n [2214, 1380],\n [2219, 1390],\n [2214, 1393],\n [2212, 1402],\n [2216, 1405],\n [2221, 1405],\n [2224, 1406],\n [2226, 1404],\n [2232, 1404],\n [2235, 1408],\n [2238, 1409],\n [2242, 1417],\n [2244, 1432],\n [2250, 1450],\n [2253, 1454],\n [2254, 1789],\n [2167, 1994],\n [2084, 2203],\n [2052, 2289],\n [2016, 2370],\n [2054, 2391],\n [2138, 2464],\n [2229, 2548]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '029',\n 'FIPS': '34029',\n 'STATE': 'NJ',\n 'NAME': 'Ocean',\n 'LSAD': 'County'},\n 'id': 2176,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[3554, 3986],\n [3563, 3979],\n [3567, 3971],\n [3568, 3972],\n [3574, 3953],\n [3579, 3944],\n [3583, 3941],\n [3585, 3898],\n [3597, 3881],\n [3604, 3879],\n [3602, 3864],\n [3612, 3843],\n [3615, 3828],\n [3661, 3502],\n [3660, 3492],\n [3660, 3476],\n [3661, 3473],\n [3658, 3473],\n [3661, 3384],\n [3622, 3372],\n [3572, 3360],\n [3440, 3315],\n [3442, 3337],\n [3428, 3340],\n [3388, 3342],\n [3336, 3332],\n [3179, 3335],\n [3179, 3339],\n [3189, 3349],\n [3195, 3352],\n [3204, 3352],\n [3203, 3354],\n [3203, 3370],\n [3192, 3388],\n [3190, 3387],\n [3189, 3389],\n [3178, 3384],\n [3178, 3386],\n [3171, 3387],\n [3163, 3394],\n [3162, 3415],\n [3193, 3431],\n [3194, 3438],\n [3198, 3438],\n [3198, 3437],\n [3197, 3432],\n [3201, 3434],\n [3198, 3446],\n [3199, 3457],\n [3217, 3463],\n [3222, 3468],\n [3217, 3493],\n [3221, 3519],\n [3220, 3551],\n [3222, 3558],\n [3219, 3578],\n [3214, 3600],\n [3228, 3606],\n [3247, 3611],\n [3258, 3633],\n [3256, 3658],\n [3190, 3712],\n [3190, 3709],\n [3159, 3751],\n [3180, 3790],\n [3179, 3801],\n [3217, 3851],\n [3220, 3859],\n [3214, 3875],\n [3216, 3877],\n [3239, 3884],\n [3294, 3859],\n [3323, 3872],\n [3325, 3900],\n [3344, 3928],\n [3355, 3938],\n [3365, 3941],\n [3379, 3938],\n [3402, 3947],\n [3411, 3956],\n [3448, 3972],\n [3454, 3972],\n [3480, 3959],\n [3522, 3970],\n [3528, 3967],\n [3550, 3977],\n [3554, 3986]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '059',\n 'FIPS': '36059',\n 'STATE': 'NY',\n 'NAME': 'Nassau',\n 'LSAD': 'County'},\n 'id': 2201,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1749, 1707],\n [1837, 1634],\n [1842, 1625],\n [1846, 1623],\n [1851, 1615],\n [1854, 1606],\n [1854, 1601],\n [1860, 1591],\n [1863, 1569],\n [1861, 1566],\n [1862, 1563],\n [1860, 1560],\n [1863, 1559],\n [1863, 1554],\n [1862, 1550],\n [1865, 1546],\n [1861, 1538],\n [1863, 1530],\n [1861, 1521],\n [1866, 1520],\n [1868, 1518],\n [1868, 1512],\n [1871, 1511],\n [1874, 1515],\n [1878, 1514],\n [1878, 1509],\n [1888, 1510],\n [1889, 1505],\n [1890, 1504],\n [1893, 1505],\n [1896, 1513],\n [1900, 1513],\n [1902, 1507],\n [1902, 1501],\n [1906, 1499],\n [1910, 1503],\n [1911, 1509],\n [1914, 1512],\n [1916, 1512],\n [1926, 1505],\n [1929, 1506],\n [1936, 1502],\n [1954, 1488],\n [1964, 1484],\n [1966, 1481],\n [1969, 1471],\n [1975, 1468],\n [1983, 1458],\n [1992, 1459],\n [2000, 1455],\n [2015, 1457],\n [2022, 1442],\n [2033, 1434],\n [2031, 1425],\n [2034, 1417],\n [2037, 1413],\n [2045, 1411],\n [2051, 1405],\n [2054, 1403],\n [2059, 1403],\n [2067, 1411],\n [2072, 1412],\n [2076, 1404],\n [2080, 1402],\n [2088, 1403],\n [2092, 1395],\n [2099, 1391],\n [2101, 1381],\n [2103, 1379],\n [2108, 1379],\n [2113, 1383],\n [2120, 1382],\n [2126, 1372],\n [2130, 1369],\n [2134, 1371],\n [2139, 1377],\n [2144, 1379],\n [2148, 1379],\n [2150, 1376],\n [2152, 1366],\n [2154, 1362],\n [2158, 1361],\n [2162, 1367],\n [2162, 1378],\n [2164, 1382],\n [2169, 1384],\n [2172, 1383],\n [2176, 1378],\n [2172, 1363],\n [2178, 1354],\n [2182, 1350],\n [2185, 1349],\n [2190, 1353],\n [2190, 1359],\n [2184, 1369],\n [2184, 1374],\n [2187, 1376],\n [2191, 1377],\n [2199, 1373],\n [2201, 1373],\n [2209, 1380],\n [2214, 1380],\n [2216, 1378],\n [2215, 1368],\n [2216, 1366],\n [2224, 1363],\n [2234, 1349],\n [2267, 1270],\n [2296, 1276],\n [2306, 1283],\n [2330, 1285],\n [2346, 1284],\n [2353, 1282],\n [2367, 1272],\n [2365, 1261],\n [2373, 1242],\n [2378, 1240],\n [2382, 1233],\n [2378, 1218],\n [2334, 1144],\n [2287, 1088],\n [2229, 1041],\n [2227, 1018],\n [2221, 1010],\n [2152, 980],\n [2062, 921],\n [2033, 896],\n [2029, 900],\n [2023, 900],\n [2011, 893],\n [1994, 894],\n [1967, 914],\n [1960, 913],\n [1918, 882],\n [1913, 881],\n [1900, 886],\n [1889, 888],\n [1885, 887],\n [1879, 882],\n [1875, 875],\n [1870, 874],\n [1863, 877],\n [1862, 879],\n [1864, 890],\n [1849, 891],\n [1845, 889],\n [1845, 881],\n [1843, 876],\n [1840, 875],\n [1838, 877],\n [1840, 883],\n [1840, 887],\n [1837, 890],\n [1835, 890],\n [1831, 884],\n [1828, 883],\n [1824, 885],\n [1820, 883],\n [1816, 878],\n [1810, 879],\n [1805, 875],\n [1802, 878],\n [1798, 885],\n [1794, 884],\n [1791, 878],\n [1789, 880],\n [1788, 886],\n [1785, 886],\n [1782, 880],\n [1778, 881],\n [1773, 886],\n [1775, 892],\n [1773, 893],\n [1764, 888],\n [1758, 892],\n [1748, 893],\n [1744, 888],\n [1735, 885],\n [1726, 886],\n [1725, 891],\n [1720, 894],\n [1709, 889],\n [1706, 893],\n [1699, 894],\n [1696, 897],\n [1688, 894],\n [1687, 894],\n [1689, 902],\n [1679, 909],\n [1676, 908],\n [1671, 909],\n [1666, 905],\n [1664, 908],\n [1659, 908],\n [1656, 911],\n [1651, 909],\n [1646, 904],\n [1643, 907],\n [1629, 907],\n [1627, 909],\n [1620, 905],\n [1611, 916],\n [1602, 924],\n [1582, 926],\n [1576, 930],\n [1568, 932],\n [1568, 940],\n [1563, 948],\n [1562, 968],\n [1564, 974],\n [1564, 981],\n [1570, 1000],\n [1569, 1012],\n [1579, 1031],\n [1581, 1041],\n [1580, 1055],\n [1572, 1075],\n [1573, 1080],\n [1572, 1086],\n [1577, 1095],\n [1580, 1106],\n [1579, 1112],\n [1574, 1123],\n [1573, 1127],\n [1575, 1130],\n [1387, 1300],\n [1749, 1707]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '001',\n 'FIPS': '34001',\n 'STATE': 'NJ',\n 'NAME': 'Atlantic',\n 'LSAD': 'County'},\n 'id': 2132,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[812, 261],\n [817, 258],\n [823, 260],\n [830, 256],\n [829, 253],\n [832, 246],\n [841, 253],\n [852, 234],\n [855, 234],\n [863, 248],\n [865, 247],\n [867, 241],\n [865, 234],\n [872, 231],\n [877, 233],\n [881, 240],\n [884, 239],\n [887, 237],\n [893, 238],\n [897, 245],\n [904, 244],\n [909, 239],\n [908, 233],\n [925, 218],\n [911, 191],\n [922, 169],\n [982, 102],\n [1027, 41],\n [1067, 0],\n [1088, -29],\n [1134, -61],\n [1170, -75],\n [1200, -74],\n [1224, -63],\n [1229, -53],\n [1224, -36],\n [1229, -35],\n [1235, -47],\n [1242, -80],\n [319, -80],\n [314, 0],\n [313, 14],\n [557, 24],\n [566, 35],\n [573, 39],\n [577, 38],\n [580, 39],\n [585, 47],\n [584, 51],\n [607, 62],\n [625, 105],\n [632, 110],\n [635, 109],\n [640, 111],\n [642, 113],\n [645, 122],\n [651, 125],\n [655, 131],\n [654, 143],\n [659, 152],\n [670, 156],\n [688, 166],\n [695, 162],\n [716, 169],\n [724, 169],\n [732, 165],\n [741, 169],\n [751, 171],\n [753, 174],\n [757, 171],\n [759, 179],\n [770, 176],\n [770, 182],\n [775, 181],\n [776, 177],\n [779, 178],\n [781, 180],\n [778, 187],\n [780, 192],\n [783, 193],\n [784, 195],\n [781, 200],\n [786, 213],\n [783, 211],\n [779, 215],\n [783, 218],\n [785, 221],\n [781, 233],\n [785, 238],\n [792, 244],\n [793, 250],\n [796, 251],\n [799, 247],\n [808, 249],\n [810, 260],\n [812, 261]]]},\n 'properties': {'STATE_FIPS': '10',\n 'COUNTY_FIP': '005',\n 'FIPS': '10005',\n 'STATE': 'DE',\n 'NAME': 'Sussex',\n 'LSAD': 'County'},\n 'id': 2175,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[4106, 4092],\n [4117, 4081],\n [4159, 4083],\n [4176, 4076],\n [4176, 3486],\n [4096, 3462],\n [3974, 3424],\n [3896, 3406],\n [3832, 3405],\n [3827, 3408],\n [3813, 3434],\n [3766, 3424],\n [3707, 3399],\n [3661, 3384],\n [3658, 3473],\n [3661, 3473],\n [3660, 3476],\n [3660, 3492],\n [3661, 3502],\n [3615, 3828],\n [3612, 3843],\n [3602, 3864],\n [3604, 3879],\n [3597, 3881],\n [3585, 3898],\n [3583, 3941],\n [3579, 3944],\n [3574, 3953],\n [3568, 3972],\n [3567, 3971],\n [3563, 3979],\n [3554, 3986],\n [3562, 4023],\n [3571, 4031],\n [3581, 4023],\n [3603, 4015],\n [3642, 4009],\n [3652, 3999],\n [3654, 3983],\n [3687, 3981],\n [3691, 3990],\n [3690, 4023],\n [3694, 4046],\n [3696, 4048],\n [3706, 4049],\n [3733, 4014],\n [3745, 4003],\n [3764, 3993],\n [3775, 3991],\n [3777, 3995],\n [3795, 3999],\n [3848, 3989],\n [3944, 3952],\n [4061, 3998],\n [4065, 4010],\n [4067, 4050],\n [4053, 4061],\n [4046, 4074],\n [4078, 4071],\n [4096, 4085],\n [4106, 4092]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '103',\n 'FIPS': '36103',\n 'STATE': 'NY',\n 'NAME': 'Suffolk',\n 'LSAD': 'County'},\n 'id': 2231,\n 'type': 'Feature'}],\n 'type': 'FeatureCollection'}}" + "objectID": "cloud-optimized-geotiffs/intro.html#additional-resources", + "href": "cloud-optimized-geotiffs/intro.html#additional-resources", + "title": "Cloud-Optimized GeoTIFFs", + "section": "Additional Resources", + "text": "Additional Resources\n\nPlanet Blog: An Introduction to Cloud Optimized GeoTIFFS (COGs) Part 1: Overview\nCOG Talk — Part 1: What’s new?\nDevelopment Seed Blog: Do you really want people using your data?" }, { - "objectID": "overview.html", - "href": "overview.html", - "title": "Cloud-Optimized Geospatial Formats Overview", - "section": "", - "text": "These slides were generated with https://quarto.org/docs/presentations/revealjs. Source: https://github.com/cloudnativegeo/cloud-optimized-geospatial-formats-guide." + "objectID": "cloud-optimized-geotiffs/intro.html#how-to-visualize-cogs", + "href": "cloud-optimized-geotiffs/intro.html#how-to-visualize-cogs", + "title": "Cloud-Optimized GeoTIFFs", + "section": "How to visualize COGs", + "text": "How to visualize COGs\n\nGDAL vis* drivers (vsicurl, vsis3, vsiaz,)\nTitiler https://github.com/developmentseed/titiler\nRio-viz https://github.com/developmentseed/rio-viz" }, { "objectID": "overview.html#what-does-cloud-optimized-mean-1", @@ -123,7 +291,7 @@ "href": "overview.html#copc-cloud-optimized-point-clouds", "title": "Cloud-Optimized Geospatial Formats Overview", "section": "COPC (Cloud-Optimized Point Clouds)", - "text": "COPC (Cloud-Optimized Point Clouds)\n\n\n\nimage source: https://copc.io/\n\nPoint clouds are a set of data points in space, such as gathered from LiDAR measurements.\nCOPC is a valid LAZ file.\nSimilar to COGs but for point clouds: COPC is just one file, but data is reorganized into a clustered octree instead of regularly gridded overviews.\n2 key features:\n\nSupport for partial decompression via storage of data in a series of chunks\nVariable-length records (VLRs) can store application-specific metadata of any kind. VLRs describe the octree structure.\n\nLimitation: Not all attribute types are compatible." + "text": "COPC (Cloud-Optimized Point Clouds)\n\n\n\nPoint clouds are a set of data points in space, such as gathered from LiDAR measurements.\nCOPC is a valid LAZ file.\nSimilar to COGs but for point clouds: COPC is just one file, but data is reorganized into a clustered octree instead of regularly gridded overviews.\n2 key features:\n\nSupport for partial decompression via storage of data in a series of chunks\nVariable-length records (VLRs) can store application-specific metadata of any kind. VLRs describe the octree structure.\n\nLimitation: Not all attribute types are compatible.\n\n\nimage source: https://copc.io/" }, { "objectID": "overview.html#not-quite", @@ -133,74 +301,179 @@ "text": "Not quite\n\nThese formats and their tooling are in active development\nSome formats were not mentioned, such as EPT, geopkg, tiledb, Cloud-Optimized HDF5. This presentation was scoped to those known best by the authors.\nThis site will continue to be updated with new content." }, { - "objectID": "cloud-optimized-geotiffs/intro.html", - "href": "cloud-optimized-geotiffs/intro.html", - "title": "Cloud-Optimized GeoTIFFs", + "objectID": "pmtiles/pmtiles-example.html", + "href": "pmtiles/pmtiles-example.html", + "title": "PMTiles example", "section": "", - "text": "Cloud-Optimized GeoTIFF (the COG) is a variant of the TIFF image format that specifies a particular layout of internal data in the GeoTIFF specification to allow for optimized (subsetted or aggregated) access over a network for display or data reading. The key components are overviews, and internal tiling.\nFor more details see https://www.cogeo.org/\n\n\n\nThis attribute is also sometimes called chunks or internal tiles.\nDimensions are the number of bands, rows and columns stored in a GeoTIFF. There is a tradeoff between storing lots of data in one GeoTIFF and storing less data in many GeoTIFFs. The larger a single file, the larger the GeoTIFF header and the multiple requests may be required just to read the spatial index before data retrieval. The opposite problem occurs if you make too many small files, then it takes many reads to retrieve data, and when rendering a combined visualization can greatly impact load time.\nIf you plan to pan and zoom a large amount of data through a tiling service in a web browser, there is a tradeoff between 1 large file, or many smaller files. The current recommendation is to meet somewhere in the middle, a moderate amount of medium files.\n\n\n\nInternal blocks are required if the dimensions of data are over 512x512. However you can control the size of the internal blocks. 256x256 or 512x512 are recommended. When displaying data at full resolution, or doing partial reading of data this size will impact the number of reads required. A size of 256 will take less time to read, and read less data outside the desired bounding box, however for reading large parts of a file, it may take more total read requests. Some clients will aggregate neighboring block reads to reduce the total number of requests.\n\n\n\nOverviews are downsampled (aggregated) data intended for visualization. The best resampling algorithm depends on the range, type, and distribution of the data.\nThe smallest size overview should match the tiling components’ fetch size, typically 256x256. Due to aspect ratio variation just aim to have at least one dimension at or slightly less than 256. The COG driver in GDAL, or rio cogeo tools should do this.\nThere are many resampling algorithms for generating overviews. When creating overviews several options should be compared before deciding which resampling method to apply." + "text": "This notebook will give an overview of how to create and visualize PMTiles archives." }, { - "objectID": "cloud-optimized-geotiffs/intro.html#what-is-a-cloud-optimized-geotiff", - "href": "cloud-optimized-geotiffs/intro.html#what-is-a-cloud-optimized-geotiff", - "title": "Cloud-Optimized GeoTIFFs", - "section": "", - "text": "Cloud-Optimized GeoTIFF (the COG) is a variant of the TIFF image format that specifies a particular layout of internal data in the GeoTIFF specification to allow for optimized (subsetted or aggregated) access over a network for display or data reading. The key components are overviews, and internal tiling.\nFor more details see https://www.cogeo.org/\n\n\n\nThis attribute is also sometimes called chunks or internal tiles.\nDimensions are the number of bands, rows and columns stored in a GeoTIFF. There is a tradeoff between storing lots of data in one GeoTIFF and storing less data in many GeoTIFFs. The larger a single file, the larger the GeoTIFF header and the multiple requests may be required just to read the spatial index before data retrieval. The opposite problem occurs if you make too many small files, then it takes many reads to retrieve data, and when rendering a combined visualization can greatly impact load time.\nIf you plan to pan and zoom a large amount of data through a tiling service in a web browser, there is a tradeoff between 1 large file, or many smaller files. The current recommendation is to meet somewhere in the middle, a moderate amount of medium files.\n\n\n\nInternal blocks are required if the dimensions of data are over 512x512. However you can control the size of the internal blocks. 256x256 or 512x512 are recommended. When displaying data at full resolution, or doing partial reading of data this size will impact the number of reads required. A size of 256 will take less time to read, and read less data outside the desired bounding box, however for reading large parts of a file, it may take more total read requests. Some clients will aggregate neighboring block reads to reduce the total number of requests.\n\n\n\nOverviews are downsampled (aggregated) data intended for visualization. The best resampling algorithm depends on the range, type, and distribution of the data.\nThe smallest size overview should match the tiling components’ fetch size, typically 256x256. Due to aspect ratio variation just aim to have at least one dimension at or slightly less than 256. The COG driver in GDAL, or rio cogeo tools should do this.\nThere are many resampling algorithms for generating overviews. When creating overviews several options should be compared before deciding which resampling method to apply." + "objectID": "pmtiles/pmtiles-example.html#environment", + "href": "pmtiles/pmtiles-example.html#environment", + "title": "PMTiles example", + "section": "Environment", + "text": "Environment\nThe packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml \nor\nmamba create -f environment.yml \nAlternatively, you can install pmtiles and mapbox-vector-tile through pip, and tippecanoe through Homebrew (brew install tippecanoe) if on MacOS." }, { - "objectID": "cloud-optimized-geotiffs/intro.html#how-to-create-and-validate-cogs", - "href": "cloud-optimized-geotiffs/intro.html#how-to-create-and-validate-cogs", - "title": "Cloud-Optimized GeoTIFFs", - "section": "How to create and validate COGs", - "text": "How to create and validate COGs\n\nRio-cogeo: GitHub - cogeotiff/rio-cogeo: Cloud Optimized GeoTIFF creation and validation plugin for rasterio\nGdal: COG – Cloud Optimized GeoTIFF generator — GDAL documentation" + "objectID": "pmtiles/pmtiles-example.html#creating-pmtiles", + "href": "pmtiles/pmtiles-example.html#creating-pmtiles", + "title": "PMTiles example", + "section": "Creating PMTiles", + "text": "Creating PMTiles\nFor this example, we’ll use the same file as used in the FlatGeobuf and GeoParquet example notebooks: a 13MB file of US counties.\nWe’ll use Tippecanoe to convert this file into tiles.\nFirst we’ll download the file to our local directory:\n\n!wget https://flatgeobuf.org/test/data/UScounties.fgb\n\n--2023-08-23 15:54:58-- https://flatgeobuf.org/test/data/UScounties.fgb\nResolving flatgeobuf.org (flatgeobuf.org)... 185.199.108.153\nConnecting to flatgeobuf.org (flatgeobuf.org)|185.199.108.153|:443... connected.\nHTTP request sent, awaiting response... 200 OK\nLength: 14100008 (13M) [application/octet-stream]\nSaving to: ‘UScounties.fgb’\n\nUScounties.fgb 100%[===================>] 13.45M 7.94MB/s in 1.7s \n\n2023-08-23 15:55:02 (7.94 MB/s) - ‘UScounties.fgb’ saved [14100008/14100008]\n\n\n\nTippecanoe has many options to customize its behavior. Here we’ll use the -zg flag to tell Tippecanoe to deduce appropriate minimum and maximum zoom levels for the dataset. The -o counties.pmtiles flag tells Tippecanoe to save the output with that name.\nTippecanoe also works especially well with FlatGeobuf files. When a FlatGeobuf file is used as input, Tippecanoe will reuse the spatial index stored in the FlatGeobuf file instead of creating its own.\n\n!tippecanoe UScounties.fgb -o counties.pmtiles -zg\n\nFor layer 0, using name \"UScountiesfgb\"\ndetected indexed FlatGeobuf: assigning feature IDs by sequence\n3221 features, 5580299 bytes of geometry, 53296 bytes of string pool\nChoosing a maxzoom of -z1 for features typically 141427 feet (43107 meters) apart, and at least 33249 feet (10135 meters) apart\nChoosing a maxzoom of -z7 for resolution of about 3195 feet (973 meters) within features\n 99.9% 7/36/49 \n 100.0% 7/127/42 \n\n\nNow we have a file named counties.pmtiles with our data:\n\n!ls -lh counties.pmtiles\n\n-rw-r--r--@ 1 kyle staff 2.8M Aug 25 13:09 counties.pmtiles" }, { - "objectID": "cloud-optimized-geotiffs/intro.html#additional-resources", - "href": "cloud-optimized-geotiffs/intro.html#additional-resources", - "title": "Cloud-Optimized GeoTIFFs", - "section": "Additional Resources", - "text": "Additional Resources\n\nPlanet Blog: An Introduction to Cloud Optimized GeoTIFFS (COGs) Part 1: Overview\nCOG Talk — Part 1: What’s new?\nDevelopment Seed Blog: Do you really want people using your data?" + "objectID": "pmtiles/pmtiles-example.html#visualization", + "href": "pmtiles/pmtiles-example.html#visualization", + "title": "PMTiles example", + "section": "Visualization", + "text": "Visualization\nThe easiest way to interpret this data is to load it into the PMTiles Viewer. Drag the counties.pmtiles file into that website, and you’ll be able to hover over areas" }, { - "objectID": "cloud-optimized-geotiffs/intro.html#how-to-visualize-cogs", - "href": "cloud-optimized-geotiffs/intro.html#how-to-visualize-cogs", - "title": "Cloud-Optimized GeoTIFFs", - "section": "How to visualize COGs", - "text": "How to visualize COGs\n\nGDAL vis* drivers (vsicurl, vsis3, vsiaz,)\nTitiler https://github.com/developmentseed/titiler\nRio-viz https://github.com/developmentseed/rio-viz" + "objectID": "pmtiles/pmtiles-example.html#reading-from-python", + "href": "pmtiles/pmtiles-example.html#reading-from-python", + "title": "PMTiles example", + "section": "Reading from Python", + "text": "Reading from Python\nIt’s possible to open and read a PMTiles file from python using the pmtiles and mapbox-vector-tile libraries. The pmtiles library is used to open the archive and fetch a specific tile, while mapbox-vector-tile is used to decode the MVT vector tile data contained within that tile.\n\nfrom pmtiles.reader import Reader, MmapSource\n\nOpen the file and create a pmtiles Reader object\n\nfile = open(\"counties.pmtiles\")\nreader = Reader(MmapSource(file))\n\nFetch a specific tile. This tile’s coordinates were found from the PMTiles viewer above, and is located over the east coast.\n\nx, y, z = 37, 48, 7\ntile_data = reader.get(z, x, y)\n\ntile_data is now a bytes object, representing the data contained in the PMTiles archive for that specific XYZ tile.\n\ntype(tile_data)\n\nbytes\n\n\n\nlen(tile_data)\n\n11878\n\n\nIn our case, the PMTiles archive contains MVT data, so we can decode the buffer using mapbox_vector_tile. It’s also possible for the archive to contain raster images (e.g. PNG files), in which case a different decoding process would be necessary.\n\nimport mapbox_vector_tile\nimport gzip\n\nWe’ll decode the tile and print the output from mapbox_vector_tile. MVT data are encoded with “quantization”, meaning reduced precision so the data can be compressed better. So the coordinates printed out have a range of 0-4096, where those are the integer steps within the local coordinate reference system within the tile. Refer to the mapbox_vector_tile docs for how to read to GeoJSON.\n\nmapbox_vector_tile.decode(gzip.decompress(tile_data))\n\n{'UScountiesfgb': {'extent': 4096,\n 'version': 2,\n 'features': [{'geometry': {'type': 'Polygon',\n 'coordinates': [[[289, 4176],\n [290, 4168],\n [299, 4151],\n [198, 4102],\n [172, 4100],\n [163, 4096],\n [128, 4080],\n [130, 4070],\n [0, 4009],\n [-71, 3976],\n [-80, 3970],\n [-80, 4176],\n [289, 4176]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '079',\n 'FIPS': '42079',\n 'STATE': 'PA',\n 'NAME': 'Luzerne',\n 'LSAD': 'County'},\n 'id': 2224,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1272, 4176],\n [1256, 4168],\n [1247, 4167],\n [1235, 4163],\n [1226, 4152],\n [1206, 4143],\n [1204, 4139],\n [1180, 4123],\n [1175, 4118],\n [1174, 4113],\n [1174, 4106],\n [1171, 4096],\n [1168, 4090],\n [1168, 4084],\n [1171, 4079],\n [1174, 4076],\n [1177, 4075],\n [1187, 4077],\n [1190, 4074],\n [1177, 4056],\n [1154, 4041],\n [1143, 4037],\n [1119, 4035],\n [1108, 4030],\n [1106, 4020],\n [1092, 4014],\n [1081, 4012],\n [1048, 3996],\n [1042, 3980],\n [1014, 3960],\n [1027, 3941],\n [976, 3908],\n [967, 3890],\n [952, 3877],\n [928, 3864],\n [898, 3857],\n [868, 3837],\n [807, 3809],\n [758, 3800],\n [753, 3795],\n [721, 3785],\n [675, 3778],\n [663, 3807],\n [648, 3835],\n [529, 4041],\n [620, 4096],\n [643, 4110],\n [590, 4176],\n [1272, 4176]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '089',\n 'FIPS': '42089',\n 'STATE': 'PA',\n 'NAME': 'Monroe',\n 'LSAD': 'County'},\n 'id': 2227,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[590, 4176],\n [643, 4110],\n [620, 4096],\n [529, 4041],\n [598, 3923],\n [655, 3822],\n [675, 3778],\n [668, 3775],\n [625, 3767],\n [609, 3766],\n [596, 3762],\n [577, 3763],\n [562, 3758],\n [553, 3752],\n [524, 3748],\n [498, 3737],\n [494, 3730],\n [488, 3726],\n [478, 3725],\n [399, 3706],\n [363, 3696],\n [354, 3692],\n [342, 3677],\n [304, 3661],\n [299, 3656],\n [289, 3642],\n [282, 3641],\n [281, 3635],\n [262, 3625],\n [75, 3781],\n [0, 3867],\n [-80, 3958],\n [-80, 3970],\n [0, 4009],\n [130, 4070],\n [128, 4080],\n [163, 4096],\n [172, 4100],\n [198, 4102],\n [299, 4151],\n [290, 4168],\n [289, 4176],\n [590, 4176]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '025',\n 'FIPS': '42025',\n 'STATE': 'PA',\n 'NAME': 'Carbon',\n 'LSAD': 'County'},\n 'id': 2217,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1529, 4176],\n [1594, 4096],\n [1703, 3965],\n [1691, 3952],\n [1687, 3954],\n [1681, 3943],\n [1671, 3935],\n [1671, 3928],\n [1668, 3923],\n [1651, 3914],\n [1647, 3908],\n [1648, 3894],\n [1645, 3883],\n [1646, 3880],\n [1654, 3874],\n [1652, 3869],\n [1643, 3865],\n [1642, 3855],\n [1637, 3849],\n [1627, 3845],\n [1625, 3843],\n [1622, 3833],\n [1627, 3829],\n [1630, 3821],\n [1617, 3812],\n [1611, 3804],\n [1613, 3799],\n [1606, 3792],\n [1606, 3786],\n [1598, 3780],\n [1592, 3771],\n [1589, 3771],\n [1588, 3765],\n [1583, 3757],\n [1576, 3756],\n [1569, 3752],\n [1567, 3756],\n [1562, 3755],\n [1558, 3742],\n [1546, 3737],\n [1539, 3731],\n [1535, 3733],\n [1532, 3732],\n [1517, 3719],\n [1505, 3712],\n [1507, 3706],\n [1506, 3698],\n [1507, 3695],\n [1494, 3686],\n [1489, 3680],\n [1489, 3677],\n [1485, 3672],\n [1473, 3663],\n [1470, 3658],\n [1467, 3656],\n [1465, 3651],\n [1452, 3636],\n [1451, 3631],\n [1443, 3624],\n [1435, 3610],\n [1426, 3606],\n [1424, 3602],\n [1419, 3597],\n [1410, 3577],\n [1396, 3578],\n [1393, 3569],\n [1378, 3563],\n [1361, 3545],\n [1356, 3544],\n [1351, 3547],\n [1345, 3543],\n [1341, 3547],\n [1337, 3542],\n [1333, 3542],\n [1315, 3526],\n [1310, 3526],\n [1308, 3524],\n [1303, 3517],\n [1301, 3516],\n [1295, 3518],\n [1288, 3512],\n [1282, 3510],\n [1278, 3505],\n [1275, 3499],\n [1267, 3497],\n [1264, 3494],\n [1254, 3488],\n [1247, 3476],\n [1236, 3472],\n [1235, 3468],\n [1226, 3458],\n [1211, 3451],\n [1207, 3447],\n [1208, 3442],\n [1205, 3440],\n [1180, 3431],\n [1168, 3424],\n [1153, 3420],\n [1150, 3416],\n [1150, 3408],\n [1147, 3405],\n [1141, 3407],\n [1129, 3399],\n [1130, 3393],\n [1128, 3387],\n [1124, 3385],\n [1120, 3378],\n [1114, 3375],\n [1111, 3363],\n [1104, 3361],\n [1102, 3355],\n [1096, 3351],\n [1088, 3349],\n [1085, 3370],\n [1072, 3393],\n [1071, 3398],\n [1077, 3402],\n [1088, 3404],\n [1091, 3412],\n [1085, 3444],\n [1073, 3455],\n [1073, 3460],\n [1087, 3484],\n [1099, 3492],\n [1107, 3505],\n [1108, 3511],\n [1107, 3514],\n [1102, 3518],\n [1097, 3519],\n [1088, 3518],\n [1079, 3521],\n [1073, 3529],\n [1068, 3541],\n [1082, 3584],\n [1089, 3592],\n [1100, 3618],\n [1095, 3629],\n [1081, 3645],\n [1079, 3656],\n [1086, 3664],\n [1099, 3671],\n [1105, 3676],\n [1107, 3681],\n [1110, 3697],\n [1115, 3707],\n [1118, 3708],\n [1127, 3708],\n [1148, 3701],\n [1170, 3699],\n [1174, 3703],\n [1186, 3724],\n [1195, 3729],\n [1203, 3730],\n [1207, 3732],\n [1219, 3749],\n [1219, 3764],\n [1225, 3773],\n [1234, 3780],\n [1243, 3797],\n [1243, 3803],\n [1241, 3807],\n [1224, 3825],\n [1223, 3832],\n [1226, 3840],\n [1233, 3844],\n [1254, 3845],\n [1262, 3841],\n [1269, 3841],\n [1272, 3842],\n [1288, 3865],\n [1291, 3876],\n [1290, 3885],\n [1287, 3891],\n [1280, 3899],\n [1270, 3914],\n [1256, 3931],\n [1255, 3956],\n [1250, 3969],\n [1226, 3989],\n [1212, 4012],\n [1211, 4018],\n [1203, 4035],\n [1194, 4044],\n [1190, 4065],\n [1191, 4070],\n [1190, 4074],\n [1187, 4077],\n [1177, 4075],\n [1174, 4076],\n [1171, 4079],\n [1168, 4084],\n [1168, 4090],\n [1171, 4096],\n [1174, 4106],\n [1174, 4113],\n [1175, 4118],\n [1180, 4123],\n [1204, 4139],\n [1206, 4143],\n [1226, 4152],\n [1235, 4163],\n [1247, 4167],\n [1256, 4168],\n [1272, 4176],\n [1529, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '041',\n 'FIPS': '34041',\n 'STATE': 'NJ',\n 'NAME': 'Warren',\n 'LSAD': 'County'},\n 'id': 2207,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[2427, 4176],\n [2425, 4170],\n [2437, 4158],\n [2460, 4120],\n [2494, 4099],\n [2518, 4096],\n [2573, 4090],\n [2573, 4096],\n [2574, 4104],\n [2583, 4096],\n [2616, 4070],\n [2606, 4022],\n [2612, 4020],\n [2619, 4007],\n [2615, 3999],\n [2616, 3992],\n [2619, 3987],\n [2630, 3985],\n [2633, 3981],\n [2628, 3967],\n [2630, 3956],\n [2627, 3941],\n [2634, 3930],\n [2632, 3915],\n [2646, 3904],\n [2654, 3892],\n [2656, 3885],\n [2661, 3874],\n [2665, 3871],\n [2665, 3860],\n [2662, 3856],\n [2659, 3857],\n [2656, 3864],\n [2649, 3864],\n [2647, 3855],\n [2648, 3849],\n [2644, 3833],\n [2641, 3826],\n [2645, 3808],\n [2641, 3793],\n [2638, 3790],\n [2632, 3788],\n [2552, 3846],\n [2534, 3860],\n [2536, 3865],\n [2529, 3868],\n [2527, 3864],\n [2472, 3904],\n [2453, 3906],\n [2451, 3909],\n [2443, 3911],\n [2437, 3917],\n [2433, 3931],\n [2425, 3938],\n [2424, 3942],\n [2427, 3948],\n [2424, 3953],\n [2426, 3956],\n [2426, 3962],\n [2430, 3957],\n [2432, 3957],\n [2433, 3959],\n [2426, 3972],\n [2416, 3973],\n [2416, 3978],\n [2408, 3975],\n [2408, 3980],\n [2405, 3984],\n [2407, 3986],\n [2402, 3989],\n [2402, 3995],\n [2401, 3997],\n [2407, 4002],\n [2411, 4013],\n [2415, 4025],\n [2418, 4047],\n [2414, 4053],\n [2415, 4069],\n [2413, 4071],\n [2412, 4075],\n [2413, 4081],\n [2417, 4079],\n [2418, 4085],\n [2412, 4090],\n [2412, 4096],\n [2407, 4094],\n [2399, 4096],\n [2392, 4094],\n [2389, 4096],\n [2389, 4103],\n [2396, 4111],\n [2391, 4117],\n [2391, 4125],\n [2388, 4128],\n [2388, 4136],\n [2387, 4140],\n [2384, 4140],\n [2382, 4133],\n [2370, 4132],\n [2363, 4139],\n [2344, 4137],\n [2341, 4140],\n [2334, 4142],\n [2334, 4147],\n [2329, 4148],\n [2325, 4146],\n [2324, 4148],\n [2318, 4144],\n [2310, 4144],\n [2310, 4156],\n [2306, 4160],\n [2298, 4162],\n [2293, 4160],\n [2292, 4157],\n [2281, 4157],\n [2274, 4161],\n [2265, 4162],\n [2259, 4157],\n [2255, 4159],\n [2251, 4165],\n [2240, 4168],\n [2235, 4173],\n [2234, 4176],\n [2427, 4176]]],\n [[[2203, 4176],\n [2202, 4172],\n [2196, 4170],\n [2189, 4174],\n [2190, 4176],\n [2203, 4176]]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '031',\n 'FIPS': '34031',\n 'STATE': 'NJ',\n 'NAME': 'Passaic',\n 'LSAD': 'County'},\n 'id': 2230,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2981, 4176],\n [2977, 4145],\n [2976, 4129],\n [2960, 4130],\n [2885, 4176],\n [2981, 4176]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '087',\n 'FIPS': '36087',\n 'STATE': 'NY',\n 'NAME': 'Rockland',\n 'LSAD': 'County'},\n 'id': 2226,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[3469, 4176],\n [3460, 4167],\n [3451, 4156],\n [3448, 4138],\n [3428, 4137],\n [3426, 4155],\n [3410, 4166],\n [3398, 4164],\n [3341, 4139],\n [3330, 4126],\n [3321, 4106],\n [3317, 4111],\n [3319, 4122],\n [3318, 4134],\n [3325, 4157],\n [3312, 4176],\n [3469, 4176]]],\n [[[3519, 4176],\n [3517, 4172],\n [3508, 4166],\n [3505, 4176],\n [3519, 4176]]]]},\n 'properties': {'STATE_FIPS': '09',\n 'COUNTY_FIP': '001',\n 'FIPS': '09001',\n 'STATE': 'CT',\n 'NAME': 'Fairfield',\n 'LSAD': 'County'},\n 'id': 1713,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[3156, 3920],\n [3161, 3917],\n [3162, 3906],\n [3161, 3904],\n [3155, 3902],\n [3149, 3908],\n [3149, 3912],\n [3153, 3912],\n [3153, 3918],\n [3156, 3920]]],\n [[[3312, 4176],\n [3325, 4157],\n [3318, 4134],\n [3319, 4122],\n [3317, 4111],\n [3321, 4106],\n [3323, 4096],\n [3317, 4074],\n [3314, 4070],\n [3310, 4072],\n [3291, 4063],\n [3283, 4036],\n [3278, 4029],\n [3262, 4018],\n [3227, 4004],\n [3212, 3990],\n [3176, 3966],\n [3140, 3914],\n [3137, 3905],\n [3079, 3925],\n [3078, 3922],\n [3057, 3931],\n [3053, 3950],\n [3048, 3950],\n [3048, 3952],\n [3042, 3955],\n [3036, 3956],\n [3039, 3961],\n [3036, 3961],\n [3033, 3958],\n [3034, 3955],\n [3031, 3954],\n [3026, 3943],\n [2941, 3976],\n [2972, 4096],\n [2981, 4176],\n [3312, 4176]]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '119',\n 'FIPS': '36119',\n 'STATE': 'NY',\n 'NAME': 'Westchester',\n 'LSAD': 'County'},\n 'id': 2232,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[-80, 3958],\n [0, 3867],\n [75, 3781],\n [219, 3660],\n [262, 3625],\n [250, 3617],\n [232, 3613],\n [230, 3605],\n [205, 3590],\n [189, 3572],\n [135, 3544],\n [120, 3544],\n [67, 3514],\n [0, 3481],\n [-80, 3442],\n [-80, 3958]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '107',\n 'FIPS': '42107',\n 'STATE': 'PA',\n 'NAME': 'Schuylkill',\n 'LSAD': 'County'},\n 'id': 2216,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[-80, 2134],\n [-80, 2627],\n [0, 2560],\n [93, 2480],\n [69, 2466],\n [0, 2431],\n [-8, 2427],\n [0, 2380],\n [2, 2369],\n [2, 2342],\n [5, 2310],\n [3, 2293],\n [0, 2287],\n [-72, 2157],\n [-71, 2150],\n [-77, 2142],\n [-80, 2134]]],\n [[[-80, 2117],\n [-77, 2115],\n [-74, 2105],\n [-65, 2097],\n [-63, 2093],\n [-64, 2086],\n [-69, 2090],\n [-73, 2086],\n [-79, 2085],\n [-78, 2081],\n [-80, 2080],\n [-80, 2117]]],\n [[[-80, 1996],\n [-74, 1988],\n [-72, 1979],\n [-77, 1974],\n [-79, 1970],\n [-80, 1971],\n [-80, 1996]]],\n [[[-80, 2026],\n [-73, 2020],\n [-72, 2015],\n [-76, 2012],\n [-78, 2006],\n [-79, 2001],\n [-77, 2000],\n [-80, 1996],\n [-80, 2026]]],\n [[[-80, 2123], [-78, 2120], [-80, 2117], [-80, 2123]]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '071',\n 'FIPS': '42071',\n 'STATE': 'PA',\n 'NAME': 'Lancaster',\n 'LSAD': 'County'},\n 'id': 2185,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[223, 1051],\n [235, 1047],\n [235, 1044],\n [240, 1045],\n [249, 1041],\n [264, 794],\n [262, 796],\n [255, 795],\n [247, 797],\n [241, 794],\n [236, 796],\n [233, 793],\n [232, 795],\n [223, 792],\n [222, 791],\n [210, 788],\n [209, 790],\n [205, 785],\n [197, 787],\n [195, 790],\n [191, 786],\n [185, 792],\n [171, 794],\n [163, 799],\n [162, 802],\n [157, 803],\n [148, 808],\n [143, 809],\n [142, 816],\n [136, 816],\n [122, 820],\n [120, 824],\n [113, 821],\n [106, 823],\n [92, 809],\n [92, 801],\n [86, 802],\n [84, 804],\n [75, 800],\n [71, 807],\n [67, 809],\n [63, 806],\n [63, 797],\n [29, 789],\n [18, 790],\n [0, 782],\n [-5, 780],\n [-10, 781],\n [-16, 787],\n [-21, 788],\n [-27, 784],\n [-30, 784],\n [-37, 786],\n [-43, 793],\n [-48, 795],\n [-58, 792],\n [-70, 794],\n [-71, 783],\n [-75, 781],\n [-80, 780],\n [-80, 1037],\n [-75, 1034],\n [-64, 1021],\n [-60, 1021],\n [-56, 1022],\n [-48, 1033],\n [-39, 1036],\n [-21, 1034],\n [-17, 1032],\n [-11, 1025],\n [-5, 1023],\n [0, 1025],\n [8, 1030],\n [14, 1030],\n [21, 1022],\n [49, 1017],\n [63, 1019],\n [71, 1012],\n [76, 1010],\n [81, 1017],\n [102, 1023],\n [107, 1021],\n [111, 1023],\n [120, 1017],\n [130, 1024],\n [134, 1023],\n [139, 1029],\n [155, 1035],\n [166, 1049],\n [173, 1050],\n [176, 1047],\n [186, 1046],\n [191, 1038],\n [193, 1037],\n [198, 1042],\n [206, 1041],\n [209, 1046],\n [213, 1046],\n [216, 1048],\n [222, 1046],\n [223, 1051]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '029',\n 'FIPS': '24029',\n 'STATE': 'MD',\n 'NAME': 'Kent',\n 'LSAD': 'County'},\n 'id': 2126,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[120, 824],\n [122, 820],\n [136, 816],\n [142, 816],\n [143, 809],\n [148, 808],\n [157, 803],\n [162, 802],\n [163, 799],\n [171, 794],\n [185, 792],\n [191, 786],\n [195, 790],\n [197, 787],\n [205, 785],\n [209, 790],\n [210, 788],\n [222, 791],\n [223, 792],\n [232, 795],\n [233, 793],\n [236, 796],\n [241, 794],\n [247, 797],\n [255, 795],\n [262, 796],\n [264, 794],\n [276, 601],\n [262, 592],\n [236, 584],\n [213, 581],\n [209, 575],\n [202, 573],\n [197, 566],\n [186, 558],\n [167, 548],\n [153, 536],\n [149, 535],\n [143, 520],\n [137, 502],\n [133, 500],\n [130, 495],\n [132, 473],\n [129, 466],\n [116, 452],\n [112, 447],\n [108, 443],\n [99, 440],\n [96, 432],\n [89, 422],\n [86, 421],\n [86, 418],\n [88, 417],\n [89, 414],\n [87, 405],\n [84, 404],\n [82, 396],\n [79, 393],\n [79, 389],\n [78, 387],\n [80, 384],\n [73, 382],\n [71, 377],\n [68, 374],\n [68, 366],\n [57, 359],\n [49, 364],\n [47, 361],\n [49, 354],\n [49, 352],\n [42, 352],\n [42, 348],\n [39, 340],\n [35, 339],\n [33, 335],\n [26, 332],\n [23, 322],\n [11, 315],\n [13, 311],\n [7, 307],\n [4, 299],\n [4, 295],\n [0, 292],\n [-5, 289],\n [-3, 285],\n [0, 284],\n [1, 283],\n [0, 278],\n [-8, 270],\n [-1, 257],\n [-2, 253],\n [-4, 254],\n [-7, 250],\n [-7, 238],\n [-10, 234],\n [-20, 231],\n [-19, 222],\n [-14, 222],\n [-14, 220],\n [-23, 217],\n [-21, 214],\n [-17, 213],\n [-15, 210],\n [-16, 204],\n [-17, 202],\n [-22, 201],\n [-22, 200],\n [-18, 190],\n [-17, 179],\n [-45, 186],\n [-50, 186],\n [-59, 190],\n [-80, 194],\n [-80, 780],\n [-75, 781],\n [-71, 783],\n [-70, 794],\n [-58, 792],\n [-48, 795],\n [-43, 793],\n [-37, 786],\n [-30, 784],\n [-27, 784],\n [-21, 788],\n [-16, 787],\n [-10, 781],\n [-5, 780],\n [0, 782],\n [18, 790],\n [29, 789],\n [63, 797],\n [63, 806],\n [67, 809],\n [71, 807],\n [75, 800],\n [84, 804],\n [86, 802],\n [92, 801],\n [92, 809],\n [106, 823],\n [113, 821],\n [120, 824]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '035',\n 'FIPS': '24035',\n 'STATE': 'MD',\n 'NAME': \"Queen Anne's\",\n 'LSAD': 'County'},\n 'id': 2137,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[-80, 194],\n [-59, 190],\n [-50, 186],\n [-45, 186],\n [-17, 179],\n [-10, 176],\n [-9, 172],\n [-16, 172],\n [-16, 167],\n [-8, 158],\n [-9, 156],\n [-15, 153],\n [-16, 149],\n [-11, 145],\n [-10, 140],\n [-12, 133],\n [-16, 130],\n [-16, 129],\n [-10, 126],\n [-4, 117],\n [-6, 115],\n [-18, 116],\n [-20, 115],\n [-20, 108],\n [-16, 104],\n [-3, 110],\n [0, 107],\n [0, 104],\n [-2, 101],\n [-9, 101],\n [-10, 98],\n [-6, 85],\n [-3, 82],\n [0, 83],\n [4, 84],\n [11, 72],\n [19, 71],\n [22, 65],\n [22, 62],\n [10, 53],\n [16, 46],\n [26, 45],\n [27, 44],\n [26, 42],\n [14, 35],\n [14, 30],\n [26, 24],\n [33, 23],\n [36, 13],\n [53, 4],\n [51, 0],\n [47, -2],\n [45, -5],\n [50, -11],\n [59, -14],\n [60, -19],\n [55, -20],\n [49, -29],\n [38, -30],\n [14, -44],\n [7, -49],\n [0, -66],\n [-15, -80],\n [-80, -80],\n [-80, 194]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '041',\n 'FIPS': '24041',\n 'STATE': 'MD',\n 'NAME': 'Talbot',\n 'LSAD': 'County'},\n 'id': 2156,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[478, 3725],\n [484, 3712],\n [485, 3703],\n [482, 3694],\n [487, 3684],\n [486, 3666],\n [489, 3653],\n [496, 3641],\n [502, 3627],\n [509, 3622],\n [528, 3625],\n [533, 3621],\n [538, 3607],\n [543, 3602],\n [552, 3597],\n [558, 3598],\n [571, 3604],\n [576, 3611],\n [578, 3626],\n [583, 3630],\n [593, 3627],\n [598, 3623],\n [599, 3618],\n [600, 3595],\n [605, 3582],\n [622, 3548],\n [633, 3537],\n [640, 3517],\n [643, 3515],\n [653, 3511],\n [659, 3502],\n [661, 3494],\n [661, 3481],\n [664, 3472],\n [739, 3508],\n [739, 3497],\n [783, 3442],\n [795, 3430],\n [801, 3429],\n [800, 3418],\n [807, 3411],\n [806, 3404],\n [808, 3398],\n [811, 3398],\n [813, 3394],\n [799, 3393],\n [795, 3359],\n [777, 3330],\n [777, 3328],\n [880, 3245],\n [769, 3151],\n [660, 3017],\n [165, 3428],\n [67, 3514],\n [120, 3544],\n [135, 3544],\n [189, 3572],\n [205, 3590],\n [230, 3605],\n [232, 3613],\n [250, 3617],\n [262, 3625],\n [281, 3635],\n [282, 3641],\n [289, 3642],\n [299, 3656],\n [304, 3661],\n [342, 3677],\n [354, 3692],\n [380, 3701],\n [478, 3725]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '077',\n 'FIPS': '42077',\n 'STATE': 'PA',\n 'NAME': 'Lehigh',\n 'LSAD': 'County'},\n 'id': 2212,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[67, 3514],\n [165, 3428],\n [594, 3072],\n [351, 2680],\n [283, 2632],\n [93, 2480],\n [0, 2560],\n [-25, 2581],\n [-80, 2627],\n [-80, 3442],\n [0, 3481],\n [67, 3514]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '011',\n 'FIPS': '42011',\n 'STATE': 'PA',\n 'NAME': 'Berks',\n 'LSAD': 'County'},\n 'id': 2193,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[594, 3072],\n [660, 3017],\n [745, 2952],\n [937, 2799],\n [1053, 2710],\n [1176, 2612],\n [1343, 2482],\n [1335, 2471],\n [1280, 2421],\n [1252, 2383],\n [1245, 2377],\n [1239, 2369],\n [1224, 2351],\n [1238, 2340],\n [1228, 2327],\n [1210, 2313],\n [1206, 2307],\n [1108, 2381],\n [1091, 2358],\n [1040, 2396],\n [980, 2322],\n [987, 2313],\n [1002, 2299],\n [1007, 2292],\n [1018, 2281],\n [1027, 2273],\n [1048, 2259],\n [1056, 2249],\n [1065, 2242],\n [1063, 2239],\n [963, 2176],\n [952, 2195],\n [912, 2257],\n [898, 2250],\n [846, 2340],\n [841, 2345],\n [839, 2344],\n [831, 2357],\n [794, 2335],\n [754, 2404],\n [728, 2390],\n [723, 2399],\n [701, 2386],\n [700, 2400],\n [691, 2405],\n [693, 2416],\n [690, 2416],\n [682, 2423],\n [679, 2432],\n [682, 2441],\n [692, 2447],\n [694, 2452],\n [693, 2457],\n [677, 2466],\n [665, 2461],\n [648, 2460],\n [629, 2472],\n [624, 2479],\n [621, 2488],\n [625, 2498],\n [635, 2512],\n [636, 2519],\n [633, 2522],\n [624, 2524],\n [620, 2522],\n [614, 2514],\n [610, 2502],\n [607, 2500],\n [602, 2501],\n [598, 2504],\n [597, 2511],\n [598, 2521],\n [595, 2538],\n [591, 2544],\n [580, 2553],\n [576, 2558],\n [566, 2580],\n [555, 2613],\n [549, 2619],\n [544, 2618],\n [543, 2612],\n [546, 2604],\n [546, 2596],\n [539, 2589],\n [535, 2589],\n [530, 2591],\n [522, 2602],\n [519, 2612],\n [510, 2627],\n [496, 2670],\n [491, 2673],\n [487, 2673],\n [483, 2671],\n [481, 2669],\n [481, 2664],\n [487, 2654],\n [487, 2652],\n [483, 2648],\n [474, 2644],\n [468, 2648],\n [463, 2658],\n [428, 2674],\n [419, 2680],\n [411, 2679],\n [406, 2680],\n [398, 2686],\n [388, 2685],\n [381, 2681],\n [367, 2670],\n [360, 2670],\n [351, 2680],\n [594, 3072]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '091',\n 'FIPS': '42091',\n 'STATE': 'PA',\n 'NAME': 'Montgomery',\n 'LSAD': 'County'},\n 'id': 2198,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[398, 2686],\n [406, 2680],\n [411, 2679],\n [419, 2680],\n [428, 2674],\n [463, 2658],\n [468, 2648],\n [474, 2644],\n [483, 2648],\n [487, 2652],\n [487, 2654],\n [481, 2664],\n [481, 2669],\n [483, 2671],\n [487, 2673],\n [491, 2673],\n [496, 2670],\n [510, 2627],\n [519, 2612],\n [521, 2604],\n [525, 2597],\n [535, 2589],\n [539, 2589],\n [546, 2596],\n [546, 2604],\n [543, 2612],\n [544, 2618],\n [549, 2619],\n [555, 2613],\n [566, 2580],\n [576, 2558],\n [580, 2553],\n [591, 2544],\n [595, 2538],\n [598, 2521],\n [597, 2511],\n [598, 2504],\n [602, 2501],\n [607, 2500],\n [610, 2502],\n [614, 2514],\n [620, 2522],\n [624, 2524],\n [633, 2522],\n [636, 2519],\n [635, 2512],\n [625, 2498],\n [621, 2488],\n [624, 2479],\n [629, 2472],\n [648, 2460],\n [665, 2461],\n [677, 2466],\n [693, 2457],\n [694, 2452],\n [692, 2447],\n [682, 2441],\n [679, 2432],\n [682, 2423],\n [690, 2416],\n [693, 2416],\n [691, 2405],\n [700, 2400],\n [701, 2386],\n [723, 2399],\n [728, 2390],\n [754, 2404],\n [794, 2335],\n [831, 2357],\n [839, 2344],\n [761, 2299],\n [776, 2272],\n [705, 2207],\n [720, 2208],\n [724, 2206],\n [728, 2208],\n [727, 2204],\n [731, 2198],\n [730, 2191],\n [602, 2113],\n [609, 2103],\n [610, 2095],\n [601, 2090],\n [599, 2096],\n [593, 2093],\n [594, 2091],\n [593, 2090],\n [603, 2073],\n [600, 2071],\n [598, 2074],\n [593, 2072],\n [585, 2086],\n [575, 2081],\n [577, 2077],\n [573, 2074],\n [573, 2068],\n [575, 2064],\n [564, 2058],\n [570, 2048],\n [555, 2040],\n [552, 2046],\n [548, 2044],\n [551, 2035],\n [530, 2023],\n [532, 2017],\n [529, 2015],\n [534, 2007],\n [520, 1999],\n [517, 2002],\n [510, 2000],\n [514, 1993],\n [495, 1981],\n [501, 1972],\n [502, 1967],\n [498, 1966],\n [496, 1962],\n [497, 1959],\n [489, 1947],\n [490, 1945],\n [493, 1943],\n [503, 1939],\n [503, 1937],\n [500, 1935],\n [499, 1925],\n [495, 1918],\n [495, 1915],\n [499, 1911],\n [466, 1904],\n [431, 1894],\n [400, 1880],\n [366, 1861],\n [344, 1845],\n [306, 1810],\n [281, 1779],\n [258, 1740],\n [239, 1693],\n [217, 1692],\n [0, 1691],\n [-80, 1691],\n [-80, 1971],\n [-79, 1970],\n [-77, 1974],\n [-72, 1979],\n [-72, 1982],\n [-74, 1988],\n [-80, 1996],\n [-77, 2000],\n [-79, 2001],\n [-78, 2006],\n [-76, 2012],\n [-72, 2015],\n [-73, 2020],\n [-80, 2026],\n [-80, 2080],\n [-78, 2081],\n [-79, 2085],\n [-73, 2086],\n [-69, 2090],\n [-64, 2086],\n [-63, 2093],\n [-65, 2097],\n [-74, 2105],\n [-77, 2115],\n [-80, 2117],\n [-78, 2120],\n [-80, 2123],\n [-80, 2134],\n [-77, 2142],\n [-71, 2150],\n [-72, 2157],\n [0, 2287],\n [3, 2293],\n [5, 2310],\n [2, 2342],\n [2, 2369],\n [0, 2380],\n [-8, 2427],\n [0, 2431],\n [69, 2466],\n [93, 2480],\n [283, 2632],\n [351, 2680],\n [360, 2670],\n [367, 2670],\n [381, 2681],\n [388, 2685],\n [398, 2686]]],\n [[[512, 1924],\n [518, 1918],\n [521, 1913],\n [502, 1911],\n [506, 1921],\n [512, 1924]]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '029',\n 'FIPS': '42029',\n 'STATE': 'PA',\n 'NAME': 'Chester',\n 'LSAD': 'County'},\n 'id': 2187,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[841, 2345],\n [846, 2340],\n [898, 2250],\n [912, 2257],\n [952, 2195],\n [963, 2176],\n [957, 2172],\n [959, 2167],\n [964, 2161],\n [972, 2160],\n [979, 2153],\n [986, 2155],\n [991, 2151],\n [995, 2154],\n [1004, 2151],\n [1004, 2146],\n [1000, 2142],\n [1003, 2138],\n [1002, 2135],\n [999, 2128],\n [1001, 2118],\n [1006, 2112],\n [1016, 2109],\n [1016, 2102],\n [1020, 2102],\n [1023, 2101],\n [1020, 2092],\n [1013, 2093],\n [1009, 2087],\n [1011, 2086],\n [1011, 2082],\n [1005, 2071],\n [1006, 2056],\n [999, 2049],\n [1001, 2045],\n [996, 2040],\n [999, 2038],\n [993, 2029],\n [994, 2027],\n [990, 2021],\n [992, 2020],\n [988, 2015],\n [984, 2014],\n [983, 2008],\n [980, 2007],\n [983, 2002],\n [980, 2001],\n [983, 1996],\n [980, 1996],\n [983, 1985],\n [1004, 1985],\n [1015, 1977],\n [1023, 1986],\n [1045, 1983],\n [1051, 1984],\n [1051, 1975],\n [1058, 1964],\n [1043, 1956],\n [1023, 1947],\n [970, 1934],\n [938, 1932],\n [914, 1935],\n [895, 1934],\n [884, 1933],\n [868, 1927],\n [849, 1916],\n [797, 1869],\n [761, 1843],\n [742, 1857],\n [705, 1878],\n [664, 1895],\n [639, 1903],\n [610, 1909],\n [580, 1912],\n [535, 1914],\n [521, 1913],\n [518, 1918],\n [512, 1924],\n [508, 1922],\n [502, 1911],\n [499, 1911],\n [495, 1915],\n [495, 1918],\n [499, 1925],\n [500, 1935],\n [503, 1937],\n [503, 1939],\n [493, 1943],\n [490, 1945],\n [489, 1947],\n [497, 1959],\n [496, 1962],\n [498, 1966],\n [502, 1967],\n [501, 1972],\n [495, 1981],\n [514, 1993],\n [510, 2000],\n [517, 2002],\n [520, 1999],\n [534, 2007],\n [529, 2015],\n [532, 2017],\n [530, 2023],\n [551, 2035],\n [548, 2044],\n [552, 2046],\n [555, 2040],\n [570, 2048],\n [564, 2058],\n [575, 2064],\n [573, 2068],\n [573, 2074],\n [577, 2077],\n [575, 2081],\n [585, 2086],\n [593, 2072],\n [598, 2074],\n [600, 2071],\n [603, 2073],\n [593, 2090],\n [594, 2091],\n [593, 2093],\n [599, 2096],\n [601, 2090],\n [610, 2095],\n [609, 2103],\n [602, 2113],\n [730, 2191],\n [731, 2198],\n [727, 2204],\n [728, 2208],\n [724, 2206],\n [720, 2208],\n [705, 2207],\n [776, 2272],\n [761, 2299],\n [841, 2345]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '045',\n 'FIPS': '42045',\n 'STATE': 'PA',\n 'NAME': 'Delaware',\n 'LSAD': 'County'},\n 'id': 2186,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1190, 4074],\n [1191, 4070],\n [1190, 4065],\n [1194, 4044],\n [1203, 4035],\n [1211, 4018],\n [1212, 4012],\n [1226, 3989],\n [1250, 3969],\n [1255, 3956],\n [1256, 3931],\n [1270, 3914],\n [1280, 3899],\n [1287, 3891],\n [1290, 3885],\n [1291, 3876],\n [1288, 3865],\n [1272, 3842],\n [1269, 3841],\n [1262, 3841],\n [1254, 3845],\n [1233, 3844],\n [1226, 3840],\n [1223, 3832],\n [1224, 3825],\n [1241, 3807],\n [1243, 3803],\n [1243, 3797],\n [1234, 3780],\n [1225, 3773],\n [1219, 3764],\n [1219, 3749],\n [1207, 3732],\n [1203, 3730],\n [1195, 3729],\n [1186, 3724],\n [1174, 3703],\n [1170, 3699],\n [1148, 3701],\n [1127, 3708],\n [1118, 3708],\n [1115, 3707],\n [1110, 3697],\n [1107, 3681],\n [1105, 3676],\n [1099, 3671],\n [1086, 3664],\n [1079, 3656],\n [1081, 3645],\n [1095, 3629],\n [1100, 3618],\n [1089, 3592],\n [1082, 3584],\n [1068, 3541],\n [1073, 3529],\n [1079, 3521],\n [1088, 3518],\n [1097, 3519],\n [1102, 3518],\n [1107, 3514],\n [1108, 3511],\n [1107, 3505],\n [1099, 3492],\n [1087, 3484],\n [1073, 3460],\n [1073, 3455],\n [1085, 3444],\n [1091, 3412],\n [1088, 3404],\n [1077, 3402],\n [1071, 3398],\n [1072, 3393],\n [1079, 3382],\n [992, 3332],\n [977, 3322],\n [938, 3292],\n [880, 3244],\n [777, 3328],\n [777, 3330],\n [795, 3359],\n [799, 3393],\n [813, 3394],\n [811, 3398],\n [808, 3398],\n [806, 3404],\n [807, 3411],\n [800, 3418],\n [801, 3429],\n [795, 3430],\n [783, 3442],\n [739, 3497],\n [739, 3508],\n [664, 3472],\n [661, 3481],\n [661, 3494],\n [659, 3502],\n [653, 3511],\n [643, 3515],\n [640, 3517],\n [633, 3537],\n [622, 3548],\n [605, 3582],\n [600, 3595],\n [599, 3618],\n [598, 3623],\n [593, 3627],\n [583, 3630],\n [578, 3626],\n [576, 3611],\n [571, 3604],\n [558, 3598],\n [552, 3597],\n [543, 3602],\n [538, 3607],\n [533, 3621],\n [528, 3625],\n [509, 3622],\n [502, 3627],\n [496, 3641],\n [489, 3653],\n [486, 3666],\n [487, 3684],\n [482, 3694],\n [485, 3703],\n [484, 3712],\n [478, 3725],\n [488, 3726],\n [494, 3730],\n [498, 3737],\n [524, 3748],\n [553, 3752],\n [562, 3758],\n [577, 3763],\n [596, 3762],\n [609, 3766],\n [625, 3767],\n [668, 3775],\n [675, 3778],\n [721, 3785],\n [753, 3795],\n [758, 3800],\n [807, 3809],\n [868, 3837],\n [898, 3857],\n [928, 3864],\n [952, 3877],\n [967, 3890],\n [976, 3908],\n [1027, 3941],\n [1014, 3960],\n [1042, 3980],\n [1048, 3996],\n [1081, 4012],\n [1092, 4014],\n [1106, 4020],\n [1108, 4030],\n [1119, 4035],\n [1143, 4037],\n [1154, 4041],\n [1177, 4056],\n [1190, 4074]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '095',\n 'FIPS': '42095',\n 'STATE': 'PA',\n 'NAME': 'Northampton',\n 'LSAD': 'County'},\n 'id': 2208,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1978, 4176],\n [1938, 4131],\n [1910, 4096],\n [1889, 4068],\n [1888, 4056],\n [1897, 4040],\n [1897, 4029],\n [1889, 4014],\n [1873, 4001],\n [1871, 3998],\n [1870, 3992],\n [1872, 3987],\n [1871, 3984],\n [1855, 3977],\n [1846, 3969],\n [1832, 3960],\n [1827, 3961],\n [1820, 3959],\n [1818, 3952],\n [1814, 3951],\n [1808, 3954],\n [1796, 3946],\n [1792, 3945],\n [1786, 3940],\n [1782, 3942],\n [1785, 3951],\n [1783, 3958],\n [1769, 3970],\n [1761, 3974],\n [1756, 3982],\n [1749, 3979],\n [1734, 3980],\n [1727, 3976],\n [1727, 3972],\n [1722, 3968],\n [1715, 3968],\n [1713, 3965],\n [1711, 3968],\n [1708, 3965],\n [1706, 3966],\n [1703, 3965],\n [1594, 4096],\n [1570, 4125],\n [1529, 4176],\n [1978, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '037',\n 'FIPS': '34037',\n 'STATE': 'NJ',\n 'NAME': 'Sussex',\n 'LSAD': 'County'},\n 'id': 2225,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1526, 3726],\n [1592, 3670],\n [1594, 3665],\n [1617, 3642],\n [1622, 3639],\n [1633, 3639],\n [1638, 3638],\n [1638, 3636],\n [1644, 3636],\n [1659, 3638],\n [1666, 3634],\n [1704, 3625],\n [1725, 3617],\n [1740, 3607],\n [1740, 3603],\n [1755, 3595],\n [1767, 3595],\n [1768, 3590],\n [1773, 3586],\n [1772, 3582],\n [1774, 3569],\n [1777, 3567],\n [1778, 3565],\n [1776, 3564],\n [1779, 3558],\n [1778, 3555],\n [1772, 3550],\n [1771, 3541],\n [1768, 3533],\n [1776, 3526],\n [1775, 3519],\n [1771, 3515],\n [1769, 3500],\n [1764, 3494],\n [1757, 3491],\n [1762, 3478],\n [1769, 3474],\n [1774, 3465],\n [1768, 3455],\n [1767, 3450],\n [1770, 3446],\n [1769, 3440],\n [1762, 3434],\n [1765, 3426],\n [1774, 3425],\n [1778, 3426],\n [1783, 3424],\n [1786, 3418],\n [1786, 3413],\n [1797, 3406],\n [1786, 3387],\n [1783, 3389],\n [1771, 3366],\n [1775, 3364],\n [1766, 3344],\n [1783, 3335],\n [1766, 3300],\n [1758, 3295],\n [1751, 3286],\n [1740, 3277],\n [1693, 3182],\n [1690, 3180],\n [1685, 3182],\n [1678, 3190],\n [1678, 3193],\n [1674, 3197],\n [1660, 3203],\n [1732, 3028],\n [1644, 3014],\n [1654, 2953],\n [1555, 2940],\n [1575, 2880],\n [1464, 2866],\n [1448, 2871],\n [1447, 2872],\n [1444, 2901],\n [1440, 2915],\n [1433, 2936],\n [1416, 2977],\n [1410, 2982],\n [1390, 2991],\n [1381, 2999],\n [1371, 3002],\n [1360, 2997],\n [1340, 2991],\n [1329, 2989],\n [1324, 2990],\n [1312, 2995],\n [1298, 3009],\n [1284, 3013],\n [1280, 3017],\n [1276, 3026],\n [1274, 3046],\n [1267, 3075],\n [1263, 3088],\n [1267, 3106],\n [1267, 3122],\n [1272, 3129],\n [1275, 3138],\n [1275, 3147],\n [1269, 3194],\n [1271, 3224],\n [1269, 3244],\n [1265, 3254],\n [1251, 3266],\n [1219, 3304],\n [1195, 3314],\n [1166, 3319],\n [1159, 3318],\n [1151, 3314],\n [1135, 3299],\n [1128, 3296],\n [1120, 3296],\n [1110, 3298],\n [1099, 3303],\n [1093, 3307],\n [1083, 3320],\n [1081, 3326],\n [1082, 3331],\n [1087, 3340],\n [1088, 3349],\n [1096, 3351],\n [1102, 3355],\n [1104, 3361],\n [1111, 3363],\n [1114, 3375],\n [1120, 3378],\n [1124, 3385],\n [1128, 3387],\n [1130, 3393],\n [1129, 3399],\n [1141, 3407],\n [1147, 3405],\n [1150, 3408],\n [1150, 3416],\n [1153, 3420],\n [1168, 3424],\n [1180, 3431],\n [1205, 3440],\n [1208, 3442],\n [1207, 3447],\n [1211, 3451],\n [1226, 3458],\n [1235, 3468],\n [1236, 3472],\n [1247, 3476],\n [1254, 3488],\n [1264, 3494],\n [1267, 3497],\n [1275, 3499],\n [1278, 3505],\n [1282, 3510],\n [1288, 3512],\n [1295, 3518],\n [1301, 3516],\n [1303, 3517],\n [1308, 3524],\n [1310, 3526],\n [1315, 3526],\n [1333, 3542],\n [1337, 3542],\n [1341, 3547],\n [1345, 3543],\n [1351, 3547],\n [1356, 3544],\n [1361, 3545],\n [1378, 3563],\n [1393, 3569],\n [1396, 3578],\n [1410, 3577],\n [1419, 3597],\n [1424, 3602],\n [1426, 3606],\n [1435, 3610],\n [1443, 3624],\n [1451, 3631],\n [1452, 3636],\n [1465, 3651],\n [1467, 3656],\n [1470, 3658],\n [1473, 3663],\n [1485, 3672],\n [1489, 3677],\n [1489, 3680],\n [1494, 3686],\n [1507, 3695],\n [1506, 3698],\n [1507, 3706],\n [1505, 3712],\n [1517, 3719],\n [1526, 3726]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '019',\n 'FIPS': '34019',\n 'STATE': 'NJ',\n 'NAME': 'Hunterdon',\n 'LSAD': 'County'},\n 'id': 2209,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2012, 3669],\n [2018, 3663],\n [2019, 3655],\n [2017, 3652],\n [2023, 3646],\n [2022, 3622],\n [2026, 3623],\n [2028, 3621],\n [2035, 3622],\n [2043, 3616],\n [2046, 3617],\n [2052, 3611],\n [2048, 3598],\n [2048, 3595],\n [2051, 3594],\n [2052, 3591],\n [2058, 3587],\n [2057, 3581],\n [2054, 3578],\n [2056, 3575],\n [2061, 3574],\n [2068, 3568],\n [2069, 3561],\n [2071, 3561],\n [2073, 3559],\n [2073, 3547],\n [2070, 3544],\n [2067, 3545],\n [2066, 3541],\n [2069, 3539],\n [2069, 3537],\n [2062, 3535],\n [2058, 3537],\n [2055, 3529],\n [2053, 3527],\n [2052, 3521],\n [2056, 3501],\n [2051, 3498],\n [2050, 3495],\n [2043, 3494],\n [2044, 3491],\n [2050, 3490],\n [2051, 3486],\n [2056, 3482],\n [2054, 3479],\n [2056, 3469],\n [2059, 3469],\n [2055, 3465],\n [2060, 3464],\n [2061, 3461],\n [2070, 3462],\n [2071, 3463],\n [2075, 3460],\n [2082, 3466],\n [2086, 3475],\n [2085, 3477],\n [2088, 3480],\n [2091, 3478],\n [2095, 3479],\n [2094, 3483],\n [2100, 3484],\n [2102, 3482],\n [2106, 3484],\n [2106, 3487],\n [2112, 3488],\n [2114, 3485],\n [2117, 3487],\n [2116, 3491],\n [2128, 3493],\n [2133, 3498],\n [2137, 3496],\n [2149, 3503],\n [2150, 3506],\n [2164, 3469],\n [2175, 3475],\n [2185, 3486],\n [2196, 3491],\n [2198, 3487],\n [2202, 3485],\n [2208, 3477],\n [2211, 3478],\n [2214, 3483],\n [2220, 3485],\n [2224, 3491],\n [2231, 3495],\n [2234, 3493],\n [2236, 3487],\n [2235, 3481],\n [2235, 3473],\n [2229, 3462],\n [2229, 3455],\n [2226, 3447],\n [2220, 3446],\n [2219, 3444],\n [2217, 3429],\n [2213, 3418],\n [2206, 3414],\n [2205, 3405],\n [2199, 3397],\n [2193, 3396],\n [2184, 3385],\n [2173, 3383],\n [2169, 3381],\n [2164, 3373],\n [2159, 3373],\n [2152, 3365],\n [2143, 3362],\n [2137, 3362],\n [2126, 3354],\n [2118, 3345],\n [2108, 3340],\n [2104, 3341],\n [2097, 3338],\n [2094, 3340],\n [2091, 3337],\n [2091, 3334],\n [2086, 3339],\n [2076, 3331],\n [2070, 3329],\n [2071, 3326],\n [2063, 3315],\n [2059, 3307],\n [2056, 3305],\n [2057, 3292],\n [2054, 3288],\n [2059, 3286],\n [2065, 3280],\n [2068, 3273],\n [2072, 3255],\n [2083, 3244],\n [2100, 3230],\n [2119, 3197],\n [2122, 3195],\n [2141, 3190],\n [2143, 3178],\n [2151, 3171],\n [2153, 3166],\n [2152, 3156],\n [2140, 3148],\n [2125, 3143],\n [2119, 3139],\n [2102, 3115],\n [2082, 3099],\n [2075, 3091],\n [2064, 3080],\n [2055, 3068],\n [2041, 3056],\n [2013, 3040],\n [1971, 3018],\n [1968, 3014],\n [1969, 2989],\n [1966, 2981],\n [1966, 2971],\n [1960, 2967],\n [1955, 2956],\n [1944, 2945],\n [1928, 2936],\n [1919, 2933],\n [1922, 2940],\n [1921, 2944],\n [1915, 2952],\n [1870, 2965],\n [1839, 2952],\n [1791, 2943],\n [1770, 2935],\n [1660, 3203],\n [1674, 3197],\n [1678, 3193],\n [1678, 3190],\n [1685, 3182],\n [1692, 3180],\n [1740, 3277],\n [1751, 3286],\n [1758, 3295],\n [1766, 3300],\n [1783, 3335],\n [1766, 3344],\n [1775, 3364],\n [1771, 3366],\n [1783, 3389],\n [1786, 3387],\n [1797, 3406],\n [1786, 3413],\n [1786, 3418],\n [1783, 3424],\n [1778, 3426],\n [1774, 3425],\n [1765, 3426],\n [1762, 3434],\n [1769, 3440],\n [1770, 3446],\n [1767, 3450],\n [1768, 3455],\n [1774, 3465],\n [1769, 3474],\n [1762, 3478],\n [1757, 3491],\n [1764, 3494],\n [1769, 3500],\n [1771, 3515],\n [1775, 3519],\n [1776, 3526],\n [1768, 3533],\n [1771, 3541],\n [1772, 3550],\n [1778, 3555],\n [1779, 3558],\n [1776, 3564],\n [1778, 3565],\n [1777, 3567],\n [1774, 3569],\n [1772, 3582],\n [1773, 3586],\n [1768, 3590],\n [1767, 3595],\n [1848, 3621],\n [2012, 3669]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '035',\n 'FIPS': '34035',\n 'STATE': 'NJ',\n 'NAME': 'Somerset',\n 'LSAD': 'County'},\n 'id': 2211,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1079, 3382],\n [1085, 3370],\n [1088, 3347],\n [1087, 3340],\n [1082, 3331],\n [1082, 3324],\n [1085, 3316],\n [1099, 3303],\n [1110, 3298],\n [1128, 3296],\n [1135, 3299],\n [1151, 3314],\n [1159, 3318],\n [1166, 3319],\n [1195, 3314],\n [1219, 3304],\n [1251, 3266],\n [1265, 3254],\n [1269, 3244],\n [1271, 3224],\n [1269, 3194],\n [1275, 3147],\n [1275, 3138],\n [1272, 3129],\n [1267, 3122],\n [1267, 3106],\n [1263, 3088],\n [1267, 3075],\n [1274, 3046],\n [1276, 3026],\n [1280, 3017],\n [1284, 3013],\n [1298, 3009],\n [1312, 2995],\n [1324, 2990],\n [1329, 2989],\n [1340, 2991],\n [1360, 2997],\n [1371, 3002],\n [1381, 2999],\n [1390, 2991],\n [1410, 2982],\n [1416, 2977],\n [1433, 2936],\n [1440, 2915],\n [1444, 2901],\n [1447, 2872],\n [1453, 2864],\n [1499, 2823],\n [1506, 2821],\n [1516, 2820],\n [1523, 2816],\n [1539, 2802],\n [1557, 2782],\n [1569, 2762],\n [1595, 2697],\n [1604, 2688],\n [1622, 2679],\n [1628, 2674],\n [1663, 2656],\n [1698, 2630],\n [1714, 2598],\n [1723, 2572],\n [1727, 2568],\n [1738, 2564],\n [1748, 2558],\n [1770, 2525],\n [1771, 2512],\n [1767, 2499],\n [1743, 2477],\n [1735, 2475],\n [1722, 2476],\n [1716, 2475],\n [1701, 2465],\n [1683, 2449],\n [1673, 2449],\n [1656, 2453],\n [1633, 2462],\n [1624, 2461],\n [1619, 2455],\n [1615, 2448],\n [1609, 2432],\n [1605, 2417],\n [1601, 2411],\n [1593, 2405],\n [1577, 2397],\n [1572, 2388],\n [1570, 2381],\n [1564, 2376],\n [1540, 2369],\n [1529, 2364],\n [1498, 2353],\n [1481, 2355],\n [1474, 2354],\n [1446, 2340],\n [1403, 2312],\n [1394, 2322],\n [1389, 2323],\n [1388, 2329],\n [1394, 2334],\n [1394, 2345],\n [1405, 2356],\n [1408, 2365],\n [1420, 2368],\n [1426, 2380],\n [1426, 2390],\n [1429, 2397],\n [1429, 2402],\n [1421, 2406],\n [1422, 2416],\n [1416, 2425],\n [1418, 2428],\n [1419, 2438],\n [1418, 2443],\n [1414, 2444],\n [1409, 2442],\n [1404, 2448],\n [1395, 2447],\n [1390, 2450],\n [1390, 2454],\n [1384, 2454],\n [1379, 2460],\n [1374, 2460],\n [1377, 2469],\n [1375, 2471],\n [1368, 2463],\n [1184, 2606],\n [1065, 2701],\n [937, 2799],\n [745, 2952],\n [660, 3017],\n [769, 3151],\n [872, 3239],\n [880, 3244],\n [938, 3292],\n [977, 3322],\n [992, 3332],\n [1079, 3382]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '017',\n 'FIPS': '42017',\n 'STATE': 'PA',\n 'NAME': 'Bucks',\n 'LSAD': 'County'},\n 'id': 2197,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1343, 2482],\n [1368, 2463],\n [1375, 2471],\n [1377, 2469],\n [1374, 2460],\n [1379, 2460],\n [1384, 2454],\n [1390, 2454],\n [1390, 2450],\n [1395, 2447],\n [1404, 2448],\n [1409, 2442],\n [1414, 2444],\n [1418, 2443],\n [1419, 2438],\n [1418, 2428],\n [1416, 2425],\n [1422, 2416],\n [1421, 2406],\n [1429, 2402],\n [1429, 2397],\n [1426, 2390],\n [1426, 2380],\n [1420, 2368],\n [1408, 2365],\n [1405, 2356],\n [1394, 2345],\n [1394, 2334],\n [1388, 2329],\n [1389, 2323],\n [1394, 2322],\n [1403, 2312],\n [1389, 2301],\n [1349, 2260],\n [1308, 2244],\n [1297, 2236],\n [1291, 2228],\n [1279, 2205],\n [1260, 2183],\n [1236, 2172],\n [1208, 2163],\n [1180, 2146],\n [1171, 2133],\n [1168, 2119],\n [1167, 2094],\n [1169, 2081],\n [1179, 2052],\n [1175, 2030],\n [1158, 2004],\n [1154, 2000],\n [1146, 1997],\n [1099, 1995],\n [1090, 1993],\n [1058, 1964],\n [1051, 1975],\n [1051, 1984],\n [1045, 1983],\n [1023, 1986],\n [1015, 1977],\n [1004, 1985],\n [983, 1985],\n [980, 1996],\n [983, 1996],\n [980, 2001],\n [983, 2002],\n [980, 2007],\n [983, 2008],\n [984, 2014],\n [988, 2015],\n [992, 2020],\n [990, 2021],\n [994, 2027],\n [993, 2029],\n [999, 2038],\n [996, 2040],\n [1001, 2045],\n [999, 2049],\n [1006, 2056],\n [1005, 2071],\n [1011, 2082],\n [1011, 2086],\n [1009, 2087],\n [1013, 2093],\n [1020, 2092],\n [1023, 2101],\n [1020, 2102],\n [1016, 2102],\n [1016, 2109],\n [1006, 2112],\n [1001, 2118],\n [999, 2128],\n [1002, 2135],\n [1003, 2138],\n [1000, 2142],\n [1004, 2146],\n [1004, 2151],\n [995, 2154],\n [991, 2151],\n [986, 2155],\n [979, 2153],\n [972, 2160],\n [964, 2161],\n [957, 2172],\n [982, 2187],\n [993, 2196],\n [1063, 2239],\n [1065, 2242],\n [1056, 2249],\n [1048, 2259],\n [1027, 2273],\n [1018, 2281],\n [1007, 2292],\n [1002, 2299],\n [987, 2313],\n [980, 2322],\n [1040, 2396],\n [1091, 2358],\n [1108, 2381],\n [1206, 2307],\n [1210, 2313],\n [1228, 2327],\n [1238, 2340],\n [1224, 2351],\n [1239, 2369],\n [1245, 2377],\n [1252, 2383],\n [1280, 2421],\n [1335, 2471],\n [1343, 2482]]]},\n 'properties': {'STATE_FIPS': '42',\n 'COUNTY_FIP': '101',\n 'FIPS': '42101',\n 'STATE': 'PA',\n 'NAME': 'Philadelphia',\n 'LSAD': 'County'},\n 'id': 2177,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1732, 3028],\n [1770, 2935],\n [1791, 2943],\n [1839, 2952],\n [1870, 2965],\n [1915, 2952],\n [1921, 2944],\n [1922, 2940],\n [1916, 2919],\n [1912, 2916],\n [1904, 2878],\n [1904, 2869],\n [1914, 2857],\n [1912, 2847],\n [1915, 2845],\n [1915, 2840],\n [1916, 2838],\n [1925, 2839],\n [1936, 2833],\n [1943, 2832],\n [1947, 2821],\n [1953, 2819],\n [1957, 2820],\n [1969, 2816],\n [1972, 2803],\n [1976, 2799],\n [1983, 2797],\n [1991, 2790],\n [1995, 2791],\n [1999, 2785],\n [2007, 2783],\n [2010, 2776],\n [2016, 2773],\n [2021, 2773],\n [2027, 2775],\n [2028, 2777],\n [2032, 2775],\n [2036, 2769],\n [2040, 2771],\n [2045, 2776],\n [2052, 2780],\n [2061, 2772],\n [2061, 2765],\n [2072, 2766],\n [2077, 2759],\n [2086, 2754],\n [2088, 2754],\n [2091, 2751],\n [2095, 2754],\n [2119, 2741],\n [2116, 2702],\n [2112, 2702],\n [2112, 2700],\n [2119, 2689],\n [2121, 2683],\n [2069, 2652],\n [2031, 2632],\n [2025, 2624],\n [2023, 2615],\n [2011, 2600],\n [2002, 2581],\n [1991, 2573],\n [1985, 2576],\n [1968, 2569],\n [1957, 2562],\n [1954, 2562],\n [1952, 2574],\n [1948, 2575],\n [1927, 2567],\n [1966, 2483],\n [1945, 2480],\n [1937, 2485],\n [1938, 2487],\n [1933, 2485],\n [1926, 2487],\n [1924, 2489],\n [1920, 2487],\n [1916, 2488],\n [1914, 2490],\n [1915, 2495],\n [1914, 2497],\n [1913, 2503],\n [1908, 2504],\n [1908, 2503],\n [1904, 2503],\n [1902, 2506],\n [1899, 2503],\n [1897, 2508],\n [1893, 2509],\n [1892, 2513],\n [1887, 2515],\n [1885, 2513],\n [1880, 2517],\n [1878, 2516],\n [1874, 2521],\n [1868, 2521],\n [1867, 2524],\n [1862, 2528],\n [1859, 2528],\n [1857, 2534],\n [1853, 2533],\n [1852, 2536],\n [1849, 2537],\n [1847, 2537],\n [1846, 2534],\n [1845, 2537],\n [1841, 2538],\n [1838, 2535],\n [1835, 2536],\n [1840, 2543],\n [1838, 2548],\n [1840, 2550],\n [1832, 2550],\n [1831, 2554],\n [1824, 2559],\n [1823, 2553],\n [1822, 2557],\n [1816, 2556],\n [1813, 2561],\n [1808, 2560],\n [1810, 2567],\n [1806, 2567],\n [1799, 2570],\n [1796, 2566],\n [1790, 2566],\n [1786, 2563],\n [1788, 2560],\n [1783, 2554],\n [1786, 2549],\n [1790, 2547],\n [1787, 2539],\n [1788, 2537],\n [1796, 2541],\n [1793, 2527],\n [1783, 2517],\n [1782, 2510],\n [1774, 2501],\n [1767, 2500],\n [1771, 2512],\n [1770, 2525],\n [1748, 2558],\n [1738, 2564],\n [1727, 2568],\n [1723, 2572],\n [1714, 2598],\n [1698, 2630],\n [1663, 2656],\n [1628, 2674],\n [1622, 2679],\n [1604, 2688],\n [1595, 2697],\n [1569, 2762],\n [1557, 2782],\n [1539, 2802],\n [1523, 2816],\n [1516, 2820],\n [1506, 2821],\n [1499, 2823],\n [1486, 2834],\n [1453, 2864],\n [1448, 2871],\n [1464, 2866],\n [1575, 2880],\n [1555, 2940],\n [1654, 2953],\n [1644, 3014],\n [1732, 3028]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '021',\n 'FIPS': '34021',\n 'STATE': 'NJ',\n 'NAME': 'Mercer',\n 'LSAD': 'County'},\n 'id': 2196,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1799, 2570],\n [1806, 2567],\n [1810, 2567],\n [1808, 2560],\n [1813, 2561],\n [1816, 2556],\n [1822, 2557],\n [1823, 2553],\n [1824, 2559],\n [1831, 2554],\n [1832, 2550],\n [1840, 2550],\n [1838, 2548],\n [1840, 2543],\n [1835, 2536],\n [1838, 2535],\n [1841, 2538],\n [1845, 2537],\n [1846, 2534],\n [1847, 2537],\n [1849, 2537],\n [1852, 2536],\n [1853, 2533],\n [1857, 2534],\n [1859, 2528],\n [1862, 2528],\n [1867, 2524],\n [1868, 2521],\n [1874, 2521],\n [1878, 2516],\n [1880, 2517],\n [1885, 2513],\n [1887, 2515],\n [1892, 2513],\n [1893, 2509],\n [1897, 2508],\n [1899, 2503],\n [1902, 2506],\n [1904, 2503],\n [1908, 2503],\n [1908, 2504],\n [1913, 2503],\n [1914, 2497],\n [1915, 2495],\n [1914, 2490],\n [1916, 2488],\n [1920, 2487],\n [1924, 2489],\n [1926, 2487],\n [1933, 2485],\n [1938, 2487],\n [1937, 2485],\n [1945, 2480],\n [1966, 2483],\n [2052, 2289],\n [2084, 2203],\n [2167, 1994],\n [2254, 1789],\n [2253, 1454],\n [2250, 1450],\n [2244, 1432],\n [2242, 1417],\n [2238, 1409],\n [2235, 1408],\n [2232, 1404],\n [2226, 1404],\n [2224, 1406],\n [2221, 1405],\n [2216, 1405],\n [2212, 1402],\n [2214, 1393],\n [2219, 1390],\n [2214, 1380],\n [2209, 1380],\n [2201, 1373],\n [2199, 1373],\n [2191, 1377],\n [2187, 1376],\n [2184, 1374],\n [2184, 1369],\n [2190, 1359],\n [2190, 1353],\n [2188, 1350],\n [2182, 1350],\n [2175, 1357],\n [2172, 1363],\n [2176, 1378],\n [2172, 1383],\n [2169, 1384],\n [2164, 1382],\n [2162, 1378],\n [2162, 1367],\n [2158, 1361],\n [2154, 1362],\n [2152, 1366],\n [2150, 1376],\n [2148, 1379],\n [2144, 1379],\n [2139, 1377],\n [2134, 1371],\n [2130, 1369],\n [2126, 1372],\n [2120, 1382],\n [2113, 1383],\n [2108, 1379],\n [2103, 1379],\n [2101, 1381],\n [2099, 1391],\n [2092, 1395],\n [2088, 1403],\n [2080, 1402],\n [2076, 1404],\n [2072, 1412],\n [2067, 1411],\n [2059, 1403],\n [2054, 1403],\n [2051, 1405],\n [2045, 1411],\n [2037, 1413],\n [2034, 1417],\n [2031, 1425],\n [2033, 1434],\n [2022, 1442],\n [2015, 1457],\n [2000, 1455],\n [1992, 1459],\n [1983, 1458],\n [1975, 1468],\n [1969, 1471],\n [1966, 1481],\n [1964, 1484],\n [1954, 1488],\n [1936, 1502],\n [1929, 1506],\n [1926, 1505],\n [1916, 1512],\n [1914, 1512],\n [1911, 1509],\n [1910, 1503],\n [1906, 1499],\n [1902, 1501],\n [1902, 1507],\n [1900, 1513],\n [1896, 1513],\n [1893, 1505],\n [1890, 1504],\n [1889, 1505],\n [1888, 1510],\n [1878, 1509],\n [1878, 1514],\n [1874, 1515],\n [1871, 1511],\n [1868, 1512],\n [1868, 1518],\n [1866, 1520],\n [1861, 1521],\n [1863, 1530],\n [1861, 1538],\n [1865, 1546],\n [1862, 1550],\n [1863, 1554],\n [1863, 1559],\n [1860, 1560],\n [1862, 1563],\n [1861, 1566],\n [1863, 1569],\n [1860, 1591],\n [1854, 1601],\n [1854, 1606],\n [1851, 1615],\n [1846, 1623],\n [1842, 1625],\n [1837, 1634],\n [1718, 1733],\n [1716, 1731],\n [1713, 1735],\n [1706, 1738],\n [1702, 1745],\n [1696, 1747],\n [1693, 1758],\n [1684, 1761],\n [1680, 1769],\n [1681, 1771],\n [1678, 1775],\n [1676, 1776],\n [1669, 1787],\n [1659, 1794],\n [1646, 1809],\n [1632, 1813],\n [1628, 1816],\n [1603, 1822],\n [1591, 1821],\n [1587, 1819],\n [1584, 1819],\n [1579, 1815],\n [1560, 1811],\n [1545, 1805],\n [1526, 1806],\n [1517, 1810],\n [1513, 1819],\n [1506, 1823],\n [1472, 1980],\n [1464, 2006],\n [1451, 2015],\n [1447, 2016],\n [1443, 2024],\n [1439, 2027],\n [1435, 2032],\n [1435, 2041],\n [1430, 2055],\n [1421, 2065],\n [1418, 2069],\n [1402, 2070],\n [1392, 2080],\n [1389, 2081],\n [1387, 2086],\n [1388, 2089],\n [1393, 2089],\n [1393, 2094],\n [1397, 2095],\n [1402, 2102],\n [1400, 2105],\n [1392, 2106],\n [1385, 2105],\n [1381, 2107],\n [1376, 2106],\n [1368, 2106],\n [1364, 2109],\n [1359, 2108],\n [1352, 2115],\n [1345, 2116],\n [1344, 2118],\n [1342, 2125],\n [1346, 2132],\n [1346, 2137],\n [1340, 2142],\n [1340, 2149],\n [1338, 2150],\n [1337, 2161],\n [1342, 2163],\n [1342, 2167],\n [1340, 2170],\n [1343, 2172],\n [1343, 2175],\n [1351, 2181],\n [1345, 2182],\n [1341, 2189],\n [1344, 2192],\n [1340, 2191],\n [1338, 2199],\n [1336, 2201],\n [1335, 2198],\n [1328, 2202],\n [1325, 2207],\n [1324, 2204],\n [1321, 2204],\n [1314, 2212],\n [1305, 2210],\n [1303, 2207],\n [1300, 2209],\n [1296, 2208],\n [1293, 2203],\n [1278, 2204],\n [1291, 2228],\n [1297, 2236],\n [1308, 2244],\n [1349, 2260],\n [1389, 2301],\n [1402, 2312],\n [1446, 2340],\n [1474, 2354],\n [1481, 2355],\n [1498, 2353],\n [1529, 2364],\n [1540, 2369],\n [1564, 2376],\n [1568, 2379],\n [1574, 2393],\n [1577, 2397],\n [1593, 2405],\n [1601, 2411],\n [1605, 2417],\n [1609, 2432],\n [1615, 2448],\n [1619, 2455],\n [1624, 2461],\n [1633, 2462],\n [1656, 2453],\n [1678, 2448],\n [1683, 2449],\n [1701, 2465],\n [1716, 2475],\n [1722, 2476],\n [1735, 2475],\n [1743, 2477],\n [1767, 2500],\n [1774, 2501],\n [1782, 2510],\n [1783, 2517],\n [1793, 2527],\n [1796, 2541],\n [1788, 2537],\n [1787, 2539],\n [1790, 2547],\n [1786, 2549],\n [1783, 2554],\n [1788, 2560],\n [1786, 2563],\n [1790, 2566],\n [1796, 2566],\n [1799, 2570]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '005',\n 'FIPS': '34005',\n 'STATE': 'NJ',\n 'NAME': 'Burlington',\n 'LSAD': 'County'},\n 'id': 2178,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[217, 1692],\n [217, 1571],\n [219, 1532],\n [249, 1041],\n [240, 1045],\n [235, 1044],\n [235, 1047],\n [223, 1051],\n [222, 1046],\n [216, 1048],\n [213, 1046],\n [209, 1046],\n [206, 1041],\n [198, 1042],\n [193, 1037],\n [191, 1038],\n [186, 1046],\n [176, 1047],\n [173, 1050],\n [166, 1049],\n [155, 1035],\n [139, 1029],\n [134, 1023],\n [130, 1024],\n [120, 1017],\n [111, 1023],\n [107, 1021],\n [102, 1023],\n [81, 1017],\n [76, 1010],\n [71, 1012],\n [63, 1019],\n [49, 1017],\n [21, 1022],\n [14, 1030],\n [8, 1030],\n [0, 1025],\n [-5, 1023],\n [-11, 1025],\n [-17, 1032],\n [-21, 1034],\n [-39, 1036],\n [-48, 1033],\n [-56, 1022],\n [-60, 1021],\n [-64, 1021],\n [-75, 1034],\n [-80, 1037],\n [-80, 1144],\n [-66, 1150],\n [-57, 1168],\n [-57, 1174],\n [-76, 1194],\n [-80, 1194],\n [-80, 1260],\n [-71, 1292],\n [-56, 1329],\n [-43, 1345],\n [-43, 1367],\n [-48, 1381],\n [-80, 1391],\n [-80, 1691],\n [0, 1691],\n [217, 1692]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '015',\n 'FIPS': '24015',\n 'STATE': 'MD',\n 'NAME': 'Cecil',\n 'LSAD': 'County'},\n 'id': 2129,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1272, 1811],\n [1283, 1808],\n [1285, 1809],\n [1287, 1804],\n [1294, 1799],\n [1292, 1795],\n [1294, 1786],\n [1300, 1777],\n [1303, 1772],\n [1302, 1767],\n [1308, 1764],\n [1313, 1759],\n [1315, 1754],\n [1333, 1738],\n [1334, 1735],\n [1332, 1727],\n [1335, 1722],\n [1334, 1714],\n [1331, 1708],\n [1373, 1681],\n [1374, 1670],\n [1381, 1667],\n [1384, 1663],\n [1397, 1657],\n [1407, 1655],\n [1413, 1656],\n [1417, 1655],\n [1425, 1656],\n [1434, 1649],\n [1447, 1647],\n [1461, 1637],\n [1463, 1634],\n [1466, 1633],\n [1467, 1629],\n [1472, 1626],\n [1472, 1621],\n [1476, 1618],\n [1480, 1611],\n [1484, 1611],\n [1486, 1600],\n [1498, 1585],\n [1496, 1582],\n [1498, 1574],\n [1497, 1569],\n [1499, 1567],\n [1496, 1561],\n [1498, 1559],\n [1501, 1539],\n [1507, 1522],\n [1522, 1508],\n [1521, 1502],\n [1523, 1495],\n [1536, 1486],\n [1537, 1482],\n [1544, 1477],\n [1387, 1300],\n [1075, 1588],\n [1044, 1578],\n [1039, 1581],\n [1026, 1578],\n [1004, 1584],\n [1000, 1592],\n [999, 1606],\n [996, 1610],\n [991, 1611],\n [984, 1616],\n [972, 1617],\n [963, 1613],\n [961, 1615],\n [948, 1612],\n [938, 1621],\n [918, 1631],\n [914, 1630],\n [912, 1627],\n [907, 1629],\n [909, 1632],\n [907, 1634],\n [898, 1634],\n [895, 1631],\n [890, 1631],\n [889, 1639],\n [891, 1643],\n [884, 1644],\n [883, 1648],\n [878, 1651],\n [876, 1658],\n [870, 1660],\n [868, 1659],\n [859, 1664],\n [856, 1671],\n [840, 1675],\n [839, 1682],\n [836, 1681],\n [831, 1684],\n [832, 1689],\n [831, 1692],\n [828, 1692],\n [823, 1697],\n [822, 1696],\n [817, 1700],\n [817, 1704],\n [821, 1717],\n [816, 1725],\n [819, 1727],\n [815, 1727],\n [817, 1729],\n [815, 1732],\n [817, 1735],\n [812, 1742],\n [803, 1739],\n [801, 1742],\n [802, 1747],\n [800, 1746],\n [800, 1749],\n [796, 1749],\n [796, 1745],\n [793, 1747],\n [794, 1749],\n [789, 1753],\n [791, 1756],\n [794, 1754],\n [797, 1756],\n [797, 1759],\n [794, 1760],\n [798, 1765],\n [788, 1767],\n [787, 1762],\n [785, 1759],\n [783, 1762],\n [784, 1766],\n [778, 1766],\n [774, 1771],\n [776, 1782],\n [779, 1783],\n [782, 1779],\n [785, 1781],\n [790, 1790],\n [788, 1791],\n [785, 1788],\n [783, 1789],\n [781, 1794],\n [782, 1799],\n [779, 1807],\n [777, 1807],\n [775, 1803],\n [771, 1804],\n [774, 1810],\n [772, 1816],\n [769, 1817],\n [764, 1812],\n [757, 1813],\n [748, 1809],\n [734, 1808],\n [723, 1803],\n [728, 1808],\n [775, 1833],\n [761, 1843],\n [797, 1869],\n [849, 1916],\n [868, 1927],\n [884, 1933],\n [895, 1934],\n [914, 1935],\n [938, 1932],\n [970, 1934],\n [1011, 1943],\n [1043, 1956],\n [1059, 1965],\n [1090, 1993],\n [1099, 1995],\n [1146, 1997],\n [1154, 2000],\n [1161, 2008],\n [1177, 1998],\n [1180, 1993],\n [1173, 1989],\n [1174, 1987],\n [1190, 1978],\n [1184, 1976],\n [1187, 1971],\n [1188, 1965],\n [1192, 1959],\n [1195, 1960],\n [1193, 1968],\n [1201, 1959],\n [1196, 1959],\n [1201, 1952],\n [1208, 1953],\n [1205, 1956],\n [1209, 1960],\n [1211, 1960],\n [1213, 1957],\n [1211, 1955],\n [1213, 1943],\n [1216, 1942],\n [1215, 1947],\n [1228, 1938],\n [1229, 1931],\n [1235, 1930],\n [1238, 1924],\n [1241, 1924],\n [1244, 1919],\n [1239, 1912],\n [1241, 1910],\n [1242, 1906],\n [1245, 1903],\n [1242, 1898],\n [1245, 1891],\n [1241, 1887],\n [1241, 1884],\n [1237, 1879],\n [1240, 1870],\n [1236, 1868],\n [1238, 1866],\n [1242, 1865],\n [1243, 1861],\n [1248, 1855],\n [1247, 1853],\n [1256, 1849],\n [1259, 1845],\n [1255, 1839],\n [1255, 1836],\n [1257, 1835],\n [1259, 1827],\n [1249, 1820],\n [1246, 1823],\n [1241, 1813],\n [1239, 1801],\n [1252, 1804],\n [1257, 1790],\n [1264, 1795],\n [1261, 1802],\n [1269, 1806],\n [1268, 1807],\n [1272, 1811]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '015',\n 'FIPS': '34015',\n 'STATE': 'NJ',\n 'NAME': 'Gloucester',\n 'LSAD': 'County'},\n 'id': 2180,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[535, 1914],\n [580, 1912],\n [610, 1909],\n [639, 1903],\n [691, 1885],\n [742, 1857],\n [775, 1833],\n [728, 1808],\n [712, 1791],\n [714, 1789],\n [707, 1781],\n [696, 1775],\n [691, 1766],\n [691, 1761],\n [682, 1733],\n [675, 1718],\n [670, 1697],\n [670, 1679],\n [668, 1677],\n [659, 1680],\n [654, 1678],\n [643, 1653],\n [627, 1645],\n [624, 1640],\n [623, 1624],\n [594, 1636],\n [547, 1568],\n [510, 1558],\n [472, 1501],\n [470, 1491],\n [472, 1486],\n [471, 1473],\n [474, 1456],\n [485, 1442],\n [509, 1425],\n [511, 1421],\n [545, 1390],\n [542, 1383],\n [536, 1349],\n [535, 1324],\n [549, 1311],\n [540, 1288],\n [526, 1290],\n [509, 1265],\n [502, 1233],\n [507, 1198],\n [520, 1180],\n [522, 1174],\n [534, 1164],\n [533, 1157],\n [556, 1141],\n [581, 1115],\n [585, 1101],\n [603, 1068],\n [619, 1019],\n [614, 1017],\n [613, 1010],\n [606, 1006],\n [603, 1008],\n [597, 1006],\n [595, 1002],\n [597, 998],\n [595, 996],\n [591, 998],\n [587, 996],\n [587, 992],\n [585, 991],\n [580, 993],\n [579, 997],\n [577, 998],\n [573, 992],\n [570, 991],\n [567, 992],\n [567, 995],\n [565, 997],\n [562, 997],\n [558, 993],\n [559, 989],\n [562, 989],\n [563, 981],\n [558, 981],\n [554, 988],\n [549, 980],\n [550, 977],\n [553, 976],\n [552, 969],\n [555, 967],\n [555, 962],\n [551, 958],\n [543, 956],\n [537, 950],\n [535, 945],\n [537, 940],\n [528, 921],\n [519, 916],\n [514, 912],\n [510, 914],\n [510, 916],\n [504, 917],\n [500, 911],\n [496, 914],\n [489, 911],\n [486, 916],\n [482, 911],\n [474, 912],\n [471, 911],\n [468, 914],\n [463, 914],\n [458, 911],\n [454, 903],\n [451, 904],\n [445, 898],\n [430, 897],\n [430, 894],\n [422, 888],\n [417, 880],\n [403, 880],\n [396, 878],\n [391, 879],\n [383, 878],\n [375, 881],\n [368, 890],\n [324, 894],\n [258, 889],\n [219, 1532],\n [217, 1571],\n [217, 1692],\n [239, 1693],\n [250, 1722],\n [269, 1760],\n [293, 1794],\n [321, 1825],\n [344, 1845],\n [366, 1861],\n [400, 1880],\n [431, 1894],\n [466, 1904],\n [501, 1911],\n [535, 1914]]],\n [[[551, 1518],\n [550, 1507],\n [554, 1484],\n [553, 1479],\n [556, 1472],\n [547, 1471],\n [539, 1487],\n [533, 1506],\n [534, 1512],\n [551, 1518]]],\n [[[521, 1459],\n [541, 1443],\n [543, 1430],\n [526, 1439],\n [523, 1445],\n [521, 1459]]]]},\n 'properties': {'STATE_FIPS': '10',\n 'COUNTY_FIP': '003',\n 'FIPS': '10003',\n 'STATE': 'DE',\n 'NAME': 'New Castle',\n 'LSAD': 'County'},\n 'id': 2130,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[276, 601],\n [314, 0],\n [314, -1],\n [319, -80],\n [-15, -80],\n [0, -66],\n [7, -49],\n [14, -44],\n [38, -30],\n [49, -29],\n [55, -20],\n [60, -19],\n [59, -14],\n [50, -11],\n [45, -5],\n [47, -2],\n [51, 0],\n [53, 4],\n [36, 13],\n [33, 23],\n [26, 24],\n [14, 30],\n [14, 35],\n [26, 42],\n [27, 44],\n [26, 45],\n [16, 46],\n [10, 53],\n [22, 62],\n [22, 65],\n [19, 71],\n [11, 72],\n [4, 84],\n [0, 83],\n [-3, 82],\n [-6, 85],\n [-10, 98],\n [-9, 101],\n [-2, 101],\n [0, 104],\n [0, 107],\n [-3, 110],\n [-16, 104],\n [-20, 108],\n [-20, 115],\n [-18, 116],\n [-6, 115],\n [-4, 117],\n [-10, 126],\n [-16, 129],\n [-16, 130],\n [-12, 133],\n [-10, 140],\n [-11, 145],\n [-16, 149],\n [-15, 153],\n [-9, 156],\n [-8, 158],\n [-16, 167],\n [-16, 172],\n [-9, 172],\n [-10, 176],\n [-17, 179],\n [-18, 190],\n [-22, 200],\n [-22, 201],\n [-17, 202],\n [-16, 204],\n [-15, 210],\n [-17, 213],\n [-21, 214],\n [-23, 217],\n [-14, 220],\n [-14, 222],\n [-19, 222],\n [-20, 231],\n [-10, 234],\n [-7, 238],\n [-7, 250],\n [-4, 254],\n [-2, 253],\n [-1, 257],\n [-8, 270],\n [0, 278],\n [1, 283],\n [0, 284],\n [-3, 285],\n [-5, 289],\n [0, 292],\n [4, 295],\n [4, 299],\n [7, 307],\n [13, 311],\n [11, 315],\n [23, 322],\n [26, 332],\n [33, 335],\n [35, 339],\n [39, 340],\n [42, 348],\n [42, 352],\n [49, 352],\n [49, 354],\n [47, 361],\n [49, 364],\n [57, 359],\n [68, 366],\n [68, 374],\n [71, 377],\n [73, 382],\n [80, 384],\n [78, 387],\n [79, 389],\n [79, 393],\n [82, 396],\n [84, 404],\n [87, 405],\n [89, 414],\n [88, 417],\n [86, 418],\n [86, 421],\n [89, 422],\n [96, 432],\n [99, 440],\n [108, 443],\n [112, 447],\n [116, 452],\n [129, 466],\n [132, 473],\n [130, 495],\n [133, 500],\n [137, 502],\n [143, 520],\n [149, 535],\n [153, 536],\n [167, 548],\n [186, 558],\n [197, 566],\n [202, 573],\n [209, 575],\n [213, 581],\n [236, 584],\n [262, 592],\n [276, 601]]]},\n 'properties': {'STATE_FIPS': '24',\n 'COUNTY_FIP': '011',\n 'FIPS': '24011',\n 'STATE': 'MD',\n 'NAME': 'Caroline',\n 'LSAD': 'County'},\n 'id': 2136,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[619, 1019],\n [629, 1007],\n [646, 998],\n [649, 993],\n [646, 983],\n [651, 976],\n [682, 953],\n [695, 949],\n [726, 921],\n [729, 913],\n [731, 889],\n [742, 867],\n [771, 829],\n [778, 810],\n [774, 789],\n [774, 752],\n [787, 738],\n [793, 716],\n [790, 685],\n [783, 682],\n [771, 660],\n [767, 625],\n [781, 498],\n [780, 457],\n [789, 443],\n [788, 441],\n [800, 428],\n [812, 424],\n [862, 379],\n [869, 369],\n [902, 310],\n [907, 296],\n [912, 271],\n [910, 240],\n [911, 231],\n [908, 233],\n [909, 239],\n [904, 244],\n [900, 245],\n [897, 245],\n [893, 238],\n [887, 237],\n [884, 239],\n [881, 240],\n [877, 233],\n [872, 231],\n [865, 234],\n [867, 241],\n [864, 248],\n [863, 248],\n [855, 234],\n [852, 234],\n [841, 253],\n [832, 246],\n [829, 253],\n [830, 256],\n [823, 260],\n [817, 258],\n [810, 260],\n [808, 249],\n [799, 247],\n [796, 251],\n [793, 250],\n [792, 244],\n [785, 238],\n [781, 233],\n [785, 221],\n [783, 218],\n [779, 215],\n [783, 211],\n [786, 213],\n [781, 200],\n [784, 195],\n [783, 193],\n [780, 192],\n [778, 187],\n [781, 180],\n [779, 178],\n [776, 177],\n [775, 181],\n [770, 182],\n [770, 176],\n [759, 179],\n [757, 171],\n [753, 174],\n [751, 171],\n [741, 169],\n [732, 165],\n [724, 169],\n [716, 169],\n [695, 162],\n [688, 166],\n [670, 156],\n [659, 152],\n [654, 143],\n [655, 131],\n [651, 125],\n [645, 122],\n [642, 113],\n [640, 111],\n [635, 109],\n [632, 110],\n [625, 105],\n [607, 62],\n [584, 51],\n [585, 47],\n [580, 39],\n [577, 38],\n [573, 39],\n [566, 35],\n [557, 24],\n [313, 14],\n [258, 889],\n [324, 894],\n [368, 890],\n [375, 881],\n [383, 878],\n [391, 879],\n [396, 878],\n [403, 880],\n [417, 880],\n [422, 888],\n [430, 894],\n [430, 897],\n [445, 898],\n [451, 904],\n [454, 903],\n [458, 911],\n [463, 914],\n [468, 914],\n [471, 911],\n [474, 912],\n [482, 911],\n [486, 916],\n [489, 911],\n [496, 914],\n [500, 911],\n [504, 917],\n [510, 916],\n [510, 914],\n [514, 912],\n [519, 916],\n [528, 921],\n [537, 940],\n [535, 945],\n [537, 950],\n [543, 956],\n [551, 958],\n [555, 962],\n [555, 967],\n [552, 969],\n [553, 976],\n [550, 977],\n [549, 980],\n [554, 988],\n [558, 981],\n [563, 981],\n [562, 989],\n [559, 989],\n [558, 993],\n [562, 997],\n [565, 997],\n [567, 995],\n [567, 992],\n [570, 991],\n [573, 992],\n [577, 998],\n [579, 997],\n [580, 993],\n [585, 991],\n [587, 992],\n [587, 996],\n [591, 998],\n [595, 996],\n [597, 998],\n [595, 1002],\n [597, 1006],\n [603, 1008],\n [606, 1006],\n [613, 1010],\n [614, 1017],\n [619, 1019]]]},\n 'properties': {'STATE_FIPS': '10',\n 'COUNTY_FIP': '001',\n 'FIPS': '10001',\n 'STATE': 'DE',\n 'NAME': 'Kent',\n 'LSAD': 'County'},\n 'id': 2138,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1272, 1811],\n [1268, 1807],\n [1269, 1806],\n [1261, 1802],\n [1264, 1795],\n [1257, 1790],\n [1252, 1804],\n [1239, 1801],\n [1241, 1813],\n [1246, 1823],\n [1249, 1820],\n [1259, 1827],\n [1257, 1835],\n [1255, 1836],\n [1255, 1839],\n [1259, 1845],\n [1256, 1849],\n [1247, 1853],\n [1248, 1855],\n [1243, 1861],\n [1242, 1865],\n [1238, 1866],\n [1236, 1868],\n [1240, 1870],\n [1237, 1879],\n [1241, 1884],\n [1241, 1887],\n [1245, 1891],\n [1242, 1898],\n [1245, 1903],\n [1242, 1906],\n [1241, 1910],\n [1239, 1912],\n [1244, 1919],\n [1241, 1924],\n [1238, 1924],\n [1235, 1930],\n [1229, 1931],\n [1228, 1938],\n [1215, 1947],\n [1216, 1942],\n [1213, 1943],\n [1211, 1955],\n [1213, 1957],\n [1211, 1960],\n [1209, 1960],\n [1205, 1956],\n [1208, 1953],\n [1201, 1952],\n [1196, 1959],\n [1201, 1959],\n [1193, 1968],\n [1195, 1960],\n [1192, 1959],\n [1188, 1965],\n [1187, 1971],\n [1184, 1976],\n [1190, 1978],\n [1174, 1987],\n [1173, 1989],\n [1180, 1993],\n [1177, 1998],\n [1161, 2008],\n [1175, 2030],\n [1179, 2052],\n [1169, 2081],\n [1167, 2094],\n [1168, 2119],\n [1171, 2133],\n [1180, 2146],\n [1208, 2163],\n [1236, 2172],\n [1260, 2183],\n [1278, 2204],\n [1293, 2203],\n [1296, 2208],\n [1300, 2209],\n [1303, 2207],\n [1305, 2210],\n [1314, 2212],\n [1321, 2204],\n [1324, 2204],\n [1325, 2207],\n [1328, 2202],\n [1335, 2198],\n [1336, 2201],\n [1338, 2199],\n [1340, 2191],\n [1344, 2192],\n [1341, 2189],\n [1345, 2182],\n [1351, 2181],\n [1343, 2175],\n [1343, 2172],\n [1340, 2170],\n [1342, 2167],\n [1342, 2163],\n [1337, 2161],\n [1338, 2150],\n [1340, 2149],\n [1340, 2142],\n [1346, 2137],\n [1346, 2132],\n [1342, 2125],\n [1344, 2118],\n [1345, 2116],\n [1352, 2115],\n [1359, 2108],\n [1364, 2109],\n [1368, 2106],\n [1376, 2106],\n [1381, 2107],\n [1385, 2105],\n [1392, 2106],\n [1400, 2105],\n [1402, 2102],\n [1397, 2095],\n [1393, 2094],\n [1393, 2089],\n [1388, 2089],\n [1387, 2086],\n [1389, 2081],\n [1392, 2080],\n [1402, 2070],\n [1418, 2069],\n [1421, 2065],\n [1430, 2055],\n [1435, 2041],\n [1435, 2032],\n [1439, 2027],\n [1443, 2024],\n [1447, 2016],\n [1451, 2015],\n [1464, 2006],\n [1472, 1980],\n [1506, 1823],\n [1513, 1819],\n [1517, 1810],\n [1526, 1806],\n [1545, 1805],\n [1560, 1811],\n [1579, 1815],\n [1584, 1819],\n [1587, 1819],\n [1591, 1821],\n [1603, 1822],\n [1628, 1816],\n [1632, 1813],\n [1646, 1809],\n [1659, 1794],\n [1669, 1787],\n [1676, 1776],\n [1678, 1775],\n [1681, 1771],\n [1680, 1769],\n [1684, 1761],\n [1693, 1758],\n [1696, 1747],\n [1702, 1745],\n [1706, 1738],\n [1713, 1735],\n [1716, 1731],\n [1718, 1733],\n [1749, 1707],\n [1544, 1477],\n [1537, 1482],\n [1536, 1486],\n [1523, 1495],\n [1521, 1502],\n [1522, 1508],\n [1507, 1522],\n [1501, 1539],\n [1498, 1559],\n [1496, 1561],\n [1499, 1567],\n [1497, 1569],\n [1498, 1574],\n [1496, 1582],\n [1498, 1585],\n [1486, 1600],\n [1484, 1611],\n [1480, 1611],\n [1476, 1618],\n [1472, 1621],\n [1472, 1626],\n [1467, 1629],\n [1466, 1633],\n [1463, 1634],\n [1461, 1637],\n [1447, 1647],\n [1434, 1649],\n [1425, 1656],\n [1417, 1655],\n [1413, 1656],\n [1407, 1655],\n [1397, 1657],\n [1384, 1663],\n [1381, 1667],\n [1374, 1670],\n [1373, 1681],\n [1331, 1708],\n [1334, 1714],\n [1335, 1722],\n [1332, 1727],\n [1334, 1735],\n [1333, 1738],\n [1315, 1754],\n [1313, 1759],\n [1308, 1764],\n [1302, 1767],\n [1303, 1772],\n [1300, 1777],\n [1294, 1786],\n [1292, 1795],\n [1294, 1799],\n [1287, 1804],\n [1285, 1809],\n [1283, 1808],\n [1272, 1811]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '007',\n 'FIPS': '34007',\n 'STATE': 'NJ',\n 'NAME': 'Camden',\n 'LSAD': 'County'},\n 'id': 2179,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[763, 1054],\n [751, 1058],\n [736, 1068],\n [721, 1088],\n [688, 1157],\n [672, 1156],\n [661, 1161],\n [629, 1184],\n [625, 1195],\n [584, 1198],\n [575, 1217],\n [573, 1234],\n [575, 1266],\n [596, 1269],\n [598, 1273],\n [594, 1275],\n [593, 1283],\n [593, 1293],\n [598, 1323],\n [598, 1332],\n [597, 1339],\n [591, 1342],\n [588, 1349],\n [590, 1354],\n [609, 1377],\n [616, 1391],\n [620, 1400],\n [619, 1420],\n [615, 1425],\n [609, 1430],\n [600, 1431],\n [583, 1448],\n [571, 1455],\n [559, 1465],\n [553, 1479],\n [554, 1484],\n [550, 1507],\n [551, 1518],\n [555, 1527],\n [568, 1538],\n [576, 1548],\n [586, 1551],\n [616, 1591],\n [620, 1602],\n [624, 1623],\n [624, 1640],\n [627, 1645],\n [643, 1653],\n [654, 1678],\n [659, 1680],\n [668, 1677],\n [670, 1679],\n [670, 1697],\n [675, 1718],\n [682, 1733],\n [691, 1761],\n [691, 1766],\n [696, 1775],\n [707, 1781],\n [714, 1789],\n [712, 1791],\n [723, 1803],\n [734, 1808],\n [748, 1809],\n [757, 1813],\n [764, 1812],\n [769, 1817],\n [772, 1816],\n [774, 1810],\n [771, 1804],\n [775, 1803],\n [777, 1807],\n [779, 1807],\n [782, 1799],\n [781, 1794],\n [783, 1789],\n [785, 1788],\n [788, 1791],\n [790, 1790],\n [785, 1781],\n [782, 1779],\n [779, 1783],\n [776, 1782],\n [774, 1771],\n [778, 1766],\n [784, 1766],\n [783, 1762],\n [785, 1759],\n [787, 1762],\n [788, 1767],\n [798, 1765],\n [794, 1760],\n [797, 1759],\n [797, 1756],\n [794, 1754],\n [791, 1756],\n [789, 1753],\n [794, 1749],\n [793, 1747],\n [796, 1745],\n [796, 1749],\n [800, 1749],\n [800, 1746],\n [802, 1747],\n [801, 1742],\n [803, 1739],\n [812, 1742],\n [817, 1735],\n [815, 1732],\n [817, 1729],\n [815, 1727],\n [819, 1727],\n [816, 1725],\n [821, 1717],\n [817, 1704],\n [817, 1700],\n [822, 1696],\n [823, 1697],\n [828, 1692],\n [831, 1692],\n [832, 1689],\n [831, 1684],\n [836, 1681],\n [839, 1682],\n [840, 1675],\n [856, 1671],\n [859, 1664],\n [868, 1659],\n [870, 1660],\n [876, 1658],\n [878, 1651],\n [883, 1648],\n [884, 1644],\n [891, 1643],\n [889, 1639],\n [890, 1631],\n [895, 1631],\n [898, 1634],\n [907, 1634],\n [909, 1632],\n [907, 1629],\n [912, 1627],\n [914, 1630],\n [918, 1631],\n [938, 1621],\n [948, 1612],\n [961, 1615],\n [963, 1613],\n [972, 1617],\n [984, 1616],\n [991, 1611],\n [996, 1610],\n [999, 1606],\n [1000, 1592],\n [1004, 1584],\n [1026, 1578],\n [1039, 1581],\n [1044, 1578],\n [1075, 1588],\n [1275, 1402],\n [1269, 1387],\n [1268, 1378],\n [1264, 1373],\n [1257, 1357],\n [1262, 1348],\n [1260, 1340],\n [1260, 1334],\n [1262, 1334],\n [1258, 1324],\n [1260, 1318],\n [1256, 1309],\n [1256, 1303],\n [1253, 1299],\n [1252, 1291],\n [1253, 1289],\n [1252, 1283],\n [1253, 1282],\n [1249, 1275],\n [1251, 1267],\n [1254, 1264],\n [1257, 1257],\n [1256, 1251],\n [1259, 1245],\n [1257, 1239],\n [1254, 1237],\n [1253, 1229],\n [1251, 1229],\n [1252, 1227],\n [1250, 1224],\n [1251, 1222],\n [1248, 1219],\n [1247, 1215],\n [1249, 1212],\n [1247, 1211],\n [1246, 1206],\n [1248, 1202],\n [1243, 1192],\n [1178, 1249],\n [1016, 1384],\n [1003, 1367],\n [902, 1257],\n [891, 1251],\n [867, 1255],\n [855, 1230],\n [854, 1219],\n [851, 1216],\n [845, 1216],\n [844, 1212],\n [836, 1205],\n [836, 1201],\n [829, 1191],\n [820, 1188],\n [813, 1192],\n [813, 1194],\n [802, 1193],\n [783, 1201],\n [771, 1194],\n [761, 1182],\n [762, 1179],\n [768, 1178],\n [766, 1169],\n [769, 1165],\n [765, 1159],\n [760, 1160],\n [759, 1156],\n [762, 1153],\n [768, 1157],\n [769, 1153],\n [766, 1147],\n [764, 1147],\n [760, 1149],\n [755, 1145],\n [757, 1140],\n [762, 1138],\n [765, 1142],\n [768, 1141],\n [768, 1135],\n [771, 1131],\n [770, 1129],\n [766, 1128],\n [765, 1126],\n [766, 1122],\n [761, 1121],\n [762, 1114],\n [756, 1113],\n [755, 1105],\n [760, 1100],\n [762, 1093],\n [764, 1092],\n [766, 1094],\n [767, 1100],\n [775, 1098],\n [775, 1094],\n [773, 1091],\n [767, 1086],\n [767, 1084],\n [772, 1080],\n [772, 1076],\n [769, 1074],\n [763, 1074],\n [758, 1076],\n [757, 1071],\n [761, 1066],\n [758, 1062],\n [758, 1060],\n [763, 1054]]],\n [[[763, 1054],\n [765, 1055],\n [767, 1060],\n [770, 1060],\n [772, 1050],\n [763, 1054]]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '033',\n 'FIPS': '34033',\n 'STATE': 'NJ',\n 'NAME': 'Salem',\n 'LSAD': 'County'},\n 'id': 2131,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1275, 1402],\n [1575, 1130],\n [1573, 1127],\n [1574, 1123],\n [1579, 1112],\n [1580, 1106],\n [1577, 1095],\n [1572, 1086],\n [1573, 1080],\n [1572, 1075],\n [1580, 1055],\n [1581, 1041],\n [1579, 1031],\n [1569, 1012],\n [1570, 1000],\n [1564, 981],\n [1564, 974],\n [1562, 968],\n [1563, 948],\n [1568, 940],\n [1563, 941],\n [1557, 929],\n [1549, 919],\n [1537, 911],\n [1515, 773],\n [1490, 757],\n [1479, 754],\n [1476, 743],\n [1474, 742],\n [1476, 740],\n [1474, 739],\n [1473, 735],\n [1475, 732],\n [1474, 730],\n [1476, 727],\n [1473, 727],\n [1473, 724],\n [1477, 722],\n [1476, 719],\n [1479, 717],\n [1474, 710],\n [1473, 701],\n [1475, 698],\n [1483, 700],\n [1484, 697],\n [1478, 691],\n [1478, 686],\n [1480, 685],\n [1481, 687],\n [1491, 685],\n [1488, 679],\n [1490, 673],\n [1493, 672],\n [1492, 669],\n [1493, 667],\n [1489, 665],\n [1420, 689],\n [1400, 693],\n [1368, 691],\n [1327, 695],\n [1323, 697],\n [1325, 706],\n [1331, 712],\n [1331, 716],\n [1327, 726],\n [1313, 736],\n [1305, 737],\n [1296, 729],\n [1289, 733],\n [1274, 733],\n [1240, 723],\n [1218, 729],\n [1209, 729],\n [1198, 722],\n [1180, 688],\n [1166, 669],\n [1163, 670],\n [1124, 711],\n [1125, 739],\n [1117, 772],\n [1107, 788],\n [1099, 795],\n [1086, 793],\n [1079, 813],\n [1066, 825],\n [1056, 825],\n [1013, 847],\n [1010, 853],\n [1012, 859],\n [1009, 869],\n [999, 895],\n [970, 903],\n [954, 894],\n [950, 881],\n [945, 876],\n [920, 899],\n [906, 915],\n [889, 957],\n [888, 969],\n [879, 981],\n [867, 987],\n [848, 985],\n [834, 973],\n [797, 1005],\n [791, 1015],\n [790, 1030],\n [784, 1045],\n [772, 1050],\n [770, 1060],\n [767, 1060],\n [765, 1055],\n [763, 1054],\n [758, 1060],\n [758, 1062],\n [761, 1066],\n [757, 1071],\n [757, 1075],\n [758, 1076],\n [763, 1074],\n [769, 1074],\n [772, 1076],\n [772, 1080],\n [767, 1084],\n [767, 1086],\n [773, 1091],\n [775, 1094],\n [775, 1098],\n [767, 1100],\n [766, 1094],\n [764, 1092],\n [762, 1093],\n [760, 1100],\n [755, 1103],\n [754, 1109],\n [756, 1113],\n [762, 1114],\n [761, 1121],\n [766, 1122],\n [765, 1126],\n [766, 1128],\n [770, 1129],\n [771, 1131],\n [768, 1135],\n [768, 1141],\n [765, 1142],\n [762, 1138],\n [757, 1140],\n [755, 1145],\n [760, 1149],\n [764, 1147],\n [766, 1147],\n [769, 1153],\n [768, 1157],\n [762, 1153],\n [759, 1156],\n [760, 1160],\n [765, 1159],\n [769, 1165],\n [766, 1169],\n [768, 1178],\n [762, 1179],\n [761, 1182],\n [771, 1194],\n [783, 1201],\n [802, 1193],\n [813, 1194],\n [813, 1192],\n [820, 1188],\n [829, 1191],\n [836, 1201],\n [836, 1205],\n [844, 1212],\n [845, 1216],\n [851, 1216],\n [854, 1219],\n [855, 1230],\n [867, 1255],\n [891, 1251],\n [902, 1257],\n [1003, 1367],\n [1016, 1384],\n [1178, 1249],\n [1243, 1192],\n [1248, 1202],\n [1246, 1206],\n [1247, 1211],\n [1249, 1212],\n [1247, 1215],\n [1248, 1219],\n [1251, 1222],\n [1250, 1224],\n [1252, 1227],\n [1251, 1229],\n [1253, 1229],\n [1254, 1237],\n [1257, 1239],\n [1259, 1245],\n [1256, 1251],\n [1257, 1257],\n [1254, 1264],\n [1251, 1267],\n [1249, 1275],\n [1253, 1282],\n [1252, 1283],\n [1253, 1289],\n [1252, 1291],\n [1253, 1299],\n [1256, 1303],\n [1256, 1309],\n [1260, 1318],\n [1258, 1324],\n [1262, 1334],\n [1260, 1334],\n [1260, 1340],\n [1262, 1348],\n [1257, 1357],\n [1264, 1373],\n [1268, 1378],\n [1269, 1387],\n [1275, 1402]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '011',\n 'FIPS': '34011',\n 'STATE': 'NJ',\n 'NAME': 'Cumberland',\n 'LSAD': 'County'},\n 'id': 2133,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1563, 941],\n [1568, 940],\n [1568, 932],\n [1576, 930],\n [1582, 926],\n [1602, 924],\n [1611, 916],\n [1620, 905],\n [1627, 909],\n [1629, 907],\n [1643, 907],\n [1646, 904],\n [1651, 909],\n [1656, 911],\n [1659, 908],\n [1664, 908],\n [1666, 905],\n [1671, 909],\n [1676, 908],\n [1679, 909],\n [1689, 902],\n [1687, 894],\n [1688, 894],\n [1696, 897],\n [1699, 894],\n [1706, 893],\n [1709, 889],\n [1720, 894],\n [1725, 891],\n [1726, 886],\n [1735, 885],\n [1744, 888],\n [1748, 893],\n [1758, 892],\n [1764, 888],\n [1773, 893],\n [1775, 892],\n [1773, 886],\n [1778, 881],\n [1782, 880],\n [1785, 886],\n [1788, 886],\n [1789, 880],\n [1791, 878],\n [1794, 884],\n [1798, 885],\n [1802, 878],\n [1805, 875],\n [1810, 879],\n [1816, 878],\n [1820, 883],\n [1824, 885],\n [1828, 883],\n [1831, 884],\n [1835, 890],\n [1837, 890],\n [1840, 887],\n [1840, 883],\n [1838, 877],\n [1840, 875],\n [1843, 876],\n [1845, 881],\n [1845, 889],\n [1849, 891],\n [1864, 890],\n [1862, 879],\n [1863, 877],\n [1870, 874],\n [1875, 875],\n [1879, 882],\n [1885, 887],\n [1889, 888],\n [1900, 886],\n [1913, 881],\n [1918, 882],\n [1960, 913],\n [1967, 914],\n [1994, 894],\n [2011, 893],\n [2023, 900],\n [2031, 899],\n [2033, 896],\n [2019, 883],\n [2016, 871],\n [2005, 855],\n [1976, 841],\n [1951, 818],\n [1927, 791],\n [1895, 747],\n [1880, 730],\n [1873, 705],\n [1844, 669],\n [1781, 557],\n [1780, 551],\n [1782, 547],\n [1796, 534],\n [1794, 525],\n [1746, 472],\n [1688, 375],\n [1676, 332],\n [1667, 317],\n [1645, 306],\n [1628, 293],\n [1583, 247],\n [1563, 220],\n [1562, 222],\n [1554, 226],\n [1537, 223],\n [1501, 205],\n [1481, 199],\n [1462, 198],\n [1419, 203],\n [1413, 207],\n [1406, 220],\n [1430, 334],\n [1439, 361],\n [1455, 398],\n [1506, 496],\n [1514, 517],\n [1522, 544],\n [1531, 601],\n [1530, 630],\n [1503, 660],\n [1489, 665],\n [1493, 667],\n [1492, 669],\n [1493, 672],\n [1490, 673],\n [1488, 679],\n [1491, 685],\n [1481, 687],\n [1480, 685],\n [1478, 686],\n [1478, 691],\n [1484, 697],\n [1482, 701],\n [1475, 698],\n [1473, 701],\n [1474, 710],\n [1479, 717],\n [1476, 719],\n [1477, 722],\n [1473, 724],\n [1473, 727],\n [1476, 727],\n [1474, 730],\n [1475, 732],\n [1473, 735],\n [1474, 739],\n [1476, 740],\n [1474, 742],\n [1476, 743],\n [1479, 754],\n [1490, 757],\n [1515, 773],\n [1537, 911],\n [1549, 919],\n [1557, 929],\n [1563, 941]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '009',\n 'FIPS': '34009',\n 'STATE': 'NJ',\n 'NAME': 'Cape May',\n 'LSAD': 'County'},\n 'id': 2134,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2885, 4176],\n [2960, 4130],\n [2976, 4129],\n [2972, 4096],\n [2941, 3976],\n [2925, 3922],\n [2889, 3842],\n [2875, 3801],\n [2844, 3745],\n [2831, 3752],\n [2832, 3760],\n [2803, 3780],\n [2803, 3784],\n [2806, 3789],\n [2802, 3794],\n [2797, 3795],\n [2792, 3789],\n [2792, 3785],\n [2788, 3785],\n [2787, 3781],\n [2785, 3780],\n [2786, 3778],\n [2781, 3781],\n [2780, 3774],\n [2775, 3774],\n [2769, 3765],\n [2742, 3763],\n [2733, 3760],\n [2727, 3755],\n [2722, 3747],\n [2718, 3744],\n [2710, 3742],\n [2708, 3736],\n [2692, 3711],\n [2690, 3703],\n [2693, 3687],\n [2690, 3677],\n [2686, 3679],\n [2683, 3677],\n [2677, 3687],\n [2679, 3692],\n [2675, 3703],\n [2672, 3702],\n [2669, 3705],\n [2662, 3706],\n [2657, 3703],\n [2653, 3694],\n [2650, 3692],\n [2640, 3696],\n [2636, 3695],\n [2633, 3699],\n [2632, 3707],\n [2607, 3723],\n [2619, 3744],\n [2620, 3749],\n [2618, 3760],\n [2623, 3784],\n [2638, 3790],\n [2641, 3793],\n [2643, 3799],\n [2645, 3808],\n [2641, 3826],\n [2644, 3833],\n [2648, 3849],\n [2647, 3855],\n [2649, 3864],\n [2656, 3864],\n [2659, 3857],\n [2661, 3856],\n [2665, 3860],\n [2666, 3864],\n [2665, 3871],\n [2661, 3874],\n [2656, 3885],\n [2654, 3892],\n [2646, 3904],\n [2632, 3915],\n [2634, 3930],\n [2627, 3941],\n [2630, 3956],\n [2628, 3967],\n [2633, 3981],\n [2630, 3985],\n [2619, 3987],\n [2616, 3992],\n [2615, 3999],\n [2619, 4007],\n [2612, 4020],\n [2606, 4022],\n [2616, 4070],\n [2583, 4096],\n [2574, 4104],\n [2573, 4096],\n [2573, 4090],\n [2518, 4096],\n [2494, 4099],\n [2460, 4120],\n [2437, 4158],\n [2425, 4170],\n [2427, 4176],\n [2885, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '003',\n 'FIPS': '34003',\n 'STATE': 'NJ',\n 'NAME': 'Bergen',\n 'LSAD': 'County'},\n 'id': 2229,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2234, 4176],\n [2235, 4173],\n [2240, 4168],\n [2251, 4165],\n [2255, 4159],\n [2259, 4157],\n [2265, 4162],\n [2274, 4161],\n [2281, 4157],\n [2292, 4157],\n [2293, 4160],\n [2298, 4162],\n [2306, 4160],\n [2310, 4156],\n [2310, 4144],\n [2318, 4144],\n [2324, 4148],\n [2325, 4146],\n [2329, 4148],\n [2334, 4147],\n [2334, 4142],\n [2341, 4140],\n [2344, 4137],\n [2363, 4139],\n [2370, 4132],\n [2382, 4133],\n [2384, 4140],\n [2387, 4140],\n [2388, 4136],\n [2388, 4128],\n [2391, 4125],\n [2391, 4117],\n [2396, 4111],\n [2389, 4103],\n [2389, 4096],\n [2392, 4094],\n [2399, 4096],\n [2407, 4094],\n [2412, 4096],\n [2412, 4090],\n [2418, 4085],\n [2417, 4079],\n [2413, 4081],\n [2412, 4075],\n [2413, 4071],\n [2415, 4069],\n [2414, 4053],\n [2418, 4047],\n [2415, 4025],\n [2407, 4002],\n [2401, 3997],\n [2402, 3995],\n [2402, 3989],\n [2407, 3986],\n [2405, 3984],\n [2408, 3980],\n [2408, 3975],\n [2416, 3978],\n [2416, 3973],\n [2426, 3972],\n [2433, 3959],\n [2432, 3957],\n [2430, 3957],\n [2426, 3962],\n [2426, 3956],\n [2424, 3953],\n [2427, 3948],\n [2424, 3942],\n [2425, 3938],\n [2427, 3936],\n [2413, 3937],\n [2411, 3934],\n [2407, 3933],\n [2408, 3938],\n [2404, 3940],\n [2411, 3942],\n [2411, 3944],\n [2406, 3945],\n [2400, 3941],\n [2397, 3942],\n [2396, 3944],\n [2397, 3945],\n [2396, 3948],\n [2397, 3953],\n [2390, 3954],\n [2380, 3950],\n [2381, 3956],\n [2376, 3952],\n [2370, 3951],\n [2367, 3952],\n [2364, 3949],\n [2358, 3948],\n [2358, 3957],\n [2355, 3957],\n [2353, 3955],\n [2351, 3956],\n [2352, 3959],\n [2347, 3958],\n [2345, 3956],\n [2348, 3956],\n [2348, 3953],\n [2344, 3953],\n [2342, 3959],\n [2340, 3955],\n [2335, 3952],\n [2330, 3939],\n [2335, 3937],\n [2335, 3935],\n [2331, 3935],\n [2328, 3931],\n [2333, 3929],\n [2328, 3927],\n [2327, 3923],\n [2329, 3918],\n [2326, 3902],\n [2330, 3902],\n [2330, 3900],\n [2334, 3898],\n [2330, 3894],\n [2335, 3891],\n [2330, 3888],\n [2332, 3885],\n [2329, 3884],\n [2331, 3878],\n [2337, 3883],\n [2339, 3878],\n [2347, 3877],\n [2353, 3873],\n [2356, 3876],\n [2359, 3874],\n [2358, 3871],\n [2354, 3869],\n [2357, 3861],\n [2349, 3861],\n [2347, 3858],\n [2348, 3854],\n [2346, 3851],\n [2345, 3847],\n [2341, 3844],\n [2343, 3835],\n [2347, 3834],\n [2345, 3830],\n [2342, 3829],\n [2341, 3816],\n [2343, 3811],\n [2334, 3803],\n [2338, 3792],\n [2335, 3785],\n [2327, 3774],\n [2320, 3776],\n [2313, 3774],\n [2311, 3763],\n [2303, 3757],\n [2298, 3749],\n [2291, 3748],\n [2291, 3742],\n [2288, 3739],\n [2287, 3711],\n [2279, 3701],\n [2282, 3700],\n [2284, 3694],\n [2280, 3687],\n [2283, 3686],\n [2282, 3680],\n [2278, 3678],\n [2274, 3678],\n [2272, 3675],\n [2275, 3674],\n [2277, 3669],\n [2287, 3675],\n [2286, 3669],\n [2291, 3668],\n [2295, 3665],\n [2301, 3665],\n [2301, 3662],\n [2298, 3655],\n [2287, 3652],\n [2286, 3650],\n [2291, 3646],\n [2288, 3642],\n [2279, 3637],\n [2282, 3626],\n [2280, 3622],\n [2276, 3622],\n [2272, 3623],\n [2268, 3621],\n [2272, 3615],\n [2262, 3604],\n [2258, 3607],\n [2253, 3606],\n [2253, 3592],\n [2246, 3590],\n [2236, 3592],\n [2228, 3583],\n [2227, 3577],\n [2222, 3577],\n [2217, 3568],\n [2200, 3560],\n [2197, 3555],\n [2194, 3545],\n [2189, 3545],\n [2184, 3542],\n [2181, 3537],\n [2169, 3529],\n [2164, 3529],\n [2162, 3524],\n [2164, 3518],\n [2162, 3514],\n [2163, 3512],\n [2160, 3507],\n [2154, 3508],\n [2150, 3506],\n [2149, 3503],\n [2137, 3496],\n [2133, 3498],\n [2128, 3493],\n [2116, 3491],\n [2117, 3487],\n [2114, 3485],\n [2112, 3488],\n [2106, 3487],\n [2106, 3484],\n [2102, 3482],\n [2100, 3484],\n [2094, 3483],\n [2095, 3479],\n [2091, 3478],\n [2088, 3480],\n [2085, 3477],\n [2086, 3475],\n [2082, 3466],\n [2075, 3460],\n [2071, 3463],\n [2070, 3462],\n [2061, 3461],\n [2060, 3464],\n [2055, 3465],\n [2059, 3469],\n [2056, 3469],\n [2054, 3479],\n [2056, 3482],\n [2051, 3486],\n [2050, 3490],\n [2044, 3491],\n [2043, 3494],\n [2050, 3495],\n [2051, 3498],\n [2056, 3501],\n [2052, 3521],\n [2053, 3527],\n [2055, 3529],\n [2058, 3537],\n [2062, 3535],\n [2069, 3537],\n [2069, 3539],\n [2066, 3541],\n [2067, 3545],\n [2070, 3544],\n [2073, 3547],\n [2073, 3559],\n [2071, 3561],\n [2069, 3561],\n [2068, 3568],\n [2061, 3574],\n [2056, 3575],\n [2054, 3578],\n [2057, 3581],\n [2058, 3587],\n [2052, 3591],\n [2051, 3594],\n [2048, 3595],\n [2048, 3598],\n [2052, 3611],\n [2046, 3617],\n [2043, 3616],\n [2035, 3622],\n [2028, 3621],\n [2026, 3623],\n [2022, 3622],\n [2023, 3646],\n [2017, 3652],\n [2019, 3655],\n [2018, 3663],\n [2012, 3669],\n [1848, 3621],\n [1765, 3594],\n [1755, 3595],\n [1740, 3603],\n [1740, 3607],\n [1725, 3617],\n [1704, 3625],\n [1666, 3634],\n [1659, 3638],\n [1644, 3636],\n [1638, 3636],\n [1638, 3638],\n [1633, 3639],\n [1622, 3639],\n [1617, 3642],\n [1594, 3665],\n [1592, 3670],\n [1526, 3726],\n [1532, 3732],\n [1535, 3733],\n [1539, 3731],\n [1546, 3737],\n [1558, 3742],\n [1562, 3755],\n [1567, 3756],\n [1569, 3752],\n [1576, 3756],\n [1583, 3757],\n [1588, 3765],\n [1589, 3771],\n [1592, 3771],\n [1598, 3780],\n [1606, 3786],\n [1606, 3792],\n [1613, 3799],\n [1611, 3804],\n [1617, 3812],\n [1630, 3821],\n [1627, 3829],\n [1622, 3833],\n [1625, 3843],\n [1627, 3845],\n [1637, 3849],\n [1642, 3855],\n [1643, 3865],\n [1652, 3869],\n [1654, 3874],\n [1646, 3880],\n [1645, 3883],\n [1648, 3894],\n [1647, 3908],\n [1651, 3914],\n [1668, 3923],\n [1671, 3928],\n [1671, 3935],\n [1681, 3943],\n [1687, 3954],\n [1691, 3952],\n [1706, 3966],\n [1708, 3965],\n [1711, 3968],\n [1713, 3965],\n [1715, 3968],\n [1722, 3968],\n [1727, 3972],\n [1727, 3976],\n [1734, 3980],\n [1749, 3979],\n [1756, 3982],\n [1761, 3974],\n [1769, 3970],\n [1783, 3958],\n [1785, 3951],\n [1782, 3942],\n [1786, 3940],\n [1792, 3945],\n [1796, 3946],\n [1808, 3954],\n [1814, 3951],\n [1818, 3952],\n [1820, 3959],\n [1827, 3961],\n [1832, 3960],\n [1846, 3969],\n [1855, 3977],\n [1871, 3984],\n [1872, 3987],\n [1870, 3992],\n [1871, 3998],\n [1873, 4001],\n [1889, 4014],\n [1897, 4029],\n [1897, 4040],\n [1888, 4056],\n [1889, 4068],\n [1910, 4096],\n [1938, 4131],\n [1978, 4176],\n [2190, 4176],\n [2189, 4174],\n [2196, 4170],\n [2202, 4172],\n [2203, 4176],\n [2234, 4176]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '027',\n 'FIPS': '34027',\n 'STATE': 'NJ',\n 'NAME': 'Morris',\n 'LSAD': 'County'},\n 'id': 2228,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2352, 3959],\n [2351, 3956],\n [2353, 3955],\n [2355, 3957],\n [2358, 3957],\n [2358, 3948],\n [2364, 3949],\n [2367, 3952],\n [2370, 3951],\n [2376, 3952],\n [2381, 3956],\n [2380, 3950],\n [2390, 3954],\n [2397, 3953],\n [2396, 3948],\n [2397, 3945],\n [2396, 3944],\n [2397, 3942],\n [2400, 3941],\n [2406, 3945],\n [2411, 3944],\n [2411, 3942],\n [2404, 3940],\n [2408, 3938],\n [2407, 3933],\n [2411, 3934],\n [2413, 3937],\n [2427, 3936],\n [2433, 3931],\n [2437, 3917],\n [2443, 3911],\n [2451, 3909],\n [2453, 3906],\n [2472, 3904],\n [2527, 3864],\n [2529, 3868],\n [2536, 3865],\n [2534, 3860],\n [2632, 3788],\n [2623, 3784],\n [2618, 3760],\n [2620, 3749],\n [2619, 3744],\n [2607, 3723],\n [2606, 3710],\n [2596, 3695],\n [2592, 3684],\n [2584, 3671],\n [2580, 3648],\n [2586, 3626],\n [2589, 3623],\n [2596, 3622],\n [2606, 3623],\n [2610, 3626],\n [2614, 3633],\n [2621, 3637],\n [2643, 3638],\n [2650, 3633],\n [2651, 3629],\n [2645, 3598],\n [2647, 3588],\n [2657, 3563],\n [2623, 3508],\n [2620, 3507],\n [2588, 3532],\n [2587, 3536],\n [2585, 3537],\n [2577, 3538],\n [2575, 3535],\n [2528, 3532],\n [2516, 3540],\n [2511, 3558],\n [2498, 3571],\n [2448, 3583],\n [2406, 3600],\n [2398, 3598],\n [2393, 3595],\n [2392, 3591],\n [2386, 3584],\n [2382, 3583],\n [2368, 3587],\n [2361, 3594],\n [2354, 3594],\n [2348, 3590],\n [2323, 3606],\n [2304, 3624],\n [2280, 3633],\n [2279, 3637],\n [2288, 3642],\n [2291, 3646],\n [2286, 3650],\n [2287, 3652],\n [2298, 3655],\n [2301, 3662],\n [2301, 3665],\n [2295, 3665],\n [2291, 3668],\n [2286, 3669],\n [2287, 3675],\n [2277, 3669],\n [2275, 3674],\n [2272, 3675],\n [2274, 3678],\n [2278, 3678],\n [2282, 3680],\n [2283, 3686],\n [2280, 3687],\n [2284, 3694],\n [2282, 3700],\n [2279, 3701],\n [2287, 3711],\n [2288, 3739],\n [2291, 3742],\n [2291, 3748],\n [2298, 3749],\n [2303, 3757],\n [2311, 3763],\n [2313, 3774],\n [2320, 3776],\n [2327, 3774],\n [2335, 3785],\n [2338, 3792],\n [2334, 3803],\n [2343, 3811],\n [2341, 3816],\n [2342, 3829],\n [2345, 3830],\n [2347, 3834],\n [2343, 3835],\n [2341, 3844],\n [2345, 3847],\n [2346, 3851],\n [2348, 3854],\n [2347, 3858],\n [2349, 3861],\n [2357, 3861],\n [2354, 3869],\n [2358, 3871],\n [2359, 3874],\n [2356, 3876],\n [2353, 3873],\n [2347, 3877],\n [2339, 3878],\n [2337, 3883],\n [2331, 3878],\n [2329, 3884],\n [2332, 3885],\n [2330, 3888],\n [2335, 3891],\n [2330, 3894],\n [2334, 3898],\n [2330, 3900],\n [2330, 3902],\n [2326, 3902],\n [2329, 3918],\n [2327, 3923],\n [2328, 3927],\n [2333, 3929],\n [2328, 3931],\n [2331, 3935],\n [2335, 3935],\n [2335, 3937],\n [2330, 3939],\n [2331, 3942],\n [2335, 3952],\n [2340, 3955],\n [2342, 3959],\n [2344, 3953],\n [2348, 3953],\n [2348, 3956],\n [2345, 3956],\n [2347, 3958],\n [2352, 3959]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '013',\n 'FIPS': '34013',\n 'STATE': 'NJ',\n 'NAME': 'Essex',\n 'LSAD': 'County'},\n 'id': 2206,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2380, 3382],\n [2380, 3379],\n [2378, 3378],\n [2380, 3376],\n [2377, 3366],\n [2385, 3361],\n [2397, 3350],\n [2443, 3360],\n [2446, 3370],\n [2467, 3365],\n [2478, 3370],\n [2484, 3363],\n [2498, 3366],\n [2504, 3368],\n [2508, 3373],\n [2512, 3369],\n [2513, 3362],\n [2521, 3355],\n [2517, 3320],\n [2509, 3290],\n [2504, 3283],\n [2484, 3287],\n [2460, 3269],\n [2456, 3255],\n [2463, 3214],\n [2431, 3172],\n [2425, 3151],\n [2432, 3120],\n [2440, 3106],\n [2477, 3093],\n [2494, 3084],\n [2494, 3083],\n [2497, 3080],\n [2490, 3067],\n [2483, 3068],\n [2481, 3065],\n [2479, 3067],\n [2461, 3042],\n [2461, 3006],\n [2458, 3003],\n [2460, 2996],\n [2455, 2990],\n [2271, 2799],\n [2248, 2769],\n [2249, 2752],\n [2150, 2698],\n [2146, 2700],\n [2116, 2702],\n [2119, 2741],\n [2095, 2754],\n [2091, 2751],\n [2088, 2754],\n [2086, 2754],\n [2077, 2759],\n [2072, 2766],\n [2061, 2765],\n [2061, 2772],\n [2052, 2780],\n [2045, 2776],\n [2040, 2771],\n [2036, 2769],\n [2032, 2775],\n [2028, 2777],\n [2027, 2775],\n [2021, 2773],\n [2016, 2773],\n [2010, 2776],\n [2007, 2783],\n [1999, 2785],\n [1995, 2791],\n [1991, 2790],\n [1983, 2797],\n [1976, 2799],\n [1972, 2803],\n [1969, 2816],\n [1957, 2820],\n [1953, 2819],\n [1947, 2821],\n [1943, 2832],\n [1936, 2833],\n [1925, 2839],\n [1916, 2838],\n [1915, 2840],\n [1915, 2845],\n [1912, 2847],\n [1914, 2857],\n [1904, 2869],\n [1904, 2878],\n [1912, 2916],\n [1916, 2919],\n [1919, 2933],\n [1928, 2936],\n [1944, 2945],\n [1955, 2956],\n [1960, 2967],\n [1966, 2971],\n [1966, 2981],\n [1969, 2989],\n [1969, 3016],\n [1983, 3025],\n [2013, 3040],\n [2041, 3056],\n [2055, 3068],\n [2064, 3080],\n [2075, 3091],\n [2082, 3099],\n [2102, 3115],\n [2119, 3139],\n [2125, 3143],\n [2140, 3148],\n [2152, 3156],\n [2153, 3166],\n [2151, 3171],\n [2143, 3178],\n [2141, 3190],\n [2122, 3195],\n [2119, 3197],\n [2100, 3230],\n [2083, 3244],\n [2072, 3255],\n [2068, 3273],\n [2065, 3280],\n [2059, 3286],\n [2054, 3288],\n [2057, 3292],\n [2056, 3305],\n [2059, 3307],\n [2063, 3315],\n [2071, 3326],\n [2070, 3329],\n [2076, 3331],\n [2086, 3339],\n [2091, 3334],\n [2091, 3337],\n [2094, 3340],\n [2097, 3338],\n [2104, 3341],\n [2108, 3340],\n [2118, 3345],\n [2126, 3354],\n [2137, 3362],\n [2143, 3362],\n [2147, 3364],\n [2154, 3355],\n [2156, 3357],\n [2203, 3362],\n [2279, 3370],\n [2380, 3382]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '023',\n 'FIPS': '34023',\n 'STATE': 'NJ',\n 'NAME': 'Middlesex',\n 'LSAD': 'County'},\n 'id': 2194,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2280, 3633],\n [2304, 3624],\n [2323, 3606],\n [2348, 3590],\n [2354, 3594],\n [2361, 3594],\n [2368, 3587],\n [2382, 3583],\n [2386, 3584],\n [2392, 3591],\n [2393, 3595],\n [2398, 3598],\n [2406, 3600],\n [2448, 3583],\n [2498, 3571],\n [2511, 3558],\n [2516, 3540],\n [2528, 3532],\n [2575, 3535],\n [2577, 3538],\n [2585, 3537],\n [2587, 3536],\n [2588, 3532],\n [2620, 3507],\n [2623, 3508],\n [2587, 3450],\n [2558, 3454],\n [2551, 3454],\n [2527, 3425],\n [2521, 3355],\n [2513, 3362],\n [2512, 3369],\n [2508, 3373],\n [2504, 3368],\n [2498, 3366],\n [2484, 3363],\n [2478, 3370],\n [2467, 3365],\n [2446, 3370],\n [2443, 3360],\n [2397, 3350],\n [2385, 3361],\n [2377, 3366],\n [2380, 3376],\n [2378, 3378],\n [2380, 3379],\n [2380, 3382],\n [2279, 3370],\n [2203, 3362],\n [2156, 3357],\n [2154, 3355],\n [2147, 3364],\n [2152, 3365],\n [2159, 3373],\n [2164, 3373],\n [2169, 3381],\n [2173, 3383],\n [2184, 3385],\n [2193, 3396],\n [2199, 3397],\n [2203, 3402],\n [2206, 3414],\n [2213, 3418],\n [2217, 3429],\n [2219, 3444],\n [2220, 3446],\n [2226, 3447],\n [2229, 3455],\n [2229, 3462],\n [2235, 3473],\n [2235, 3481],\n [2236, 3487],\n [2234, 3493],\n [2231, 3495],\n [2224, 3491],\n [2220, 3485],\n [2214, 3483],\n [2211, 3478],\n [2208, 3477],\n [2202, 3485],\n [2198, 3487],\n [2196, 3491],\n [2185, 3486],\n [2175, 3475],\n [2164, 3469],\n [2150, 3506],\n [2154, 3508],\n [2160, 3507],\n [2163, 3512],\n [2162, 3514],\n [2164, 3518],\n [2162, 3524],\n [2164, 3529],\n [2169, 3529],\n [2181, 3537],\n [2184, 3542],\n [2189, 3545],\n [2194, 3545],\n [2197, 3555],\n [2200, 3560],\n [2217, 3568],\n [2222, 3577],\n [2227, 3577],\n [2228, 3583],\n [2236, 3592],\n [2246, 3590],\n [2253, 3592],\n [2253, 3606],\n [2258, 3607],\n [2262, 3604],\n [2272, 3615],\n [2268, 3621],\n [2272, 3623],\n [2276, 3622],\n [2280, 3622],\n [2282, 3626],\n [2280, 3633]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '039',\n 'FIPS': '34039',\n 'STATE': 'NJ',\n 'NAME': 'Union',\n 'LSAD': 'County'},\n 'id': 2204,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[2941, 3976],\n [3026, 3943],\n [3031, 3954],\n [3034, 3955],\n [3033, 3958],\n [3036, 3961],\n [3039, 3961],\n [3036, 3956],\n [3042, 3955],\n [3048, 3952],\n [3048, 3950],\n [3053, 3950],\n [3057, 3931],\n [3078, 3922],\n [3079, 3925],\n [3137, 3905],\n [3135, 3901],\n [3134, 3882],\n [3129, 3862],\n [3130, 3854],\n [3135, 3849],\n [3139, 3841],\n [3140, 3824],\n [3136, 3820],\n [3130, 3830],\n [3126, 3839],\n [3128, 3848],\n [3124, 3857],\n [3122, 3856],\n [3117, 3850],\n [3114, 3842],\n [3103, 3845],\n [3094, 3839],\n [3090, 3817],\n [3091, 3809],\n [3096, 3798],\n [3106, 3785],\n [3117, 3779],\n [3133, 3751],\n [3131, 3749],\n [3122, 3749],\n [3097, 3759],\n [3088, 3760],\n [3074, 3755],\n [3038, 3749],\n [3022, 3744],\n [3011, 3732],\n [3010, 3724],\n [2998, 3722],\n [2982, 3725],\n [2981, 3730],\n [2949, 3742],\n [2940, 3748],\n [2935, 3754],\n [2928, 3754],\n [2921, 3765],\n [2920, 3772],\n [2919, 3818],\n [2926, 3837],\n [2938, 3859],\n [2952, 3877],\n [2957, 3891],\n [2957, 3896],\n [2953, 3901],\n [2946, 3897],\n [2945, 3895],\n [2938, 3897],\n [2936, 3900],\n [2919, 3907],\n [2925, 3922],\n [2941, 3976]]],\n [[[3156, 3866],\n [3162, 3860],\n [3164, 3854],\n [3162, 3836],\n [3157, 3835],\n [3152, 3842],\n [3152, 3864],\n [3156, 3866]]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '005',\n 'FIPS': '36005',\n 'STATE': 'NY',\n 'NAME': 'Bronx',\n 'LSAD': 'County'},\n 'id': 2199,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2797, 3795],\n [2802, 3794],\n [2806, 3789],\n [2803, 3784],\n [2803, 3780],\n [2832, 3760],\n [2831, 3752],\n [2844, 3745],\n [2802, 3666],\n [2786, 3575],\n [2766, 3578],\n [2747, 3549],\n [2722, 3481],\n [2694, 3468],\n [2685, 3461],\n [2613, 3446],\n [2587, 3450],\n [2657, 3563],\n [2647, 3588],\n [2645, 3598],\n [2651, 3629],\n [2650, 3633],\n [2643, 3638],\n [2621, 3637],\n [2614, 3633],\n [2610, 3626],\n [2606, 3623],\n [2596, 3622],\n [2589, 3623],\n [2583, 3633],\n [2580, 3648],\n [2582, 3665],\n [2586, 3675],\n [2592, 3684],\n [2596, 3695],\n [2606, 3710],\n [2607, 3723],\n [2632, 3707],\n [2633, 3699],\n [2636, 3695],\n [2640, 3696],\n [2650, 3692],\n [2653, 3694],\n [2657, 3703],\n [2662, 3706],\n [2669, 3705],\n [2672, 3702],\n [2675, 3703],\n [2679, 3692],\n [2677, 3687],\n [2683, 3677],\n [2686, 3679],\n [2690, 3677],\n [2693, 3687],\n [2690, 3703],\n [2692, 3711],\n [2708, 3736],\n [2710, 3742],\n [2718, 3744],\n [2722, 3747],\n [2727, 3755],\n [2733, 3760],\n [2742, 3763],\n [2769, 3765],\n [2775, 3774],\n [2780, 3774],\n [2781, 3781],\n [2786, 3778],\n [2785, 3780],\n [2787, 3781],\n [2788, 3785],\n [2792, 3785],\n [2792, 3789],\n [2797, 3795]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '017',\n 'FIPS': '34017',\n 'STATE': 'NJ',\n 'NAME': 'Hudson',\n 'LSAD': 'County'},\n 'id': 2205,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2711, 3458],\n [2720, 3444],\n [2723, 3411],\n [2734, 3388],\n [2744, 3372],\n [2735, 3353],\n [2722, 3335],\n [2689, 3301],\n [2659, 3263],\n [2658, 3265],\n [2644, 3255],\n [2622, 3231],\n [2617, 3238],\n [2611, 3241],\n [2605, 3240],\n [2587, 3225],\n [2562, 3211],\n [2556, 3213],\n [2531, 3196],\n [2515, 3192],\n [2502, 3179],\n [2482, 3176],\n [2462, 3166],\n [2457, 3167],\n [2451, 3178],\n [2449, 3189],\n [2454, 3200],\n [2468, 3214],\n [2470, 3233],\n [2461, 3257],\n [2488, 3279],\n [2506, 3279],\n [2515, 3290],\n [2525, 3345],\n [2535, 3360],\n [2537, 3369],\n [2536, 3396],\n [2529, 3398],\n [2528, 3403],\n [2530, 3426],\n [2546, 3446],\n [2559, 3453],\n [2568, 3452],\n [2574, 3446],\n [2599, 3440],\n [2647, 3446],\n [2696, 3458],\n [2711, 3458]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '085',\n 'FIPS': '36085',\n 'STATE': 'NY',\n 'NAME': 'Richmond',\n 'LSAD': 'County'},\n 'id': 2210,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2891, 3633],\n [2906, 3626],\n [2913, 3615],\n [2925, 3611],\n [2933, 3596],\n [2934, 3590],\n [2932, 3587],\n [2937, 3577],\n [2936, 3574],\n [2950, 3564],\n [2949, 3562],\n [2952, 3559],\n [2950, 3557],\n [2961, 3549],\n [2959, 3546],\n [2966, 3541],\n [2966, 3533],\n [2972, 3523],\n [2976, 3529],\n [2978, 3525],\n [2982, 3528],\n [2983, 3527],\n [3005, 3546],\n [3013, 3548],\n [3017, 3522],\n [3020, 3523],\n [3025, 3502],\n [3029, 3503],\n [3032, 3488],\n [3028, 3487],\n [3029, 3480],\n [3021, 3477],\n [3031, 3460],\n [3033, 3447],\n [3043, 3449],\n [3064, 3420],\n [3063, 3379],\n [3040, 3344],\n [2998, 3317],\n [2911, 3300],\n [2902, 3305],\n [2894, 3314],\n [2834, 3308],\n [2819, 3309],\n [2804, 3316],\n [2803, 3323],\n [2819, 3347],\n [2817, 3357],\n [2806, 3367],\n [2774, 3374],\n [2764, 3390],\n [2760, 3413],\n [2766, 3436],\n [2775, 3454],\n [2795, 3478],\n [2792, 3516],\n [2788, 3521],\n [2793, 3518],\n [2798, 3520],\n [2799, 3522],\n [2816, 3536],\n [2825, 3557],\n [2831, 3566],\n [2852, 3568],\n [2862, 3574],\n [2866, 3570],\n [2866, 3575],\n [2869, 3589],\n [2878, 3603],\n [2876, 3630],\n [2878, 3628],\n [2889, 3633],\n [2891, 3633]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '047',\n 'FIPS': '36047',\n 'STATE': 'NY',\n 'NAME': 'Kings',\n 'LSAD': 'County'},\n 'id': 2203,\n 'type': 'Feature'},\n {'geometry': {'type': 'MultiPolygon',\n 'coordinates': [[[[2919, 3907],\n [2936, 3900],\n [2938, 3897],\n [2945, 3895],\n [2946, 3897],\n [2953, 3901],\n [2957, 3896],\n [2957, 3891],\n [2952, 3877],\n [2938, 3859],\n [2926, 3837],\n [2919, 3813],\n [2921, 3765],\n [2928, 3754],\n [2935, 3754],\n [2940, 3748],\n [2949, 3742],\n [2952, 3732],\n [2945, 3727],\n [2941, 3721],\n [2927, 3705],\n [2921, 3708],\n [2918, 3706],\n [2916, 3709],\n [2915, 3705],\n [2911, 3701],\n [2912, 3697],\n [2916, 3695],\n [2915, 3693],\n [2903, 3681],\n [2887, 3656],\n [2882, 3645],\n [2878, 3640],\n [2876, 3630],\n [2878, 3603],\n [2869, 3589],\n [2866, 3575],\n [2866, 3570],\n [2862, 3574],\n [2852, 3568],\n [2830, 3565],\n [2816, 3536],\n [2799, 3522],\n [2798, 3520],\n [2793, 3518],\n [2787, 3523],\n [2786, 3532],\n [2790, 3545],\n [2795, 3548],\n [2797, 3561],\n [2793, 3571],\n [2786, 3575],\n [2802, 3666],\n [2875, 3801],\n [2889, 3842],\n [2919, 3907]]],\n [[[2763, 3558],\n [2766, 3555],\n [2757, 3535],\n [2753, 3539],\n [2763, 3558]]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '061',\n 'FIPS': '36061',\n 'STATE': 'NY',\n 'NAME': 'New York',\n 'LSAD': 'County'},\n 'id': 2200,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[3088, 3760],\n [3097, 3759],\n [3122, 3749],\n [3131, 3749],\n [3133, 3751],\n [3140, 3740],\n [3148, 3740],\n [3159, 3751],\n [3190, 3709],\n [3190, 3712],\n [3228, 3682],\n [3255, 3660],\n [3257, 3655],\n [3258, 3633],\n [3247, 3611],\n [3228, 3606],\n [3214, 3600],\n [3219, 3578],\n [3222, 3558],\n [3220, 3551],\n [3221, 3519],\n [3217, 3493],\n [3222, 3468],\n [3217, 3463],\n [3199, 3457],\n [3198, 3446],\n [3201, 3434],\n [3197, 3432],\n [3198, 3437],\n [3198, 3438],\n [3194, 3438],\n [3193, 3431],\n [3162, 3415],\n [3163, 3394],\n [3171, 3387],\n [3178, 3386],\n [3178, 3384],\n [3189, 3389],\n [3190, 3387],\n [3192, 3388],\n [3203, 3370],\n [3203, 3354],\n [3204, 3352],\n [3195, 3352],\n [3189, 3349],\n [3179, 3339],\n [3181, 3347],\n [3149, 3348],\n [3103, 3336],\n [3063, 3322],\n [2998, 3290],\n [2908, 3256],\n [2909, 3278],\n [2920, 3289],\n [2915, 3298],\n [2911, 3300],\n [2998, 3317],\n [3040, 3344],\n [3063, 3379],\n [3064, 3420],\n [3043, 3449],\n [3033, 3447],\n [3031, 3460],\n [3021, 3477],\n [3029, 3480],\n [3028, 3487],\n [3032, 3488],\n [3029, 3503],\n [3025, 3502],\n [3020, 3523],\n [3017, 3522],\n [3013, 3548],\n [3005, 3546],\n [2983, 3527],\n [2982, 3528],\n [2978, 3525],\n [2976, 3529],\n [2972, 3523],\n [2966, 3533],\n [2966, 3541],\n [2959, 3546],\n [2961, 3549],\n [2950, 3557],\n [2952, 3559],\n [2949, 3562],\n [2950, 3564],\n [2936, 3574],\n [2937, 3577],\n [2932, 3587],\n [2934, 3590],\n [2933, 3596],\n [2925, 3611],\n [2913, 3615],\n [2906, 3626],\n [2891, 3633],\n [2889, 3633],\n [2878, 3628],\n [2876, 3630],\n [2878, 3640],\n [2882, 3645],\n [2887, 3656],\n [2903, 3681],\n [2915, 3693],\n [2916, 3695],\n [2912, 3697],\n [2911, 3701],\n [2915, 3705],\n [2916, 3709],\n [2918, 3706],\n [2921, 3708],\n [2927, 3705],\n [2941, 3721],\n [2945, 3727],\n [2952, 3732],\n [2949, 3742],\n [2981, 3730],\n [2982, 3725],\n [2998, 3722],\n [3010, 3724],\n [3011, 3732],\n [3022, 3744],\n [3038, 3749],\n [3074, 3755],\n [3088, 3760]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '081',\n 'FIPS': '36081',\n 'STATE': 'NY',\n 'NAME': 'Queens',\n 'LSAD': 'County'},\n 'id': 2202,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2801, 3129],\n [2811, 3126],\n [2828, 3114],\n [2853, 3059],\n [2855, 2998],\n [2863, 2928],\n [2863, 2883],\n [2855, 2790],\n [2848, 2752],\n [2831, 2672],\n [2778, 2453],\n [2772, 2416],\n [2775, 2412],\n [2765, 2418],\n [2763, 2417],\n [2757, 2420],\n [2749, 2418],\n [2740, 2412],\n [2738, 2405],\n [2733, 2403],\n [2718, 2389],\n [2700, 2402],\n [2684, 2424],\n [2685, 2431],\n [2683, 2435],\n [2685, 2438],\n [2681, 2448],\n [2682, 2464],\n [2680, 2465],\n [2677, 2463],\n [2670, 2472],\n [2671, 2475],\n [2665, 2480],\n [2603, 2407],\n [2597, 2409],\n [2596, 2413],\n [2592, 2416],\n [2591, 2419],\n [2588, 2423],\n [2571, 2424],\n [2561, 2430],\n [2556, 2429],\n [2551, 2431],\n [2546, 2430],\n [2539, 2433],\n [2533, 2432],\n [2525, 2434],\n [2513, 2429],\n [2503, 2429],\n [2495, 2427],\n [2483, 2429],\n [2480, 2432],\n [2473, 2433],\n [2465, 2445],\n [2467, 2453],\n [2469, 2454],\n [2470, 2466],\n [2458, 2494],\n [2453, 2500],\n [2447, 2503],\n [2443, 2503],\n [2445, 2509],\n [2443, 2518],\n [2446, 2524],\n [2442, 2535],\n [2438, 2540],\n [2229, 2548],\n [2138, 2464],\n [2054, 2391],\n [2016, 2370],\n [1927, 2567],\n [1948, 2575],\n [1952, 2574],\n [1954, 2562],\n [1957, 2562],\n [1968, 2569],\n [1985, 2576],\n [1991, 2573],\n [2002, 2581],\n [2011, 2600],\n [2023, 2615],\n [2025, 2624],\n [2031, 2632],\n [2069, 2652],\n [2120, 2682],\n [2119, 2689],\n [2114, 2695],\n [2112, 2702],\n [2146, 2700],\n [2150, 2698],\n [2249, 2752],\n [2248, 2769],\n [2271, 2799],\n [2460, 2996],\n [2458, 3003],\n [2461, 3006],\n [2461, 3042],\n [2479, 3067],\n [2481, 3065],\n [2483, 3068],\n [2490, 3067],\n [2497, 3080],\n [2494, 3083],\n [2494, 3084],\n [2516, 3073],\n [2521, 3060],\n [2521, 3056],\n [2518, 3054],\n [2520, 3050],\n [2527, 3057],\n [2539, 3061],\n [2548, 3073],\n [2567, 3089],\n [2567, 3076],\n [2564, 3074],\n [2566, 3071],\n [2574, 3079],\n [2578, 3075],\n [2580, 3072],\n [2584, 3075],\n [2592, 3071],\n [2599, 3073],\n [2614, 3079],\n [2620, 3086],\n [2624, 3088],\n [2651, 3070],\n [2693, 3056],\n [2711, 3047],\n [2736, 3026],\n [2752, 3018],\n [2812, 3003],\n [2824, 3003],\n [2828, 3019],\n [2834, 3064],\n [2813, 3106],\n [2796, 3120],\n [2796, 3124],\n [2801, 3129]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '025',\n 'FIPS': '34025',\n 'STATE': 'NJ',\n 'NAME': 'Monmouth',\n 'LSAD': 'County'},\n 'id': 2195,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[2229, 2548],\n [2438, 2540],\n [2442, 2535],\n [2446, 2524],\n [2443, 2518],\n [2445, 2509],\n [2443, 2503],\n [2447, 2503],\n [2453, 2500],\n [2458, 2494],\n [2470, 2466],\n [2469, 2454],\n [2467, 2453],\n [2465, 2445],\n [2473, 2433],\n [2480, 2432],\n [2483, 2429],\n [2495, 2427],\n [2503, 2429],\n [2513, 2429],\n [2525, 2434],\n [2533, 2432],\n [2539, 2433],\n [2546, 2430],\n [2551, 2431],\n [2556, 2429],\n [2561, 2430],\n [2571, 2424],\n [2588, 2423],\n [2591, 2419],\n [2592, 2416],\n [2596, 2413],\n [2597, 2409],\n [2603, 2407],\n [2665, 2480],\n [2671, 2475],\n [2670, 2472],\n [2677, 2463],\n [2680, 2465],\n [2682, 2464],\n [2681, 2448],\n [2685, 2438],\n [2683, 2435],\n [2685, 2431],\n [2684, 2424],\n [2700, 2402],\n [2718, 2389],\n [2733, 2403],\n [2738, 2405],\n [2740, 2412],\n [2749, 2418],\n [2757, 2420],\n [2763, 2417],\n [2765, 2418],\n [2776, 2411],\n [2773, 2409],\n [2764, 2374],\n [2728, 2180],\n [2709, 2050],\n [2689, 1840],\n [2680, 1779],\n [2681, 1770],\n [2678, 1763],\n [2656, 1727],\n [2615, 1630],\n [2544, 1509],\n [2471, 1376],\n [2459, 1363],\n [2418, 1299],\n [2396, 1277],\n [2381, 1273],\n [2378, 1286],\n [2369, 1285],\n [2367, 1272],\n [2353, 1282],\n [2346, 1284],\n [2330, 1285],\n [2306, 1283],\n [2296, 1276],\n [2267, 1270],\n [2234, 1349],\n [2224, 1363],\n [2216, 1366],\n [2215, 1368],\n [2216, 1378],\n [2214, 1380],\n [2219, 1390],\n [2214, 1393],\n [2212, 1402],\n [2216, 1405],\n [2221, 1405],\n [2224, 1406],\n [2226, 1404],\n [2232, 1404],\n [2235, 1408],\n [2238, 1409],\n [2242, 1417],\n [2244, 1432],\n [2250, 1450],\n [2253, 1454],\n [2254, 1789],\n [2167, 1994],\n [2084, 2203],\n [2052, 2289],\n [2016, 2370],\n [2054, 2391],\n [2138, 2464],\n [2229, 2548]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '029',\n 'FIPS': '34029',\n 'STATE': 'NJ',\n 'NAME': 'Ocean',\n 'LSAD': 'County'},\n 'id': 2176,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[3554, 3986],\n [3563, 3979],\n [3567, 3971],\n [3568, 3972],\n [3574, 3953],\n [3579, 3944],\n [3583, 3941],\n [3585, 3898],\n [3597, 3881],\n [3604, 3879],\n [3602, 3864],\n [3612, 3843],\n [3615, 3828],\n [3661, 3502],\n [3660, 3492],\n [3660, 3476],\n [3661, 3473],\n [3658, 3473],\n [3661, 3384],\n [3622, 3372],\n [3572, 3360],\n [3440, 3315],\n [3442, 3337],\n [3428, 3340],\n [3388, 3342],\n [3336, 3332],\n [3179, 3335],\n [3179, 3339],\n [3189, 3349],\n [3195, 3352],\n [3204, 3352],\n [3203, 3354],\n [3203, 3370],\n [3192, 3388],\n [3190, 3387],\n [3189, 3389],\n [3178, 3384],\n [3178, 3386],\n [3171, 3387],\n [3163, 3394],\n [3162, 3415],\n [3193, 3431],\n [3194, 3438],\n [3198, 3438],\n [3198, 3437],\n [3197, 3432],\n [3201, 3434],\n [3198, 3446],\n [3199, 3457],\n [3217, 3463],\n [3222, 3468],\n [3217, 3493],\n [3221, 3519],\n [3220, 3551],\n [3222, 3558],\n [3219, 3578],\n [3214, 3600],\n [3228, 3606],\n [3247, 3611],\n [3258, 3633],\n [3256, 3658],\n [3190, 3712],\n [3190, 3709],\n [3159, 3751],\n [3180, 3790],\n [3179, 3801],\n [3217, 3851],\n [3220, 3859],\n [3214, 3875],\n [3216, 3877],\n [3239, 3884],\n [3294, 3859],\n [3323, 3872],\n [3325, 3900],\n [3344, 3928],\n [3355, 3938],\n [3365, 3941],\n [3379, 3938],\n [3402, 3947],\n [3411, 3956],\n [3448, 3972],\n [3454, 3972],\n [3480, 3959],\n [3522, 3970],\n [3528, 3967],\n [3550, 3977],\n [3554, 3986]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '059',\n 'FIPS': '36059',\n 'STATE': 'NY',\n 'NAME': 'Nassau',\n 'LSAD': 'County'},\n 'id': 2201,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[1749, 1707],\n [1837, 1634],\n [1842, 1625],\n [1846, 1623],\n [1851, 1615],\n [1854, 1606],\n [1854, 1601],\n [1860, 1591],\n [1863, 1569],\n [1861, 1566],\n [1862, 1563],\n [1860, 1560],\n [1863, 1559],\n [1863, 1554],\n [1862, 1550],\n [1865, 1546],\n [1861, 1538],\n [1863, 1530],\n [1861, 1521],\n [1866, 1520],\n [1868, 1518],\n [1868, 1512],\n [1871, 1511],\n [1874, 1515],\n [1878, 1514],\n [1878, 1509],\n [1888, 1510],\n [1889, 1505],\n [1890, 1504],\n [1893, 1505],\n [1896, 1513],\n [1900, 1513],\n [1902, 1507],\n [1902, 1501],\n [1906, 1499],\n [1910, 1503],\n [1911, 1509],\n [1914, 1512],\n [1916, 1512],\n [1926, 1505],\n [1929, 1506],\n [1936, 1502],\n [1954, 1488],\n [1964, 1484],\n [1966, 1481],\n [1969, 1471],\n [1975, 1468],\n [1983, 1458],\n [1992, 1459],\n [2000, 1455],\n [2015, 1457],\n [2022, 1442],\n [2033, 1434],\n [2031, 1425],\n [2034, 1417],\n [2037, 1413],\n [2045, 1411],\n [2051, 1405],\n [2054, 1403],\n [2059, 1403],\n [2067, 1411],\n [2072, 1412],\n [2076, 1404],\n [2080, 1402],\n [2088, 1403],\n [2092, 1395],\n [2099, 1391],\n [2101, 1381],\n [2103, 1379],\n [2108, 1379],\n [2113, 1383],\n [2120, 1382],\n [2126, 1372],\n [2130, 1369],\n [2134, 1371],\n [2139, 1377],\n [2144, 1379],\n [2148, 1379],\n [2150, 1376],\n [2152, 1366],\n [2154, 1362],\n [2158, 1361],\n [2162, 1367],\n [2162, 1378],\n [2164, 1382],\n [2169, 1384],\n [2172, 1383],\n [2176, 1378],\n [2172, 1363],\n [2178, 1354],\n [2182, 1350],\n [2185, 1349],\n [2190, 1353],\n [2190, 1359],\n [2184, 1369],\n [2184, 1374],\n [2187, 1376],\n [2191, 1377],\n [2199, 1373],\n [2201, 1373],\n [2209, 1380],\n [2214, 1380],\n [2216, 1378],\n [2215, 1368],\n [2216, 1366],\n [2224, 1363],\n [2234, 1349],\n [2267, 1270],\n [2296, 1276],\n [2306, 1283],\n [2330, 1285],\n [2346, 1284],\n [2353, 1282],\n [2367, 1272],\n [2365, 1261],\n [2373, 1242],\n [2378, 1240],\n [2382, 1233],\n [2378, 1218],\n [2334, 1144],\n [2287, 1088],\n [2229, 1041],\n [2227, 1018],\n [2221, 1010],\n [2152, 980],\n [2062, 921],\n [2033, 896],\n [2029, 900],\n [2023, 900],\n [2011, 893],\n [1994, 894],\n [1967, 914],\n [1960, 913],\n [1918, 882],\n [1913, 881],\n [1900, 886],\n [1889, 888],\n [1885, 887],\n [1879, 882],\n [1875, 875],\n [1870, 874],\n [1863, 877],\n [1862, 879],\n [1864, 890],\n [1849, 891],\n [1845, 889],\n [1845, 881],\n [1843, 876],\n [1840, 875],\n [1838, 877],\n [1840, 883],\n [1840, 887],\n [1837, 890],\n [1835, 890],\n [1831, 884],\n [1828, 883],\n [1824, 885],\n [1820, 883],\n [1816, 878],\n [1810, 879],\n [1805, 875],\n [1802, 878],\n [1798, 885],\n [1794, 884],\n [1791, 878],\n [1789, 880],\n [1788, 886],\n [1785, 886],\n [1782, 880],\n [1778, 881],\n [1773, 886],\n [1775, 892],\n [1773, 893],\n [1764, 888],\n [1758, 892],\n [1748, 893],\n [1744, 888],\n [1735, 885],\n [1726, 886],\n [1725, 891],\n [1720, 894],\n [1709, 889],\n [1706, 893],\n [1699, 894],\n [1696, 897],\n [1688, 894],\n [1687, 894],\n [1689, 902],\n [1679, 909],\n [1676, 908],\n [1671, 909],\n [1666, 905],\n [1664, 908],\n [1659, 908],\n [1656, 911],\n [1651, 909],\n [1646, 904],\n [1643, 907],\n [1629, 907],\n [1627, 909],\n [1620, 905],\n [1611, 916],\n [1602, 924],\n [1582, 926],\n [1576, 930],\n [1568, 932],\n [1568, 940],\n [1563, 948],\n [1562, 968],\n [1564, 974],\n [1564, 981],\n [1570, 1000],\n [1569, 1012],\n [1579, 1031],\n [1581, 1041],\n [1580, 1055],\n [1572, 1075],\n [1573, 1080],\n [1572, 1086],\n [1577, 1095],\n [1580, 1106],\n [1579, 1112],\n [1574, 1123],\n [1573, 1127],\n [1575, 1130],\n [1387, 1300],\n [1749, 1707]]]},\n 'properties': {'STATE_FIPS': '34',\n 'COUNTY_FIP': '001',\n 'FIPS': '34001',\n 'STATE': 'NJ',\n 'NAME': 'Atlantic',\n 'LSAD': 'County'},\n 'id': 2132,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[812, 261],\n [817, 258],\n [823, 260],\n [830, 256],\n [829, 253],\n [832, 246],\n [841, 253],\n [852, 234],\n [855, 234],\n [863, 248],\n [865, 247],\n [867, 241],\n [865, 234],\n [872, 231],\n [877, 233],\n [881, 240],\n [884, 239],\n [887, 237],\n [893, 238],\n [897, 245],\n [904, 244],\n [909, 239],\n [908, 233],\n [925, 218],\n [911, 191],\n [922, 169],\n [982, 102],\n [1027, 41],\n [1067, 0],\n [1088, -29],\n [1134, -61],\n [1170, -75],\n [1200, -74],\n [1224, -63],\n [1229, -53],\n [1224, -36],\n [1229, -35],\n [1235, -47],\n [1242, -80],\n [319, -80],\n [314, 0],\n [313, 14],\n [557, 24],\n [566, 35],\n [573, 39],\n [577, 38],\n [580, 39],\n [585, 47],\n [584, 51],\n [607, 62],\n [625, 105],\n [632, 110],\n [635, 109],\n [640, 111],\n [642, 113],\n [645, 122],\n [651, 125],\n [655, 131],\n [654, 143],\n [659, 152],\n [670, 156],\n [688, 166],\n [695, 162],\n [716, 169],\n [724, 169],\n [732, 165],\n [741, 169],\n [751, 171],\n [753, 174],\n [757, 171],\n [759, 179],\n [770, 176],\n [770, 182],\n [775, 181],\n [776, 177],\n [779, 178],\n [781, 180],\n [778, 187],\n [780, 192],\n [783, 193],\n [784, 195],\n [781, 200],\n [786, 213],\n [783, 211],\n [779, 215],\n [783, 218],\n [785, 221],\n [781, 233],\n [785, 238],\n [792, 244],\n [793, 250],\n [796, 251],\n [799, 247],\n [808, 249],\n [810, 260],\n [812, 261]]]},\n 'properties': {'STATE_FIPS': '10',\n 'COUNTY_FIP': '005',\n 'FIPS': '10005',\n 'STATE': 'DE',\n 'NAME': 'Sussex',\n 'LSAD': 'County'},\n 'id': 2175,\n 'type': 'Feature'},\n {'geometry': {'type': 'Polygon',\n 'coordinates': [[[4106, 4092],\n [4117, 4081],\n [4159, 4083],\n [4176, 4076],\n [4176, 3486],\n [4096, 3462],\n [3974, 3424],\n [3896, 3406],\n [3832, 3405],\n [3827, 3408],\n [3813, 3434],\n [3766, 3424],\n [3707, 3399],\n [3661, 3384],\n [3658, 3473],\n [3661, 3473],\n [3660, 3476],\n [3660, 3492],\n [3661, 3502],\n [3615, 3828],\n [3612, 3843],\n [3602, 3864],\n [3604, 3879],\n [3597, 3881],\n [3585, 3898],\n [3583, 3941],\n [3579, 3944],\n [3574, 3953],\n [3568, 3972],\n [3567, 3971],\n [3563, 3979],\n [3554, 3986],\n [3562, 4023],\n [3571, 4031],\n [3581, 4023],\n [3603, 4015],\n [3642, 4009],\n [3652, 3999],\n [3654, 3983],\n [3687, 3981],\n [3691, 3990],\n [3690, 4023],\n [3694, 4046],\n [3696, 4048],\n [3706, 4049],\n [3733, 4014],\n [3745, 4003],\n [3764, 3993],\n [3775, 3991],\n [3777, 3995],\n [3795, 3999],\n [3848, 3989],\n [3944, 3952],\n [4061, 3998],\n [4065, 4010],\n [4067, 4050],\n [4053, 4061],\n [4046, 4074],\n [4078, 4071],\n [4096, 4085],\n [4106, 4092]]]},\n 'properties': {'STATE_FIPS': '36',\n 'COUNTY_FIP': '103',\n 'FIPS': '36103',\n 'STATE': 'NY',\n 'NAME': 'Suffolk',\n 'LSAD': 'County'},\n 'id': 2231,\n 'type': 'Feature'}],\n 'type': 'FeatureCollection'}}" }, { - "objectID": "geoparquet/index.html", - "href": "geoparquet/index.html", - "title": "GeoParquet", + "objectID": "pmtiles/intro.html", + "href": "pmtiles/intro.html", + "title": "PMTiles", "section": "", - "text": "GeoParquet is an encoding for how to store geospatial vector data (point, lines, polygons) in Apache Parquet, a popular columnar storage format for tabular data.\nParquet has a wide ecosystem of tools and support; GeoParquet builds on this success by defining how to store geometries in the Parquet format. Because GeoParquet is not a separate format, any program that can read Parquet is able to load GeoParquet as well, even if it can’t make sense of the geometry information. This is very similar to how GeoTIFF layers geospatial information on top of the existing TIFF image standard.\nThe two main things that GeoParquet defines on top of Parquet are how to encode geometries in the geometry column and how to include metadata like the geometries’ Coordinate Reference System (CRS).\nGeoParquet is a relatively young format, and the specification has not yet reached a 1.0 release (as of August 2023, it’s at 1.0.0-rc.1). However, reading and writing GeoParquet has been supported in GDAL since version 3.5, and thus can be used in programs like GeoPandas and QGIS.\n\n\n\n\n\n\nWarning\n\n\n\nIn GeoPandas use read_parquet and to_parquet to read and write GeoParquet, not read_file and to_file as one would use with most other formats. 1\n\n\nBecause GeoParquet stores geometries in standard Well-Known Binary (WKB), it supports any vector geometry type defined in the OGC Simple Features specification. This includes the standard building blocks of Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection. A best practice is to store only geometries with the same type, as that allows readers to know which geometry type is stored without scanning the entire file.\nSome of the sections below will discuss strengths of Parquet in general. Keep in mind that because GeoParquet is built on top of Parquet, GeoParquet inherits all of these strengths.\n\n\nParquet files are laid out differently than other tabular formats like CSV or FlatGeobuf, so it’s helpful to see a diagram:\n\n\n\nSchematic of Parquet file layout\n\n\nA Parquet file consists of a sequence of chunks called row groups. These are logical groups of columns with the same number of rows. A row group consists of multiple columns, each of which is called a column chunk. These are sequences of raw column values that are guaranteed to be contiguous in the file. All row groups in the file must have the same schema, meaning that the data type of each column must be the same for every row group.\nA Parquet file includes metadata describing the internal chunking. This metadata includes the byte range of every column chunk in the dataset. This allows a Parquet reader to fetch any given column chunk once they have the file metadata.\nThe Parquet metadata also includes column statistics (the minimum and maximum value) for each column chunk. This means that if a user is interested in data where column “A” has values greater than 100, the Parquet reader can skip loading and parsing any column chunks where the maximum is known to be less than 100.\nIn Parquet, the metadata is located at the end of the file rather than at the beginning. This makes it much easier to write, as you don’t need to know how many total rows you have at the beginning, but makes it slightly harder to read. In practice, this is not too much more difficult to read: a Parquet reader first reads the end of the file, then makes reads for select columns.\n\n\nThe bytes of each column are contiguous, instead of each row. This means that it’s easy to filter on columns — fetching all rows of a single column — but not possible to filter on individual rows.\n\n\n\nBecause Parquet is column-oriented, a Parquet reader can fetch only specific columns that the user is interested in.\n\n\n\nBecause Parquet is internally chunked, Parquet can fetch only specific row groups that meet a specific filtering condition.\nNote that row group filtering on a specific column tends to only work well if the Parquet file was sorted on that column when saved. Non-sorted columns tend to have random values, and so the column statistics won’t tend to filter out many row groups.\n\n\n\n\n\n\nNote\n\n\n\nIn general it’s only possible to optimize filtering row groups by one column. This is the biggest difference between file formats and databases. Databases can have multiple indexes on whatever columns you want, and then when you run a query, and it will use all of the indexes. But that’s why it’s hard to make databases work as cloud-native files, because if you have high latency, you don’t want to make lots of tiny fetches.\n\n\n\n\n\nParquet is internally compressed by default and Parquet compression is more efficient compared to other formats.\nCompression algorithms are more effective when nearby bytes are more similar to each other. Data within a column tends to be much more similar than data across a row. Since Parquet is column-oriented, compression algorithms work better and result in smaller file sizes than a comparable row-based format.\nIt’s possible to have random access to one of the internal chunks inside the file at large, even though that chunk is compressed. Note that it isn’t possible to fetch partial data inside one chunk without loading and decompressing the entire chunk.\n\n\n\nFor maximum compatibility with existing systems, geometries are stored as ISO-standard WKB. Most geospatial programs are able to read and write WKB.\n\n\n\nGeoParquet is a young specification, and spatial indices are not yet part of the standard. Future revisions of GeoParquet are expected to add support for spatial indexes.\nOne way around this is to store multiple GeoParquet files according to some region identifier, cataloging each file with the SpatioTemporal Asset Catalog (STAC) specification.\n\n\n\nIn a streaming download, you read bytes starting at the beginning of the file, progressing towards the end. In Parquet, this is not helpful because the metadata is in the footer of the file instead of the header.\nInstead, we can replicate something similar to streaming by first fetching only the metadata region at the end of the file, and then making multiple requests for each internal chunk.\n\n\n\nOnce written, a Parquet file is immutable. No modification or appending can happen to that Parquet file. Instead, create a new Parquet file.\n\n\n\nWhile at medium data sizes GeoParquet is most easily distributed as a single file, at large data sizes a single dataset is often split into multiple files. Sometimes multiple files can be easier to write, such as if the data is output from a distributed system.\nA best practice when writing multiple files is to store a top-level metadata file, often named _metadata, with the metadata of all Parquet files in the directory. Without a top-level metadata file, a reader must read the Parquet footer of every individual file in the directory before reading any data. With a metadata file, a Parquet reader can read just that one metadata file, and then read the relevant chunks in the directory. For more information on this, read the “Partitioned Datasets” and “Writing _metadata and _common_metadata files” of the pyarrow documentation. As of August 2023, GeoPandas has no way to write multiple GeoParquet files out of the box, though you may be able to pass a * glob with multiple paths into geopandas.read_parquet.\nStoring Parquet data in multiple files makes it possible to in effect append to the dataset by adding a new file to the directory, but you must be careful to ensure that the new file has the exact same data schema as the existing files, and if a top-level metadata file exists, it must be rewritten to reflect the new file.\nSome elements of how to store GeoParquet-specific metadata in a multi-file layout have not yet been standardized.\n\n\n\nParquet supports a very extensive type system, including nested types such as lists and maps (i.e. like a Python dict). This means that you can store a key-value mapping or a multi-dimensional array within an attribute column of a GeoParquet dataset.\n\n\n\n\n\nDemystifying the Parquet File Format" + "text": "PMTiles is a single-file archive format for tiled data, usually used for visualization.\nAs an “archive format”, PMTiles is similar to a ZIP file: it contains the contents of many individual files inside of one PMTiles file. A single file is often much easier to use and keep track of than many very small files.\nPMTiles is designed for tiled data. That is, data where one inner file represents a small square somewhere on a map, usually representing the Web Mercator grid. PMTiles can be used for any format of tiled data. PMTiles is used most often with vector data, where each tile data contained within the archive is encoded as a Mapbox Vector Tile (MVT), but can also be used with e.g. raster data or terrain mesh data.\n\n\nTo understand PMTiles, it’s important to understand the difference between “analytical” data and “tiled” data. Analytical data refers to data in its original form, without any modifications to geometry. Tiled data formats apply a variety of modifications to geometries, including clipping and simplification, to save space and make it faster to visualize.\n\nConsider the above diagram. In an analytical format, every coordinate of the complex polygon would be included in one single file. In a tiled format, there are predefined tile sets (or grids) and the geometry would be split into one or more files, where each file represents one cell of the grid.\nThe analytical format is more useful for operations like a spatial join, because the entire geometry is available. It’s harder to perform such analyses on tiled data because given any one tile, it’s impossible to know whether the data contained in that tile represents the full geometry or not.\n\nKnow which other tiles contain part of this polygon (This is hard! It requires some other pre-generated attribute other than the geometry itself.)\nFetch each of those neighboring tiles\nAssemble the dissected geometries back into a single geometry\nApply the desired operation\n\nThe tiled format is more useful for visualization because a user who wants to visualize a small area only needs to download a few tiles. Additionally loading the data is faster because of simplification. It’s slower to visualize analytical data because the entire shape with all coordinates must be loaded, even if visualizing only a small area.\nThus analytical and visualization formats strive for different goals.\n\n\n\nPMTiles is designed to be a cloud-native file format: used directly from a client over a network via HTTP range requests, without having a server in the middle.\n\n\n\nPMTiles has a file header, one or more metadata regions, and a region of tile data.\nThe header is fixed length, located at the beginning of the file, and includes necessary information to decode the rest of the file accurately.\nPMTiles includes directories, or regions of bytes with metadata about tiles. It’s important for each directory to remain small, so while there will always be at least one directory, larger PMTiles archives with many tiles may include more than one directory.\nAt the end of the file is the tile data. This includes all data for all the tiles in the archive.\nThe full specification is defined here.\n\n\nInterally, tiles are oriented along a Hilbert Curve. This means that tiles that are spatially near each other are also located near each other in the file structure.\nThis is especially appropriate for PMTiles because visualization purposes most often request data within a specific geographic area. Because spatially-nearby tiles are likely to be nearby in the file as well, this allows the PMTiles client to merge multiple requests for tiles into one larger request, rather than needing to fetch a different area of the file for each tile.\n\n\n\n\nPMTiles archives support storing a full XYZ pyramid of tile data. This means that you can store multiple zoom levels of data inside a single file.\n\n\n\nPMTiles allows tiles to be stored in the file with compression.\n\n\n\n\n\nThe easiest way to generate PMTiles for vector data is through the tippecanoe tool. This will generate vector tiles that are ideal for visualization, removing small features at low zoom levels to keep tiles a manageable size.\n\n\n\nPMTiles has a command-line program for creating PMTiles if you already have an MBTiles file or a directory of tiles.\n\n\n\n\n\n\nIf you have an existing PMTiles archive, either as a local file or hosted on cloud storage, you can use the PMTiles Viewer to inspect the tiles hosted within the file.\n\n\n\nPMTiles doesn’t have a standalone JavaScript library, but rather is designed to be used in conjunction with a JavaScript map rendering library.\nSee the docs on viewing PMTiles in Leaflet, MapLibre GL JS and OpenLayers.\n\n\n\nPMTiles has a Python package, which allows reading and writing PMTiles archives from Python.\n\n\n\n\n\n\nThe most common alternative for PMTiles is MBTiles, which was in many ways the precursor to PMTiles. MBTiles stores the included vector tiles in a table in a SQLite database. MBTiles has the benefit of being much easier to use than manually managing millions of tiny, individual files, but MBTiles is not serverless. In general, it’s impossible to read from a SQLite database without fetching the entire file’s content. This means that frontend clients like a web browser couldn’t fetch tiles directly using range requests, but rather a server has to be running to fetch tiles from the MBTiles file.\n\n\n\nIt’s also possible to upload the bare tiled data directly to cloud storage as individual files.\nThis has significant downsides of needing to manage many millions of tiny individual files. Uploading millions of files to a cloud storage provider such as S3 takes time and money. For example, AWS charges $5 per million files added to an S3 bucket. So a 10 million PMTiles archive would cost $50, compared to 5-millionths of a cent to upload the PMTiles file." }, { - "objectID": "geoparquet/index.html#file-layout", - "href": "geoparquet/index.html#file-layout", - "title": "GeoParquet", + "objectID": "pmtiles/intro.html#analytical-vs-tiled-data-formats", + "href": "pmtiles/intro.html#analytical-vs-tiled-data-formats", + "title": "PMTiles", "section": "", - "text": "Parquet files are laid out differently than other tabular formats like CSV or FlatGeobuf, so it’s helpful to see a diagram:\n\n\n\nSchematic of Parquet file layout\n\n\nA Parquet file consists of a sequence of chunks called row groups. These are logical groups of columns with the same number of rows. A row group consists of multiple columns, each of which is called a column chunk. These are sequences of raw column values that are guaranteed to be contiguous in the file. All row groups in the file must have the same schema, meaning that the data type of each column must be the same for every row group.\nA Parquet file includes metadata describing the internal chunking. This metadata includes the byte range of every column chunk in the dataset. This allows a Parquet reader to fetch any given column chunk once they have the file metadata.\nThe Parquet metadata also includes column statistics (the minimum and maximum value) for each column chunk. This means that if a user is interested in data where column “A” has values greater than 100, the Parquet reader can skip loading and parsing any column chunks where the maximum is known to be less than 100.\nIn Parquet, the metadata is located at the end of the file rather than at the beginning. This makes it much easier to write, as you don’t need to know how many total rows you have at the beginning, but makes it slightly harder to read. In practice, this is not too much more difficult to read: a Parquet reader first reads the end of the file, then makes reads for select columns.\n\n\nThe bytes of each column are contiguous, instead of each row. This means that it’s easy to filter on columns — fetching all rows of a single column — but not possible to filter on individual rows.\n\n\n\nBecause Parquet is column-oriented, a Parquet reader can fetch only specific columns that the user is interested in.\n\n\n\nBecause Parquet is internally chunked, Parquet can fetch only specific row groups that meet a specific filtering condition.\nNote that row group filtering on a specific column tends to only work well if the Parquet file was sorted on that column when saved. Non-sorted columns tend to have random values, and so the column statistics won’t tend to filter out many row groups.\n\n\n\n\n\n\nNote\n\n\n\nIn general it’s only possible to optimize filtering row groups by one column. This is the biggest difference between file formats and databases. Databases can have multiple indexes on whatever columns you want, and then when you run a query, and it will use all of the indexes. But that’s why it’s hard to make databases work as cloud-native files, because if you have high latency, you don’t want to make lots of tiny fetches.\n\n\n\n\n\nParquet is internally compressed by default and Parquet compression is more efficient compared to other formats.\nCompression algorithms are more effective when nearby bytes are more similar to each other. Data within a column tends to be much more similar than data across a row. Since Parquet is column-oriented, compression algorithms work better and result in smaller file sizes than a comparable row-based format.\nIt’s possible to have random access to one of the internal chunks inside the file at large, even though that chunk is compressed. Note that it isn’t possible to fetch partial data inside one chunk without loading and decompressing the entire chunk.\n\n\n\nFor maximum compatibility with existing systems, geometries are stored as ISO-standard WKB. Most geospatial programs are able to read and write WKB.\n\n\n\nGeoParquet is a young specification, and spatial indices are not yet part of the standard. Future revisions of GeoParquet are expected to add support for spatial indexes.\nOne way around this is to store multiple GeoParquet files according to some region identifier, cataloging each file with the SpatioTemporal Asset Catalog (STAC) specification.\n\n\n\nIn a streaming download, you read bytes starting at the beginning of the file, progressing towards the end. In Parquet, this is not helpful because the metadata is in the footer of the file instead of the header.\nInstead, we can replicate something similar to streaming by first fetching only the metadata region at the end of the file, and then making multiple requests for each internal chunk.\n\n\n\nOnce written, a Parquet file is immutable. No modification or appending can happen to that Parquet file. Instead, create a new Parquet file.\n\n\n\nWhile at medium data sizes GeoParquet is most easily distributed as a single file, at large data sizes a single dataset is often split into multiple files. Sometimes multiple files can be easier to write, such as if the data is output from a distributed system.\nA best practice when writing multiple files is to store a top-level metadata file, often named _metadata, with the metadata of all Parquet files in the directory. Without a top-level metadata file, a reader must read the Parquet footer of every individual file in the directory before reading any data. With a metadata file, a Parquet reader can read just that one metadata file, and then read the relevant chunks in the directory. For more information on this, read the “Partitioned Datasets” and “Writing _metadata and _common_metadata files” of the pyarrow documentation. As of August 2023, GeoPandas has no way to write multiple GeoParquet files out of the box, though you may be able to pass a * glob with multiple paths into geopandas.read_parquet.\nStoring Parquet data in multiple files makes it possible to in effect append to the dataset by adding a new file to the directory, but you must be careful to ensure that the new file has the exact same data schema as the existing files, and if a top-level metadata file exists, it must be rewritten to reflect the new file.\nSome elements of how to store GeoParquet-specific metadata in a multi-file layout have not yet been standardized.\n\n\n\nParquet supports a very extensive type system, including nested types such as lists and maps (i.e. like a Python dict). This means that you can store a key-value mapping or a multi-dimensional array within an attribute column of a GeoParquet dataset." + "text": "To understand PMTiles, it’s important to understand the difference between “analytical” data and “tiled” data. Analytical data refers to data in its original form, without any modifications to geometry. Tiled data formats apply a variety of modifications to geometries, including clipping and simplification, to save space and make it faster to visualize.\n\nConsider the above diagram. In an analytical format, every coordinate of the complex polygon would be included in one single file. In a tiled format, there are predefined tile sets (or grids) and the geometry would be split into one or more files, where each file represents one cell of the grid.\nThe analytical format is more useful for operations like a spatial join, because the entire geometry is available. It’s harder to perform such analyses on tiled data because given any one tile, it’s impossible to know whether the data contained in that tile represents the full geometry or not.\n\nKnow which other tiles contain part of this polygon (This is hard! It requires some other pre-generated attribute other than the geometry itself.)\nFetch each of those neighboring tiles\nAssemble the dissected geometries back into a single geometry\nApply the desired operation\n\nThe tiled format is more useful for visualization because a user who wants to visualize a small area only needs to download a few tiles. Additionally loading the data is faster because of simplification. It’s slower to visualize analytical data because the entire shape with all coordinates must be loaded, even if visualizing only a small area.\nThus analytical and visualization formats strive for different goals." }, { - "objectID": "geoparquet/index.html#references", - "href": "geoparquet/index.html#references", - "title": "GeoParquet", + "objectID": "pmtiles/intro.html#cloud-native", + "href": "pmtiles/intro.html#cloud-native", + "title": "PMTiles", "section": "", - "text": "Demystifying the Parquet File Format" + "text": "PMTiles is designed to be a cloud-native file format: used directly from a client over a network via HTTP range requests, without having a server in the middle." }, { - "objectID": "geoparquet/index.html#footnotes", - "href": "geoparquet/index.html#footnotes", - "title": "GeoParquet", - "section": "Footnotes", - "text": "Footnotes\n\n\nAs pointed out by GDAL developer Even Rouault, reading GeoParquet through GDAL is just as fast as reading through the geopandas.read_parquet function if you’re using GDAL’s Arrow API. As of September 2023, this is not the default, so you need to opt into the pyogrio engine and opt into the Arrow API:\nimport geopandas as gpd\ngpd.read_file(\"file.parquet\", engine=\"pyogrio\", use_arrow=True)\nIt’s also necessary to note that the Python wheels distributed by pyogrio do not include the Arrow and Parquet drivers by default. In order to use the pyogrio driver for a GeoParquet file, you need to compile from source when installing. You’ll need to have a GDAL installation version 3.6 or later (and built with Arrow and Parquet support, as seen by ogrinfo --formats) on your computer already, and then you can build pyogrio from source with:\npip install pyogrio --no-binary pyogrio\n↩︎" + "objectID": "pmtiles/intro.html#internal-format", + "href": "pmtiles/intro.html#internal-format", + "title": "PMTiles", + "section": "", + "text": "PMTiles has a file header, one or more metadata regions, and a region of tile data.\nThe header is fixed length, located at the beginning of the file, and includes necessary information to decode the rest of the file accurately.\nPMTiles includes directories, or regions of bytes with metadata about tiles. It’s important for each directory to remain small, so while there will always be at least one directory, larger PMTiles archives with many tiles may include more than one directory.\nAt the end of the file is the tile data. This includes all data for all the tiles in the archive.\nThe full specification is defined here.\n\n\nInterally, tiles are oriented along a Hilbert Curve. This means that tiles that are spatially near each other are also located near each other in the file structure.\nThis is especially appropriate for PMTiles because visualization purposes most often request data within a specific geographic area. Because spatially-nearby tiles are likely to be nearby in the file as well, this allows the PMTiles client to merge multiple requests for tiles into one larger request, rather than needing to fetch a different area of the file for each tile." }, { - "objectID": "flatgeobuf/intro.html", - "href": "flatgeobuf/intro.html", - "title": "FlatGeobuf", + "objectID": "pmtiles/intro.html#multiple-resolution", + "href": "pmtiles/intro.html#multiple-resolution", + "title": "PMTiles", "section": "", - "text": "FlatGeobuf\nFlatGeobuf is a binary file format for geographic vector data, such as points, lines, and polygons.\nUnlike some formats like Cloud-Optimized GeoTIFF, which builds on the previous success of TIFF and GeoTIFF, FlatGeobuf is a new format, designed from the ground up to be faster for geospatial data.\nFlatGeobuf is widely supported — via its GDAL implementation — in many programming languages as well as applications like QGIS.\nFlatGeobuf supports any vector geometry type defined in the OGC Simple Features specification. This includes the standard building blocks of Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection, but also includes more obscure types such as CircularString, Surface, and TIN (Triangulated irregular network). A best practice is to store only geometries with the same type, as that allows readers to know which geometry type is stored without scanning the entire file.\nAn optional row-based spatial index optimizes for remote reading.\n\nFile layout\nThe internal layout of the file has four sections: magic bytes (aka signature), header, index, and data (aka features).\n\n\nImage source: Horace Williams, Kicking the Tires: Flatgeobuf\n\n\nThe file signature is 8 “magic bytes” indicating the file type and specification version, which allows readers to know a file is FlatGeobuf, even if it’s missing a file extension.\nNext comes the header, which stores the bounding box of the dataset, the geometry type of the features (if known and unique), the attribute schema, the number of features, and coordinate reference system information.\nAfter the file header is an optional spatial index. If included, this lets a reader skip reading features that are not within a provided spatial query.\nLast come the individual features. The rest of the file is a sequence of feature records, placed end to end in a row-wise fashion.\n\n\n\nRow based\nInternally, features are laid out in a row-oriented fashion rather than a column-oriented fasion. This means that it’s relatively cheap to select specific records from the file, but relatively expensive to select a specific column. This is ideal for a small spatial query (assuming an index exists in the file) but to load all geometries requires loading all attribute information as well.\n\n\nNo internal compression\nFlatGeobuf does not support compression while maintaining the ability to seek within the file. In particular, FlatGeobuf’s spatial index describes the byte ranges in the uncompressed file. Those byte ranges will be incorrect if the file is compressed.\nA compression like gzip can be applied to the FlatGeobuf file in full, but keep in mind that storing the compressed file will eliminate random access support.\n\n\nNo append support\nFlatGeobuf is a write-only format, and doesn’t support appending, as that would invalidate the spatial index in the file.\n\n\nRandom access supported via spatial index\nFlatGeobuf optionally supports a spatial index at the beginning of the file, which can speed up reading portions of a file based on a spatial query. For more information on how this spatial index works, refer to the Hilbert R Tree page.\n\n\n\nStreaming features is supported\nFlatGeobuf supports streaming, meaning that you can use part of the file before the entire file has finished downloading. This is different than random access, because you have no ability to skip around in the file.\nStreaming can be valuable because it makes an application seem more responsive; you can have something happen without having to wait for the full download to complete. A good example of this is this example by FlatGeobuf’s author Björn Harrtell. As the file is downloaded to the browser, portions of the file get rendered progressively in parts.\nThis works even with full-file compression like gzip or deflate because those compression algorithms support streaming decompression.\n\n\nBroad type system\nFlatGeobuf supports attributes with a range of types:\n\nByte: Signed 8-bit integer\nUByte: Unsigned 8-bit integer\nBool: Boolean\nShort: Signed 16-bit integer\nUShort: Unsigned 16-bit integer\nInt: Signed 32-bit integer\nUInt: Unsigned 32-bit integer\nLong: Signed 64-bit integer\nULong: Unsigned 64-bit integer\nFloat: Single precision floating point number\nDouble: Double precision floating point number\nString: UTF8 string\nJson: General JSON type intended to be application specific\nDateTime: ISO 8601 date time\nBinary: General binary type intended to be application specific\n\n\n\n\n\n\n\nNote\n\n\n\nNote that FlatGeobuf is unable to store nested types without overhead. It doesn’t support a “list” or “dict” type apart from JSON, which has a parsing overhead.\nIn some situations, having strong nested type support can be useful. For example STAC stored as GeoParquet has columns that are nested, such as the assets column that needs to store a dictionary-like mapping from asset names to their information. FlatGeobuf is able to store such data by serializing it to JSON, but it’s not possible to see the nested schema before parsing the full dataset.\n\n\n\n\nKnown table schema\nFlatGeobuf declares the schema of properties at the beginning of the file. This makes it much easier to read the file — compared to a fully schemaless format like GeoJSON — because the reader knows what data type each attribute has in advance.\n\n\nReferences\n\nflatgeobuf.org: Official project website.\nFlatgeobuf: Implementer’s Guide" + "text": "PMTiles archives support storing a full XYZ pyramid of tile data. This means that you can store multiple zoom levels of data inside a single file." + }, + { + "objectID": "pmtiles/intro.html#internal-compression", + "href": "pmtiles/intro.html#internal-compression", + "title": "PMTiles", + "section": "", + "text": "PMTiles allows tiles to be stored in the file with compression." + }, + { + "objectID": "pmtiles/intro.html#generating-pmtiles", + "href": "pmtiles/intro.html#generating-pmtiles", + "title": "PMTiles", + "section": "", + "text": "The easiest way to generate PMTiles for vector data is through the tippecanoe tool. This will generate vector tiles that are ideal for visualization, removing small features at low zoom levels to keep tiles a manageable size.\n\n\n\nPMTiles has a command-line program for creating PMTiles if you already have an MBTiles file or a directory of tiles." + }, + { + "objectID": "pmtiles/intro.html#using-pmtiles", + "href": "pmtiles/intro.html#using-pmtiles", + "title": "PMTiles", + "section": "", + "text": "If you have an existing PMTiles archive, either as a local file or hosted on cloud storage, you can use the PMTiles Viewer to inspect the tiles hosted within the file.\n\n\n\nPMTiles doesn’t have a standalone JavaScript library, but rather is designed to be used in conjunction with a JavaScript map rendering library.\nSee the docs on viewing PMTiles in Leaflet, MapLibre GL JS and OpenLayers.\n\n\n\nPMTiles has a Python package, which allows reading and writing PMTiles archives from Python." + }, + { + "objectID": "pmtiles/intro.html#alternatives", + "href": "pmtiles/intro.html#alternatives", + "title": "PMTiles", + "section": "", + "text": "The most common alternative for PMTiles is MBTiles, which was in many ways the precursor to PMTiles. MBTiles stores the included vector tiles in a table in a SQLite database. MBTiles has the benefit of being much easier to use than manually managing millions of tiny, individual files, but MBTiles is not serverless. In general, it’s impossible to read from a SQLite database without fetching the entire file’s content. This means that frontend clients like a web browser couldn’t fetch tiles directly using range requests, but rather a server has to be running to fetch tiles from the MBTiles file.\n\n\n\nIt’s also possible to upload the bare tiled data directly to cloud storage as individual files.\nThis has significant downsides of needing to manage many millions of tiny individual files. Uploading millions of files to a cloud storage provider such as S3 takes time and money. For example, AWS charges $5 per million files added to an S3 bucket. So a 10 million PMTiles archive would cost $50, compared to 5-millionths of a cent to upload the PMTiles file." + }, + { + "objectID": "cloud-optimized-netcdf4-hdf5/index.html", + "href": "cloud-optimized-netcdf4-hdf5/index.html", + "title": "Cloud-Optimized NetCDF4/HDF5", + "section": "", + "text": "Cloud-optimized access to NetCDF4/HDF5 files is possible. However, there are no standards for the metadata, chunking and compression for cloud-optimized access for these file types.\n\n\n\n\n\n\nNote\n\n\n\nNote: NetCDF4 are valid HDF5 files, see Reading and Editing NetCDF-4 Files with HDF5.\n\n\nNetCDF4/HDF5 were designed for disk access and thus moving them to the cloud has borne little fruit. Matt Rocklin describes the issue in HDF in the Cloud: Challenges and Solutions for Scientific Data:\n\nThe HDF format is complex and metadata is strewn throughout the file, so that a complex sequence of reads is required to reach a specific chunk of data. The only pragmatic way to read a chunk of data from an HDF file today is to use the existing HDF C library, which expects to receive a C FILE object, pointing to a normal file system (not a cloud object store) (this is not entirely true, as we’ll see below).\nSo organizations like NASA are dumping large amounts of HDF onto Amazon’s S3 that no one can actually read, except by downloading the entire file to their local hard drive, and then pulling out the particular bits that they need with the HDF library. This is inefficient. It misses out on the potential that cloud-hosted public data can offer to our society.\n\nTo provide cloud-optimized access to these files without an intermediate service like Hyrax or the Highly Scalable Data Service (HSDS), it is recommended to determine if the NetCDF4/HDF5 data you wish to provide can be used with kerchunk. Rich Signell provided some insightful examples and instructions on how to create a kerchunk reference file (aka fsspec.ReferenceFileSystem) for NetCDF4/HDF5 and the things to be aware of in Cloud-Performant NetCDF4/HDF5 with Zarr, Fsspec, and Intake. Note, the post is from 2020, so it’s possible details have changed; however, the approach of using kerchunk for NetCDF4/HDF5 is still recommended.\nStay tuned for more information on cloud-optimized NetCDF4/HDF5 in future releases of this guide." + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html", + "href": "cloud-optimized-geotiffs/cogs-examples.html", + "title": "Examples of Working with COGs", + "section": "", + "text": "The packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment." + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#environment", + "href": "cloud-optimized-geotiffs/cogs-examples.html#environment", + "title": "Examples of Working with COGs", + "section": "", + "text": "The packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment." + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#setup", + "href": "cloud-optimized-geotiffs/cogs-examples.html#setup", + "title": "Examples of Working with COGs", + "section": "Setup", + "text": "Setup\nFor demonstrating some COG concepts, we will download a regular GeoTIFF, create a Cloud-Optimized GeoTIFF and explore how they are different.\nFirst we use the earthaccess library to setup credentials to fetch data from NASA’s EarthData catalog.\n\nimport earthaccess\nimport rasterio\nfrom rasterio.plot import show\nfrom rio_cogeo import cog_validate, cog_info\n\n/Users/kyle/local/micromamba/envs/coguide-cog/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n from .autonotebook import tqdm as notebook_tqdm\n\n\n\nearthaccess.login()\n\nEARTHDATA_USERNAME and EARTHDATA_PASSWORD are not set in the current environment, try setting them or use a different strategy (netrc, interactive)\nYou're now authenticated with NASA Earthdata Login\nUsing token with expiration date: 10/24/2023\nUsing .netrc file for EDL\n\n\n<earthaccess.auth.Auth at 0x10427d390>" + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#download-a-geotiff-from-earthdata", + "href": "cloud-optimized-geotiffs/cogs-examples.html#download-a-geotiff-from-earthdata", + "title": "Examples of Working with COGs", + "section": "Download a GeoTIFF from EarthData", + "text": "Download a GeoTIFF from EarthData\nNote: The whole point of is that we don’t download data. So in future examples, we will demonstrate how to access just subsets of data using COG and compare that with a GeoTIFF.\n\n# Download data\nshort_name = 'VCF5KYR'\nversion = '001'\n\nveg_item_results = earthaccess.search_data(\n short_name=short_name,\n version=version,\n count=1\n)\n\nGranules found: 33\n\n\n\ntest_data_dir = \"./test_data\"\nveg_files = earthaccess.download(veg_item_results, test_data_dir)\nveg_gtiff_filename = f\"{test_data_dir}/{veg_files[0]}\"\n\n Getting 1 granules, approx download size: 0.07 GB\nFile VCF5KYR_1982001_001_2018224204211.tif already downloaded\n\n\nQUEUEING TASKS | : 100%|██████████| 1/1 [00:00<00:00, 900.84it/s]\nPROCESSING TASKS | : 100%|██████████| 1/1 [00:00<00:00, 15887.52it/s]\nCOLLECTING RESULTS | : 100%|██████████| 1/1 [00:00<00:00, 29330.80it/s]" + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#is-it-a-valid-cog", + "href": "cloud-optimized-geotiffs/cogs-examples.html#is-it-a-valid-cog", + "title": "Examples of Working with COGs", + "section": "Is it a valid COG?", + "text": "Is it a valid COG?\nWe can use rio_cogeo.cog_validate to check. It returns is_valid, errors and warnings.\n\ncog_validate(veg_gtiff_filename)\n\nThe following warnings were found:\n- The file is greater than 512xH or 512xW, it is recommended to include internal overviews\n\nThe following errors were found:\n- The file is greater than 512xH or 512xW, but is not tiled\n\n\n(False,\n ['The file is greater than 512xH or 512xW, but is not tiled'],\n ['The file is greater than 512xH or 512xW, it is recommended to include internal overviews'])\n\n\nReturn values:\n\nis_valid is False: this is not a valid COG.\nerrors are 'The file is greater than 512xH or 512xW, but is not tiled'. To be a valid COG, the file should be tiled since it has a height and width both greater than 512.\nwarnings are 'The file is greater than 512xH or 512xW, it is recommended to include internal overviews'. It is recommended to provide overviews." + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#converting-a-geotiff-to-cog", + "href": "cloud-optimized-geotiffs/cogs-examples.html#converting-a-geotiff-to-cog", + "title": "Examples of Working with COGs", + "section": "Converting a GeoTIFF to COG", + "text": "Converting a GeoTIFF to COG\nWe can use rio_cogeo.cog_create to convert a GeoTIFF into a Cloud Optimized GeoTIFF\n\nveg_cog_filename = veg_gtiff_filename.replace(\".tif\", \"_cog.tif\")\n\n!rio cogeo create {veg_gtiff_filename} {veg_cog_filename}\n\nReading input: /Users/kyle/ds/cloud-optimized-geospatial-formats-guide/cloud-optimized-geotiffs/test_data/VCF5KYR_1982001_001_2018224204211.tif\n [####################################] 100%\nAdding overviews...\nUpdating dataset tags...\nWriting output to: /Users/kyle/ds/cloud-optimized-geospatial-formats-guide/cloud-optimized-geotiffs/test_data/VCF5KYR_1982001_001_2018224204211_cog.tif\n\n\n\ncog_validate(veg_cog_filename)\n\n(True, [], [])\n\n\nThis is a valid COG, so we will use it to compare with our GeoTIFF." + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#dimensions", + "href": "cloud-optimized-geotiffs/cogs-examples.html#dimensions", + "title": "Examples of Working with COGs", + "section": "Dimensions", + "text": "Dimensions\nThis attribute is also sometimes called chunks or internal tiles.\nDimensions are the number of bands, rows and columns stored in a GeoTIFF. There is a tradeoff between storing lots of data in one GeoTIFF and storing less data in many GeoTIFFs. The larger a single file, the larger the GeoTIFF header and the multiple requests may be required just to read the spatial index before data retrieval. The opposite problem occurs if you make too many small files, then it takes many reads to retrieve data, and when rendering a combined visualization can greatly impact load time.\nIf you plan to pan and zoom a large amount of data through a tiling service in a web browser, there is a tradeoff between 1 large file, or many smaller files. The current recommendation is to meet somewhere in the middle, a moderate amount of medium files.\n\nInternal Blocks\nInternal blocks are required if the dimensions of data are over 512x512. However you can control the size of the internal blocks. 256x256 or 512x512 are recommended. When displaying data at full resolution, or doing partial reading of data this size will impact the number of reads required. A size of 256 will take less time to read, and read less data outside the desired bounding box, however for reading large parts of a file, it may take more total read requests. Some clients will aggregate neighboring block reads to reduce the total number of requests.\nLet’s check out the dimensions and blocks of our GeoTIFF and Cloud-Optimized GeoTIFF.\n\nveg_gtiff_rio = rasterio.open(veg_gtiff_filename)\nveg_cog_rio = rasterio.open(veg_cog_filename)\n\n\nprint(veg_gtiff_rio.shape)\nveg_cog_rio.shape\n\n(3600, 7200)\n\n\n(3600, 7200)\n\n\nThey have the same dimensions which is what we expect, so that is good!\nWe can also print information about the GeoTIFF’s IFD (Internal File Directory). Only one item is returned because the GeoTIFF doesn’t have overviews. When we print the IFD info for the COG, which has overviews, we see more items returned.\n\ncog_info(veg_gtiff_filename).IFD\n\n[IFD(Level=0, Width=7200, Height=3600, Blocksize=(1, 7200), Decimation=0)]\n\n\n\ncog_info(veg_cog_filename).IFD\n\n[IFD(Level=0, Width=7200, Height=3600, Blocksize=(512, 512), Decimation=0),\n IFD(Level=1, Width=3600, Height=1800, Blocksize=(128, 128), Decimation=2),\n IFD(Level=2, Width=1800, Height=900, Blocksize=(128, 128), Decimation=4),\n IFD(Level=3, Width=900, Height=450, Blocksize=(128, 128), Decimation=8)]\n\n\nNote for IFD Level 0, the regular GeoTIFF has a blocksize of (1, 7200) which implies each row of data is a separate block. So whenever reading in data, even if only a few columns are required, the full row must be read." + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#overviews", + "href": "cloud-optimized-geotiffs/cogs-examples.html#overviews", + "title": "Examples of Working with COGs", + "section": "Overviews", + "text": "Overviews\nOverviews are downsampled (aggregated) data intended for visualization. The best resampling algorithm depends on the range, type, and distribution of the data.\nThe smallest size overview should match the tiling components’ fetch size, typically 256x256. Due to aspect ratio variation just aim to have at least one dimension at or slightly less than 256. The COG driver in GDAL, or rio cogeo tools should do this.\nThere are many resampling algorithms for generating overviews. When creating overviews several options should be compared before deciding which resampling method to apply.\nGDAL >= 3.2 allows for the overview resampling method to be set directly.\nTODO: need to add hints on how to check which resampling method to use for overviews. Possibly provide code for comparing.\n\nveg_gtiff_rio.overviews(1)\n\n[]\n\n\n\nveg_cog_rio.overviews(1)\n\n[2, 4, 8]\n\n\nBy displaying each overview, we can see how the dimensions get coarser for each overview level.\n\ndef show_overviews(geotiff): \n for overview in geotiff.overviews(1):\n out_height = int(geotiff.height // overview)\n out_width = int(geotiff.width // overview)\n print(f\"out height: {out_height}\")\n print(f\"out width: {out_width}\") \n # read first band of file and set shape of new output array\n window_size_height = round(out_height/8)\n window_size_width = round(out_width/8)\n image = veg_cog_rio.read(1, out_shape=(1, out_height, out_width))[\n window_size_height:(window_size_height*2),\n window_size_width:(window_size_width*2),\n ]\n show(image)\n \nshow_overviews(veg_cog_rio)\n\nout height: 1800\nout width: 3600\nout height: 900\nout width: 1800\nout height: 450\nout width: 900\n\n\n\n\n\n\n\n\n\n\n\nWe can generate more and different overviews, through different tilesizes and resampling.\n\nimport gen_overviews\n\n\ntmp_dst = gen_overviews.create_overviews_from_gtiff(veg_gtiff_rio)\ntmp_cog = rasterio.open(tmp_dst)\ncog_info(tmp_dst).IFD\n\n\n\n\n[IFD(Level=0, Width=7200, Height=3600, Blocksize=(1, 7200), Decimation=0),\n IFD(Level=1, Width=3600, Height=1800, Blocksize=(128, 128), Decimation=2),\n IFD(Level=2, Width=1800, Height=900, Blocksize=(128, 128), Decimation=4),\n IFD(Level=3, Width=900, Height=450, Blocksize=(128, 128), Decimation=8),\n IFD(Level=4, Width=450, Height=225, Blocksize=(128, 128), Decimation=16)]\n\n\nNote: Now we have overviews but there are still no tiles on the Level 0 IFD.\n\noverviews = tmp_cog.overviews(1)\noverviews\n\n[2, 4, 8, 16]\n\n\n\nshow_overviews(tmp_cog)\n\nout height: 1800\nout width: 3600\nout height: 900\nout width: 1800\nout height: 450\nout width: 900\nout height: 225\nout width: 450" + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#what-we-dont-know-areas-of-research", + "href": "cloud-optimized-geotiffs/cogs-examples.html#what-we-dont-know-areas-of-research", + "title": "Examples of Working with COGs", + "section": "What we don’t know (areas of research)", + "text": "What we don’t know (areas of research)\n\nThe optimum size of data at which splitting across files improves performance as a multi-file dataset instead of a single file.\nWhen to recommend particular internal tile sizes\nCompression impacts on http transfer rates.\nSupport for COG creation in other common scientific platforms (e.g. R)" + }, + { + "objectID": "cloud-optimized-geotiffs/cogs-examples.html#additional-resources", + "href": "cloud-optimized-geotiffs/cogs-examples.html#additional-resources", + "title": "Examples of Working with COGs", + "section": "Additional Resources", + "text": "Additional Resources\n\nAn Introduction to Cloud Optimized GeoTIFFS (COGs) Part 1: Overview\nDo you really want people using your data?" }, { "objectID": "flatgeobuf/flatgeobuf.html", @@ -252,46 +525,25 @@ "text": "Reading from the cloud\nKnowing how to read and write local files is important, but given that FlatGeobuf is a cloud-optimized format, it’s important to be able to read from cloud-hosted files as well.\nFor this example, we’ll use the EuroCrops data hosted on Source Cooperative because it has versions of the same data in both FlatGeobuf and GeoParquet format. Hopefully using the same dataset for both the FlatGeobuf and GeoParquet example notebooks will be helpful.\n\nurl = \"https://data.source.coop/cholmes/eurocrops/unprojected/flatgeobuf/FR_2018_EC21.fgb\"\n\nUsually when reading from the cloud, you want to filter on some spatial extent. Pyogrio offers a read_info function to access many pieces of information about the file:\n\npyogrio.read_info(url)\n\n{'crs': 'EPSG:4326',\n 'encoding': 'UTF-8',\n 'fields': array(['ID_PARCEL', 'SURF_PARC', 'CODE_CULTU', 'CODE_GROUP', 'CULTURE_D1',\n 'CULTURE_D2', 'EC_org_n', 'EC_trans_n', 'EC_hcat_n', 'EC_hcat_c'],\n dtype=object),\n 'dtypes': array(['object', 'float64', 'object', 'object', 'object', 'object',\n 'object', 'object', 'object', 'object'], dtype=object),\n 'geometry_type': 'MultiPolygon',\n 'features': 9517874,\n 'driver': 'FlatGeobuf',\n 'capabilities': {'random_read': 1,\n 'fast_set_next_by_index': 0,\n 'fast_spatial_filter': 1},\n 'layer_metadata': None,\n 'dataset_metadata': None}\n\n\n\n\n\n\n\n\nNote\n\n\n\nSadly the output of read_info does not yet include the bounding box of the file, even though the FlatGeobuf file contains that information in the header. This may be a reason to consider externalizing metadata using Spatio-Temporal Asset Catalog files (STAC) in the future.\n\n\nFor now we’ll hard-code a region around Valence in the south of France, which we know the be within our dataset.\n\n# The order of bounds is\n# (min longitude, min latitude, max longitude, max latitude)\nbounds = (4.301042, 44.822783, 4.410535, 44.877149)\n\nWe can fetch a dataframe containing only the records in these bounds by passing a bbox argument to read_file. Note that the Coordinate Reference System of this bounding box must match the CRS of the dataset. Here, we know from the output of read_info that the CRS of the dataset is EPSG:4326, so we can pass a longitude-latitude bounding box.\n\n%time crops_gdf = gpd.read_file(url, bbox=bounds)\n\nCPU times: user 144 ms, sys: 21.4 ms, total: 165 ms\nWall time: 6 s\n\n\nPassing engine=\"pyogrio\" is only slightly faster, which may mean that most of the time is taken up in network requests, not in parsing the actual data into Python.\n\n%time crops_gdf = gpd.read_file(url, bbox=bounds, engine=\"pyogrio\")\n\nCPU times: user 26.9 ms, sys: 2.98 ms, total: 29.9 ms\nWall time: 490 ms\n\n\nThis gives us a much smaller dataset of only 400 rows (down from 9.5 million rows in the original dataset).\n\ncrops_gdf.head()\n\n\n\n\n\n\n\n\nID_PARCEL\nSURF_PARC\nCODE_CULTU\nCODE_GROUP\nCULTURE_D1\nCULTURE_D2\nEC_org_n\nEC_trans_n\nEC_hcat_n\nEC_hcat_c\ngeometry\n\n\n\n\n0\n9484573\n11.08\nSPL\n17\nNone\nNone\nSurface pastorale - ressources fourragères lig...\nPastoral area - predominant woody fodder resou...\nother_tree_wood_forest\n3306990000\nMULTIPOLYGON (((4.41142 44.85441, 4.41145 44.8...\n\n\n1\n487218\n2.53\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((4.41366 44.85898, 4.41373 44.8...\n\n\n2\n487224\n0.89\nCTG\n22\nNone\nNone\nChâtaigne\nChestnut\nsweet_chestnuts\n3303030500\nMULTIPOLYGON (((4.41159 44.85891, 4.41034 44.8...\n\n\n3\n9484542\n1.31\nCTG\n22\nNone\nNone\nChâtaigne\nChestnut\nsweet_chestnuts\n3303030500\nMULTIPOLYGON (((4.40904 44.85805, 4.41034 44.8...\n\n\n4\n487216\n1.70\nBOP\n17\nNone\nNone\nBois pâturé\nGrazed wood\nother_tree_wood_forest\n3306990000\nMULTIPOLYGON (((4.41135 44.85476, 4.41134 44.8...\n\n\n\n\n\n\n\n\ncrops_gdf.shape\n\n(415, 11)\n\n\nThere are other useful keyword arguments to read_file. Since we’re using the pyogrio engine, we can pass specific column names into read_file, and only those columns will be parsed. In the case of FlatGeobuf, this doesn’t save us much time, because the same amount of data needs to be fetched. (Though if running this cell soon after the previous one, the relevant data will be cached and won’t be downloaded again.)\n\ncolumn_names = [\"ID_PARCEL\", \"SURF_PARC\", \"CODE_CULTU\", \"geometry\"]\n%time crops_gdf = gpd.read_file(url, bbox=bounds, columns=column_names, engine=\"pyogrio\")\n\nCPU times: user 25 ms, sys: 2.47 ms, total: 27.4 ms\nWall time: 706 ms\n\n\n\ncrops_gdf.head()\n\n\n\n\n\n\n\n\nCODE_CULTU\nID_PARCEL\nSURF_PARC\ngeometry\n\n\n\n\n0\nSPL\n9484573\n11.08\nMULTIPOLYGON (((4.41142 44.85441, 4.41145 44.8...\n\n\n1\nPPH\n487218\n2.53\nMULTIPOLYGON (((4.41366 44.85898, 4.41373 44.8...\n\n\n2\nCTG\n487224\n0.89\nMULTIPOLYGON (((4.41159 44.85891, 4.41034 44.8...\n\n\n3\nCTG\n9484542\n1.31\nMULTIPOLYGON (((4.40904 44.85805, 4.41034 44.8...\n\n\n4\nBOP\n487216\n1.70\nMULTIPOLYGON (((4.41135 44.85476, 4.41134 44.8..." }, { - "objectID": "zarr/zarr-in-practice.html", - "href": "zarr/zarr-in-practice.html", - "title": "Zarr in Practice", + "objectID": "flatgeobuf/hilbert-r-tree.html", + "href": "flatgeobuf/hilbert-r-tree.html", + "title": "FlatGeobuf Spatial Index", "section": "", - "text": "This notebook demonstrates how to create, explore and modify a Zarr store.\nThese concepts are explored in more detail in the official Zarr Tutorial.\nIt also shows the use of public Zarr stores for geospatial data." - }, - { - "objectID": "zarr/zarr-in-practice.html#how-to-create-a-zarr-store", - "href": "zarr/zarr-in-practice.html#how-to-create-a-zarr-store", - "title": "Zarr in Practice", - "section": "How to create a Zarr store", - "text": "How to create a Zarr store\n\nimport sys\nimport numpy as np\nimport xarray as xr\nimport zarr\n\n# Here we create a simple Zarr store.\nzstore = zarr.array(np.arange(10))\n\nThis is an in-memory Zarr store. To persist it to disk, we can use .save.\n\nzarr.save(\"test.zarr\", zstore)\n\nWe can open the metadata about this dataset, which gives us some interesting information. The dataset has a shape of 10 chunks of 10, so we know all the data was stored in 1 chunk, and was compressed with the blosc compressor.\n\n!cat test.zarr/.zarray \n\n{\n \"chunks\": [\n 10\n ],\n \"compressor\": {\n \"blocksize\": 0,\n \"clevel\": 5,\n \"cname\": \"lz4\",\n \"id\": \"blosc\",\n \"shuffle\": 1\n },\n \"dtype\": \"<i8\",\n \"fill_value\": 0,\n \"filters\": null,\n \"order\": \"C\",\n \"shape\": [\n 10\n ],\n \"zarr_format\": 2\n}\n\n\nThis was a pretty basic example. Let’s explore the other things we might want to do when creating Zarr." - }, - { - "objectID": "zarr/zarr-in-practice.html#how-to-create-a-group", - "href": "zarr/zarr-in-practice.html#how-to-create-a-group", - "title": "Zarr in Practice", - "section": "How to create a group", - "text": "How to create a group\n\nroot = zarr.group()\ngroup1 = root.create_group('group1')\ngroup2 = root.create_group('group2')\nz1 = group1.create_dataset('ds_in_group', shape=(100,100), chunks=(10,10), dtype='i4')\nz2 = group2.create_dataset('ds_in_group', shape=(1000,1000), chunks=(10,10), dtype='i4')\nroot.tree(expand=True)" - }, - { - "objectID": "zarr/zarr-in-practice.html#how-to-examine-and-modify-the-chunk-shape", - "href": "zarr/zarr-in-practice.html#how-to-examine-and-modify-the-chunk-shape", - "title": "Zarr in Practice", - "section": "How to Examine and Modify the Chunk Shape", - "text": "How to Examine and Modify the Chunk Shape\nIf your data is sufficiently large, Zarr will chose a chunksize for you.\n\nzarr_no_chunks = zarr.array(np.arange(100), chunks=True)\nzarr_no_chunks.chunks, zarr_no_chunks.shape\n\n((100,), (100,))\n\n\n\nzarr_with_chunks = zarr.array(np.arange(10000000), chunks=True)\nzarr_with_chunks.chunks, zarr_with_chunks.shape\n\n((156250,), (10000000,))\n\n\nFor zarr_with_chunks we see the chunks are smaller than the shape, so we know the data has been chunked. Other ways to examine the chunk structure are zarr.info and zarr.cdata_shape.\n\n?zarr_no_chunks.cdata_shape\n\n\nType: property\nString form: <property object at 0x7efde6ecfb00>\nDocstring: \nA tuple of integers describing the number of chunks along each\ndimension of the array.\n\n\n\n\nzarr_no_chunks.cdata_shape, zarr_with_chunks.cdata_shape\n\n((1,), (64,))\n\n\nThe zarr store with chunks has 64 chunks. The number of chunks multiplied by the chunk size equals the length of the whole array.\n\nzarr_with_chunks.cdata_shape[0] * zarr_with_chunks.chunks[0] == zarr_with_chunks.shape[0]\n\nTrue\n\n\n\nWhat’s the storage size of these chunks?\nThe default chunks are pretty small.\n\nsys.getsizeof(zarr_with_chunks.chunk_store['0']) # this is in bytes\n\n8049\n\n\n\nzarr_with_big_chunks = zarr.array(np.arange(10000000), chunks=(500000))\n\n\nzarr_with_big_chunks.chunks, zarr_with_big_chunks.shape, zarr_with_big_chunks.cdata_shape\n\n((500000,), (10000000,), (20,))\n\n\nThis Zarr store has 10 million values, stored in 20 chunks of 500,000 data values.\n\nsys.getsizeof(zarr_with_big_chunks.chunk_store['0'])\n\n24941\n\n\nThese chunks are still pretty small, but this is just a silly example. In the real world, you will likely want to deal in Zarr chunks of 1MB or greater, especially when dealing with remote storatge options where data is read over a network and the number of requests should be minimized." + "text": "FlatGeobuf optionally supports including a spatial index that enables random access for each geometry in the file." }, { - "objectID": "zarr/zarr-in-practice.html#exploring-and-modifying-data-compression", - "href": "zarr/zarr-in-practice.html#exploring-and-modifying-data-compression", - "title": "Zarr in Practice", - "section": "Exploring and Modifying Data Compression", - "text": "Exploring and Modifying Data Compression\nContinuing with data from the example above, we can tell that Zarr has also compressed the data for us using zarr.info or zarr.compressor.\n\nzarr_with_chunks.compressor\n\nBlosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)\n\n\nThe Blosc compressor is actually a meta compressor so actually implements multiple different internal compressors. In this case, it has implemented lz4 compression. We can also explore how much space was saved by using this compression method.\n\nzarr_with_chunks.info\n\n\n\n\nType\nzarr.core.Array\n\n\nData type\nint64\n\n\nShape\n(10000000,)\n\n\nChunk shape\n(156250,)\n\n\nOrder\nC\n\n\nRead-only\nFalse\n\n\nCompressor\nBlosc(cname='lz4', clevel=5, shuffle=SHUFFLE, blocksize=0)\n\n\nStore type\nzarr.storage.KVStore\n\n\nNo. bytes\n80000000 (76.3M)\n\n\nNo. bytes stored\n514193 (502.1K)\n\n\nStorage ratio\n155.6\n\n\nChunks initialized\n64/64\n\n\n\n\n\nWe can see, from the storage ratio above, that compression has made our data 155 times smaller 😱 .\nYou can set compression=None when creating a Zarr array to turn off this behavior, but I’m not sure why you would do that.\nLet’s see what happens when we use a different compression method. We can checkout a full list of numcodecs compressors here: https://numcodecs.readthedocs.io/.\n\nfrom numcodecs import GZip\ncompressor = GZip()\nzstore_gzip_compressed = zarr.array(np.arange(10000000), chunks=True, compressor=compressor)\nzstore_gzip_compressed.info\n\n\n\n\nType\nzarr.core.Array\n\n\nData type\nint64\n\n\nShape\n(10000000,)\n\n\nChunk shape\n(156250,)\n\n\nOrder\nC\n\n\nRead-only\nFalse\n\n\nCompressor\nGZip(level=1)\n\n\nStore type\nzarr.storage.KVStore\n\n\nNo. bytes\n80000000 (76.3M)\n\n\nNo. bytes stored\n15086009 (14.4M)\n\n\nStorage ratio\n5.3\n\n\nChunks initialized\n64/64\n\n\n\n\n\nIn this case, the storage ratio is 5.3 - so not as good! How to chose a compression algorithm is a topic for future investigation." + "objectID": "flatgeobuf/hilbert-r-tree.html#when-to-use", + "href": "flatgeobuf/hilbert-r-tree.html#when-to-use", + "title": "FlatGeobuf Spatial Index", + "section": "When to use", + "text": "When to use\nWhen writing a FlatGeobuf file, one must decide whether to include a spatial index. A spatial index cannot be added to a FlatGeobuf file after the file has been written.\nA spatial index can enable much more efficient reading from FlatGeobuf, by allowing the reader to skip over portions of the file that fall outside of a qiven spatial query region." }, { - "objectID": "zarr/zarr-in-practice.html#consolidating-metadata", - "href": "zarr/zarr-in-practice.html#consolidating-metadata", - "title": "Zarr in Practice", - "section": "Consolidating metadata", - "text": "Consolidating metadata\nIt’s important to consolidate metadata to minimize requests. Each group and array will have a metadata file, so in order to limit requests to read the whole tree of metadata files, Zarr provides the ability to consolidate metdata into a metadata file at the of the store.\nSo far we have only been dealing in single array Zarr data stores. In this next example, we will create a zarr store with multiple arrays and then consolidate metadata. The speed up with local storage is insignificant, but becomes significant when dealing in remote storage options, which we will see in the following example on accessing cloud storage.\n\nroot = zarr.group()\nzarr_store = 'example.zarr'\n# Let's create many groups and many arrays\nnum_groups, num_arrays_per_group = 100, 100\nfor i in range(num_groups):\n group = root.create_group(f'group-{i}')\n for j in range(num_arrays_per_group):\n group.create_dataset(f'array-{j}', shape=(1000,1000), dtype='i4')\n\nstore = zarr.DirectoryStore(zarr_store)\nzarr.save(store, root)\n\n\n# We don't expect it to exist yet!\n!cat {zarr_store}/.zmetadata\n\ncat: {zarr_store}/.zmetadata: No such file or directory\n\n\n\nzarr.consolidate_metadata(zarr_store)\n\n<zarr.core.Array (100,) <U8>\n\n\n\nzarr.open_consolidated(zarr_store)\n\n<zarr.core.Array (100,) <U8>\n\n\n\n!cat {zarr_store}/.zmetadata\n\n{\n \"metadata\": {\n \".zarray\": {\n \"chunks\": [\n 100\n ],\n \"compressor\": {\n \"blocksize\": 0,\n \"clevel\": 5,\n \"cname\": \"lz4\",\n \"id\": \"blosc\",\n \"shuffle\": 1\n },\n \"dtype\": \"<U8\",\n \"fill_value\": \"\",\n \"filters\": null,\n \"order\": \"C\",\n \"shape\": [\n 100\n ],\n \"zarr_format\": 2\n }\n },\n \"zarr_consolidated_format\": 1\n}" + "objectID": "flatgeobuf/hilbert-r-tree.html#technical-details", + "href": "flatgeobuf/hilbert-r-tree.html#technical-details", + "title": "FlatGeobuf Spatial Index", + "section": "Technical details", + "text": "Technical details\nIn this section we’ll get into some gory technical details of how FlatGeobuf’s spatial index works. Understanding the below isn’t necessary for using FlatGeobuf, but it may add context for understanding how to create and work with FlatGeobuf files, and why FlatGeobuf is performant.\nFlatGeobuf’s spatial index is a static packed Hilbert R-tree index. That’s a mouthful, so let’s break it down:\n\nR-tree index\nAn R-Tree is a hierarchical collection of bounding boxes. At the lowest level of the tree is a bounding box of every geometry. Then one level above the lowest level exists a collection of bounding boxes, each of which is formed as the union of all child boxes. This means that each box encompasses every child box. There are fewer boxes at this level, because each box contains many child boxes, each of which represents one original geometry. This process continues repeatedly until there’s only one bounding box that indirectly contains the entire dataset.\nThis index allows you to quickly search for features that intersect a given bounding box query. At the top level, compare the bounding box of each node to your query region. If those two don’t intersect, you can discard that node and all of its child nodes from the search, because you know that none of them could possibly fall within your search region.\nContinuing this process allows you to quickly find only the specific items that are candidates for your search query.\n\n\nR-Tree diagram from Wikipedia. From top to bottom, the three levels of this tree are the black, blue, and red boxes. The black boxes contain the most items and encompass the largest area, while the red boxes contain the fewest items and encompass a smaller area.\n\nThe Wikipedia article and this Mapbox blog post are great resources for better understanding how R-Trees work.\n\n\nHilbert\nThe elements of an R-Tree must be sorted before insertion to make the R-Tree useful. This is because the core benefit of an R-Tree is to exclude elements that aren’t within a spatial filter. If elements of each node are randomly drawn from different geographies, then each node’s bounding box will be so large that no nodes can be excluded.\nBut how do you sort geometries? They encompass two dimensions and a range of shapes. If you sort all geometries first on the x coordinate, then you may pair geometries that are far from each other on the y dimension. Instead, it’s ideal to use a space-filling curve. That’s math jargon, but essentially defines a way to sort elements in n dimensions using 1 dimensional numbers.\nA Hilbert R-Tree uses a Hilbert Curve, a special type of space-filling curve, to sort the centers of geometries. This ensures that geometries that are nearby on both the x and y dimensions are placed close to each other in the R-Tree. This ensures that the resulting bounding boxes of the R-Tree are as small as possible, which means that the maximum number of elements can be discarded for any given spatial query.\nThis Crunchy Data blog post has helpful examples for why sorting input is important.\n\n\nStatic\nFlatGeobuf files can’t be modified without rewriting the entire file, so this R-Tree is constructed in such a way that it can’t be modified, which allows for improved tree generation.\n\n\nPacked\nAn R-Tree has a series of nodes at each level, where each node can contain up to n children. If the R-Tree might be updated, not every node will have a total of n children, because some space needs to be reserved for future elements.\nBecause the index is static and immutable, we can construct a packed index, where every node is completely full. This achieves better space utilization, and is more efficient for queries because there are fewer nodes to traverse." }, { "objectID": "index.html", @@ -363,48 +615,6 @@ "section": "Thank you to our supporters", "text": "Thank you to our supporters\nThis guide has been made possible through the support of:" }, - { - "objectID": "cloud-optimized-netcdf4-hdf5/index.html", - "href": "cloud-optimized-netcdf4-hdf5/index.html", - "title": "Cloud-Optimized NetCDF4/HDF5", - "section": "", - "text": "Cloud-optimized access to NetCDF4/HDF5 files is possible. However, there are no standards for the metadata, chunking and compression for cloud-optimized access for these file types.\n\n\n\n\n\n\nNote\n\n\n\nNote: NetCDF4 are valid HDF5 files, see Reading and Editing NetCDF-4 Files with HDF5.\n\n\nNetCDF4/HDF5 were designed for disk access and thus moving them to the cloud has borne little fruit. Matt Rocklin describes the issue in HDF in the Cloud: Challenges and Solutions for Scientific Data:\n\nThe HDF format is complex and metadata is strewn throughout the file, so that a complex sequence of reads is required to reach a specific chunk of data. The only pragmatic way to read a chunk of data from an HDF file today is to use the existing HDF C library, which expects to receive a C FILE object, pointing to a normal file system (not a cloud object store) (this is not entirely true, as we’ll see below).\nSo organizations like NASA are dumping large amounts of HDF onto Amazon’s S3 that no one can actually read, except by downloading the entire file to their local hard drive, and then pulling out the particular bits that they need with the HDF library. This is inefficient. It misses out on the potential that cloud-hosted public data can offer to our society.\n\nTo provide cloud-optimized access to these files without an intermediate service like Hyrax or the Highly Scalable Data Service (HSDS), it is recommended to determine if the NetCDF4/HDF5 data you wish to provide can be used with kerchunk. Rich Signell provided some insightful examples and instructions on how to create a kerchunk reference file (aka fsspec.ReferenceFileSystem) for NetCDF4/HDF5 and the things to be aware of in Cloud-Performant NetCDF4/HDF5 with Zarr, Fsspec, and Intake. Note, the post is from 2020, so it’s possible details have changed; however, the approach of using kerchunk for NetCDF4/HDF5 is still recommended.\nStay tuned for more information on cloud-optimized NetCDF4/HDF5 in future releases of this guide." - }, - { - "objectID": "kerchunk/kerchunk-in-practice.html", - "href": "kerchunk/kerchunk-in-practice.html", - "title": "Kerchunk in Practice", - "section": "", - "text": "In this notebook, we demonstrate how to create a kerchunk reference file for one and then multiple publicly available NetCDF files and how to open a kerchunk store with xarray.\nGenerally, NetCDF should work with kerchunk. Some nested data structures and data types, such as those that can exist in HDF5, won’t work with kerchunk. A future release of this guide will provide further information and/or resources on limitations of kerchunk." - }, - { - "objectID": "kerchunk/kerchunk-in-practice.html#environment", - "href": "kerchunk/kerchunk-in-practice.html#environment", - "title": "Kerchunk in Practice", - "section": "Environment", - "text": "Environment\nThe packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment." - }, - { - "objectID": "kerchunk/kerchunk-in-practice.html#how-to-create-a-kerchunk-store", - "href": "kerchunk/kerchunk-in-practice.html#how-to-create-a-kerchunk-store", - "title": "Kerchunk in Practice", - "section": "How to create a kerchunk store", - "text": "How to create a kerchunk store\nWe can use the publicly available NEX GDDP CMIP6 dataset for this example. This dataset is provided by NASA and publicly available on AWS S3. You can browse that data in the nex-gddp-cmip6 file browser.\n\nimport json\nimport os\nfrom tempfile import TemporaryDirectory\n\nimport fsspec\nimport imagecodecs.numcodecs\nimport xarray as xr\nfrom kerchunk.combine import MultiZarrToZarr\nfrom kerchunk.hdf import SingleHdf5ToZarr\n\nimagecodecs.numcodecs.register_codecs() \n\n\n# Set variables\n## Since there are a number of CMIP6 models and variables to chose from, we make the model and variable selections variables.\nmodel = \"ACCESS-CM2\"\n# `tasmax` is daily-maximum near-surface air temperature, see https://pcmdi.llnl.gov/mips/cmip3/variableList.html.\nvariable = \"tasmax\"\n## Note we are only reading historical data here, but model data is available for SSPs (Shared Socio-economic Pathways) as well.\n## SSPs are scenarios are used to model the future, so SSP files predict environment variables into the future.\ns3_path = f\"s3://nex-gddp-cmip6/NEX-GDDP-CMIP6/{model}/historical/r1i1p1*/{variable}/*\"\n\n# Initiate fsspec filesystem for reading.\n## We set anon=True because this specific dataset on AWS does not require users to be logged in to access.\nfs_read = fsspec.filesystem(\"s3\", anon=True)\n\n# Retrieve list of available data.\nfile_paths = fs_read.glob(s3_path)\nprint(f\"{len(file_paths)} discovered from {s3_path}\")\n\n65 discovered from s3://nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1*/tasmax/*\n\n\nTo start, we are just going to create a single reference file for a single NetCDF file.\n\nnetcdf_file = file_paths[0]\nnetcdf_file\n\n'nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc'\n\n\n\n# Define a function to generate the kerchunk file so we can use it again for other files.\ndef generate_json_reference(input_file, temp_dir: str):\n \"\"\"\n Use Kerchunk's `SingleHdf5ToZarr` method to create a `Kerchunk` index from a NetCDF file.\n \"\"\"\n with fs_read.open(input_file, **dict(mode=\"rb\")) as infile:\n print(f\"Running kerchunk generation for {input_file}...\")\n # Chunks smaller than `inline_threshold` will be stored directly in the reference file as data (as opposed to a URL and byte range).\n h5chunks = SingleHdf5ToZarr(infile, input_file, inline_threshold=300)\n fname = input_file.split(\"/\")[-1].strip(\".nc\")\n outf = os.path.join(temp_dir, f\"{fname}.json\")\n with open(outf, \"wb\") as f:\n f.write(json.dumps(h5chunks.translate()).encode())\n return outf\n\n\n# Create a temporary directory to store the .json reference files.\n# Alternately, you could write these to cloud storage.\ntd = TemporaryDirectory()\ntemp_dir = td.name\nprint(f\"Writing single file references to {temp_dir}\")\n\nWriting single file references to /var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo\n\n\n\nsingle_kerchunk_reference = generate_json_reference(netcdf_file, temp_dir)\n\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc...\n\n\nWe might also want to inspect what was just created. Below we load just the first few keys and values of the “refs” part of the kerchunk reference file.\n\n# Read and load the JSON file\nwith open(single_kerchunk_reference, 'r') as file:\n data = json.load(file)\nkeys_to_select = ['.zgroup', 'tasmax/.zarray', 'tasmax/0.0.0']\n\n# Pretty print JSON data\ndata_to_print = {}\nfor key, value in data['refs'].items():\n if key in keys_to_select:\n if isinstance(value, str):\n data_to_print[key] = json.loads(value)\n else:\n data_to_print[key] = value\nprint(json.dumps(data_to_print, indent=4))\n\n{\n \".zgroup\": {\n \"zarr_format\": 2\n },\n \"tasmax/.zarray\": {\n \"chunks\": [\n 1,\n 600,\n 1440\n ],\n \"compressor\": {\n \"id\": \"zlib\",\n \"level\": 5\n },\n \"dtype\": \"<f4\",\n \"fill_value\": 1.0000000200408773e+20,\n \"filters\": [\n {\n \"elementsize\": 4,\n \"id\": \"shuffle\"\n }\n ],\n \"order\": \"C\",\n \"shape\": [\n 365,\n 600,\n 1440\n ],\n \"zarr_format\": 2\n },\n \"tasmax/0.0.0\": [\n \"nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc\",\n 18097,\n 674483\n ]\n}\n\n\nWe can also check that our reference file works with xarray.\n\n# Open dataset as zarr object using fsspec reference file system and Xarray\nfs_single = fsspec.filesystem(\n \"reference\", fo=single_kerchunk_reference, remote_protocol=\"https\"\n)\nsingle_map = fs_single.get_mapper(\"\")\n\n\nds_single = xr.open_dataset(single_map, engine=\"zarr\", backend_kwargs=dict(consolidated=False))\nds_single\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (lat: 600, lon: 1440, time: 365)\nCoordinates:\n * lat (lat) float64 0.0 1.23e-321 0.0 ... -3.218e+163 -3.218e+163\n * lon (lon) float64 0.0 2.164e-314 0.0 ... -2.022e-53 -1.699e+282\n * time (time) datetime64[ns] 1950-01-01T12:00:00 ... 1950-12-31T12:00:00\nData variables:\n tasmax (time, lat, lon) float32 ...\nAttributes: (12/22)\n Conventions: CF-1.7\n activity: NEX-GDDP-CMIP6\n cmip6_institution_id: CSIRO-ARCCSS\n cmip6_license: CC-BY-SA 4.0\n cmip6_source_id: ACCESS-CM2\n contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...\n ... ...\n scenario: historical\n source: BCSD\n title: ACCESS-CM2, r1i1p1f1, historical, global downscale...\n tracking_id: f85d4c2e-48e4-484f-aad4-6a3f30a04326\n variant_label: r1i1p1f1\n version: 1.0xarray.DatasetDimensions:lat: 600lon: 1440time: 365Coordinates: (3)lat(lat)float640.0 1.23e-321 ... -3.218e+163axis :Ylong_name :latitudestandard_name :latitudeunits :degrees_northarray([ 0.000000e+000, 1.230223e-321, 0.000000e+000, ..., -3.218047e+163,\n -3.218047e+163, -3.218047e+163])lon(lon)float640.0 2.164e-314 ... -1.699e+282axis :Xlong_name :longitudestandard_name :longitudeunits :degrees_eastarray([ 0.000000e+000, 2.163912e-314, 0.000000e+000, ..., 1.902013e-242,\n -2.022208e-053, -1.698612e+282])time(time)datetime64[ns]1950-01-01T12:00:00 ... 1950-12-...axis :Tlong_name :timestandard_name :timearray(['1950-01-01T12:00:00.000000000', '1950-01-02T12:00:00.000000000',\n '1950-01-03T12:00:00.000000000', ..., '1950-12-29T12:00:00.000000000',\n '1950-12-30T12:00:00.000000000', '1950-12-31T12:00:00.000000000'],\n dtype='datetime64[ns]')Data variables: (1)tasmax(time, lat, lon)float32...cell_measures :area: areacellacell_methods :area: mean time: maximumcomment :maximum near-surface (usually, 2 meter) air temperature (add cell_method attribute 'time: max')long_name :Daily Maximum Near-Surface Air Temperaturestandard_name :air_temperatureunits :K[315360000 values with dtype=float32]Indexes: (3)latPandasIndexPandasIndex(Index([ 0.0, 1.23e-321,\n 0.0, 1.23e-321,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n ...\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163,\n -3.2180465730379564e+163, -3.2180465730379564e+163],\n dtype='float64', name='lat', length=600))lonPandasIndexPandasIndex(Index([ 0.0, 2.163911906e-314,\n 0.0, nan,\n 0.0, 0.0,\n 0.0, 0.0,\n 0.0, 0.0,\n ...\n 0.0, 0.0,\n 1.5390572997222847e+73, 1.0494093556865241e-86,\n 7.328222560480262e-213, 3.493934932025909e-195,\n 7.981962361089973e-296, 1.90201295465319e-242,\n -2.022208454662242e-53, -1.698612219286841e+282],\n dtype='float64', name='lon', length=1440))timePandasIndexPandasIndex(DatetimeIndex(['1950-01-01 12:00:00', '1950-01-02 12:00:00',\n '1950-01-03 12:00:00', '1950-01-04 12:00:00',\n '1950-01-05 12:00:00', '1950-01-06 12:00:00',\n '1950-01-07 12:00:00', '1950-01-08 12:00:00',\n '1950-01-09 12:00:00', '1950-01-10 12:00:00',\n ...\n '1950-12-22 12:00:00', '1950-12-23 12:00:00',\n '1950-12-24 12:00:00', '1950-12-25 12:00:00',\n '1950-12-26 12:00:00', '1950-12-27 12:00:00',\n '1950-12-28 12:00:00', '1950-12-29 12:00:00',\n '1950-12-30 12:00:00', '1950-12-31 12:00:00'],\n dtype='datetime64[ns]', name='time', length=365, freq=None))Attributes: (22)Conventions :CF-1.7activity :NEX-GDDP-CMIP6cmip6_institution_id :CSIRO-ARCCSScmip6_license :CC-BY-SA 4.0cmip6_source_id :ACCESS-CM2contact :Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget Thrasher: bridget@climateanalyticsgroup.orgcreation_date :2021-10-04T14:00:55.510838+00:00disclaimer :This data is considered provisional and subject to change. This data is provided as is without any warranty of any kind, either express or implied, arising by law or otherwise, including but not limited to warranties of completeness, non-infringement, accuracy, merchantability, or fitness for a particular purpose. The user assumes all risk associated with the use of, or inability to use, this data.external_variables :areacellafrequency :dayhistory :2021-10-04T14:00:55.510838+00:00: install global attributesinstitution :NASA Earth Exchange, NASA Ames Research Center, Moffett Field, CA 94035product :outputrealm :atmosreferences :BCSD method: Thrasher et al., 2012, Hydrol. Earth Syst. Sci.,16, 3309-3314. Ref period obs: latest version of the Princeton Global Meteorological Forcings (http://hydrology.princeton.edu/data.php), based on Sheffield et al., 2006, J. Climate, 19 (13), 3088-3111.resolution_id :0.25 degreescenario :historicalsource :BCSDtitle :ACCESS-CM2, r1i1p1f1, historical, global downscaled CMIP6 climate projection datatracking_id :f85d4c2e-48e4-484f-aad4-6a3f30a04326variant_label :r1i1p1f1version :1.0\n\n\nIt worked! But we can do even better. What if you want to open multiple NetCDF files with xarray? Let’s create kerchunk references for 3 files and then combine them.\n\nsubset_files = file_paths[0:3]\nsubset_files\n\n['nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc',\n 'nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1951.nc',\n 'nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1952.nc']\n\n\n\n# Iterate through filelist to generate Kerchunked files. Good use for `dask.bag`, see: https://docs.dask.org/en/stable/bag.html.\noutput_files = []\nfor single_file in subset_files:\n out_file = generate_json_reference(single_file, temp_dir)\n output_files.append(out_file)\n\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.nc...\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1951.nc...\nRunning kerchunk generation for nex-gddp-cmip6/NEX-GDDP-CMIP6/ACCESS-CM2/historical/r1i1p1f1/tasmax/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1952.nc...\n\n\n\noutput_files\n\n['/var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1950.json',\n '/var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1951.json',\n '/var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/tasmax_day_ACCESS-CM2_historical_r1i1p1f1_gn_1952.json']\n\n\n\n# combine individual references into single consolidated reference\nmzz = MultiZarrToZarr(\n output_files,\n remote_protocol='s3',\n remote_options={'anon': True},\n concat_dims=['time'],\n coo_map={'time': 'cf:time'},\n # inline_threshold=0 means don't story any raw data in the kerchunk reference file.\n inline_threshold=0\n)\nmulti_kerchunk = mzz.translate()\n\n\n# Write kerchunk .json record\noutput_fname = os.path.join(temp_dir, f\"combined_CMIP6_daily_{model}_{variable}_kerchunk.json\")\nwith open(f\"{output_fname}\", \"wb\") as f:\n print(f\"Writing combined kerchunk reference file {output_fname}\")\n f.write(json.dumps(multi_kerchunk).encode())\n\nWriting combined kerchunk reference file /var/folders/42/5jr6891d4ds4xysz7q0rsghw0000gn/T/tmpn1bas0mo/combined_CMIP6_daily_ACCESS-CM2_tasmax_kerchunk.json\n\n\n\n# open dataset as zarr object using fsspec reference file system and Xarray\nfs_multi = fsspec.filesystem(\n \"reference\",\n fo=multi_kerchunk,\n remote_protocol=\"s3\"\n)\nmulti_map = fs_multi.get_mapper(\"\")\n\n\nds_multi = xr.open_dataset(multi_map, engine=\"zarr\", backend_kwargs=dict(consolidated=False))\nds_multi\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (lat: 600, lon: 1440, time: 1096)\nCoordinates:\n * lat (lat) float64 0.0 2.164e-314 0.0 ... 2.961e-314 2.961e-314\n * lon (lon) float64 0.0 2.164e-314 0.0 ... -6.915e+193 -4.603e+95\n * time (time) datetime64[ns] 1950-01-01T12:00:00 ... 1952-12-31T12:00:00\nData variables:\n tasmax (time, lat, lon) float32 ...\nAttributes: (12/22)\n Conventions: CF-1.7\n activity: NEX-GDDP-CMIP6\n cmip6_institution_id: CSIRO-ARCCSS\n cmip6_license: CC-BY-SA 4.0\n cmip6_source_id: ACCESS-CM2\n contact: Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget...\n ... ...\n scenario: historical\n source: BCSD\n title: ACCESS-CM2, r1i1p1f1, historical, global downscale...\n tracking_id: f85d4c2e-48e4-484f-aad4-6a3f30a04326\n variant_label: r1i1p1f1\n version: 1.0xarray.DatasetDimensions:lat: 600lon: 1440time: 1096Coordinates: (3)lat(lat)float640.0 2.164e-314 ... 2.961e-314axis :Ylong_name :latitudestandard_name :latitudeunits :degrees_northarray([0.000000e+000, 2.163912e-314, 0.000000e+000, ..., 2.961067e-314,\n 2.960919e-314, 2.961067e-314])lon(lon)float640.0 2.164e-314 ... -4.603e+95axis :Xlong_name :longitudestandard_name :longitudeunits :degrees_eastarray([ 0.000000e+000, 2.163912e-314, 0.000000e+000, ..., 2.334981e+006,\n -6.914611e+193, -4.603478e+095])time(time)datetime64[ns]1950-01-01T12:00:00 ... 1952-12-...axis :Tlong_name :timestandard_name :timearray(['1950-01-01T12:00:00.000000000', '1950-01-02T12:00:00.000000000',\n '1950-01-03T12:00:00.000000000', ..., '1952-12-29T12:00:00.000000000',\n '1952-12-30T12:00:00.000000000', '1952-12-31T12:00:00.000000000'],\n dtype='datetime64[ns]')Data variables: (1)tasmax(time, lat, lon)float32...cell_measures :area: areacellacell_methods :area: mean time: maximumcomment :maximum near-surface (usually, 2 meter) air temperature (add cell_method attribute 'time: max')long_name :Daily Maximum Near-Surface Air Temperaturestandard_name :air_temperatureunits :K[946944000 values with dtype=float32]Indexes: (3)latPandasIndexPandasIndex(Index([ 0.0, 2.163911906e-314, 0.0,\n nan, 0.0, 2.847840319e-314,\n 2.847840477e-314, 5e-324, 2.8478403307e-314,\n 2.8478403347e-314,\n ...\n 2.960919408e-314, 2.9610663864e-314, 2.960919313e-314,\n 2.9610664496e-314, 2.9609193446e-314, 2.961066513e-314,\n 2.9609192497e-314, 2.961066576e-314, 2.9609192813e-314,\n 2.9610666394e-314],\n dtype='float64', name='lat', length=600))lonPandasIndexPandasIndex(Index([ 0.0, 2.163911906e-314,\n 0.0, nan,\n 1.8178640317427325e+185, 1.0640025030406259e+248,\n 6.01334685394558e-154, 9.363931581572749e+252,\n 1.2064976717019484e+285, 2.582765705848744e-144,\n ...\n 2.7454590140292026e+40, -3.255930979178767e-308,\n 1.5281971544072024e-111, -7.088607689435405e+42,\n 1.1472324330854862e+22, 3.6014577529949115e+106,\n 9.851096278175061e+67, 2334981.4421286285,\n -6.9146108782833415e+193, -4.603477998061419e+95],\n dtype='float64', name='lon', length=1440))timePandasIndexPandasIndex(DatetimeIndex(['1950-01-01 12:00:00', '1950-01-02 12:00:00',\n '1950-01-03 12:00:00', '1950-01-04 12:00:00',\n '1950-01-05 12:00:00', '1950-01-06 12:00:00',\n '1950-01-07 12:00:00', '1950-01-08 12:00:00',\n '1950-01-09 12:00:00', '1950-01-10 12:00:00',\n ...\n '1952-12-22 12:00:00', '1952-12-23 12:00:00',\n '1952-12-24 12:00:00', '1952-12-25 12:00:00',\n '1952-12-26 12:00:00', '1952-12-27 12:00:00',\n '1952-12-28 12:00:00', '1952-12-29 12:00:00',\n '1952-12-30 12:00:00', '1952-12-31 12:00:00'],\n dtype='datetime64[ns]', name='time', length=1096, freq=None))Attributes: (22)Conventions :CF-1.7activity :NEX-GDDP-CMIP6cmip6_institution_id :CSIRO-ARCCSScmip6_license :CC-BY-SA 4.0cmip6_source_id :ACCESS-CM2contact :Dr. Rama Nemani: rama.nemani@nasa.gov, Dr. Bridget Thrasher: bridget@climateanalyticsgroup.orgcreation_date :2021-10-04T14:00:55.510838+00:00disclaimer :This data is considered provisional and subject to change. This data is provided as is without any warranty of any kind, either express or implied, arising by law or otherwise, including but not limited to warranties of completeness, non-infringement, accuracy, merchantability, or fitness for a particular purpose. The user assumes all risk associated with the use of, or inability to use, this data.external_variables :areacellafrequency :dayhistory :2021-10-04T14:00:55.510838+00:00: install global attributesinstitution :NASA Earth Exchange, NASA Ames Research Center, Moffett Field, CA 94035product :outputrealm :atmosreferences :BCSD method: Thrasher et al., 2012, Hydrol. Earth Syst. Sci.,16, 3309-3314. Ref period obs: latest version of the Princeton Global Meteorological Forcings (http://hydrology.princeton.edu/data.php), based on Sheffield et al., 2006, J. Climate, 19 (13), 3088-3111.resolution_id :0.25 degreescenario :historicalsource :BCSDtitle :ACCESS-CM2, r1i1p1f1, historical, global downscaled CMIP6 climate projection datatracking_id :f85d4c2e-48e4-484f-aad4-6a3f30a04326variant_label :r1i1p1f1version :1.0\n\n\nCool! Now we have 1096 days (3 years) of data." - }, - { - "objectID": "kerchunk/kerchunk-in-practice.html#how-to-read-a-kerchunk-store", - "href": "kerchunk/kerchunk-in-practice.html#how-to-read-a-kerchunk-store", - "title": "Kerchunk in Practice", - "section": "How to read a Kerchunk Store", - "text": "How to read a Kerchunk Store\nWe’ve already demonstrated how to open the datasets with Xarray:\nfs_multi = fsspec.filesystem(\n \"reference\",\n fo=multi_kerchunk,\n remote_protocol=\"s3\"\n)\nLet’s take it line by line to understand what’s happening.\n\nfsspec.filesystem is used to open the kerchunk reference. It is not necessary to have kerchunk installed to read data.\nThe first argument to fsspec.filesystem is the protocol. In the case of a kerchunk reference the protocol is the string \"reference\".\nThe fo argument is the set of reference files used to create a ReferenceFileSystem instance.\nThe remote_protocol argument is the protocol of the filesystem on which the references will be evaluated (unless fs is provided). If not given, will be derived from the first URL that has a protocol in the templates or in the references.\n\nNotice how the fs_multi object we’ve created is a fsspec.implementations.reference.ReferenceFileSystem.\n\ntype(fs_multi)\n\nfsspec.implementations.reference.ReferenceFileSystem\n\n\nRead about all the options for a fsspec.ReferenceFileSystem in the fsspec docs.\nOne other common situation is to load data over HTTP (as opposed to a local filesystem or via the S3 protocol). Here’s an example from the kerchunk case studies that loads a reference file and data files over HTTP:\n\nzarr_all_url='https://sentinel-1-global-coherence-earthbigdata.s3.us-west-2.amazonaws.com/data/wrappers/zarr-all.json'\n\nmapper = fsspec.get_mapper(\n 'reference://',\n fo=zarr_all_url,\n target_protocol='http',\n remote_protocol='http'\n)\ndataset = xr.open_dataset(\n mapper, engine='zarr', backend_kwargs={'consolidated': False}\n)\ndataset\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (season: 4, polarization: 4, latitude: 193200,\n longitude: 432000, coherence: 6, flightdirection: 2,\n orbit: 175)\nCoordinates:\n * coherence (coherence) float32 6.0 12.0 18.0 24.0 36.0 48.0\n * flightdirection (flightdirection) object 'A' 'D'\n * latitude (latitude) float32 82.0 82.0 82.0 ... -79.0 -79.0 -79.0\n * longitude (longitude) float32 -180.0 -180.0 -180.0 ... 180.0 180.0\n * orbit (orbit) float64 1.0 2.0 3.0 4.0 ... 172.0 173.0 174.0 175.0\n * polarization (polarization) object 'vv' 'vh' 'hv' 'hh'\n * season (season) object 'winter' 'spring' 'summer' 'fall'\nData variables:\n AMP (season, polarization, latitude, longitude) float32 ...\n COH (season, polarization, coherence, latitude, longitude) float32 ...\n inc (orbit, flightdirection, latitude, longitude) float32 ...\n lsmap (orbit, flightdirection, latitude, longitude) float32 ...\n rho (season, polarization, latitude, longitude) float32 ...\n rmse (season, polarization, latitude, longitude) float32 ...\n tau (season, polarization, latitude, longitude) float32 ...xarray.DatasetDimensions:season: 4polarization: 4latitude: 193200longitude: 432000coherence: 6flightdirection: 2orbit: 175Coordinates: (7)coherence(coherence)float326.0 12.0 18.0 24.0 36.0 48.0array([ 6., 12., 18., 24., 36., 48.], dtype=float32)flightdirection(flightdirection)object'A' 'D'array(['A', 'D'], dtype=object)latitude(latitude)float3282.0 82.0 82.0 ... -79.0 -79.0array([ 81.99958, 81.99875, 81.99792, ..., -78.99792, -78.99875, -78.99958],\n dtype=float32)longitude(longitude)float32-180.0 -180.0 ... 180.0 180.0array([-179.99959, -179.99875, -179.99791, ..., 179.99791, 179.99875,\n 179.99959], dtype=float32)orbit(orbit)float641.0 2.0 3.0 ... 173.0 174.0 175.0array([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12.,\n 13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24.,\n 25., 26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36.,\n 37., 38., 39., 40., 41., 42., 43., 44., 45., 46., 47., 48.,\n 49., 50., 51., 52., 53., 54., 55., 56., 57., 58., 59., 60.,\n 61., 62., 63., 64., 65., 66., 67., 68., 69., 70., 71., 72.,\n 73., 74., 75., 76., 77., 78., 79., 80., 81., 82., 83., 84.,\n 85., 86., 87., 88., 89., 90., 91., 92., 93., 94., 95., 96.,\n 97., 98., 99., 100., 101., 102., 103., 104., 105., 106., 107., 108.,\n 109., 110., 111., 112., 113., 114., 115., 116., 117., 118., 119., 120.,\n 121., 122., 123., 124., 125., 126., 127., 128., 129., 130., 131., 132.,\n 133., 134., 135., 136., 137., 138., 139., 140., 141., 142., 143., 144.,\n 145., 146., 147., 148., 149., 150., 151., 152., 153., 154., 155., 156.,\n 157., 158., 159., 160., 161., 162., 163., 164., 165., 166., 167., 168.,\n 169., 170., 171., 172., 173., 174., 175.])polarization(polarization)object'vv' 'vh' 'hv' 'hh'array(['vv', 'vh', 'hv', 'hh'], dtype=object)season(season)object'winter' 'spring' 'summer' 'fall'array(['winter', 'spring', 'summer', 'fall'], dtype=object)Data variables: (7)AMP(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]COH(season, polarization, coherence, latitude, longitude)float32...[8012390400000 values with dtype=float32]inc(orbit, flightdirection, latitude, longitude)float32...[29211840000000 values with dtype=float32]lsmap(orbit, flightdirection, latitude, longitude)float32...[29211840000000 values with dtype=float32]rho(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]rmse(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]tau(season, polarization, latitude, longitude)float32...[1335398400000 values with dtype=float32]Indexes: (7)coherencePandasIndexPandasIndex(Index([6.0, 12.0, 18.0, 24.0, 36.0, 48.0], dtype='float32', name='coherence'))flightdirectionPandasIndexPandasIndex(Index(['A', 'D'], dtype='object', name='flightdirection'))latitudePandasIndexPandasIndex(Index([ 81.99958038330078, 81.99874877929688, 81.99791717529297,\n 81.99708557128906, 81.99624633789062, 81.99541473388672,\n 81.99458312988281, 81.9937515258789, 81.992919921875,\n 81.99208068847656,\n ...\n -78.99208068847656, -78.992919921875, -78.9937515258789,\n -78.99458312988281, -78.99541473388672, -78.99624633789062,\n -78.99708557128906, -78.99791717529297, -78.99874877929688,\n -78.99958038330078],\n dtype='float32', name='latitude', length=193200))longitudePandasIndexPandasIndex(Index([ -179.9995880126953, -179.99874877929688, -179.99790954589844,\n -179.99708557128906, -179.99624633789062, -179.99542236328125,\n -179.9945831298828, -179.99374389648438, -179.992919921875,\n -179.99208068847656,\n ...\n 179.99208068847656, 179.992919921875, 179.99374389648438,\n 179.9945831298828, 179.99542236328125, 179.99624633789062,\n 179.99708557128906, 179.99790954589844, 179.99874877929688,\n 179.9995880126953],\n dtype='float32', name='longitude', length=432000))orbitPandasIndexPandasIndex(Index([ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,\n ...\n 166.0, 167.0, 168.0, 169.0, 170.0, 171.0, 172.0, 173.0, 174.0, 175.0],\n dtype='float64', name='orbit', length=175))polarizationPandasIndexPandasIndex(Index(['vv', 'vh', 'hv', 'hh'], dtype='object', name='polarization'))seasonPandasIndexPandasIndex(Index(['winter', 'spring', 'summer', 'fall'], dtype='object', name='season'))Attributes: (0)\n\n\nBecause xarray uses fsspec to read data, you can also bypass creating a fsspec object explicitly. Here’s an example using of opening a kerchunk reference generated with pangeo-forge for the NOAA 1/4° daily Optimum Interpolation Sea Surface Temperature (or daily OISST) Climate Data Record (CDR).\n\nurl = \"https://ncsa.osn.xsede.org/Pangeo/pangeo-forge/pangeo-forge/aws-noaa-oisst-feedstock/aws-noaa-oisst-avhrr-only.zarr/reference.json\"\nds = xr.open_dataset(\n \"reference://\",\n engine='zarr',\n backend_kwargs={\n 'consolidated': False,\n 'storage_options': {\n 'fo': url,\n 'remote_options': {'anon': True},\n 'remote_protocol': 's3'}},\n chunks={})\nds\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<xarray.Dataset>\nDimensions: (time: 15044, zlev: 1, lat: 720, lon: 1440)\nCoordinates:\n * lat (lat) float32 -89.88 -89.62 -89.38 -89.12 ... 89.38 89.62 89.88\n * lon (lon) float32 0.125 0.375 0.625 0.875 ... 359.1 359.4 359.6 359.9\n * time (time) datetime64[ns] 1981-09-01T12:00:00 ... 2022-11-08T12:00:00\n * zlev (zlev) float32 0.0\nData variables:\n anom (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n err (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n ice (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n sst (time, zlev, lat, lon) float32 dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\nAttributes: (12/37)\n Conventions: CF-1.6, ACDD-1.3\n cdm_data_type: Grid\n comment: Data was converted from NetCDF-3 to NetCDF-4 ...\n creator_email: oisst-help@noaa.gov\n creator_url: https://www.ncei.noaa.gov/\n date_created: 2020-05-08T19:05:13Z\n ... ...\n source: ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfin...\n standard_name_vocabulary: CF Standard Name Table (v40, 25 January 2017)\n summary: NOAAs 1/4-degree Daily Optimum Interpolation ...\n time_coverage_end: 1981-09-01T23:59:59Z\n time_coverage_start: 1981-09-01T00:00:00Z\n title: NOAA/NCEI 1/4 Degree Daily Optimum Interpolat...xarray.DatasetDimensions:time: 15044zlev: 1lat: 720lon: 1440Coordinates: (4)lat(lat)float32-89.88 -89.62 ... 89.62 89.88grids :Uniform grid from -89.875 to 89.875 by 0.25long_name :Latitudeunits :degrees_northarray([-89.875, -89.625, -89.375, ..., 89.375, 89.625, 89.875],\n dtype=float32)lon(lon)float320.125 0.375 0.625 ... 359.6 359.9grids :Uniform grid from 0.125 to 359.875 by 0.25long_name :Longitudeunits :degrees_eastarray([1.25000e-01, 3.75000e-01, 6.25000e-01, ..., 3.59375e+02, 3.59625e+02,\n 3.59875e+02], dtype=float32)time(time)datetime64[ns]1981-09-01T12:00:00 ... 2022-11-...long_name :Center time of the dayarray(['1981-09-01T12:00:00.000000000', '1981-09-02T12:00:00.000000000',\n '1981-09-03T12:00:00.000000000', ..., '2022-11-06T12:00:00.000000000',\n '2022-11-07T12:00:00.000000000', '2022-11-08T12:00:00.000000000'],\n dtype='datetime64[ns]')zlev(zlev)float320.0actual_range :0, 0long_name :Sea surface heightpositive :downunits :metersarray([0.], dtype=float32)Data variables: (4)anom(time, zlev, lat, lon)float32dask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>long_name :Daily sea surface temperature anomaliesunits :Celsiusvalid_max :1200valid_min :-1200\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\nerr\n\n\n(time, zlev, lat, lon)\n\n\nfloat32\n\n\ndask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n\n\n\n\nlong_name :\n\nEstimated error standard deviation of analysed_sst\n\nunits :\n\nCelsius\n\nvalid_max :\n\n1000\n\nvalid_min :\n\n0\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\n\nice\n\n\n(time, zlev, lat, lon)\n\n\nfloat32\n\n\ndask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n\n\n\n\nlong_name :\n\nSea ice concentration\n\nunits :\n\n%\n\nvalid_max :\n\n100\n\nvalid_min :\n\n0\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\n\nsst\n\n\n(time, zlev, lat, lon)\n\n\nfloat32\n\n\ndask.array<chunksize=(1, 1, 720, 1440), meta=np.ndarray>\n\n\n\n\nlong_name :\n\nDaily sea surface temperature\n\nunits :\n\nCelsius\n\nvalid_max :\n\n4500\n\nvalid_min :\n\n-300\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nArray\nChunk\n\n\n\n\nBytes\n58.11 GiB\n3.96 MiB\n\n\nShape\n(15044, 1, 720, 1440)\n(1, 1, 720, 1440)\n\n\nDask graph\n15044 chunks in 2 graph layers\n\n\nData type\nfloat32 numpy.ndarray\n\n\n\n\n\n\n\n\n\nIndexes: (4)latPandasIndexPandasIndex(Index([-89.875, -89.625, -89.375, -89.125, -88.875, -88.625, -88.375, -88.125,\n -87.875, -87.625,\n ...\n 87.625, 87.875, 88.125, 88.375, 88.625, 88.875, 89.125, 89.375,\n 89.625, 89.875],\n dtype='float32', name='lat', length=720))lonPandasIndexPandasIndex(Index([ 0.125, 0.375, 0.625, 0.875, 1.125, 1.375, 1.625, 1.875,\n 2.125, 2.375,\n ...\n 357.625, 357.875, 358.125, 358.375, 358.625, 358.875, 359.125, 359.375,\n 359.625, 359.875],\n dtype='float32', name='lon', length=1440))timePandasIndexPandasIndex(DatetimeIndex(['1981-09-01 12:00:00', '1981-09-02 12:00:00',\n '1981-09-03 12:00:00', '1981-09-04 12:00:00',\n '1981-09-05 12:00:00', '1981-09-06 12:00:00',\n '1981-09-07 12:00:00', '1981-09-08 12:00:00',\n '1981-09-09 12:00:00', '1981-09-10 12:00:00',\n ...\n '2022-10-30 12:00:00', '2022-10-31 12:00:00',\n '2022-11-01 12:00:00', '2022-11-02 12:00:00',\n '2022-11-03 12:00:00', '2022-11-04 12:00:00',\n '2022-11-05 12:00:00', '2022-11-06 12:00:00',\n '2022-11-07 12:00:00', '2022-11-08 12:00:00'],\n dtype='datetime64[ns]', name='time', length=15044, freq=None))zlevPandasIndexPandasIndex(Index([0.0], dtype='float32', name='zlev'))Attributes: (37)Conventions :CF-1.6, ACDD-1.3cdm_data_type :Gridcomment :Data was converted from NetCDF-3 to NetCDF-4 format with metadata updates in November 2017.creator_email :oisst-help@noaa.govcreator_url :https://www.ncei.noaa.gov/date_created :2020-05-08T19:05:13Zdate_modified :2020-05-08T19:05:13Zgeospatial_lat_max :90.0geospatial_lat_min :-90.0geospatial_lat_resolution :0.25geospatial_lat_units :degrees_northgeospatial_lon_max :360.0geospatial_lon_min :0.0geospatial_lon_resolution :0.25geospatial_lon_units :degrees_easthistory :Final file created using preliminary as first guess, and 3 days of AVHRR data. Preliminary uses only 1 day of AVHRR data.id :oisst-avhrr-v02r01.19810901.ncinstitution :NOAA/National Centers for Environmental Informationinstrument :Earth Remote Sensing Instruments > Passive Remote Sensing > Spectrometers/Radiometers > Imaging Spectrometers/Radiometers > AVHRR > Advanced Very High Resolution Radiometerinstrument_vocabulary :Global Change Master Directory (GCMD) Instrument Keywordskeywords :Earth Science > Oceans > Ocean Temperature > Sea Surface Temperaturekeywords_vocabulary :Global Change Master Directory (GCMD) Earth Science Keywordsmetadata_link :https://doi.org/10.25921/RE9P-PT57naming_authority :gov.noaa.nceincei_template_version :NCEI_NetCDF_Grid_Template_v2.0platform :Ships, buoys, Argo floats, MetOp-A, MetOp-Bplatform_vocabulary :Global Change Master Directory (GCMD) Platform Keywordsprocessing_level :NOAA Level 4product_version :Version v02r01references :Reynolds, et al.(2007) Daily High-Resolution-Blended Analyses for Sea Surface Temperature (available at https://doi.org/10.1175/2007JCLI1824.1). Banzon, et al.(2016) A long-term record of blended satellite and in situ sea-surface temperature for climate monitoring, modeling and environmental studies (available at https://doi.org/10.5194/essd-8-165-2016). Huang et al. (2020) Improvements of the Daily Optimum Interpolation Sea Surface Temperature (DOISST) Version v02r01, submitted.Climatology is based on 1971-2000 OI.v2 SST. Satellite data: Pathfinder AVHRR SST and Navy AVHRR SST. Ice data: NCEP Ice and GSFC Ice.sensor :Thermometer, AVHRRsource :ICOADS, NCEP_GTS, GSFC_ICE, NCEP_ICE, Pathfinder_AVHRR, Navy_AVHRRstandard_name_vocabulary :CF Standard Name Table (v40, 25 January 2017)summary :NOAAs 1/4-degree Daily Optimum Interpolation Sea Surface Temperature (OISST) (sometimes referred to as Reynolds SST, which however also refers to earlier products at different resolution), currently available as version v02r01, is created by interpolating and extrapolating SST observations from different sources, resulting in a smoothed complete field. The sources of data are satellite (AVHRR) and in situ platforms (i.e., ships and buoys), and the specific datasets employed may change over time. At the marginal ice zone, sea ice concentrations are used to generate proxy SSTs. A preliminary version of this file is produced in near-real time (1-day latency), and then replaced with a final version after 2 weeks. Note that this is the AVHRR-ONLY DOISST, available from Oct 1981, but there is a companion DOISST product that includes microwave satellite data, available from June 2002time_coverage_end :1981-09-01T23:59:59Ztime_coverage_start :1981-09-01T00:00:00Ztitle :NOAA/NCEI 1/4 Degree Daily Optimum Interpolation Sea Surface Temperature (OISST) Analysis, Version 2.1 - Final" - }, - { - "objectID": "kerchunk/kerchunk-in-practice.html#other-examples-of-existing-kerchunk-data", - "href": "kerchunk/kerchunk-in-practice.html#other-examples-of-existing-kerchunk-data", - "title": "Kerchunk in Practice", - "section": "Other examples of existing kerchunk data", - "text": "Other examples of existing kerchunk data\n\nCase Studies on kerchunk Docs page" - }, { "objectID": "kerchunk/intro.html", "href": "kerchunk/intro.html", @@ -433,13 +643,6 @@ "section": "How to kerchunk", "text": "How to kerchunk\nAs noted above, kerchunk is a python library you can use to create a reference file from any of the file formats it supports. The reference file is used by the fsspec.ReferenceFileSystem to read data from local or remote storage.\nHere’s an example:\nimport fsspec\nimport json\nfrom kerchunk.hdf import SingleHdf5ToZarr\n\nlocal_file = 'some_data.nc'\nout_file = 'some_references.json'\n\n# Instantiate the local file system with fsspec to save kerchunk's reference data as json.\nfs = fsspec.filesystem('')\nin_file = fs.open(local_file)\n\n# The inline threshold adjusts the size below which binary blocks are included directly in the output.\n# A higher inline threshold can result in a larger json file but faster loading time overally, since fewer requests are made.\nh5chunks = SingleHdf5ToZarr(in_file, local_file, inline_threshold=300)\nwith fs.open(out_file, 'wb') as f:\n f.write(json.dumps(h5chunks.translate()).encode())\n\n\n\n\n\n\nNote\n\n\n\nThe powerful fsspec library provides a uniform file system interface to many different storage backends and protocols. In addition to abstracting existing protocols, its ReferenceFileSystem class lets you view byte ranges of some other file as a file system. Kerchunk generates these ReferenceFileSystem objects.\n\n\nKerchunk generates a “reference set” which is a set of references to data or URLs under a key value store that matches the Zarr spec. For example, a simple reference file for a NetCDF file might look like:\n{\n \".zgroup\": \"{\\n \\\"zarr_format\\\": 2\\n}\",\n \".zattrs\": \"{\\n \\\"Conventions\\\": \\\"UGRID-0.9.0\\n\\\"}\",\n \"x/.zattrs\": \"{\\n \\\"_ARRAY_DIMENSIONS\\\": [\\n \\\"node\\\"\\n ...\",\n \"x/.zarray\": \"{\\n \\\"chunks\\\": [\\n 9228245\\n ],\\n \\\"compressor\\\": null,\\n \\\"dtype\\\": \\\"<f8\\\",\\n ...\",\n \"x/0\": [\"s3://bucket/path/file.nc\", 294094376, 73825960]\n}\nThe [\"s3://bucket/path/file.nc\", 294094376, 73825960] is the key part, which says that to load the first chunk in the x dimension, the Zarr reader needs to fetch a byte range starting at 294094376 with a length of 73825960 bytes. This allows for efficient cloud-native data access without using the standard NetCDF driver.\nLearn more about how to read and write kerchunk reference files in the Kerchunk in Practice notebook." }, - { - "objectID": "template.html", - "href": "template.html", - "title": "Template", - "section": "", - "text": "Format Basics (or What is a XX?)\n\n\nExample of Creating this Format\n\n\nExample of Cloud-Optimized Access for this Format" - }, { "objectID": "zarr/intro.html", "href": "zarr/intro.html", @@ -482,181 +685,6 @@ "section": "Zarr is in Development", "text": "Zarr is in Development\nThere are some limitations of Zarr which is why there are Zarr Enhancement Proposals.\nZarr Version 3 was itself a ZEP, which has been accepted.\nDraft ZEPs are recommended reading for anyone considering creating a new Zarr store, since they address common challenges with Zarr data to date." }, - { - "objectID": "transition-from-rmarkdown.html", - "href": "transition-from-rmarkdown.html", - "title": "Transition from RMarkdown", - "section": "", - "text": "You may already have workflows in RMarkdown and are interested in transitioning to Quarto. There’s no hurry to migrate to Quarto. Keep using Rmarkdown and when you’re ready the migration will be fine.\nHere are some notes as we migrate RMarkdown sites and books.\nTODO: translating R code chunks" - }, - { - "objectID": "transition-from-rmarkdown.html#bookdown-to-quarto", - "href": "transition-from-rmarkdown.html#bookdown-to-quarto", - "title": "Transition from RMarkdown", - "section": "Bookdown to Quarto", - "text": "Bookdown to Quarto\nConverting a Bookdown book to Quarto is slightly more involved than converting a website. A book has chapters whose order must be defined, and likely has citations and cross-refs. Still, conversion is not that hard.\nWe got some practice converting from Bookdown to Quarto by helping Gavin Fay convert his lab’s fantastic onboarding documentation, the Faylab Lab Manual. Here’s the GitHub view before and after.\nOur best first reference material for this was Nick Tierney’s Notes on Changing from Rmarkdown/Bookdown to Quarto. Nick shares some scripts in that post to automate some changes. In our case, the book was small enough that we made all changes manually. Quarto documentation was indispensable.\n\nExperimenting in a low-risk environment\nWe forked a copy of the Faylab Lab manual to the Openscapes organization, and worked in a branch so we could make changes relatively risk-free. We could always fork a new copy of the original if we “broke” something. (Caution: the default when making a pull request from a fork is to push changes to the original upstream repo, not your fork and it does this without warning if you have write-access to that repo.) With local previews it’s easy to test / play with settings to see what they do. We tended to make a change, Preview, then compare the look and functionality of the book to the original. It was helpful to comment out some elements of the configuration file _output.yml after their counterparts had been added to the Quarto configuration file _quarto.yml, or to confirm they were no longer needed, before making the drastic move of deleting them.\n\n\nThe conversion\nHere are the main steps to convert the Faylab Lab manual from Bookdown to Quarto.\nCreate new empty file called _quarto.yml and add book metadata there. The screenshots below\nSet project type as book.\nMove metadata out of index.qmd and into _quarto.yml. Title, author, and publication date were in index.qmd with date set using date: \"Last updated:r Sys.Date()\". Now these are in _quarto.yml with date set using date: last-modified. Note that having R code would require you to adjust code chunk options in the Quarto style (#|). This tripped us up a bit; see GitHub Actions.\nMove chapters listing out of _bookdown.yml and into _quarto.yml.\nAdd page footer to _quarto.yml.\nHere’s what ours looked like when we finished the steps above (_quarto.yml).\n\n\n\n\n\n\n_quarto.yml contents\n\n\n\n\n\n\n\nFaylab Lab Manual\n\n\n\n\n\nChange insertion of images from html style to Quarto style. (Note Quarto calls them “figures”, not “images”.) The following snippet will insert the GitHub octocat logo in a page:\n![](https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png){fig-align=\"left\" width=\"35px\"}\nChange all filename extensions .Rmd -> .qmd (you could Preview after this change and see that the book looks the same). Note that Quarto works with .Rmd files just as well as it does .qmd, so this change is not urgent. In fact, if you have a lot of R code in your .Rmds (unlike the Faylab Lab Manual), there will be additional tinkering needed to make the code chunks happy.\n\n\nCitations\nThe Faylab Lab Manual cited two papers, presenting us with an opportunity to see how easy it is to add references to a Quarto book. Briefly, in the Visual Editor, Insert > Citation > DOI. Pasting the DOI or its full URL, we can insert the citation. This automatically creates a references.bib file and adds the full citations at the bottom of the chapter page (watch demo). In July 2022, we had to manually add a ## References heading, but this may not be necessary in future Quarto updates.\n\n\n\n\n\n\nInsert citation via its DOI using RStudio Visual Editor\n\n\n\n\n\n\n\n\n\n\nPublishing notes\nIf the book’s output is strictly html, there’s no need to specify output-dir in _quarto.yml. The output directory default is _book/, which is what we’d like. If we wanted other types of output like like PDF or EPUB, etc. those single file outputs are also written to the output-dir (Quarto docs).\nIf you currently have a docs/ folder, delete it.\nUpdate .gitignore to ignore _book/. At the same time, we have it ignore caches and a .quarto file:\n/.quarto/\n*_cache/\n_book/\nOnce all is settled, delete _output.yml.\nOnce the Openscapes fork was fully reviewed, we made a pull request from that to the main branch of the book’s repo. Once that was merged, we set up GitHub Actions to render the book. (TODO: instructions for GitHub Actions)\n\n\nGitHub Actions\nThis book was mostly prose and screenshots without any R code. This made the conversion from RMarkdown to Quarto likely more straightforward than if you also needed to adjust code chunk options in the quarto style (#|). Our initial GitHub Action to render the converted Faylab Lab Manual failed because we had a piece of R code - even though the code was commented out! This was resolved when we deleted the line." - }, - { - "objectID": "transition-from-rmarkdown.html#distill-to-quarto", - "href": "transition-from-rmarkdown.html#distill-to-quarto", - "title": "Transition from RMarkdown", - "section": "Distill to quarto", - "text": "Distill to quarto\nWe transitioned our events site from distill to quarto in May 2022 (github view before and after). We followed excellent notes and examples from Nick Tierney and Danielle Navarro.\nAfter we had changed all the files, the Build tab in the RStudio IDE still showed “Build website” rather then “Render Website” and “Preview Website”, and would error when we pushed them (because that button was expecting a distill site, not a quarto site). To fix this, we updated the .Rproj file. Clicking on the .Rproj file in the RStudio IDE will open a dialog box where you can click things you want (you can also open these in a text editor or from the GitHub website to see the actual text). To fix this situation with the Build tab: Project Options > Build Tools > Project Build Tools > None.\nLooking at files /posts/_metadata.yml and _quarto.yml helps see where things are defined. For example, to make event post citations appear, we added citation: true to /posts/_metadata.yml and in _quarto.yml under the website key we set site-url: https://openscapes.github.io/events. We deleted footer.html used with distill because footer is now defined in quarto.yml.\n\nPublishing notes\n\nBackground: Our distill site had been set up to output to a docs folder, and had GitHub Settings > Pages set to look there rather gh-pages branch. (Julie note: this was a new-to-me capability when we set up the events distill site in Spring 2021 so I had forgotten that was an option). We’ve inititally kept this same set-up for now with our events page in _quarto.yml: output-dir: docs. However, this is sub-optimal; better to not have to commit and push these files but to instead have a GitHub Action generate them upon a commit. So the following is what we did -\n\nDon’t specify output-dir in _quarto.yml. The output directory default is _site/, which is what we’d like.\nIf you currently have a docs/ folder (like we did as we were experimenting), delete it.\nUpdate .gitignore to ignore _site/. At the same time, we have it ignore caches and a .quarto file:\n/.quarto/\n*_cache/\n_site/\nPush these changes, merge into main.\nOn GitHub.com, in your repo, set up GitHub publishing\nFollow instructions from the explore and setup chapter." - }, - { - "objectID": "transition-from-rmarkdown.html#troubleshooting", - "href": "transition-from-rmarkdown.html#troubleshooting", - "title": "Transition from RMarkdown", - "section": "Troubleshooting", - "text": "Troubleshooting\n\nGitHub Action fails, says you need RMarkdown but you don’t have R code!\nAnd you changed all .Rmds to .qmds!\nYou likely have a few setup code chunks from RMarkdown, that look like this:\n{r setup, include=FALSE}\nknitr::opts_chunk$set(echo = FALSE)\nYou can find them by opening each of your files and having a look, or use GitHub’s search for the keyword knitr" - }, - { - "objectID": "flatgeobuf/hilbert-r-tree.html", - "href": "flatgeobuf/hilbert-r-tree.html", - "title": "FlatGeobuf Spatial Index", - "section": "", - "text": "FlatGeobuf optionally supports including a spatial index that enables random access for each geometry in the file." - }, - { - "objectID": "flatgeobuf/hilbert-r-tree.html#when-to-use", - "href": "flatgeobuf/hilbert-r-tree.html#when-to-use", - "title": "FlatGeobuf Spatial Index", - "section": "When to use", - "text": "When to use\nWhen writing a FlatGeobuf file, one must decide whether to include a spatial index. A spatial index cannot be added to a FlatGeobuf file after the file has been written.\nA spatial index can enable much more efficient reading from FlatGeobuf, by allowing the reader to skip over portions of the file that fall outside of a qiven spatial query region." - }, - { - "objectID": "flatgeobuf/hilbert-r-tree.html#technical-details", - "href": "flatgeobuf/hilbert-r-tree.html#technical-details", - "title": "FlatGeobuf Spatial Index", - "section": "Technical details", - "text": "Technical details\nIn this section we’ll get into some gory technical details of how FlatGeobuf’s spatial index works. Understanding the below isn’t necessary for using FlatGeobuf, but it may add context for understanding how to create and work with FlatGeobuf files, and why FlatGeobuf is performant.\nFlatGeobuf’s spatial index is a static packed Hilbert R-tree index. That’s a mouthful, so let’s break it down:\n\nR-tree index\nAn R-Tree is a hierarchical collection of bounding boxes. At the lowest level of the tree is a bounding box of every geometry. Then one level above the lowest level exists a collection of bounding boxes, each of which is formed as the union of all child boxes. This means that each box encompasses every child box. There are fewer boxes at this level, because each box contains many child boxes, each of which represents one original geometry. This process continues repeatedly until there’s only one bounding box that indirectly contains the entire dataset.\nThis index allows you to quickly search for features that intersect a given bounding box query. At the top level, compare the bounding box of each node to your query region. If those two don’t intersect, you can discard that node and all of its child nodes from the search, because you know that none of them could possibly fall within your search region.\nContinuing this process allows you to quickly find only the specific items that are candidates for your search query.\n\n\nR-Tree diagram from Wikipedia. From top to bottom, the three levels of this tree are the black, blue, and red boxes. The black boxes contain the most items and encompass the largest area, while the red boxes contain the fewest items and encompass a smaller area.\n\nThe Wikipedia article and this Mapbox blog post are great resources for better understanding how R-Trees work.\n\n\nHilbert\nThe elements of an R-Tree must be sorted before insertion to make the R-Tree useful. This is because the core benefit of an R-Tree is to exclude elements that aren’t within a spatial filter. If elements of each node are randomly drawn from different geographies, then each node’s bounding box will be so large that no nodes can be excluded.\nBut how do you sort geometries? They encompass two dimensions and a range of shapes. If you sort all geometries first on the x coordinate, then you may pair geometries that are far from each other on the y dimension. Instead, it’s ideal to use a space-filling curve. That’s math jargon, but essentially defines a way to sort elements in n dimensions using 1 dimensional numbers.\nA Hilbert R-Tree uses a Hilbert Curve, a special type of space-filling curve, to sort the centers of geometries. This ensures that geometries that are nearby on both the x and y dimensions are placed close to each other in the R-Tree. This ensures that the resulting bounding boxes of the R-Tree are as small as possible, which means that the maximum number of elements can be discarded for any given spatial query.\nThis Crunchy Data blog post has helpful examples for why sorting input is important.\n\n\nStatic\nFlatGeobuf files can’t be modified without rewriting the entire file, so this R-Tree is constructed in such a way that it can’t be modified, which allows for improved tree generation.\n\n\nPacked\nAn R-Tree has a series of nodes at each level, where each node can contain up to n children. If the R-Tree might be updated, not every node will have a total of n children, because some space needs to be reserved for future elements.\nBecause the index is static and immutable, we can construct a packed index, where every node is completely full. This achieves better space utilization, and is more efficient for queries because there are fewer nodes to traverse." - }, - { - "objectID": "cookbooks/index.html", - "href": "cookbooks/index.html", - "title": "Cloud-Optimized Cookbooks", - "section": "", - "text": "Cookbooks should address common questions and present solutions for cloud-optimized access and visualization.\nCookbooks:\n\nZarr Visualization Cookbook (in development)" - }, - { - "objectID": "geoparquet/geoparquet-example.html", - "href": "geoparquet/geoparquet-example.html", - "title": "GeoParquet Example", - "section": "", - "text": "This notebook will give an overview of how to read and write GeoParquet files with GeoPandas, putting an emphasis on cloud-native operations where possible.\nThe easiest way to read and write GeoParquet files is to use GeoPandas’ read_parquet and to_parquet functions." - }, - { - "objectID": "geoparquet/geoparquet-example.html#environment", - "href": "geoparquet/geoparquet-example.html#environment", - "title": "GeoParquet Example", - "section": "Environment", - "text": "Environment\nThe packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment. If you don’t want to use Conda or Mamba, install the latest versions of geopandas, fsspec, and pyarrow with pip. Note that you’ll also need the GDAL CLI with Parquet driver. If you’re on MacOS, you can install that via brew install gdal." - }, - { - "objectID": "geoparquet/geoparquet-example.html#imports", - "href": "geoparquet/geoparquet-example.html#imports", - "title": "GeoParquet Example", - "section": "Imports", - "text": "Imports\n\nfrom urllib.request import urlretrieve\n\nimport fsspec\nimport geopandas as gpd\nfrom fsspec.implementations.http import HTTPFileSystem" - }, - { - "objectID": "geoparquet/geoparquet-example.html#comparison-with-flatgeobuf", - "href": "geoparquet/geoparquet-example.html#comparison-with-flatgeobuf", - "title": "GeoParquet Example", - "section": "Comparison with FlatGeobuf", - "text": "Comparison with FlatGeobuf\nIn order to compare reading GeoParquet with FlatGeobuf, we’ll cover reading and writing GeoParquet files on local disk storage. To be consistent with the FlatGeobuf example, we’ll fetch the same US counties FlatGeobuf file (13 MB) and convert it to GeoParquet using ogr2ogr.\n\n# URL to download\nurl = \"https://flatgeobuf.org/test/data/UScounties.fgb\"\n\n# Download, saving to the current directory\nlocal_fgb_path, _ = urlretrieve(url, \"countries.fgb\")\n\n\n!ogr2ogr countries.parquet countries.fgb\n\nLoading this GeoParquet file is really fast! 13% faster than loading the same data via FlatGeobuf (shown in the FlatGeobuf example notebook).\n\n%time gdf = gpd.read_parquet(\"countries.parquet\")\n\nCPU times: user 23.8 ms, sys: 11.8 ms, total: 35.6 ms\nWall time: 34.1 ms\n\n\n\ngdf\n\n\n\n\n\n\n\n\nSTATE_FIPS\nCOUNTY_FIP\nFIPS\nSTATE\nNAME\nLSAD\ngeometry\n\n\n\n\n0\n23\n009\n23009\nME\nHancock\nCounty\nMULTIPOLYGON (((-68.53108 44.33278, -68.53348 ...\n\n\n1\n33\n007\n33007\nNH\nCoos\nCounty\nMULTIPOLYGON (((-71.05975 45.01485, -71.06939 ...\n\n\n2\n50\n009\n50009\nVT\nEssex\nCounty\nMULTIPOLYGON (((-71.49463 44.90874, -71.49392 ...\n\n\n3\n50\n019\n50019\nVT\nOrleans\nCounty\nMULTIPOLYGON (((-72.14193 45.00600, -72.16051 ...\n\n\n4\n23\n007\n23007\nME\nFranklin\nCounty\nMULTIPOLYGON (((-70.83471 45.27514, -70.77984 ...\n\n\n...\n...\n...\n...\n...\n...\n...\n...\n\n\n3216\n15\n003\n15003\nHI\nHonolulu\nCounty\nMULTIPOLYGON (((-171.73761 25.79210, -171.7513...\n\n\n3217\n15\n007\n15007\nHI\nKauai\nCounty\nMULTIPOLYGON (((-160.55535 21.66345, -160.5541...\n\n\n3218\n15\n009\n15009\nHI\nMaui\nCounty\nMULTIPOLYGON (((-157.06121 20.89150, -157.0611...\n\n\n3219\n15\n001\n15001\nHI\nHawaii\nCounty\nMULTIPOLYGON (((-155.08767 19.72887, -155.0909...\n\n\n3220\n15\n005\n15005\nHI\nKalawao\nCounty\nMULTIPOLYGON (((-157.01455 21.18550, -157.0145...\n\n\n\n\n3221 rows × 7 columns" - }, - { - "objectID": "geoparquet/geoparquet-example.html#writing-to-local-disk", - "href": "geoparquet/geoparquet-example.html#writing-to-local-disk", - "title": "GeoParquet Example", - "section": "Writing to local disk", - "text": "Writing to local disk\nWe can use GeoDataFrame.to_parquet to write out this data to GeoParquet files locally. This is about 3x faster than writing the same dataset to FlatGeobuf, but note that FlatGeobuf’s writing is also calculating a spatial index.\n\n%time gdf.to_parquet(\"countries_written.parquet\")\n\nCPU times: user 42.3 ms, sys: 12.6 ms, total: 55 ms\nWall time: 53.9 ms" - }, - { - "objectID": "geoparquet/geoparquet-example.html#reading-from-the-cloud", - "href": "geoparquet/geoparquet-example.html#reading-from-the-cloud", - "title": "GeoParquet Example", - "section": "Reading from the cloud", - "text": "Reading from the cloud\nAs of GeoParquet version 1.0.0-rc.1, spatial indexing has not yet been implemented. Therefore, there is not yet an API in GeoPandas to read data given a specific bounding box.\nWhat is already efficient in GeoParquet is reading only specified columns from a dataset.\n\nurl = \"https://data.source.coop/cholmes/eurocrops/unprojected/geoparquet/FR_2018_EC21.parquet\"\n\nNote that since we’re fetching this data directly from the cloud, we need to pass in an fsspec filesystem object. Otherwise GeoPandas will attempt to load a local file.\n\nfilesystem = HTTPFileSystem()\n\nBy default, calling read_parquet will fetch the entire file and parse it all into a single GeoDataFrame. Since this is a 3GB file, downloading the file takes a long time:\n\n# This cell will take a few minutes to run, because it downloads the entire file\n# %time gdf = gpd.read_parquet(url, filesystem=filesystem)\n\nWe can make this faster by only fetching specific columns. Because GeoParquet stores data in a columnar fashion, when selecting only specific columns we can download a lot less data.\n\n# This cell will take a few minutes to run, because it downloads the entire file for these columns\n# %time gdf = gpd.read_parquet(url, columns=[\"ID_PARCEL\", \"geometry\"], filesystem=filesystem)" - }, - { - "objectID": "geoparquet/geoparquet-example.html#working-with-geoparquet-row-groups-advanced", - "href": "geoparquet/geoparquet-example.html#working-with-geoparquet-row-groups-advanced", - "title": "GeoParquet Example", - "section": "Working with GeoParquet row groups (Advanced)", - "text": "Working with GeoParquet row groups (Advanced)\nAs described in the intro document, GeoParquet is a chunked format, which allows you to access one of the chunks of rows very efficiently. This can allow you to stream a dataset — loading and operating on one chunk at a time — if the dataset is larger than your memory.\nGeoPandas does not yet have built-in support for working with row groups, so this section will use the underlying pyarrow library directly.\n\nimport pyarrow.parquet as pq\nfrom geopandas.io.arrow import _arrow_to_geopandas\n\nFirst, we’ll create a ParquetFile object from the remote URL. All this does is load the metadata from the file, allowing you to inspect the schema and number of columns, rows, and row groups. Because this doesn’t load any actual data, it’s nearly instant to complete.\n\nparquet_file = pq.ParquetFile(url, filesystem=filesystem)\n\nWe can access the column names in the dataset:\n\nparquet_file.schema_arrow.names\n\n['ID_PARCEL',\n 'SURF_PARC',\n 'CODE_CULTU',\n 'CODE_GROUP',\n 'CULTURE_D1',\n 'CULTURE_D2',\n 'EC_org_n',\n 'EC_trans_n',\n 'EC_hcat_n',\n 'EC_hcat_c',\n 'geometry']\n\n\nThis Parquet file includes 9.5 million rows:\n\nparquet_file.metadata.num_rows\n\n9517874\n\n\nAnd 146 row groups. Given that each row group is about the same number of rows, each one contains around 65,000 rows.\n\nparquet_file.num_row_groups\n\n146\n\n\nThen to load one of the row groups by numeric index, we can call ParquetFile.read_row_group.\n\npyarrow_table = parquet_file.read_row_group(0)\n\nNote that this returns a pyarrow.Table, not a geopandas.GeoDataFrame. To convert between the two, we can use _arrow_to_geopandas. This conversion is very fast.\n\ngeopandas_gdf = _arrow_to_geopandas(pyarrow_table, parquet_file.metadata.metadata)\n\nAs expected, this row group contains right around 65,000 rows\n\ngeopandas_gdf.shape\n\n(65536, 11)\n\n\n\ngeopandas_gdf.head()\n\n\n\n\n\n\n\n\nID_PARCEL\nSURF_PARC\nCODE_CULTU\nCODE_GROUP\nCULTURE_D1\nCULTURE_D2\nEC_org_n\nEC_trans_n\nEC_hcat_n\nEC_hcat_c\ngeometry\n\n\n\n\n0\n123563\n6.38\nCZH\n5\nNone\nNone\nColza d’hiver\nWinter rapeseed\nwinter_rapeseed_rape\n3301060401\nMULTIPOLYGON (((3.33896 49.84122, 3.33948 49.8...\n\n\n1\n5527076\n2.30\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((-1.44483 49.61280, -1.44467 49...\n\n\n2\n11479241\n6.33\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((2.87821 46.53674, 2.87820 46.5...\n\n\n3\n12928442\n5.10\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((-0.19026 48.28723, -0.19025 48...\n\n\n4\n318389\n0.92\nPPH\n18\nNone\nNone\nPrairie permanente - herbe prédominante (resso...\nPermanent pasture - predominantly grass (woody...\npasture_meadow_grassland_grass\n3302000000\nMULTIPOLYGON (((5.72084 44.03576, 5.72081 44.0...\n\n\n\n\n\n\n\nAs before, we can speed up the data fetching by requesting only specific columns in the read_row_group call.:\n\npyarrow_table = parquet_file.read_row_group(0, columns=[\"ID_PARCEL\", \"geometry\"])\n\nThen the resulting GeoDataFrame will only have those two columns:\n\n_arrow_to_geopandas(pyarrow_table, parquet_file.metadata.metadata).head()\n\n\n\n\n\n\n\n\nID_PARCEL\ngeometry\n\n\n\n\n0\n123563\nMULTIPOLYGON (((3.33896 49.84122, 3.33948 49.8...\n\n\n1\n5527076\nMULTIPOLYGON (((-1.44483 49.61280, -1.44467 49...\n\n\n2\n11479241\nMULTIPOLYGON (((2.87821 46.53674, 2.87820 46.5...\n\n\n3\n12928442\nMULTIPOLYGON (((-0.19026 48.28723, -0.19025 48...\n\n\n4\n318389\nMULTIPOLYGON (((5.72084 44.03576, 5.72081 44.0..." - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html", - "href": "cloud-optimized-geotiffs/cogs-examples.html", - "title": "Examples of Working with COGs", - "section": "", - "text": "The packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment." - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#environment", - "href": "cloud-optimized-geotiffs/cogs-examples.html#environment", - "title": "Examples of Working with COGs", - "section": "", - "text": "The packages needed for this notebook can be installed with conda or mamba. Using the environment.yml from this folder run:\nconda create -f environment.yml\nor\nmamba create -f environment.yml\nThis notebook has been tested to work with the listed Conda environment." - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#setup", - "href": "cloud-optimized-geotiffs/cogs-examples.html#setup", - "title": "Examples of Working with COGs", - "section": "Setup", - "text": "Setup\nFor demonstrating some COG concepts, we will download a regular GeoTIFF, create a Cloud-Optimized GeoTIFF and explore how they are different.\nFirst we use the earthaccess library to setup credentials to fetch data from NASA’s EarthData catalog.\n\nimport earthaccess\nimport rasterio\nfrom rasterio.plot import show\nfrom rio_cogeo import cog_validate, cog_info\n\n/Users/kyle/local/micromamba/envs/coguide-cog/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n from .autonotebook import tqdm as notebook_tqdm\n\n\n\nearthaccess.login()\n\nEARTHDATA_USERNAME and EARTHDATA_PASSWORD are not set in the current environment, try setting them or use a different strategy (netrc, interactive)\nYou're now authenticated with NASA Earthdata Login\nUsing token with expiration date: 10/24/2023\nUsing .netrc file for EDL\n\n\n<earthaccess.auth.Auth at 0x10427d390>" - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#download-a-geotiff-from-earthdata", - "href": "cloud-optimized-geotiffs/cogs-examples.html#download-a-geotiff-from-earthdata", - "title": "Examples of Working with COGs", - "section": "Download a GeoTIFF from EarthData", - "text": "Download a GeoTIFF from EarthData\nNote: The whole point of is that we don’t download data. So in future examples, we will demonstrate how to access just subsets of data using COG and compare that with a GeoTIFF.\n\n# Download data\nshort_name = 'VCF5KYR'\nversion = '001'\n\nveg_item_results = earthaccess.search_data(\n short_name=short_name,\n version=version,\n count=1\n)\n\nGranules found: 33\n\n\n\ntest_data_dir = \"./test_data\"\nveg_files = earthaccess.download(veg_item_results, test_data_dir)\nveg_gtiff_filename = f\"{test_data_dir}/{veg_files[0]}\"\n\n Getting 1 granules, approx download size: 0.07 GB\nFile VCF5KYR_1982001_001_2018224204211.tif already downloaded\n\n\nQUEUEING TASKS | : 100%|██████████| 1/1 [00:00<00:00, 900.84it/s]\nPROCESSING TASKS | : 100%|██████████| 1/1 [00:00<00:00, 15887.52it/s]\nCOLLECTING RESULTS | : 100%|██████████| 1/1 [00:00<00:00, 29330.80it/s]" - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#is-it-a-valid-cog", - "href": "cloud-optimized-geotiffs/cogs-examples.html#is-it-a-valid-cog", - "title": "Examples of Working with COGs", - "section": "Is it a valid COG?", - "text": "Is it a valid COG?\nWe can use rio_cogeo.cog_validate to check. It returns is_valid, errors and warnings.\n\ncog_validate(veg_gtiff_filename)\n\nThe following warnings were found:\n- The file is greater than 512xH or 512xW, it is recommended to include internal overviews\n\nThe following errors were found:\n- The file is greater than 512xH or 512xW, but is not tiled\n\n\n(False,\n ['The file is greater than 512xH or 512xW, but is not tiled'],\n ['The file is greater than 512xH or 512xW, it is recommended to include internal overviews'])\n\n\nReturn values:\n\nis_valid is False: this is not a valid COG.\nerrors are 'The file is greater than 512xH or 512xW, but is not tiled'. To be a valid COG, the file should be tiled since it has a height and width both greater than 512.\nwarnings are 'The file is greater than 512xH or 512xW, it is recommended to include internal overviews'. It is recommended to provide overviews." - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#converting-a-geotiff-to-cog", - "href": "cloud-optimized-geotiffs/cogs-examples.html#converting-a-geotiff-to-cog", - "title": "Examples of Working with COGs", - "section": "Converting a GeoTIFF to COG", - "text": "Converting a GeoTIFF to COG\nWe can use rio_cogeo.cog_create to convert a GeoTIFF into a Cloud Optimized GeoTIFF\n\nveg_cog_filename = veg_gtiff_filename.replace(\".tif\", \"_cog.tif\")\n\n!rio cogeo create {veg_gtiff_filename} {veg_cog_filename}\n\nReading input: /Users/kyle/ds/cloud-optimized-geospatial-formats-guide/cloud-optimized-geotiffs/test_data/VCF5KYR_1982001_001_2018224204211.tif\n [####################################] 100%\nAdding overviews...\nUpdating dataset tags...\nWriting output to: /Users/kyle/ds/cloud-optimized-geospatial-formats-guide/cloud-optimized-geotiffs/test_data/VCF5KYR_1982001_001_2018224204211_cog.tif\n\n\n\ncog_validate(veg_cog_filename)\n\n(True, [], [])\n\n\nThis is a valid COG, so we will use it to compare with our GeoTIFF." - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#dimensions", - "href": "cloud-optimized-geotiffs/cogs-examples.html#dimensions", - "title": "Examples of Working with COGs", - "section": "Dimensions", - "text": "Dimensions\nThis attribute is also sometimes called chunks or internal tiles.\nDimensions are the number of bands, rows and columns stored in a GeoTIFF. There is a tradeoff between storing lots of data in one GeoTIFF and storing less data in many GeoTIFFs. The larger a single file, the larger the GeoTIFF header and the multiple requests may be required just to read the spatial index before data retrieval. The opposite problem occurs if you make too many small files, then it takes many reads to retrieve data, and when rendering a combined visualization can greatly impact load time.\nIf you plan to pan and zoom a large amount of data through a tiling service in a web browser, there is a tradeoff between 1 large file, or many smaller files. The current recommendation is to meet somewhere in the middle, a moderate amount of medium files.\n\nInternal Blocks\nInternal blocks are required if the dimensions of data are over 512x512. However you can control the size of the internal blocks. 256x256 or 512x512 are recommended. When displaying data at full resolution, or doing partial reading of data this size will impact the number of reads required. A size of 256 will take less time to read, and read less data outside the desired bounding box, however for reading large parts of a file, it may take more total read requests. Some clients will aggregate neighboring block reads to reduce the total number of requests.\nLet’s check out the dimensions and blocks of our GeoTIFF and Cloud-Optimized GeoTIFF.\n\nveg_gtiff_rio = rasterio.open(veg_gtiff_filename)\nveg_cog_rio = rasterio.open(veg_cog_filename)\n\n\nprint(veg_gtiff_rio.shape)\nveg_cog_rio.shape\n\n(3600, 7200)\n\n\n(3600, 7200)\n\n\nThey have the same dimensions which is what we expect, so that is good!\nWe can also print information about the GeoTIFF’s IFD (Internal File Directory). Only one item is returned because the GeoTIFF doesn’t have overviews. When we print the IFD info for the COG, which has overviews, we see more items returned.\n\ncog_info(veg_gtiff_filename).IFD\n\n[IFD(Level=0, Width=7200, Height=3600, Blocksize=(1, 7200), Decimation=0)]\n\n\n\ncog_info(veg_cog_filename).IFD\n\n[IFD(Level=0, Width=7200, Height=3600, Blocksize=(512, 512), Decimation=0),\n IFD(Level=1, Width=3600, Height=1800, Blocksize=(128, 128), Decimation=2),\n IFD(Level=2, Width=1800, Height=900, Blocksize=(128, 128), Decimation=4),\n IFD(Level=3, Width=900, Height=450, Blocksize=(128, 128), Decimation=8)]\n\n\nNote for IFD Level 0, the regular GeoTIFF has a blocksize of (1, 7200) which implies each row of data is a separate block. So whenever reading in data, even if only a few columns are required, the full row must be read." - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#overviews", - "href": "cloud-optimized-geotiffs/cogs-examples.html#overviews", - "title": "Examples of Working with COGs", - "section": "Overviews", - "text": "Overviews\nOverviews are downsampled (aggregated) data intended for visualization. The best resampling algorithm depends on the range, type, and distribution of the data.\nThe smallest size overview should match the tiling components’ fetch size, typically 256x256. Due to aspect ratio variation just aim to have at least one dimension at or slightly less than 256. The COG driver in GDAL, or rio cogeo tools should do this.\nThere are many resampling algorithms for generating overviews. When creating overviews several options should be compared before deciding which resampling method to apply.\nGDAL >= 3.2 allows for the overview resampling method to be set directly.\nTODO: need to add hints on how to check which resampling method to use for overviews. Possibly provide code for comparing.\n\nveg_gtiff_rio.overviews(1)\n\n[]\n\n\n\nveg_cog_rio.overviews(1)\n\n[2, 4, 8]\n\n\nBy displaying each overview, we can see how the dimensions get coarser for each overview level.\n\ndef show_overviews(geotiff): \n for overview in geotiff.overviews(1):\n out_height = int(geotiff.height // overview)\n out_width = int(geotiff.width // overview)\n print(f\"out height: {out_height}\")\n print(f\"out width: {out_width}\") \n # read first band of file and set shape of new output array\n window_size_height = round(out_height/8)\n window_size_width = round(out_width/8)\n image = veg_cog_rio.read(1, out_shape=(1, out_height, out_width))[\n window_size_height:(window_size_height*2),\n window_size_width:(window_size_width*2),\n ]\n show(image)\n \nshow_overviews(veg_cog_rio)\n\nout height: 1800\nout width: 3600\nout height: 900\nout width: 1800\nout height: 450\nout width: 900\n\n\n\n\n\n\n\n\n\n\n\nWe can generate more and different overviews, through different tilesizes and resampling.\n\nimport gen_overviews\n\n\ntmp_dst = gen_overviews.create_overviews_from_gtiff(veg_gtiff_rio)\ntmp_cog = rasterio.open(tmp_dst)\ncog_info(tmp_dst).IFD\n\n\n\n\n[IFD(Level=0, Width=7200, Height=3600, Blocksize=(1, 7200), Decimation=0),\n IFD(Level=1, Width=3600, Height=1800, Blocksize=(128, 128), Decimation=2),\n IFD(Level=2, Width=1800, Height=900, Blocksize=(128, 128), Decimation=4),\n IFD(Level=3, Width=900, Height=450, Blocksize=(128, 128), Decimation=8),\n IFD(Level=4, Width=450, Height=225, Blocksize=(128, 128), Decimation=16)]\n\n\nNote: Now we have overviews but there are still no tiles on the Level 0 IFD.\n\noverviews = tmp_cog.overviews(1)\noverviews\n\n[2, 4, 8, 16]\n\n\n\nshow_overviews(tmp_cog)\n\nout height: 1800\nout width: 3600\nout height: 900\nout width: 1800\nout height: 450\nout width: 900\nout height: 225\nout width: 450" - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#what-we-dont-know-areas-of-research", - "href": "cloud-optimized-geotiffs/cogs-examples.html#what-we-dont-know-areas-of-research", - "title": "Examples of Working with COGs", - "section": "What we don’t know (areas of research)", - "text": "What we don’t know (areas of research)\n\nThe optimum size of data at which splitting across files improves performance as a multi-file dataset instead of a single file.\nWhen to recommend particular internal tile sizes\nCompression impacts on http transfer rates.\nSupport for COG creation in other common scientific platforms (e.g. R)" - }, - { - "objectID": "cloud-optimized-geotiffs/cogs-examples.html#additional-resources", - "href": "cloud-optimized-geotiffs/cogs-examples.html#additional-resources", - "title": "Examples of Working with COGs", - "section": "Additional Resources", - "text": "Additional Resources\n\nAn Introduction to Cloud Optimized GeoTIFFS (COGs) Part 1: Overview\nDo you really want people using your data?" - }, { "objectID": "copc/index.html", "href": "copc/index.html", @@ -665,66 +693,31 @@ "text": "The LASER (LAS) file format is designed to store 3-dimensional (x,y,z) point cloud data typically collected from LiDAR. An LAZ file is a compressed LAS file and a Cloud-Optimized Point Cloud (COPC) file is a valid LAZ file.\nCOPC files are similar to COGs for GeoTIFFs: Both are valid versions of the original file format but with additional requirements to support cloud-optimized data access. In the case of COGs, there are additional requirements for tiling and overviews. For COPC, data must be organized into a clustered octree with a variable-length record (VLR) describing the octree structure.\nRead more at https://copc.io/.\nStay tuned for more information on COPC in future releases of this guide." }, { - "objectID": "pmtiles/intro.html", - "href": "pmtiles/intro.html", - "title": "PMTiles", - "section": "", - "text": "PMTiles is a single-file archive format for tiled data, usually used for visualization.\nAs an “archive format”, PMTiles is similar to a ZIP file: it contains the contents of many individual files inside of one PMTiles file. A single file is often much easier to use and keep track of than many very small files.\nPMTiles is designed for tiled data. That is, data where one inner file represents a small square somewhere on a map, usually representing the Web Mercator grid. PMTiles can be used for any format of tiled data. PMTiles is used most often with vector data, where each tile data contained within the archive is encoded as a Mapbox Vector Tile (MVT), but can also be used with e.g. raster data or terrain mesh data.\n\n\nTo understand PMTiles, it’s important to understand the difference between “analytical” data and “tiled” data. Analytical data refers to data in its original form, without any modifications to geometry. Tiled data formats apply a variety of modifications to geometries, including clipping and simplification, to save space and make it faster to visualize.\n\nConsider the above diagram. In an analytical format, every coordinate of the complex polygon would be included in one single file. In a tiled format, there are predefined tile sets (or grids) and the geometry would be split into one or more files, where each file represents one cell of the grid.\nThe analytical format is more useful for operations like a spatial join, because the entire geometry is available. It’s harder to perform such analyses on tiled data because given any one tile, it’s impossible to know whether the data contained in that tile represents the full geometry or not.\n\nKnow which other tiles contain part of this polygon (This is hard! It requires some other pre-generated attribute other than the geometry itself.)\nFetch each of those neighboring tiles\nAssemble the dissected geometries back into a single geometry\nApply the desired operation\n\nThe tiled format is more useful for visualization because a user who wants to visualize a small area only needs to download a few tiles. Additionally loading the data is faster because of simplification. It’s slower to visualize analytical data because the entire shape with all coordinates must be loaded, even if visualizing only a small area.\nThus analytical and visualization formats strive for different goals.\n\n\n\nPMTiles is designed to be a cloud-native file format: used directly from a client over a network via HTTP range requests, without having a server in the middle.\n\n\n\nPMTiles has a file header, one or more metadata regions, and a region of tile data.\nThe header is fixed length, located at the beginning of the file, and includes necessary information to decode the rest of the file accurately.\nPMTiles includes directories, or regions of bytes with metadata about tiles. It’s important for each directory to remain small, so while there will always be at least one directory, larger PMTiles archives with many tiles may include more than one directory.\nAt the end of the file is the tile data. This includes all data for all the tiles in the archive.\nThe full specification is defined here.\n\n\nInterally, tiles are oriented along a Hilbert Curve. This means that tiles that are spatially near each other are also located near each other in the file structure.\nThis is especially appropriate for PMTiles because visualization purposes most often request data within a specific geographic area. Because spatially-nearby tiles are likely to be nearby in the file as well, this allows the PMTiles client to merge multiple requests for tiles into one larger request, rather than needing to fetch a different area of the file for each tile.\n\n\n\n\nPMTiles archives support storing a full XYZ pyramid of tile data. This means that you can store multiple zoom levels of data inside a single file.\n\n\n\nPMTiles allows tiles to be stored in the file with compression.\n\n\n\n\n\nThe easiest way to generate PMTiles for vector data is through the tippecanoe tool. This will generate vector tiles that are ideal for visualization, removing small features at low zoom levels to keep tiles a manageable size.\n\n\n\nPMTiles has a command-line program for creating PMTiles if you already have an MBTiles file or a directory of tiles.\n\n\n\n\n\n\nIf you have an existing PMTiles archive, either as a local file or hosted on cloud storage, you can use the PMTiles Viewer to inspect the tiles hosted within the file.\n\n\n\nPMTiles doesn’t have a standalone JavaScript library, but rather is designed to be used in conjunction with a JavaScript map rendering library.\nSee the docs on viewing PMTiles in Leaflet, MapLibre GL JS and OpenLayers.\n\n\n\nPMTiles has a Python package, which allows reading and writing PMTiles archives from Python.\n\n\n\n\n\n\nThe most common alternative for PMTiles is MBTiles, which was in many ways the precursor to PMTiles. MBTiles stores the included vector tiles in a table in a SQLite database. MBTiles has the benefit of being much easier to use than manually managing millions of tiny, individual files, but MBTiles is not serverless. In general, it’s impossible to read from a SQLite database without fetching the entire file’s content. This means that frontend clients like a web browser couldn’t fetch tiles directly using range requests, but rather a server has to be running to fetch tiles from the MBTiles file.\n\n\n\nIt’s also possible to upload the bare tiled data directly to cloud storage as individual files.\nThis has significant downsides of needing to manage many millions of tiny individual files. Uploading millions of files to a cloud storage provider such as S3 takes time and money. For example, AWS charges $5 per million files added to an S3 bucket. So a 10 million PMTiles archive would cost $50, compared to 5-millionths of a cent to upload the PMTiles file." - }, - { - "objectID": "pmtiles/intro.html#analytical-vs-tiled-data-formats", - "href": "pmtiles/intro.html#analytical-vs-tiled-data-formats", - "title": "PMTiles", - "section": "", - "text": "To understand PMTiles, it’s important to understand the difference between “analytical” data and “tiled” data. Analytical data refers to data in its original form, without any modifications to geometry. Tiled data formats apply a variety of modifications to geometries, including clipping and simplification, to save space and make it faster to visualize.\n\nConsider the above diagram. In an analytical format, every coordinate of the complex polygon would be included in one single file. In a tiled format, there are predefined tile sets (or grids) and the geometry would be split into one or more files, where each file represents one cell of the grid.\nThe analytical format is more useful for operations like a spatial join, because the entire geometry is available. It’s harder to perform such analyses on tiled data because given any one tile, it’s impossible to know whether the data contained in that tile represents the full geometry or not.\n\nKnow which other tiles contain part of this polygon (This is hard! It requires some other pre-generated attribute other than the geometry itself.)\nFetch each of those neighboring tiles\nAssemble the dissected geometries back into a single geometry\nApply the desired operation\n\nThe tiled format is more useful for visualization because a user who wants to visualize a small area only needs to download a few tiles. Additionally loading the data is faster because of simplification. It’s slower to visualize analytical data because the entire shape with all coordinates must be loaded, even if visualizing only a small area.\nThus analytical and visualization formats strive for different goals." - }, - { - "objectID": "pmtiles/intro.html#cloud-native", - "href": "pmtiles/intro.html#cloud-native", - "title": "PMTiles", - "section": "", - "text": "PMTiles is designed to be a cloud-native file format: used directly from a client over a network via HTTP range requests, without having a server in the middle." - }, - { - "objectID": "pmtiles/intro.html#internal-format", - "href": "pmtiles/intro.html#internal-format", - "title": "PMTiles", - "section": "", - "text": "PMTiles has a file header, one or more metadata regions, and a region of tile data.\nThe header is fixed length, located at the beginning of the file, and includes necessary information to decode the rest of the file accurately.\nPMTiles includes directories, or regions of bytes with metadata about tiles. It’s important for each directory to remain small, so while there will always be at least one directory, larger PMTiles archives with many tiles may include more than one directory.\nAt the end of the file is the tile data. This includes all data for all the tiles in the archive.\nThe full specification is defined here.\n\n\nInterally, tiles are oriented along a Hilbert Curve. This means that tiles that are spatially near each other are also located near each other in the file structure.\nThis is especially appropriate for PMTiles because visualization purposes most often request data within a specific geographic area. Because spatially-nearby tiles are likely to be nearby in the file as well, this allows the PMTiles client to merge multiple requests for tiles into one larger request, rather than needing to fetch a different area of the file for each tile." - }, - { - "objectID": "pmtiles/intro.html#multiple-resolution", - "href": "pmtiles/intro.html#multiple-resolution", - "title": "PMTiles", - "section": "", - "text": "PMTiles archives support storing a full XYZ pyramid of tile data. This means that you can store multiple zoom levels of data inside a single file." - }, - { - "objectID": "pmtiles/intro.html#internal-compression", - "href": "pmtiles/intro.html#internal-compression", - "title": "PMTiles", + "objectID": "geoparquet/index.html", + "href": "geoparquet/index.html", + "title": "GeoParquet", "section": "", - "text": "PMTiles allows tiles to be stored in the file with compression." + "text": "GeoParquet is an encoding for how to store geospatial vector data (point, lines, polygons) in Apache Parquet, a popular columnar storage format for tabular data.\nParquet has a wide ecosystem of tools and support; GeoParquet builds on this success by defining how to store geometries in the Parquet format. Because GeoParquet is not a separate format, any program that can read Parquet is able to load GeoParquet as well, even if it can’t make sense of the geometry information. This is very similar to how GeoTIFF layers geospatial information on top of the existing TIFF image standard.\nThe two main things that GeoParquet defines on top of Parquet are how to encode geometries in the geometry column and how to include metadata like the geometries’ Coordinate Reference System (CRS).\nGeoParquet is a relatively young format, and the specification has not yet reached a 1.0 release (as of August 2023, it’s at 1.0.0-rc.1). However, reading and writing GeoParquet has been supported in GDAL since version 3.5, and thus can be used in programs like GeoPandas and QGIS.\n\n\n\n\n\n\nWarning\n\n\n\nIn GeoPandas use read_parquet and to_parquet to read and write GeoParquet, not read_file and to_file as one would use with most other formats. 1\n\n\nBecause GeoParquet stores geometries in standard Well-Known Binary (WKB), it supports any vector geometry type defined in the OGC Simple Features specification. This includes the standard building blocks of Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon, and GeometryCollection. A best practice is to store only geometries with the same type, as that allows readers to know which geometry type is stored without scanning the entire file.\nSome of the sections below will discuss strengths of Parquet in general. Keep in mind that because GeoParquet is built on top of Parquet, GeoParquet inherits all of these strengths.\n\n\nParquet files are laid out differently than other tabular formats like CSV or FlatGeobuf, so it’s helpful to see a diagram:\n\n\n\nSchematic of Parquet file layout\n\n\nA Parquet file consists of a sequence of chunks called row groups. These are logical groups of columns with the same number of rows. A row group consists of multiple columns, each of which is called a column chunk. These are sequences of raw column values that are guaranteed to be contiguous in the file. All row groups in the file must have the same schema, meaning that the data type of each column must be the same for every row group.\nA Parquet file includes metadata describing the internal chunking. This metadata includes the byte range of every column chunk in the dataset. This allows a Parquet reader to fetch any given column chunk once they have the file metadata.\nThe Parquet metadata also includes column statistics (the minimum and maximum value) for each column chunk. This means that if a user is interested in data where column “A” has values greater than 100, the Parquet reader can skip loading and parsing any column chunks where the maximum is known to be less than 100.\nIn Parquet, the metadata is located at the end of the file rather than at the beginning. This makes it much easier to write, as you don’t need to know how many total rows you have at the beginning, but makes it slightly harder to read. In practice, this is not too much more difficult to read: a Parquet reader first reads the end of the file, then makes reads for select columns.\n\n\nThe bytes of each column are contiguous, instead of each row. This means that it’s easy to filter on columns — fetching all rows of a single column — but not possible to filter on individual rows.\n\n\n\nBecause Parquet is column-oriented, a Parquet reader can fetch only specific columns that the user is interested in.\n\n\n\nBecause Parquet is internally chunked, Parquet can fetch only specific row groups that meet a specific filtering condition.\nNote that row group filtering on a specific column tends to only work well if the Parquet file was sorted on that column when saved. Non-sorted columns tend to have random values, and so the column statistics won’t tend to filter out many row groups.\n\n\n\n\n\n\nNote\n\n\n\nIn general it’s only possible to optimize filtering row groups by one column. This is the biggest difference between file formats and databases. Databases can have multiple indexes on whatever columns you want, and then when you run a query, and it will use all of the indexes. But that’s why it’s hard to make databases work as cloud-native files, because if you have high latency, you don’t want to make lots of tiny fetches.\n\n\n\n\n\nParquet is internally compressed by default and Parquet compression is more efficient compared to other formats.\nCompression algorithms are more effective when nearby bytes are more similar to each other. Data within a column tends to be much more similar than data across a row. Since Parquet is column-oriented, compression algorithms work better and result in smaller file sizes than a comparable row-based format.\nIt’s possible to have random access to one of the internal chunks inside the file at large, even though that chunk is compressed. Note that it isn’t possible to fetch partial data inside one chunk without loading and decompressing the entire chunk.\n\n\n\nFor maximum compatibility with existing systems, geometries are stored as ISO-standard WKB. Most geospatial programs are able to read and write WKB.\n\n\n\nGeoParquet is a young specification, and spatial indices are not yet part of the standard. Future revisions of GeoParquet are expected to add support for spatial indexes.\nOne way around this is to store multiple GeoParquet files according to some region identifier, cataloging each file with the SpatioTemporal Asset Catalog (STAC) specification.\n\n\n\nIn a streaming download, you read bytes starting at the beginning of the file, progressing towards the end. In Parquet, this is not helpful because the metadata is in the footer of the file instead of the header.\nInstead, we can replicate something similar to streaming by first fetching only the metadata region at the end of the file, and then making multiple requests for each internal chunk.\n\n\n\nOnce written, a Parquet file is immutable. No modification or appending can happen to that Parquet file. Instead, create a new Parquet file.\n\n\n\nWhile at medium data sizes GeoParquet is most easily distributed as a single file, at large data sizes a single dataset is often split into multiple files. Sometimes multiple files can be easier to write, such as if the data is output from a distributed system.\nA best practice when writing multiple files is to store a top-level metadata file, often named _metadata, with the metadata of all Parquet files in the directory. Without a top-level metadata file, a reader must read the Parquet footer of every individual file in the directory before reading any data. With a metadata file, a Parquet reader can read just that one metadata file, and then read the relevant chunks in the directory. For more information on this, read the “Partitioned Datasets” and “Writing _metadata and _common_metadata files” of the pyarrow documentation. As of August 2023, GeoPandas has no way to write multiple GeoParquet files out of the box, though you may be able to pass a * glob with multiple paths into geopandas.read_parquet.\nStoring Parquet data in multiple files makes it possible to in effect append to the dataset by adding a new file to the directory, but you must be careful to ensure that the new file has the exact same data schema as the existing files, and if a top-level metadata file exists, it must be rewritten to reflect the new file.\nSome elements of how to store GeoParquet-specific metadata in a multi-file layout have not yet been standardized.\n\n\n\nParquet supports a very extensive type system, including nested types such as lists and maps (i.e. like a Python dict). This means that you can store a key-value mapping or a multi-dimensional array within an attribute column of a GeoParquet dataset.\n\n\n\n\n\nDemystifying the Parquet File Format" }, { - "objectID": "pmtiles/intro.html#generating-pmtiles", - "href": "pmtiles/intro.html#generating-pmtiles", - "title": "PMTiles", + "objectID": "geoparquet/index.html#file-layout", + "href": "geoparquet/index.html#file-layout", + "title": "GeoParquet", "section": "", - "text": "The easiest way to generate PMTiles for vector data is through the tippecanoe tool. This will generate vector tiles that are ideal for visualization, removing small features at low zoom levels to keep tiles a manageable size.\n\n\n\nPMTiles has a command-line program for creating PMTiles if you already have an MBTiles file or a directory of tiles." + "text": "Parquet files are laid out differently than other tabular formats like CSV or FlatGeobuf, so it’s helpful to see a diagram:\n\n\n\nSchematic of Parquet file layout\n\n\nA Parquet file consists of a sequence of chunks called row groups. These are logical groups of columns with the same number of rows. A row group consists of multiple columns, each of which is called a column chunk. These are sequences of raw column values that are guaranteed to be contiguous in the file. All row groups in the file must have the same schema, meaning that the data type of each column must be the same for every row group.\nA Parquet file includes metadata describing the internal chunking. This metadata includes the byte range of every column chunk in the dataset. This allows a Parquet reader to fetch any given column chunk once they have the file metadata.\nThe Parquet metadata also includes column statistics (the minimum and maximum value) for each column chunk. This means that if a user is interested in data where column “A” has values greater than 100, the Parquet reader can skip loading and parsing any column chunks where the maximum is known to be less than 100.\nIn Parquet, the metadata is located at the end of the file rather than at the beginning. This makes it much easier to write, as you don’t need to know how many total rows you have at the beginning, but makes it slightly harder to read. In practice, this is not too much more difficult to read: a Parquet reader first reads the end of the file, then makes reads for select columns.\n\n\nThe bytes of each column are contiguous, instead of each row. This means that it’s easy to filter on columns — fetching all rows of a single column — but not possible to filter on individual rows.\n\n\n\nBecause Parquet is column-oriented, a Parquet reader can fetch only specific columns that the user is interested in.\n\n\n\nBecause Parquet is internally chunked, Parquet can fetch only specific row groups that meet a specific filtering condition.\nNote that row group filtering on a specific column tends to only work well if the Parquet file was sorted on that column when saved. Non-sorted columns tend to have random values, and so the column statistics won’t tend to filter out many row groups.\n\n\n\n\n\n\nNote\n\n\n\nIn general it’s only possible to optimize filtering row groups by one column. This is the biggest difference between file formats and databases. Databases can have multiple indexes on whatever columns you want, and then when you run a query, and it will use all of the indexes. But that’s why it’s hard to make databases work as cloud-native files, because if you have high latency, you don’t want to make lots of tiny fetches.\n\n\n\n\n\nParquet is internally compressed by default and Parquet compression is more efficient compared to other formats.\nCompression algorithms are more effective when nearby bytes are more similar to each other. Data within a column tends to be much more similar than data across a row. Since Parquet is column-oriented, compression algorithms work better and result in smaller file sizes than a comparable row-based format.\nIt’s possible to have random access to one of the internal chunks inside the file at large, even though that chunk is compressed. Note that it isn’t possible to fetch partial data inside one chunk without loading and decompressing the entire chunk.\n\n\n\nFor maximum compatibility with existing systems, geometries are stored as ISO-standard WKB. Most geospatial programs are able to read and write WKB.\n\n\n\nGeoParquet is a young specification, and spatial indices are not yet part of the standard. Future revisions of GeoParquet are expected to add support for spatial indexes.\nOne way around this is to store multiple GeoParquet files according to some region identifier, cataloging each file with the SpatioTemporal Asset Catalog (STAC) specification.\n\n\n\nIn a streaming download, you read bytes starting at the beginning of the file, progressing towards the end. In Parquet, this is not helpful because the metadata is in the footer of the file instead of the header.\nInstead, we can replicate something similar to streaming by first fetching only the metadata region at the end of the file, and then making multiple requests for each internal chunk.\n\n\n\nOnce written, a Parquet file is immutable. No modification or appending can happen to that Parquet file. Instead, create a new Parquet file.\n\n\n\nWhile at medium data sizes GeoParquet is most easily distributed as a single file, at large data sizes a single dataset is often split into multiple files. Sometimes multiple files can be easier to write, such as if the data is output from a distributed system.\nA best practice when writing multiple files is to store a top-level metadata file, often named _metadata, with the metadata of all Parquet files in the directory. Without a top-level metadata file, a reader must read the Parquet footer of every individual file in the directory before reading any data. With a metadata file, a Parquet reader can read just that one metadata file, and then read the relevant chunks in the directory. For more information on this, read the “Partitioned Datasets” and “Writing _metadata and _common_metadata files” of the pyarrow documentation. As of August 2023, GeoPandas has no way to write multiple GeoParquet files out of the box, though you may be able to pass a * glob with multiple paths into geopandas.read_parquet.\nStoring Parquet data in multiple files makes it possible to in effect append to the dataset by adding a new file to the directory, but you must be careful to ensure that the new file has the exact same data schema as the existing files, and if a top-level metadata file exists, it must be rewritten to reflect the new file.\nSome elements of how to store GeoParquet-specific metadata in a multi-file layout have not yet been standardized.\n\n\n\nParquet supports a very extensive type system, including nested types such as lists and maps (i.e. like a Python dict). This means that you can store a key-value mapping or a multi-dimensional array within an attribute column of a GeoParquet dataset." }, { - "objectID": "pmtiles/intro.html#using-pmtiles", - "href": "pmtiles/intro.html#using-pmtiles", - "title": "PMTiles", + "objectID": "geoparquet/index.html#references", + "href": "geoparquet/index.html#references", + "title": "GeoParquet", "section": "", - "text": "If you have an existing PMTiles archive, either as a local file or hosted on cloud storage, you can use the PMTiles Viewer to inspect the tiles hosted within the file.\n\n\n\nPMTiles doesn’t have a standalone JavaScript library, but rather is designed to be used in conjunction with a JavaScript map rendering library.\nSee the docs on viewing PMTiles in Leaflet, MapLibre GL JS and OpenLayers.\n\n\n\nPMTiles has a Python package, which allows reading and writing PMTiles archives from Python." + "text": "Demystifying the Parquet File Format" }, { - "objectID": "pmtiles/intro.html#alternatives", - "href": "pmtiles/intro.html#alternatives", - "title": "PMTiles", - "section": "", - "text": "The most common alternative for PMTiles is MBTiles, which was in many ways the precursor to PMTiles. MBTiles stores the included vector tiles in a table in a SQLite database. MBTiles has the benefit of being much easier to use than manually managing millions of tiny, individual files, but MBTiles is not serverless. In general, it’s impossible to read from a SQLite database without fetching the entire file’s content. This means that frontend clients like a web browser couldn’t fetch tiles directly using range requests, but rather a server has to be running to fetch tiles from the MBTiles file.\n\n\n\nIt’s also possible to upload the bare tiled data directly to cloud storage as individual files.\nThis has significant downsides of needing to manage many millions of tiny individual files. Uploading millions of files to a cloud storage provider such as S3 takes time and money. For example, AWS charges $5 per million files added to an S3 bucket. So a 10 million PMTiles archive would cost $50, compared to 5-millionths of a cent to upload the PMTiles file." + "objectID": "geoparquet/index.html#footnotes", + "href": "geoparquet/index.html#footnotes", + "title": "GeoParquet", + "section": "Footnotes", + "text": "Footnotes\n\n\nAs pointed out by GDAL developer Even Rouault, reading GeoParquet through GDAL is just as fast as reading through the geopandas.read_parquet function if you’re using GDAL’s Arrow API. As of September 2023, this is not the default, so you need to opt into the pyogrio engine and opt into the Arrow API:\nimport geopandas as gpd\ngpd.read_file(\"file.parquet\", engine=\"pyogrio\", use_arrow=True)\nIt’s also necessary to note that the Python wheels distributed by pyogrio do not include the Arrow and Parquet drivers by default. In order to use the pyogrio driver for a GeoParquet file, you need to compile from source when installing. You’ll need to have a GDAL installation version 3.6 or later (and built with Arrow and Parquet support, as seen by ogrinfo --formats) on your computer already, and then you can build pyogrio from source with:\npip install pyogrio --no-binary pyogrio\n↩︎" } ] \ No newline at end of file diff --git a/pr-preview/pr-55/site_libs/bootstrap/bootstrap-dark.min.css b/site_libs/bootstrap/bootstrap-dark.min.css similarity index 99% rename from pr-preview/pr-55/site_libs/bootstrap/bootstrap-dark.min.css rename to site_libs/bootstrap/bootstrap-dark.min.css index 16e83c4..fb0be67 100644 --- a/pr-preview/pr-55/site_libs/bootstrap/bootstrap-dark.min.css +++ b/site_libs/bootstrap/bootstrap-dark.min.css @@ -7,4 +7,4 @@ * * ansi colors from IPython notebook's * -*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-fg{color:#282c36}.ansi-black-intense-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-fg{color:#b22b31}.ansi-red-intense-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-fg{color:#007427}.ansi-green-intense-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-fg{color:#b27d12}.ansi-yellow-intense-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-fg{color:#0065ca}.ansi-blue-intense-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-fg{color:#a03196}.ansi-magenta-intense-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-fg{color:#258f8f}.ansi-cyan-intense-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-fg{color:#a1a6b2}.ansi-white-intense-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #181818;--quarto-body-color: white;--quarto-text-muted: #6c757d;--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.25rem}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:absolute;right:.5em;left:inherit;background-color:rgba(0,0,0,0)}:root{--mermaid-bg-color: #181818;--mermaid-edge-color: #373a3c;--mermaid-node-fg-color: white;--mermaid-fg-color: white;--mermaid-fg-color--lighter: white;--mermaid-fg-color--lightest: white;--mermaid-font-family: Source Sans Pro, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--mermaid-label-bg-color: #181818;--mermaid-label-fg-color: #2780e3;--mermaid-node-bg-color: rgba(39, 128, 227, 0.1);--mermaid-node-fg-color: white}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button:focus{outline:none}.code-copy-button-tooltip{font-size:.75em}.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}main ol ol,main ul ul,main ol ul,main ul ol{margin-bottom:1em}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}body{margin:0}main.page-columns>header>h1.title,main.page-columns>header>.title.h1{margin-bottom:0}@media(min-width: 992px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] 35px [page-end-inset page-end] 5fr [screen-end-inset] 1.5em}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 56px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 3em [body-end] 80px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(80px, 160px) [page-end] 1fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 150px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 991.98px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 1250px - 3em )) [body-content-end body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1.5em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 767.98px){body .page-columns,body.fullcontent:not(.floating):not(.docked) .page-columns,body.slimcontent:not(.floating):not(.docked) .page-columns,body.docked .page-columns,body.docked.slimcontent .page-columns,body.docked.fullcontent .page-columns,body.floating .page-columns,body.floating.slimcontent .page-columns,body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}nav[role=doc-toc]{display:none}}body,.page-row-navigation{grid-template-rows:[page-top] max-content [contents-top] max-content [contents-bottom] max-content [page-bottom]}.page-rows-contents{grid-template-rows:[content-top] minmax(max-content, 1fr) [content-bottom] minmax(60px, max-content) [page-bottom]}.page-full{grid-column:screen-start/screen-end !important}.page-columns>*{grid-column:body-content-start/body-content-end}.page-columns.column-page>*{grid-column:page-start/page-end}.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right>*{grid-column:body-content-start/page-end}.page-rows{grid-auto-rows:auto}.header{grid-column:screen-start/screen-end;grid-row:page-top/contents-top}#quarto-content{padding:0;grid-column:screen-start/screen-end;grid-row:contents-top/contents-bottom}body.floating .sidebar.sidebar-navigation{grid-column:page-start/body-start;grid-row:content-top/page-bottom}body.docked .sidebar.sidebar-navigation{grid-column:screen-start/body-start;grid-row:content-top/page-bottom}.sidebar.toc-left{grid-column:page-start/body-start;grid-row:content-top/page-bottom}.sidebar.margin-sidebar{grid-column:body-end/page-end;grid-row:content-top/page-bottom}.page-columns .content{grid-column:body-content-start/body-content-end;grid-row:content-top/content-bottom;align-content:flex-start}.page-columns .page-navigation{grid-column:body-content-start/body-content-end;grid-row:content-bottom/page-bottom}.page-columns .footer{grid-column:screen-start/screen-end;grid-row:contents-bottom/page-bottom}.page-columns .column-body{grid-column:body-content-start/body-content-end}.page-columns .column-body-fullbleed{grid-column:body-start/body-end}.page-columns .column-body-outset{grid-column:body-start-outset/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset table{background:#181818}.page-columns .column-body-outset-left{grid-column:body-start-outset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-left table{background:#181818}.page-columns .column-body-outset-right{grid-column:body-content-start/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-right table{background:#181818}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page table{background:#181818}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset table{background:#181818}.page-columns .column-page-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-left table{background:#181818}.page-columns .column-page-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-right figcaption table{background:#181818}.page-columns .column-page-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-left table{background:#181818}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-right figcaption table{background:#181818}#quarto-content.page-columns #quarto-margin-sidebar,#quarto-content.page-columns #quarto-sidebar{z-index:1}@media(max-width: 991.98px){#quarto-content.page-columns #quarto-margin-sidebar.collapse,#quarto-content.page-columns #quarto-sidebar.collapse,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{z-index:1055}}#quarto-content.page-columns main.column-page,#quarto-content.page-columns main.column-page-right,#quarto-content.page-columns main.column-page-left{z-index:0}.page-columns .column-screen-inset{grid-column:screen-start-inset/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#181818}.page-columns .column-screen-inset-left{grid-column:screen-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#181818}.page-columns .column-screen-inset-right{grid-column:body-content-start/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#181818}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#181818}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#181818}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#181818}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#525252;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}.zindex-content{z-index:998;transform:translate3d(0, 0, 0)}.zindex-modal{z-index:1055;transform:translate3d(0, 0, 0)}.zindex-over-content{z-index:999;transform:translate3d(0, 0, 0)}img.img-fluid.column-screen,img.img-fluid.column-screen-inset-shaded,img.img-fluid.column-screen-inset,img.img-fluid.column-screen-inset-left,img.img-fluid.column-screen-inset-right,img.img-fluid.column-screen-left,img.img-fluid.column-screen-right{width:100%}@media(min-width: 992px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.column-sidebar{grid-column:page-start/body-start !important;z-index:998}.column-leftmargin{grid-column:screen-start-inset/body-start !important;z-index:998}.no-row-height{height:1em;overflow:visible}}@media(max-width: 991.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.no-row-height{height:1em;overflow:visible}.page-columns.page-full{overflow:visible}.page-columns.toc-left .margin-caption,.page-columns.toc-left div.aside,.page-columns.toc-left aside,.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{display:none}.hidden-sm{display:none}}.panel-grid{display:grid;grid-template-rows:repeat(1, 1fr);grid-template-columns:repeat(24, 1fr);gap:1em}.panel-grid .g-col-1{grid-column:auto/span 1}.panel-grid .g-col-2{grid-column:auto/span 2}.panel-grid .g-col-3{grid-column:auto/span 3}.panel-grid .g-col-4{grid-column:auto/span 4}.panel-grid .g-col-5{grid-column:auto/span 5}.panel-grid .g-col-6{grid-column:auto/span 6}.panel-grid .g-col-7{grid-column:auto/span 7}.panel-grid .g-col-8{grid-column:auto/span 8}.panel-grid .g-col-9{grid-column:auto/span 9}.panel-grid .g-col-10{grid-column:auto/span 10}.panel-grid .g-col-11{grid-column:auto/span 11}.panel-grid .g-col-12{grid-column:auto/span 12}.panel-grid .g-col-13{grid-column:auto/span 13}.panel-grid .g-col-14{grid-column:auto/span 14}.panel-grid .g-col-15{grid-column:auto/span 15}.panel-grid .g-col-16{grid-column:auto/span 16}.panel-grid .g-col-17{grid-column:auto/span 17}.panel-grid .g-col-18{grid-column:auto/span 18}.panel-grid .g-col-19{grid-column:auto/span 19}.panel-grid .g-col-20{grid-column:auto/span 20}.panel-grid .g-col-21{grid-column:auto/span 21}.panel-grid .g-col-22{grid-column:auto/span 22}.panel-grid .g-col-23{grid-column:auto/span 23}.panel-grid .g-col-24{grid-column:auto/span 24}.panel-grid .g-start-1{grid-column-start:1}.panel-grid .g-start-2{grid-column-start:2}.panel-grid .g-start-3{grid-column-start:3}.panel-grid .g-start-4{grid-column-start:4}.panel-grid .g-start-5{grid-column-start:5}.panel-grid .g-start-6{grid-column-start:6}.panel-grid .g-start-7{grid-column-start:7}.panel-grid .g-start-8{grid-column-start:8}.panel-grid .g-start-9{grid-column-start:9}.panel-grid .g-start-10{grid-column-start:10}.panel-grid .g-start-11{grid-column-start:11}.panel-grid .g-start-12{grid-column-start:12}.panel-grid .g-start-13{grid-column-start:13}.panel-grid .g-start-14{grid-column-start:14}.panel-grid .g-start-15{grid-column-start:15}.panel-grid .g-start-16{grid-column-start:16}.panel-grid .g-start-17{grid-column-start:17}.panel-grid .g-start-18{grid-column-start:18}.panel-grid .g-start-19{grid-column-start:19}.panel-grid .g-start-20{grid-column-start:20}.panel-grid .g-start-21{grid-column-start:21}.panel-grid .g-start-22{grid-column-start:22}.panel-grid .g-start-23{grid-column-start:23}@media(min-width: 576px){.panel-grid .g-col-sm-1{grid-column:auto/span 1}.panel-grid .g-col-sm-2{grid-column:auto/span 2}.panel-grid .g-col-sm-3{grid-column:auto/span 3}.panel-grid .g-col-sm-4{grid-column:auto/span 4}.panel-grid .g-col-sm-5{grid-column:auto/span 5}.panel-grid .g-col-sm-6{grid-column:auto/span 6}.panel-grid .g-col-sm-7{grid-column:auto/span 7}.panel-grid .g-col-sm-8{grid-column:auto/span 8}.panel-grid .g-col-sm-9{grid-column:auto/span 9}.panel-grid .g-col-sm-10{grid-column:auto/span 10}.panel-grid .g-col-sm-11{grid-column:auto/span 11}.panel-grid .g-col-sm-12{grid-column:auto/span 12}.panel-grid .g-col-sm-13{grid-column:auto/span 13}.panel-grid .g-col-sm-14{grid-column:auto/span 14}.panel-grid .g-col-sm-15{grid-column:auto/span 15}.panel-grid .g-col-sm-16{grid-column:auto/span 16}.panel-grid .g-col-sm-17{grid-column:auto/span 17}.panel-grid .g-col-sm-18{grid-column:auto/span 18}.panel-grid .g-col-sm-19{grid-column:auto/span 19}.panel-grid .g-col-sm-20{grid-column:auto/span 20}.panel-grid .g-col-sm-21{grid-column:auto/span 21}.panel-grid .g-col-sm-22{grid-column:auto/span 22}.panel-grid .g-col-sm-23{grid-column:auto/span 23}.panel-grid .g-col-sm-24{grid-column:auto/span 24}.panel-grid .g-start-sm-1{grid-column-start:1}.panel-grid .g-start-sm-2{grid-column-start:2}.panel-grid .g-start-sm-3{grid-column-start:3}.panel-grid .g-start-sm-4{grid-column-start:4}.panel-grid .g-start-sm-5{grid-column-start:5}.panel-grid .g-start-sm-6{grid-column-start:6}.panel-grid .g-start-sm-7{grid-column-start:7}.panel-grid .g-start-sm-8{grid-column-start:8}.panel-grid .g-start-sm-9{grid-column-start:9}.panel-grid .g-start-sm-10{grid-column-start:10}.panel-grid .g-start-sm-11{grid-column-start:11}.panel-grid .g-start-sm-12{grid-column-start:12}.panel-grid .g-start-sm-13{grid-column-start:13}.panel-grid .g-start-sm-14{grid-column-start:14}.panel-grid .g-start-sm-15{grid-column-start:15}.panel-grid .g-start-sm-16{grid-column-start:16}.panel-grid .g-start-sm-17{grid-column-start:17}.panel-grid .g-start-sm-18{grid-column-start:18}.panel-grid .g-start-sm-19{grid-column-start:19}.panel-grid .g-start-sm-20{grid-column-start:20}.panel-grid .g-start-sm-21{grid-column-start:21}.panel-grid .g-start-sm-22{grid-column-start:22}.panel-grid .g-start-sm-23{grid-column-start:23}}@media(min-width: 768px){.panel-grid .g-col-md-1{grid-column:auto/span 1}.panel-grid .g-col-md-2{grid-column:auto/span 2}.panel-grid .g-col-md-3{grid-column:auto/span 3}.panel-grid .g-col-md-4{grid-column:auto/span 4}.panel-grid .g-col-md-5{grid-column:auto/span 5}.panel-grid .g-col-md-6{grid-column:auto/span 6}.panel-grid .g-col-md-7{grid-column:auto/span 7}.panel-grid .g-col-md-8{grid-column:auto/span 8}.panel-grid .g-col-md-9{grid-column:auto/span 9}.panel-grid .g-col-md-10{grid-column:auto/span 10}.panel-grid .g-col-md-11{grid-column:auto/span 11}.panel-grid .g-col-md-12{grid-column:auto/span 12}.panel-grid .g-col-md-13{grid-column:auto/span 13}.panel-grid .g-col-md-14{grid-column:auto/span 14}.panel-grid .g-col-md-15{grid-column:auto/span 15}.panel-grid .g-col-md-16{grid-column:auto/span 16}.panel-grid .g-col-md-17{grid-column:auto/span 17}.panel-grid .g-col-md-18{grid-column:auto/span 18}.panel-grid .g-col-md-19{grid-column:auto/span 19}.panel-grid .g-col-md-20{grid-column:auto/span 20}.panel-grid .g-col-md-21{grid-column:auto/span 21}.panel-grid .g-col-md-22{grid-column:auto/span 22}.panel-grid .g-col-md-23{grid-column:auto/span 23}.panel-grid .g-col-md-24{grid-column:auto/span 24}.panel-grid .g-start-md-1{grid-column-start:1}.panel-grid .g-start-md-2{grid-column-start:2}.panel-grid .g-start-md-3{grid-column-start:3}.panel-grid .g-start-md-4{grid-column-start:4}.panel-grid .g-start-md-5{grid-column-start:5}.panel-grid .g-start-md-6{grid-column-start:6}.panel-grid .g-start-md-7{grid-column-start:7}.panel-grid .g-start-md-8{grid-column-start:8}.panel-grid .g-start-md-9{grid-column-start:9}.panel-grid .g-start-md-10{grid-column-start:10}.panel-grid .g-start-md-11{grid-column-start:11}.panel-grid .g-start-md-12{grid-column-start:12}.panel-grid .g-start-md-13{grid-column-start:13}.panel-grid .g-start-md-14{grid-column-start:14}.panel-grid .g-start-md-15{grid-column-start:15}.panel-grid .g-start-md-16{grid-column-start:16}.panel-grid .g-start-md-17{grid-column-start:17}.panel-grid .g-start-md-18{grid-column-start:18}.panel-grid .g-start-md-19{grid-column-start:19}.panel-grid .g-start-md-20{grid-column-start:20}.panel-grid .g-start-md-21{grid-column-start:21}.panel-grid .g-start-md-22{grid-column-start:22}.panel-grid .g-start-md-23{grid-column-start:23}}@media(min-width: 992px){.panel-grid .g-col-lg-1{grid-column:auto/span 1}.panel-grid .g-col-lg-2{grid-column:auto/span 2}.panel-grid .g-col-lg-3{grid-column:auto/span 3}.panel-grid .g-col-lg-4{grid-column:auto/span 4}.panel-grid .g-col-lg-5{grid-column:auto/span 5}.panel-grid .g-col-lg-6{grid-column:auto/span 6}.panel-grid .g-col-lg-7{grid-column:auto/span 7}.panel-grid .g-col-lg-8{grid-column:auto/span 8}.panel-grid .g-col-lg-9{grid-column:auto/span 9}.panel-grid .g-col-lg-10{grid-column:auto/span 10}.panel-grid .g-col-lg-11{grid-column:auto/span 11}.panel-grid .g-col-lg-12{grid-column:auto/span 12}.panel-grid .g-col-lg-13{grid-column:auto/span 13}.panel-grid .g-col-lg-14{grid-column:auto/span 14}.panel-grid .g-col-lg-15{grid-column:auto/span 15}.panel-grid .g-col-lg-16{grid-column:auto/span 16}.panel-grid .g-col-lg-17{grid-column:auto/span 17}.panel-grid .g-col-lg-18{grid-column:auto/span 18}.panel-grid .g-col-lg-19{grid-column:auto/span 19}.panel-grid .g-col-lg-20{grid-column:auto/span 20}.panel-grid .g-col-lg-21{grid-column:auto/span 21}.panel-grid .g-col-lg-22{grid-column:auto/span 22}.panel-grid .g-col-lg-23{grid-column:auto/span 23}.panel-grid .g-col-lg-24{grid-column:auto/span 24}.panel-grid .g-start-lg-1{grid-column-start:1}.panel-grid .g-start-lg-2{grid-column-start:2}.panel-grid .g-start-lg-3{grid-column-start:3}.panel-grid .g-start-lg-4{grid-column-start:4}.panel-grid .g-start-lg-5{grid-column-start:5}.panel-grid .g-start-lg-6{grid-column-start:6}.panel-grid .g-start-lg-7{grid-column-start:7}.panel-grid .g-start-lg-8{grid-column-start:8}.panel-grid .g-start-lg-9{grid-column-start:9}.panel-grid .g-start-lg-10{grid-column-start:10}.panel-grid .g-start-lg-11{grid-column-start:11}.panel-grid .g-start-lg-12{grid-column-start:12}.panel-grid .g-start-lg-13{grid-column-start:13}.panel-grid .g-start-lg-14{grid-column-start:14}.panel-grid .g-start-lg-15{grid-column-start:15}.panel-grid .g-start-lg-16{grid-column-start:16}.panel-grid .g-start-lg-17{grid-column-start:17}.panel-grid .g-start-lg-18{grid-column-start:18}.panel-grid .g-start-lg-19{grid-column-start:19}.panel-grid .g-start-lg-20{grid-column-start:20}.panel-grid .g-start-lg-21{grid-column-start:21}.panel-grid .g-start-lg-22{grid-column-start:22}.panel-grid .g-start-lg-23{grid-column-start:23}}@media(min-width: 1200px){.panel-grid .g-col-xl-1{grid-column:auto/span 1}.panel-grid .g-col-xl-2{grid-column:auto/span 2}.panel-grid .g-col-xl-3{grid-column:auto/span 3}.panel-grid .g-col-xl-4{grid-column:auto/span 4}.panel-grid .g-col-xl-5{grid-column:auto/span 5}.panel-grid .g-col-xl-6{grid-column:auto/span 6}.panel-grid .g-col-xl-7{grid-column:auto/span 7}.panel-grid .g-col-xl-8{grid-column:auto/span 8}.panel-grid .g-col-xl-9{grid-column:auto/span 9}.panel-grid .g-col-xl-10{grid-column:auto/span 10}.panel-grid .g-col-xl-11{grid-column:auto/span 11}.panel-grid .g-col-xl-12{grid-column:auto/span 12}.panel-grid .g-col-xl-13{grid-column:auto/span 13}.panel-grid .g-col-xl-14{grid-column:auto/span 14}.panel-grid .g-col-xl-15{grid-column:auto/span 15}.panel-grid .g-col-xl-16{grid-column:auto/span 16}.panel-grid .g-col-xl-17{grid-column:auto/span 17}.panel-grid .g-col-xl-18{grid-column:auto/span 18}.panel-grid .g-col-xl-19{grid-column:auto/span 19}.panel-grid .g-col-xl-20{grid-column:auto/span 20}.panel-grid .g-col-xl-21{grid-column:auto/span 21}.panel-grid .g-col-xl-22{grid-column:auto/span 22}.panel-grid .g-col-xl-23{grid-column:auto/span 23}.panel-grid .g-col-xl-24{grid-column:auto/span 24}.panel-grid .g-start-xl-1{grid-column-start:1}.panel-grid .g-start-xl-2{grid-column-start:2}.panel-grid .g-start-xl-3{grid-column-start:3}.panel-grid .g-start-xl-4{grid-column-start:4}.panel-grid .g-start-xl-5{grid-column-start:5}.panel-grid .g-start-xl-6{grid-column-start:6}.panel-grid .g-start-xl-7{grid-column-start:7}.panel-grid .g-start-xl-8{grid-column-start:8}.panel-grid .g-start-xl-9{grid-column-start:9}.panel-grid .g-start-xl-10{grid-column-start:10}.panel-grid .g-start-xl-11{grid-column-start:11}.panel-grid .g-start-xl-12{grid-column-start:12}.panel-grid .g-start-xl-13{grid-column-start:13}.panel-grid .g-start-xl-14{grid-column-start:14}.panel-grid .g-start-xl-15{grid-column-start:15}.panel-grid .g-start-xl-16{grid-column-start:16}.panel-grid .g-start-xl-17{grid-column-start:17}.panel-grid .g-start-xl-18{grid-column-start:18}.panel-grid .g-start-xl-19{grid-column-start:19}.panel-grid .g-start-xl-20{grid-column-start:20}.panel-grid .g-start-xl-21{grid-column-start:21}.panel-grid .g-start-xl-22{grid-column-start:22}.panel-grid .g-start-xl-23{grid-column-start:23}}@media(min-width: 1400px){.panel-grid .g-col-xxl-1{grid-column:auto/span 1}.panel-grid .g-col-xxl-2{grid-column:auto/span 2}.panel-grid .g-col-xxl-3{grid-column:auto/span 3}.panel-grid .g-col-xxl-4{grid-column:auto/span 4}.panel-grid .g-col-xxl-5{grid-column:auto/span 5}.panel-grid .g-col-xxl-6{grid-column:auto/span 6}.panel-grid .g-col-xxl-7{grid-column:auto/span 7}.panel-grid .g-col-xxl-8{grid-column:auto/span 8}.panel-grid .g-col-xxl-9{grid-column:auto/span 9}.panel-grid .g-col-xxl-10{grid-column:auto/span 10}.panel-grid .g-col-xxl-11{grid-column:auto/span 11}.panel-grid .g-col-xxl-12{grid-column:auto/span 12}.panel-grid .g-col-xxl-13{grid-column:auto/span 13}.panel-grid .g-col-xxl-14{grid-column:auto/span 14}.panel-grid .g-col-xxl-15{grid-column:auto/span 15}.panel-grid .g-col-xxl-16{grid-column:auto/span 16}.panel-grid .g-col-xxl-17{grid-column:auto/span 17}.panel-grid .g-col-xxl-18{grid-column:auto/span 18}.panel-grid .g-col-xxl-19{grid-column:auto/span 19}.panel-grid .g-col-xxl-20{grid-column:auto/span 20}.panel-grid .g-col-xxl-21{grid-column:auto/span 21}.panel-grid .g-col-xxl-22{grid-column:auto/span 22}.panel-grid .g-col-xxl-23{grid-column:auto/span 23}.panel-grid .g-col-xxl-24{grid-column:auto/span 24}.panel-grid .g-start-xxl-1{grid-column-start:1}.panel-grid .g-start-xxl-2{grid-column-start:2}.panel-grid .g-start-xxl-3{grid-column-start:3}.panel-grid .g-start-xxl-4{grid-column-start:4}.panel-grid .g-start-xxl-5{grid-column-start:5}.panel-grid .g-start-xxl-6{grid-column-start:6}.panel-grid .g-start-xxl-7{grid-column-start:7}.panel-grid .g-start-xxl-8{grid-column-start:8}.panel-grid .g-start-xxl-9{grid-column-start:9}.panel-grid .g-start-xxl-10{grid-column-start:10}.panel-grid .g-start-xxl-11{grid-column-start:11}.panel-grid .g-start-xxl-12{grid-column-start:12}.panel-grid .g-start-xxl-13{grid-column-start:13}.panel-grid .g-start-xxl-14{grid-column-start:14}.panel-grid .g-start-xxl-15{grid-column-start:15}.panel-grid .g-start-xxl-16{grid-column-start:16}.panel-grid .g-start-xxl-17{grid-column-start:17}.panel-grid .g-start-xxl-18{grid-column-start:18}.panel-grid .g-start-xxl-19{grid-column-start:19}.panel-grid .g-start-xxl-20{grid-column-start:20}.panel-grid .g-start-xxl-21{grid-column-start:21}.panel-grid .g-start-xxl-22{grid-column-start:22}.panel-grid .g-start-xxl-23{grid-column-start:23}}main{margin-top:1em;margin-bottom:1em}h1,.h1,h2,.h2{opacity:.9;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#bfbfbf}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,caption,.figure-caption{font-size:.9rem}.panel-caption,.figure-caption,figcaption{color:#bfbfbf}.table-caption,caption{color:#fff}.quarto-layout-cell[data-ref-parent] caption{color:#bfbfbf}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#bfbfbf;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse){padding-top:.5em;padding-bottom:.5em;display:block}.column-margin.column-container>*.collapse:not(.show){display:none}@media(min-width: 768px){.column-margin.column-container .callout-margin-content:first-child{margin-top:4.5em}.column-margin.column-container .callout-margin-content-simple:first-child{margin-top:3.5em}}.margin-caption>*{padding-top:.5em;padding-bottom:.5em}@media(max-width: 767.98px){.quarto-layout-row{flex-direction:column}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.tab-content{margin-top:0px;border-left:#dee2e6 1px solid;border-right:#dee2e6 1px solid;border-bottom:#dee2e6 1px solid;margin-left:0;padding:1em;margin-bottom:1em}@media(max-width: 767.98px){.layout-sidebar{margin-left:0;margin-right:0}}.panel-sidebar,.panel-sidebar .form-control,.panel-input,.panel-input .form-control,.selectize-dropdown{font-size:.9rem}.panel-sidebar .form-control,.panel-input .form-control{padding-top:.1rem}.tab-pane div.sourceCode{margin-top:0px}.tab-pane>p{padding-top:1em}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.2);border:1px solid rgba(233,236,239,.2);border-radius:.25rem}pre.sourceCode{background-color:rgba(0,0,0,0)}pre.sourceCode{border:none;font-size:.875em;overflow:visible !important;padding:.4em}.callout pre.sourceCode{padding-left:0}div.sourceCode{overflow-y:hidden}.callout div.sourceCode{margin-left:initial}.blockquote{font-size:inherit;padding-left:1rem;padding-right:1.5rem;color:#bfbfbf}.blockquote h1:first-child,.blockquote .h1:first-child,.blockquote h2:first-child,.blockquote .h2:first-child,.blockquote h3:first-child,.blockquote .h3:first-child,.blockquote h4:first-child,.blockquote .h4:first-child,.blockquote h5:first-child,.blockquote .h5:first-child{margin-top:0}pre{background-color:initial;padding:initial;border:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#212121;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#quarto-embedded-source-code-modal>.modal-dialog{max-width:1000px;padding-left:1.75rem;padding-right:1.75rem}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body{padding:0}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body div.sourceCode{margin:0;padding:.2rem .2rem;border-radius:0px;border:none}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-header{padding:.7rem}.code-tools-button{font-size:1rem;padding:.15rem .15rem;margin-left:5px;color:#6c757d;background-color:rgba(0,0,0,0);transition:initial;cursor:pointer}.code-tools-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.sidebar{will-change:top;transition:top 200ms linear;position:sticky;overflow-y:auto;padding-top:1.2em;max-height:100vh}.sidebar.toc-left,.sidebar.margin-sidebar{top:0px;padding-top:1em}.sidebar.toc-left>*,.sidebar.margin-sidebar>*{padding-top:.5em}.sidebar.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:#6c757d;margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#75aadb}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.sidebar .quarto-alternate-formats a,.sidebar .quarto-alternate-notebooks a{text-decoration:none}.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#75aadb}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem;font-weight:400;margin-bottom:.5rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2{margin-top:1rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul,.sidebar nav[role=doc-toc] ul{padding-left:0;list-style:none;font-size:.875rem;font-weight:300}.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.sidebar nav[role=doc-toc]>ul li a{line-height:1.1rem;padding-bottom:.2rem;padding-top:.2rem;color:inherit}.sidebar nav[role=doc-toc] ul>li>ul>li>a{padding-left:1.2em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>a{padding-left:2.4em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>a{padding-left:3.6em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:4.8em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:6em}.sidebar nav[role=doc-toc] ul>li>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #75aadb;color:#75aadb !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#75aadb !important}kbd,.kbd{color:#fff;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}div.hanging-indent{margin-left:1em;text-indent:-1em}.citation a,.footnote-ref{text-decoration:none}.footnotes ol{padding-left:1em}.tippy-content>*{margin-bottom:.7em}.tippy-content>*:last-child{margin-bottom:0}.table a{word-break:break-word}.table>thead{border-top-width:1px;border-top-color:#dee2e6;border-bottom:1px solid #fff}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.callout.callout-style-simple{padding:.4em .7em;border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout.callout-style-default{border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout .callout-body-container{flex-grow:1}.callout.callout-style-simple .callout-body{font-size:.9rem;font-weight:400}.callout.callout-style-default .callout-body{font-size:.9rem;font-weight:400}.callout.callout-titled .callout-body{margin-top:.2em}.callout:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.callout-header{padding-top:.2em;margin-bottom:-0.2em}.callout.callout-style-simple>div.callout-header{border-bottom:none;font-size:.9rem;font-weight:600;opacity:75%}.callout.callout-style-default>div.callout-header{border-bottom:none;font-weight:600;opacity:85%;font-size:.9rem;padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body>:first-child{margin-top:.5em}.callout>div.callout-header[data-bs-toggle=collapse]{cursor:pointer}.callout.callout-style-default .callout-header[aria-expanded=false],.callout.callout-style-default .callout-header[aria-expanded=true]{padding-top:0px;margin-bottom:0px;align-items:center}.callout.callout-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){margin-bottom:.5rem}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .callout-body>div>:last-child{margin-bottom:.2rem}.callout.callout-style-simple .callout-icon::before,.callout.callout-style-simple .callout-toggle::before{height:1rem;width:1rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.callout.callout-style-default .callout-icon::before,.callout.callout-style-default .callout-toggle::before{height:.9rem;width:.9rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:.9rem .9rem}.callout.callout-style-default .callout-toggle::before{margin-top:5px}.callout .callout-btn-toggle .callout-toggle::before{transition:transform .2s linear}.callout .callout-header[aria-expanded=false] .callout-toggle::before{transform:rotate(-90deg)}.callout .callout-header[aria-expanded=true] .callout-toggle::before{transform:none}.callout.callout-style-simple:not(.no-icon) div.callout-icon-container{padding-top:.2em;padding-right:.55em}.callout.callout-style-default:not(.no-icon) div.callout-icon-container{padding-top:.1em;padding-right:.35em}.callout.callout-style-default:not(.no-icon) div.callout-title-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:#6c757d}div.callout.callout-style-default>.callout-header{background-color:#6c757d}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#0c2644}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#133707}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#4d2307}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#483417}div.callout-caution:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#4d0011}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important .callout-toggle::before{background-image:url('data:image/svg+xml,')}.quarto-toggle-container{display:flex;align-items:center}.quarto-reader-toggle .bi::before,.quarto-color-scheme-toggle .bi::before{display:inline-block;height:1rem;width:1rem;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.sidebar-navigation{padding-left:20px}.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.quarto-sidebar-toggle{border-color:#dee2e6;border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem;border-style:solid;border-width:1px;overflow:hidden;border-top-width:0px;padding-top:0px !important}.quarto-sidebar-toggle-title{cursor:pointer;padding-bottom:2px;margin-left:.25em;text-align:center;font-weight:400;font-size:.775em}#quarto-content .quarto-sidebar-toggle{background:#1d1d1d}#quarto-content .quarto-sidebar-toggle-title{color:#fff}.quarto-sidebar-toggle-icon{color:#dee2e6;margin-right:.5em;float:right;transition:transform .2s ease}.quarto-sidebar-toggle-icon::before{padding-top:5px}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-icon{transform:rotate(-180deg)}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-title{border-bottom:solid #dee2e6 1px}.quarto-sidebar-toggle-contents{background-color:#181818;padding-right:10px;padding-left:10px;margin-top:0px !important;transition:max-height .5s ease}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-contents{padding-top:1em;padding-bottom:10px}.quarto-sidebar-toggle:not(.expanded) .quarto-sidebar-toggle-contents{padding-top:0px !important;padding-bottom:0px}nav[role=doc-toc]{z-index:1020}#quarto-sidebar>*,nav[role=doc-toc]>*{transition:opacity .1s ease,border .1s ease}#quarto-sidebar.slow>*,nav[role=doc-toc].slow>*{transition:opacity .4s ease,border .4s ease}.quarto-color-scheme-toggle:not(.alternate).top-right .bi::before{background-image:url('data:image/svg+xml,')}.quarto-color-scheme-toggle.alternate.top-right .bi::before{background-image:url('data:image/svg+xml,')}#quarto-appendix.default{border-top:1px solid #dee2e6}#quarto-appendix.default{background-color:#181818;padding-top:1.5em;margin-top:2em;z-index:998}#quarto-appendix.default .quarto-appendix-heading{margin-top:0;line-height:1.4em;font-weight:600;opacity:.9;border-bottom:none;margin-bottom:0}#quarto-appendix.default .footnotes ol,#quarto-appendix.default .footnotes ol li>p:last-of-type,#quarto-appendix.default .quarto-appendix-contents>p:last-of-type{margin-bottom:0}#quarto-appendix.default .quarto-appendix-secondary-label{margin-bottom:.4em}#quarto-appendix.default .quarto-appendix-bibtex{font-size:.7em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-bibtex code.sourceCode{white-space:pre-wrap}#quarto-appendix.default .quarto-appendix-citeas{font-size:.9em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-heading{font-size:1em !important}#quarto-appendix.default *[role=doc-endnotes]>ol,#quarto-appendix.default .quarto-appendix-contents>*:not(h2):not(.h2){font-size:.9em}#quarto-appendix.default section{padding-bottom:1.5em}#quarto-appendix.default section *[role=doc-endnotes],#quarto-appendix.default section>*:not(a){opacity:.9;word-wrap:break-word}.btn.btn-quarto,div.cell-output-display .btn-quarto{color:#cbcccc;background-color:#373a3c;border-color:#373a3c}.btn.btn-quarto:hover,div.cell-output-display .btn-quarto:hover{color:#cbcccc;background-color:#555859;border-color:#4b4e50}.btn-check:focus+.btn.btn-quarto,.btn.btn-quarto:focus,.btn-check:focus+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:focus{color:#cbcccc;background-color:#555859;border-color:#4b4e50;box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn-check:checked+.btn.btn-quarto,.btn-check:active+.btn.btn-quarto,.btn.btn-quarto:active,.btn.btn-quarto.active,.show>.btn.btn-quarto.dropdown-toggle,.btn-check:checked+div.cell-output-display .btn-quarto,.btn-check:active+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:active,div.cell-output-display .btn-quarto.active,.show>div.cell-output-display .btn-quarto.dropdown-toggle{color:#fff;background-color:#5f6163;border-color:#4b4e50}.btn-check:checked+.btn.btn-quarto:focus,.btn-check:active+.btn.btn-quarto:focus,.btn.btn-quarto:active:focus,.btn.btn-quarto.active:focus,.show>.btn.btn-quarto.dropdown-toggle:focus,.btn-check:checked+div.cell-output-display .btn-quarto:focus,.btn-check:active+div.cell-output-display .btn-quarto:focus,div.cell-output-display .btn-quarto:active:focus,div.cell-output-display .btn-quarto.active:focus,.show>div.cell-output-display .btn-quarto.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn.btn-quarto:disabled,.btn.btn-quarto.disabled,div.cell-output-display .btn-quarto:disabled,div.cell-output-display .btn-quarto.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}nav.quarto-secondary-nav.color-navbar{background-color:#303030;color:#bdbdbd}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#bdbdbd}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:0}body.nav-sidebar #title-block-header{margin-block-end:0}}p.subtitle{margin-top:.25em;margin-bottom:.5em}code a:any-link{color:inherit;text-decoration-color:#6c757d}/*! dark */div.observablehq table thead tr th{background-color:var(--bs-body-bg)}input,button,select,optgroup,textarea{background-color:var(--bs-body-bg)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#181818}.code-annotation-gutter{background-color:rgba(233,236,239,.2)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:var(--bs-font-monospace);color:#e6e6e6;border:solid #e6e6e6 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#181818;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:var(--bs-font-monospace);color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#181818}.page-columns .column-screen-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#181818}.page-columns .column-screen-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#181818}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#181818}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#181818}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#181818}.page-columns .column-screen-inset-shaded{grid-column:page-start-inset/page-end-inset;padding:1em;background:#525252;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table>thead{border-top-width:0}.table>:not(caption)>*:not(:last-child)>*{border-bottom-color:#fff;border-bottom-style:solid;border-bottom-width:1px}.table>:not(:first-child){border-top:1px solid #fff;border-bottom:1px solid inherit}.table tbody{border-bottom-color:#fff}a.external:after{display:inline-block;height:.75rem;width:.75rem;margin-bottom:.15em;margin-left:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}div.sourceCode code a.external:after{content:none}a.external:after:hover{cursor:pointer}.quarto-ext-icon{display:inline-block;font-size:.75em;padding-left:.3em}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file,.code-with-filename .code-with-filename-file pre{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file,.quarto-dark .code-with-filename .code-with-filename-file pre{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#373a3c}.progress .progress-bar{font-size:8px;line-height:8px}.layout-example{background:#495057}.quarto-title-banner{margin-bottom:1em;color:#bdbdbd;background:#303030}.quarto-title-banner .code-tools-button{color:#8a8a8a}.quarto-title-banner .code-tools-button:hover{color:#bdbdbd}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;column-gap:.4em;padding-bottom:.5em;margin-top:.75em}.quarto-title .quarto-categories .quarto-category{padding:.25em .75em;font-size:.65em;text-transform:uppercase;border:solid 1px;border-radius:.25rem;opacity:.6}.quarto-title .quarto-categories .quarto-category a{color:inherit}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:repeat(2, 1fr)}#title-block-header.quarto-title-block.default .quarto-title .title{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-author-orcid img{margin-top:-5px}#title-block-header.quarto-title-block.default .quarto-description p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p,#title-block-header.quarto-title-block.default .quarto-title-authors p,#title-block-header.quarto-title-block.default .quarto-title-affiliations p{margin-bottom:.1em}#title-block-header.quarto-title-block.default .quarto-title-meta-heading{text-transform:uppercase;margin-top:1em;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-contents{font-size:.9em}#title-block-header.quarto-title-block.default .quarto-title-meta-contents a{color:#fff}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p.affiliation:last-of-type{margin-bottom:.7em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .description,#title-block-header.quarto-title-block.default .abstract{margin-top:0}#title-block-header.quarto-title-block.default .description>p,#title-block-header.quarto-title-block.default .abstract>p{font-size:.9em}#title-block-header.quarto-title-block.default .description>p:last-of-type,#title-block-header.quarto-title-block.default .abstract>p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .description .abstract-title,#title-block-header.quarto-title-block.default .abstract .abstract-title{margin-top:1em;text-transform:uppercase;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-author{display:grid;grid-template-columns:1fr 1fr}.quarto-title-tools-only{display:flex;justify-content:right}/*# sourceMappingURL=945575463e70190d99eb671cb8520afc.css.map */ +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-fg{color:#282c36}.ansi-black-intense-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-fg{color:#b22b31}.ansi-red-intense-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-fg{color:#007427}.ansi-green-intense-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-fg{color:#b27d12}.ansi-yellow-intense-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-fg{color:#0065ca}.ansi-blue-intense-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-fg{color:#a03196}.ansi-magenta-intense-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-fg{color:#258f8f}.ansi-cyan-intense-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-fg{color:#a1a6b2}.ansi-white-intense-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #181818;--quarto-body-color: white;--quarto-text-muted: #6c757d;--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.25rem}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:absolute;right:.5em;left:inherit;background-color:rgba(0,0,0,0)}:root{--mermaid-bg-color: #181818;--mermaid-edge-color: #373a3c;--mermaid-node-fg-color: white;--mermaid-fg-color: white;--mermaid-fg-color--lighter: white;--mermaid-fg-color--lightest: white;--mermaid-font-family: Source Sans Pro, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--mermaid-label-bg-color: #181818;--mermaid-label-fg-color: #2780e3;--mermaid-node-bg-color: rgba(39, 128, 227, 0.1);--mermaid-node-fg-color: white}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button:focus{outline:none}.code-copy-button-tooltip{font-size:.75em}.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}main ol ol,main ul ul,main ol ul,main ul ol{margin-bottom:1em}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}body{margin:0}main.page-columns>header>h1.title,main.page-columns>header>.title.h1{margin-bottom:0}@media(min-width: 992px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] 35px [page-end-inset page-end] 5fr [screen-end-inset] 1.5em}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 56px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 3em [body-end] 80px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(80px, 160px) [page-end] 1fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 150px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 991.98px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 1250px - 3em )) [body-content-end body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1.5em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 767.98px){body .page-columns,body.fullcontent:not(.floating):not(.docked) .page-columns,body.slimcontent:not(.floating):not(.docked) .page-columns,body.docked .page-columns,body.docked.slimcontent .page-columns,body.docked.fullcontent .page-columns,body.floating .page-columns,body.floating.slimcontent .page-columns,body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}nav[role=doc-toc]{display:none}}body,.page-row-navigation{grid-template-rows:[page-top] max-content [contents-top] max-content [contents-bottom] max-content [page-bottom]}.page-rows-contents{grid-template-rows:[content-top] minmax(max-content, 1fr) [content-bottom] minmax(60px, max-content) [page-bottom]}.page-full{grid-column:screen-start/screen-end !important}.page-columns>*{grid-column:body-content-start/body-content-end}.page-columns.column-page>*{grid-column:page-start/page-end}.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right>*{grid-column:body-content-start/page-end}.page-rows{grid-auto-rows:auto}.header{grid-column:screen-start/screen-end;grid-row:page-top/contents-top}#quarto-content{padding:0;grid-column:screen-start/screen-end;grid-row:contents-top/contents-bottom}body.floating .sidebar.sidebar-navigation{grid-column:page-start/body-start;grid-row:content-top/page-bottom}body.docked .sidebar.sidebar-navigation{grid-column:screen-start/body-start;grid-row:content-top/page-bottom}.sidebar.toc-left{grid-column:page-start/body-start;grid-row:content-top/page-bottom}.sidebar.margin-sidebar{grid-column:body-end/page-end;grid-row:content-top/page-bottom}.page-columns .content{grid-column:body-content-start/body-content-end;grid-row:content-top/content-bottom;align-content:flex-start}.page-columns .page-navigation{grid-column:body-content-start/body-content-end;grid-row:content-bottom/page-bottom}.page-columns .footer{grid-column:screen-start/screen-end;grid-row:contents-bottom/page-bottom}.page-columns .column-body{grid-column:body-content-start/body-content-end}.page-columns .column-body-fullbleed{grid-column:body-start/body-end}.page-columns .column-body-outset{grid-column:body-start-outset/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset table{background:#181818}.page-columns .column-body-outset-left{grid-column:body-start-outset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-left table{background:#181818}.page-columns .column-body-outset-right{grid-column:body-content-start/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-right table{background:#181818}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page table{background:#181818}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset table{background:#181818}.page-columns .column-page-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-left table{background:#181818}.page-columns .column-page-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-right figcaption table{background:#181818}.page-columns .column-page-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-left table{background:#181818}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-right figcaption table{background:#181818}#quarto-content.page-columns #quarto-margin-sidebar,#quarto-content.page-columns #quarto-sidebar{z-index:1}@media(max-width: 991.98px){#quarto-content.page-columns #quarto-margin-sidebar.collapse,#quarto-content.page-columns #quarto-sidebar.collapse,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{z-index:1055}}#quarto-content.page-columns main.column-page,#quarto-content.page-columns main.column-page-right,#quarto-content.page-columns main.column-page-left{z-index:0}.page-columns .column-screen-inset{grid-column:screen-start-inset/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#181818}.page-columns .column-screen-inset-left{grid-column:screen-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#181818}.page-columns .column-screen-inset-right{grid-column:body-content-start/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#181818}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#181818}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#181818}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#181818}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#525252;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}.zindex-content{z-index:998;transform:translate3d(0, 0, 0)}.zindex-modal{z-index:1055;transform:translate3d(0, 0, 0)}.zindex-over-content{z-index:999;transform:translate3d(0, 0, 0)}img.img-fluid.column-screen,img.img-fluid.column-screen-inset-shaded,img.img-fluid.column-screen-inset,img.img-fluid.column-screen-inset-left,img.img-fluid.column-screen-inset-right,img.img-fluid.column-screen-left,img.img-fluid.column-screen-right{width:100%}@media(min-width: 992px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.column-sidebar{grid-column:page-start/body-start !important;z-index:998}.column-leftmargin{grid-column:screen-start-inset/body-start !important;z-index:998}.no-row-height{height:1em;overflow:visible}}@media(max-width: 991.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.no-row-height{height:1em;overflow:visible}.page-columns.page-full{overflow:visible}.page-columns.toc-left .margin-caption,.page-columns.toc-left div.aside,.page-columns.toc-left aside,.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{display:none}.hidden-sm{display:none}}.panel-grid{display:grid;grid-template-rows:repeat(1, 1fr);grid-template-columns:repeat(24, 1fr);gap:1em}.panel-grid .g-col-1{grid-column:auto/span 1}.panel-grid .g-col-2{grid-column:auto/span 2}.panel-grid .g-col-3{grid-column:auto/span 3}.panel-grid .g-col-4{grid-column:auto/span 4}.panel-grid .g-col-5{grid-column:auto/span 5}.panel-grid .g-col-6{grid-column:auto/span 6}.panel-grid .g-col-7{grid-column:auto/span 7}.panel-grid .g-col-8{grid-column:auto/span 8}.panel-grid .g-col-9{grid-column:auto/span 9}.panel-grid .g-col-10{grid-column:auto/span 10}.panel-grid .g-col-11{grid-column:auto/span 11}.panel-grid .g-col-12{grid-column:auto/span 12}.panel-grid .g-col-13{grid-column:auto/span 13}.panel-grid .g-col-14{grid-column:auto/span 14}.panel-grid .g-col-15{grid-column:auto/span 15}.panel-grid .g-col-16{grid-column:auto/span 16}.panel-grid .g-col-17{grid-column:auto/span 17}.panel-grid .g-col-18{grid-column:auto/span 18}.panel-grid .g-col-19{grid-column:auto/span 19}.panel-grid .g-col-20{grid-column:auto/span 20}.panel-grid .g-col-21{grid-column:auto/span 21}.panel-grid .g-col-22{grid-column:auto/span 22}.panel-grid .g-col-23{grid-column:auto/span 23}.panel-grid .g-col-24{grid-column:auto/span 24}.panel-grid .g-start-1{grid-column-start:1}.panel-grid .g-start-2{grid-column-start:2}.panel-grid .g-start-3{grid-column-start:3}.panel-grid .g-start-4{grid-column-start:4}.panel-grid .g-start-5{grid-column-start:5}.panel-grid .g-start-6{grid-column-start:6}.panel-grid .g-start-7{grid-column-start:7}.panel-grid .g-start-8{grid-column-start:8}.panel-grid .g-start-9{grid-column-start:9}.panel-grid .g-start-10{grid-column-start:10}.panel-grid .g-start-11{grid-column-start:11}.panel-grid .g-start-12{grid-column-start:12}.panel-grid .g-start-13{grid-column-start:13}.panel-grid .g-start-14{grid-column-start:14}.panel-grid .g-start-15{grid-column-start:15}.panel-grid .g-start-16{grid-column-start:16}.panel-grid .g-start-17{grid-column-start:17}.panel-grid .g-start-18{grid-column-start:18}.panel-grid .g-start-19{grid-column-start:19}.panel-grid .g-start-20{grid-column-start:20}.panel-grid .g-start-21{grid-column-start:21}.panel-grid .g-start-22{grid-column-start:22}.panel-grid .g-start-23{grid-column-start:23}@media(min-width: 576px){.panel-grid .g-col-sm-1{grid-column:auto/span 1}.panel-grid .g-col-sm-2{grid-column:auto/span 2}.panel-grid .g-col-sm-3{grid-column:auto/span 3}.panel-grid .g-col-sm-4{grid-column:auto/span 4}.panel-grid .g-col-sm-5{grid-column:auto/span 5}.panel-grid .g-col-sm-6{grid-column:auto/span 6}.panel-grid .g-col-sm-7{grid-column:auto/span 7}.panel-grid .g-col-sm-8{grid-column:auto/span 8}.panel-grid .g-col-sm-9{grid-column:auto/span 9}.panel-grid .g-col-sm-10{grid-column:auto/span 10}.panel-grid .g-col-sm-11{grid-column:auto/span 11}.panel-grid .g-col-sm-12{grid-column:auto/span 12}.panel-grid .g-col-sm-13{grid-column:auto/span 13}.panel-grid .g-col-sm-14{grid-column:auto/span 14}.panel-grid .g-col-sm-15{grid-column:auto/span 15}.panel-grid .g-col-sm-16{grid-column:auto/span 16}.panel-grid .g-col-sm-17{grid-column:auto/span 17}.panel-grid .g-col-sm-18{grid-column:auto/span 18}.panel-grid .g-col-sm-19{grid-column:auto/span 19}.panel-grid .g-col-sm-20{grid-column:auto/span 20}.panel-grid .g-col-sm-21{grid-column:auto/span 21}.panel-grid .g-col-sm-22{grid-column:auto/span 22}.panel-grid .g-col-sm-23{grid-column:auto/span 23}.panel-grid .g-col-sm-24{grid-column:auto/span 24}.panel-grid .g-start-sm-1{grid-column-start:1}.panel-grid .g-start-sm-2{grid-column-start:2}.panel-grid .g-start-sm-3{grid-column-start:3}.panel-grid .g-start-sm-4{grid-column-start:4}.panel-grid .g-start-sm-5{grid-column-start:5}.panel-grid .g-start-sm-6{grid-column-start:6}.panel-grid .g-start-sm-7{grid-column-start:7}.panel-grid .g-start-sm-8{grid-column-start:8}.panel-grid .g-start-sm-9{grid-column-start:9}.panel-grid .g-start-sm-10{grid-column-start:10}.panel-grid .g-start-sm-11{grid-column-start:11}.panel-grid .g-start-sm-12{grid-column-start:12}.panel-grid .g-start-sm-13{grid-column-start:13}.panel-grid .g-start-sm-14{grid-column-start:14}.panel-grid .g-start-sm-15{grid-column-start:15}.panel-grid .g-start-sm-16{grid-column-start:16}.panel-grid .g-start-sm-17{grid-column-start:17}.panel-grid .g-start-sm-18{grid-column-start:18}.panel-grid .g-start-sm-19{grid-column-start:19}.panel-grid .g-start-sm-20{grid-column-start:20}.panel-grid .g-start-sm-21{grid-column-start:21}.panel-grid .g-start-sm-22{grid-column-start:22}.panel-grid .g-start-sm-23{grid-column-start:23}}@media(min-width: 768px){.panel-grid .g-col-md-1{grid-column:auto/span 1}.panel-grid .g-col-md-2{grid-column:auto/span 2}.panel-grid .g-col-md-3{grid-column:auto/span 3}.panel-grid .g-col-md-4{grid-column:auto/span 4}.panel-grid .g-col-md-5{grid-column:auto/span 5}.panel-grid .g-col-md-6{grid-column:auto/span 6}.panel-grid .g-col-md-7{grid-column:auto/span 7}.panel-grid .g-col-md-8{grid-column:auto/span 8}.panel-grid .g-col-md-9{grid-column:auto/span 9}.panel-grid .g-col-md-10{grid-column:auto/span 10}.panel-grid .g-col-md-11{grid-column:auto/span 11}.panel-grid .g-col-md-12{grid-column:auto/span 12}.panel-grid .g-col-md-13{grid-column:auto/span 13}.panel-grid .g-col-md-14{grid-column:auto/span 14}.panel-grid .g-col-md-15{grid-column:auto/span 15}.panel-grid .g-col-md-16{grid-column:auto/span 16}.panel-grid .g-col-md-17{grid-column:auto/span 17}.panel-grid .g-col-md-18{grid-column:auto/span 18}.panel-grid .g-col-md-19{grid-column:auto/span 19}.panel-grid .g-col-md-20{grid-column:auto/span 20}.panel-grid .g-col-md-21{grid-column:auto/span 21}.panel-grid .g-col-md-22{grid-column:auto/span 22}.panel-grid .g-col-md-23{grid-column:auto/span 23}.panel-grid .g-col-md-24{grid-column:auto/span 24}.panel-grid .g-start-md-1{grid-column-start:1}.panel-grid .g-start-md-2{grid-column-start:2}.panel-grid .g-start-md-3{grid-column-start:3}.panel-grid .g-start-md-4{grid-column-start:4}.panel-grid .g-start-md-5{grid-column-start:5}.panel-grid .g-start-md-6{grid-column-start:6}.panel-grid .g-start-md-7{grid-column-start:7}.panel-grid .g-start-md-8{grid-column-start:8}.panel-grid .g-start-md-9{grid-column-start:9}.panel-grid .g-start-md-10{grid-column-start:10}.panel-grid .g-start-md-11{grid-column-start:11}.panel-grid .g-start-md-12{grid-column-start:12}.panel-grid .g-start-md-13{grid-column-start:13}.panel-grid .g-start-md-14{grid-column-start:14}.panel-grid .g-start-md-15{grid-column-start:15}.panel-grid .g-start-md-16{grid-column-start:16}.panel-grid .g-start-md-17{grid-column-start:17}.panel-grid .g-start-md-18{grid-column-start:18}.panel-grid .g-start-md-19{grid-column-start:19}.panel-grid .g-start-md-20{grid-column-start:20}.panel-grid .g-start-md-21{grid-column-start:21}.panel-grid .g-start-md-22{grid-column-start:22}.panel-grid .g-start-md-23{grid-column-start:23}}@media(min-width: 992px){.panel-grid .g-col-lg-1{grid-column:auto/span 1}.panel-grid .g-col-lg-2{grid-column:auto/span 2}.panel-grid .g-col-lg-3{grid-column:auto/span 3}.panel-grid .g-col-lg-4{grid-column:auto/span 4}.panel-grid .g-col-lg-5{grid-column:auto/span 5}.panel-grid .g-col-lg-6{grid-column:auto/span 6}.panel-grid .g-col-lg-7{grid-column:auto/span 7}.panel-grid .g-col-lg-8{grid-column:auto/span 8}.panel-grid .g-col-lg-9{grid-column:auto/span 9}.panel-grid .g-col-lg-10{grid-column:auto/span 10}.panel-grid .g-col-lg-11{grid-column:auto/span 11}.panel-grid .g-col-lg-12{grid-column:auto/span 12}.panel-grid .g-col-lg-13{grid-column:auto/span 13}.panel-grid .g-col-lg-14{grid-column:auto/span 14}.panel-grid .g-col-lg-15{grid-column:auto/span 15}.panel-grid .g-col-lg-16{grid-column:auto/span 16}.panel-grid .g-col-lg-17{grid-column:auto/span 17}.panel-grid .g-col-lg-18{grid-column:auto/span 18}.panel-grid .g-col-lg-19{grid-column:auto/span 19}.panel-grid .g-col-lg-20{grid-column:auto/span 20}.panel-grid .g-col-lg-21{grid-column:auto/span 21}.panel-grid .g-col-lg-22{grid-column:auto/span 22}.panel-grid .g-col-lg-23{grid-column:auto/span 23}.panel-grid .g-col-lg-24{grid-column:auto/span 24}.panel-grid .g-start-lg-1{grid-column-start:1}.panel-grid .g-start-lg-2{grid-column-start:2}.panel-grid .g-start-lg-3{grid-column-start:3}.panel-grid .g-start-lg-4{grid-column-start:4}.panel-grid .g-start-lg-5{grid-column-start:5}.panel-grid .g-start-lg-6{grid-column-start:6}.panel-grid .g-start-lg-7{grid-column-start:7}.panel-grid .g-start-lg-8{grid-column-start:8}.panel-grid .g-start-lg-9{grid-column-start:9}.panel-grid .g-start-lg-10{grid-column-start:10}.panel-grid .g-start-lg-11{grid-column-start:11}.panel-grid .g-start-lg-12{grid-column-start:12}.panel-grid .g-start-lg-13{grid-column-start:13}.panel-grid .g-start-lg-14{grid-column-start:14}.panel-grid .g-start-lg-15{grid-column-start:15}.panel-grid .g-start-lg-16{grid-column-start:16}.panel-grid .g-start-lg-17{grid-column-start:17}.panel-grid .g-start-lg-18{grid-column-start:18}.panel-grid .g-start-lg-19{grid-column-start:19}.panel-grid .g-start-lg-20{grid-column-start:20}.panel-grid .g-start-lg-21{grid-column-start:21}.panel-grid .g-start-lg-22{grid-column-start:22}.panel-grid .g-start-lg-23{grid-column-start:23}}@media(min-width: 1200px){.panel-grid .g-col-xl-1{grid-column:auto/span 1}.panel-grid .g-col-xl-2{grid-column:auto/span 2}.panel-grid .g-col-xl-3{grid-column:auto/span 3}.panel-grid .g-col-xl-4{grid-column:auto/span 4}.panel-grid .g-col-xl-5{grid-column:auto/span 5}.panel-grid .g-col-xl-6{grid-column:auto/span 6}.panel-grid .g-col-xl-7{grid-column:auto/span 7}.panel-grid .g-col-xl-8{grid-column:auto/span 8}.panel-grid .g-col-xl-9{grid-column:auto/span 9}.panel-grid .g-col-xl-10{grid-column:auto/span 10}.panel-grid .g-col-xl-11{grid-column:auto/span 11}.panel-grid .g-col-xl-12{grid-column:auto/span 12}.panel-grid .g-col-xl-13{grid-column:auto/span 13}.panel-grid .g-col-xl-14{grid-column:auto/span 14}.panel-grid .g-col-xl-15{grid-column:auto/span 15}.panel-grid .g-col-xl-16{grid-column:auto/span 16}.panel-grid .g-col-xl-17{grid-column:auto/span 17}.panel-grid .g-col-xl-18{grid-column:auto/span 18}.panel-grid .g-col-xl-19{grid-column:auto/span 19}.panel-grid .g-col-xl-20{grid-column:auto/span 20}.panel-grid .g-col-xl-21{grid-column:auto/span 21}.panel-grid .g-col-xl-22{grid-column:auto/span 22}.panel-grid .g-col-xl-23{grid-column:auto/span 23}.panel-grid .g-col-xl-24{grid-column:auto/span 24}.panel-grid .g-start-xl-1{grid-column-start:1}.panel-grid .g-start-xl-2{grid-column-start:2}.panel-grid .g-start-xl-3{grid-column-start:3}.panel-grid .g-start-xl-4{grid-column-start:4}.panel-grid .g-start-xl-5{grid-column-start:5}.panel-grid .g-start-xl-6{grid-column-start:6}.panel-grid .g-start-xl-7{grid-column-start:7}.panel-grid .g-start-xl-8{grid-column-start:8}.panel-grid .g-start-xl-9{grid-column-start:9}.panel-grid .g-start-xl-10{grid-column-start:10}.panel-grid .g-start-xl-11{grid-column-start:11}.panel-grid .g-start-xl-12{grid-column-start:12}.panel-grid .g-start-xl-13{grid-column-start:13}.panel-grid .g-start-xl-14{grid-column-start:14}.panel-grid .g-start-xl-15{grid-column-start:15}.panel-grid .g-start-xl-16{grid-column-start:16}.panel-grid .g-start-xl-17{grid-column-start:17}.panel-grid .g-start-xl-18{grid-column-start:18}.panel-grid .g-start-xl-19{grid-column-start:19}.panel-grid .g-start-xl-20{grid-column-start:20}.panel-grid .g-start-xl-21{grid-column-start:21}.panel-grid .g-start-xl-22{grid-column-start:22}.panel-grid .g-start-xl-23{grid-column-start:23}}@media(min-width: 1400px){.panel-grid .g-col-xxl-1{grid-column:auto/span 1}.panel-grid .g-col-xxl-2{grid-column:auto/span 2}.panel-grid .g-col-xxl-3{grid-column:auto/span 3}.panel-grid .g-col-xxl-4{grid-column:auto/span 4}.panel-grid .g-col-xxl-5{grid-column:auto/span 5}.panel-grid .g-col-xxl-6{grid-column:auto/span 6}.panel-grid .g-col-xxl-7{grid-column:auto/span 7}.panel-grid .g-col-xxl-8{grid-column:auto/span 8}.panel-grid .g-col-xxl-9{grid-column:auto/span 9}.panel-grid .g-col-xxl-10{grid-column:auto/span 10}.panel-grid .g-col-xxl-11{grid-column:auto/span 11}.panel-grid .g-col-xxl-12{grid-column:auto/span 12}.panel-grid .g-col-xxl-13{grid-column:auto/span 13}.panel-grid .g-col-xxl-14{grid-column:auto/span 14}.panel-grid .g-col-xxl-15{grid-column:auto/span 15}.panel-grid .g-col-xxl-16{grid-column:auto/span 16}.panel-grid .g-col-xxl-17{grid-column:auto/span 17}.panel-grid .g-col-xxl-18{grid-column:auto/span 18}.panel-grid .g-col-xxl-19{grid-column:auto/span 19}.panel-grid .g-col-xxl-20{grid-column:auto/span 20}.panel-grid .g-col-xxl-21{grid-column:auto/span 21}.panel-grid .g-col-xxl-22{grid-column:auto/span 22}.panel-grid .g-col-xxl-23{grid-column:auto/span 23}.panel-grid .g-col-xxl-24{grid-column:auto/span 24}.panel-grid .g-start-xxl-1{grid-column-start:1}.panel-grid .g-start-xxl-2{grid-column-start:2}.panel-grid .g-start-xxl-3{grid-column-start:3}.panel-grid .g-start-xxl-4{grid-column-start:4}.panel-grid .g-start-xxl-5{grid-column-start:5}.panel-grid .g-start-xxl-6{grid-column-start:6}.panel-grid .g-start-xxl-7{grid-column-start:7}.panel-grid .g-start-xxl-8{grid-column-start:8}.panel-grid .g-start-xxl-9{grid-column-start:9}.panel-grid .g-start-xxl-10{grid-column-start:10}.panel-grid .g-start-xxl-11{grid-column-start:11}.panel-grid .g-start-xxl-12{grid-column-start:12}.panel-grid .g-start-xxl-13{grid-column-start:13}.panel-grid .g-start-xxl-14{grid-column-start:14}.panel-grid .g-start-xxl-15{grid-column-start:15}.panel-grid .g-start-xxl-16{grid-column-start:16}.panel-grid .g-start-xxl-17{grid-column-start:17}.panel-grid .g-start-xxl-18{grid-column-start:18}.panel-grid .g-start-xxl-19{grid-column-start:19}.panel-grid .g-start-xxl-20{grid-column-start:20}.panel-grid .g-start-xxl-21{grid-column-start:21}.panel-grid .g-start-xxl-22{grid-column-start:22}.panel-grid .g-start-xxl-23{grid-column-start:23}}main{margin-top:1em;margin-bottom:1em}h1,.h1,h2,.h2{opacity:.9;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#bfbfbf}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,caption,.figure-caption{font-size:.9rem}.panel-caption,.figure-caption,figcaption{color:#bfbfbf}.table-caption,caption{color:#fff}.quarto-layout-cell[data-ref-parent] caption{color:#bfbfbf}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#bfbfbf;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse){padding-top:.5em;padding-bottom:.5em;display:block}.column-margin.column-container>*.collapse:not(.show){display:none}@media(min-width: 768px){.column-margin.column-container .callout-margin-content:first-child{margin-top:4.5em}.column-margin.column-container .callout-margin-content-simple:first-child{margin-top:3.5em}}.margin-caption>*{padding-top:.5em;padding-bottom:.5em}@media(max-width: 767.98px){.quarto-layout-row{flex-direction:column}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.tab-content{margin-top:0px;border-left:#dee2e6 1px solid;border-right:#dee2e6 1px solid;border-bottom:#dee2e6 1px solid;margin-left:0;padding:1em;margin-bottom:1em}@media(max-width: 767.98px){.layout-sidebar{margin-left:0;margin-right:0}}.panel-sidebar,.panel-sidebar .form-control,.panel-input,.panel-input .form-control,.selectize-dropdown{font-size:.9rem}.panel-sidebar .form-control,.panel-input .form-control{padding-top:.1rem}.tab-pane div.sourceCode{margin-top:0px}.tab-pane>p{padding-top:1em}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.2);border:1px solid rgba(233,236,239,.2);border-radius:.25rem}pre.sourceCode{background-color:rgba(0,0,0,0)}pre.sourceCode{border:none;font-size:.875em;overflow:visible !important;padding:.4em}.callout pre.sourceCode{padding-left:0}div.sourceCode{overflow-y:hidden}.callout div.sourceCode{margin-left:initial}.blockquote{font-size:inherit;padding-left:1rem;padding-right:1.5rem;color:#bfbfbf}.blockquote h1:first-child,.blockquote .h1:first-child,.blockquote h2:first-child,.blockquote .h2:first-child,.blockquote h3:first-child,.blockquote .h3:first-child,.blockquote h4:first-child,.blockquote .h4:first-child,.blockquote h5:first-child,.blockquote .h5:first-child{margin-top:0}pre{background-color:initial;padding:initial;border:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#212121;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#quarto-embedded-source-code-modal>.modal-dialog{max-width:1000px;padding-left:1.75rem;padding-right:1.75rem}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body{padding:0}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body div.sourceCode{margin:0;padding:.2rem .2rem;border-radius:0px;border:none}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-header{padding:.7rem}.code-tools-button{font-size:1rem;padding:.15rem .15rem;margin-left:5px;color:#6c757d;background-color:rgba(0,0,0,0);transition:initial;cursor:pointer}.code-tools-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.sidebar{will-change:top;transition:top 200ms linear;position:sticky;overflow-y:auto;padding-top:1.2em;max-height:100vh}.sidebar.toc-left,.sidebar.margin-sidebar{top:0px;padding-top:1em}.sidebar.toc-left>*,.sidebar.margin-sidebar>*{padding-top:.5em}.sidebar.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:#6c757d;margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#75aadb}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.sidebar .quarto-alternate-formats a,.sidebar .quarto-alternate-notebooks a{text-decoration:none}.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#75aadb}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem;font-weight:400;margin-bottom:.5rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2{margin-top:1rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul,.sidebar nav[role=doc-toc] ul{padding-left:0;list-style:none;font-size:.875rem;font-weight:300}.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.sidebar nav[role=doc-toc]>ul li a{line-height:1.1rem;padding-bottom:.2rem;padding-top:.2rem;color:inherit}.sidebar nav[role=doc-toc] ul>li>ul>li>a{padding-left:1.2em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>a{padding-left:2.4em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>a{padding-left:3.6em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:4.8em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:6em}.sidebar nav[role=doc-toc] ul>li>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #75aadb;color:#75aadb !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#75aadb !important}kbd,.kbd{color:#fff;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}div.hanging-indent{margin-left:1em;text-indent:-1em}.citation a,.footnote-ref{text-decoration:none}.footnotes ol{padding-left:1em}.tippy-content>*{margin-bottom:.7em}.tippy-content>*:last-child{margin-bottom:0}.table a{word-break:break-word}.table>thead{border-top-width:1px;border-top-color:#dee2e6;border-bottom:1px solid #fff}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.callout.callout-style-simple{padding:.4em .7em;border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout.callout-style-default{border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout .callout-body-container{flex-grow:1}.callout.callout-style-simple .callout-body{font-size:.9rem;font-weight:400}.callout.callout-style-default .callout-body{font-size:.9rem;font-weight:400}.callout.callout-titled .callout-body{margin-top:.2em}.callout:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.callout-header{padding-top:.2em;margin-bottom:-0.2em}.callout.callout-style-simple>div.callout-header{border-bottom:none;font-size:.9rem;font-weight:600;opacity:75%}.callout.callout-style-default>div.callout-header{border-bottom:none;font-weight:600;opacity:85%;font-size:.9rem;padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body>:first-child{margin-top:.5em}.callout>div.callout-header[data-bs-toggle=collapse]{cursor:pointer}.callout.callout-style-default .callout-header[aria-expanded=false],.callout.callout-style-default .callout-header[aria-expanded=true]{padding-top:0px;margin-bottom:0px;align-items:center}.callout.callout-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){margin-bottom:.5rem}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .callout-body>div>:last-child{margin-bottom:.2rem}.callout.callout-style-simple .callout-icon::before,.callout.callout-style-simple .callout-toggle::before{height:1rem;width:1rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.callout.callout-style-default .callout-icon::before,.callout.callout-style-default .callout-toggle::before{height:.9rem;width:.9rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:.9rem .9rem}.callout.callout-style-default .callout-toggle::before{margin-top:5px}.callout .callout-btn-toggle .callout-toggle::before{transition:transform .2s linear}.callout .callout-header[aria-expanded=false] .callout-toggle::before{transform:rotate(-90deg)}.callout .callout-header[aria-expanded=true] .callout-toggle::before{transform:none}.callout.callout-style-simple:not(.no-icon) div.callout-icon-container{padding-top:.2em;padding-right:.55em}.callout.callout-style-default:not(.no-icon) div.callout-icon-container{padding-top:.1em;padding-right:.35em}.callout.callout-style-default:not(.no-icon) div.callout-title-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:#6c757d}div.callout.callout-style-default>.callout-header{background-color:#6c757d}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#0c2644}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#133707}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#4d2307}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#483417}div.callout-caution:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#4d0011}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important .callout-toggle::before{background-image:url('data:image/svg+xml,')}.quarto-toggle-container{display:flex;align-items:center}.quarto-reader-toggle .bi::before,.quarto-color-scheme-toggle .bi::before{display:inline-block;height:1rem;width:1rem;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.sidebar-navigation{padding-left:20px}.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.quarto-sidebar-toggle{border-color:#dee2e6;border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem;border-style:solid;border-width:1px;overflow:hidden;border-top-width:0px;padding-top:0px !important}.quarto-sidebar-toggle-title{cursor:pointer;padding-bottom:2px;margin-left:.25em;text-align:center;font-weight:400;font-size:.775em}#quarto-content .quarto-sidebar-toggle{background:#1d1d1d}#quarto-content .quarto-sidebar-toggle-title{color:#fff}.quarto-sidebar-toggle-icon{color:#dee2e6;margin-right:.5em;float:right;transition:transform .2s ease}.quarto-sidebar-toggle-icon::before{padding-top:5px}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-icon{transform:rotate(-180deg)}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-title{border-bottom:solid #dee2e6 1px}.quarto-sidebar-toggle-contents{background-color:#181818;padding-right:10px;padding-left:10px;margin-top:0px !important;transition:max-height .5s ease}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-contents{padding-top:1em;padding-bottom:10px}.quarto-sidebar-toggle:not(.expanded) .quarto-sidebar-toggle-contents{padding-top:0px !important;padding-bottom:0px}nav[role=doc-toc]{z-index:1020}#quarto-sidebar>*,nav[role=doc-toc]>*{transition:opacity .1s ease,border .1s ease}#quarto-sidebar.slow>*,nav[role=doc-toc].slow>*{transition:opacity .4s ease,border .4s ease}.quarto-color-scheme-toggle:not(.alternate).top-right .bi::before{background-image:url('data:image/svg+xml,')}.quarto-color-scheme-toggle.alternate.top-right .bi::before{background-image:url('data:image/svg+xml,')}#quarto-appendix.default{border-top:1px solid #dee2e6}#quarto-appendix.default{background-color:#181818;padding-top:1.5em;margin-top:2em;z-index:998}#quarto-appendix.default .quarto-appendix-heading{margin-top:0;line-height:1.4em;font-weight:600;opacity:.9;border-bottom:none;margin-bottom:0}#quarto-appendix.default .footnotes ol,#quarto-appendix.default .footnotes ol li>p:last-of-type,#quarto-appendix.default .quarto-appendix-contents>p:last-of-type{margin-bottom:0}#quarto-appendix.default .quarto-appendix-secondary-label{margin-bottom:.4em}#quarto-appendix.default .quarto-appendix-bibtex{font-size:.7em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-bibtex code.sourceCode{white-space:pre-wrap}#quarto-appendix.default .quarto-appendix-citeas{font-size:.9em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-heading{font-size:1em !important}#quarto-appendix.default *[role=doc-endnotes]>ol,#quarto-appendix.default .quarto-appendix-contents>*:not(h2):not(.h2){font-size:.9em}#quarto-appendix.default section{padding-bottom:1.5em}#quarto-appendix.default section *[role=doc-endnotes],#quarto-appendix.default section>*:not(a){opacity:.9;word-wrap:break-word}.btn.btn-quarto,div.cell-output-display .btn-quarto{color:#cbcccc;background-color:#373a3c;border-color:#373a3c}.btn.btn-quarto:hover,div.cell-output-display .btn-quarto:hover{color:#cbcccc;background-color:#555859;border-color:#4b4e50}.btn-check:focus+.btn.btn-quarto,.btn.btn-quarto:focus,.btn-check:focus+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:focus{color:#cbcccc;background-color:#555859;border-color:#4b4e50;box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn-check:checked+.btn.btn-quarto,.btn-check:active+.btn.btn-quarto,.btn.btn-quarto:active,.btn.btn-quarto.active,.show>.btn.btn-quarto.dropdown-toggle,.btn-check:checked+div.cell-output-display .btn-quarto,.btn-check:active+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:active,div.cell-output-display .btn-quarto.active,.show>div.cell-output-display .btn-quarto.dropdown-toggle{color:#fff;background-color:#5f6163;border-color:#4b4e50}.btn-check:checked+.btn.btn-quarto:focus,.btn-check:active+.btn.btn-quarto:focus,.btn.btn-quarto:active:focus,.btn.btn-quarto.active:focus,.show>.btn.btn-quarto.dropdown-toggle:focus,.btn-check:checked+div.cell-output-display .btn-quarto:focus,.btn-check:active+div.cell-output-display .btn-quarto:focus,div.cell-output-display .btn-quarto:active:focus,div.cell-output-display .btn-quarto.active:focus,.show>div.cell-output-display .btn-quarto.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn.btn-quarto:disabled,.btn.btn-quarto.disabled,div.cell-output-display .btn-quarto:disabled,div.cell-output-display .btn-quarto.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}nav.quarto-secondary-nav.color-navbar{background-color:#303030;color:#bdbdbd}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#bdbdbd}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:0}body.nav-sidebar #title-block-header{margin-block-end:0}}p.subtitle{margin-top:.25em;margin-bottom:.5em}code a:any-link{color:inherit;text-decoration-color:#6c757d}/*! dark */div.observablehq table thead tr th{background-color:var(--bs-body-bg)}input,button,select,optgroup,textarea{background-color:var(--bs-body-bg)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#181818}.code-annotation-gutter{background-color:rgba(233,236,239,.2)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:var(--bs-font-monospace);color:#e6e6e6;border:solid #e6e6e6 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#181818;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:var(--bs-font-monospace);color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#181818}.page-columns .column-screen-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#181818}.page-columns .column-screen-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#181818}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#181818}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#181818}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#181818}.page-columns .column-screen-inset-shaded{grid-column:page-start-inset/page-end-inset;padding:1em;background:#525252;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table>thead{border-top-width:0}.table>:not(caption)>*:not(:last-child)>*{border-bottom-color:#fff;border-bottom-style:solid;border-bottom-width:1px}.table>:not(:first-child){border-top:1px solid #fff;border-bottom:1px solid inherit}.table tbody{border-bottom-color:#fff}a.external:after{display:inline-block;height:.75rem;width:.75rem;margin-bottom:.15em;margin-left:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}div.sourceCode code a.external:after{content:none}a.external:after:hover{cursor:pointer}.quarto-ext-icon{display:inline-block;font-size:.75em;padding-left:.3em}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file,.code-with-filename .code-with-filename-file pre{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file,.quarto-dark .code-with-filename .code-with-filename-file pre{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#373a3c}.progress .progress-bar{font-size:8px;line-height:8px}.layout-example{background:#495057}.quarto-title-banner{margin-bottom:1em;color:#bdbdbd;background:#303030}.quarto-title-banner .code-tools-button{color:#8a8a8a}.quarto-title-banner .code-tools-button:hover{color:#bdbdbd}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;column-gap:.4em;padding-bottom:.5em;margin-top:.75em}.quarto-title .quarto-categories .quarto-category{padding:.25em .75em;font-size:.65em;text-transform:uppercase;border:solid 1px;border-radius:.25rem;opacity:.6}.quarto-title .quarto-categories .quarto-category a{color:inherit}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:repeat(2, 1fr)}#title-block-header.quarto-title-block.default .quarto-title .title{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-author-orcid img{margin-top:-5px}#title-block-header.quarto-title-block.default .quarto-description p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p,#title-block-header.quarto-title-block.default .quarto-title-authors p,#title-block-header.quarto-title-block.default .quarto-title-affiliations p{margin-bottom:.1em}#title-block-header.quarto-title-block.default .quarto-title-meta-heading{text-transform:uppercase;margin-top:1em;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-contents{font-size:.9em}#title-block-header.quarto-title-block.default .quarto-title-meta-contents a{color:#fff}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p.affiliation:last-of-type{margin-bottom:.7em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .description,#title-block-header.quarto-title-block.default .abstract{margin-top:0}#title-block-header.quarto-title-block.default .description>p,#title-block-header.quarto-title-block.default .abstract>p{font-size:.9em}#title-block-header.quarto-title-block.default .description>p:last-of-type,#title-block-header.quarto-title-block.default .abstract>p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .description .abstract-title,#title-block-header.quarto-title-block.default .abstract .abstract-title{margin-top:1em;text-transform:uppercase;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-author{display:grid;grid-template-columns:1fr 1fr}.quarto-title-tools-only{display:flex;justify-content:right}/*# sourceMappingURL=4bfef878f23bf82584b07c8278ef38b6.css.map */ diff --git a/pr-preview/pr-55/site_libs/bootstrap/bootstrap-icons.css b/site_libs/bootstrap/bootstrap-icons.css similarity index 100% rename from pr-preview/pr-55/site_libs/bootstrap/bootstrap-icons.css rename to site_libs/bootstrap/bootstrap-icons.css diff --git a/pr-preview/pr-55/site_libs/bootstrap/bootstrap-icons.woff b/site_libs/bootstrap/bootstrap-icons.woff similarity index 100% rename from pr-preview/pr-55/site_libs/bootstrap/bootstrap-icons.woff rename to site_libs/bootstrap/bootstrap-icons.woff diff --git a/pr-preview/pr-55/site_libs/bootstrap/bootstrap.min.css b/site_libs/bootstrap/bootstrap.min.css similarity index 99% rename from pr-preview/pr-55/site_libs/bootstrap/bootstrap.min.css rename to site_libs/bootstrap/bootstrap.min.css index 8a75a25..5c332d7 100644 --- a/pr-preview/pr-55/site_libs/bootstrap/bootstrap.min.css +++ b/site_libs/bootstrap/bootstrap.min.css @@ -7,4 +7,4 @@ * * ansi colors from IPython notebook's * -*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-fg{color:#282c36}.ansi-black-intense-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-fg{color:#b22b31}.ansi-red-intense-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-fg{color:#007427}.ansi-green-intense-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-fg{color:#b27d12}.ansi-yellow-intense-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-fg{color:#0065ca}.ansi-blue-intense-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-fg{color:#a03196}.ansi-magenta-intense-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-fg{color:#258f8f}.ansi-cyan-intense-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-fg{color:#a1a6b2}.ansi-white-intense-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #373a3c;--quarto-text-muted: #6c757d;--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.25rem}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:absolute;right:.5em;left:inherit;background-color:rgba(0,0,0,0)}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #373a3c;--mermaid-node-fg-color: #373a3c;--mermaid-fg-color: #373a3c;--mermaid-fg-color--lighter: #4f5457;--mermaid-fg-color--lightest: #686d71;--mermaid-font-family: Source Sans Pro, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2780e3;--mermaid-node-bg-color: rgba(39, 128, 227, 0.1);--mermaid-node-fg-color: #373a3c}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button:focus{outline:none}.code-copy-button-tooltip{font-size:.75em}.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}main ol ol,main ul ul,main ol ul,main ul ol{margin-bottom:1em}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}body{margin:0}main.page-columns>header>h1.title,main.page-columns>header>.title.h1{margin-bottom:0}@media(min-width: 992px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] 35px [page-end-inset page-end] 5fr [screen-end-inset] 1.5em}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 56px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 3em [body-end] 80px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(80px, 160px) [page-end] 1fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 150px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 991.98px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 1250px - 3em )) [body-content-end body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1.5em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 767.98px){body .page-columns,body.fullcontent:not(.floating):not(.docked) .page-columns,body.slimcontent:not(.floating):not(.docked) .page-columns,body.docked .page-columns,body.docked.slimcontent .page-columns,body.docked.fullcontent .page-columns,body.floating .page-columns,body.floating.slimcontent .page-columns,body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}nav[role=doc-toc]{display:none}}body,.page-row-navigation{grid-template-rows:[page-top] max-content [contents-top] max-content [contents-bottom] max-content [page-bottom]}.page-rows-contents{grid-template-rows:[content-top] minmax(max-content, 1fr) [content-bottom] minmax(60px, max-content) [page-bottom]}.page-full{grid-column:screen-start/screen-end !important}.page-columns>*{grid-column:body-content-start/body-content-end}.page-columns.column-page>*{grid-column:page-start/page-end}.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right>*{grid-column:body-content-start/page-end}.page-rows{grid-auto-rows:auto}.header{grid-column:screen-start/screen-end;grid-row:page-top/contents-top}#quarto-content{padding:0;grid-column:screen-start/screen-end;grid-row:contents-top/contents-bottom}body.floating .sidebar.sidebar-navigation{grid-column:page-start/body-start;grid-row:content-top/page-bottom}body.docked .sidebar.sidebar-navigation{grid-column:screen-start/body-start;grid-row:content-top/page-bottom}.sidebar.toc-left{grid-column:page-start/body-start;grid-row:content-top/page-bottom}.sidebar.margin-sidebar{grid-column:body-end/page-end;grid-row:content-top/page-bottom}.page-columns .content{grid-column:body-content-start/body-content-end;grid-row:content-top/content-bottom;align-content:flex-start}.page-columns .page-navigation{grid-column:body-content-start/body-content-end;grid-row:content-bottom/page-bottom}.page-columns .footer{grid-column:screen-start/screen-end;grid-row:contents-bottom/page-bottom}.page-columns .column-body{grid-column:body-content-start/body-content-end}.page-columns .column-body-fullbleed{grid-column:body-start/body-end}.page-columns .column-body-outset{grid-column:body-start-outset/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset table{background:#fff}.page-columns .column-body-outset-left{grid-column:body-start-outset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-left table{background:#fff}.page-columns .column-body-outset-right{grid-column:body-content-start/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-right table{background:#fff}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page table{background:#fff}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset table{background:#fff}.page-columns .column-page-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-left table{background:#fff}.page-columns .column-page-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-right figcaption table{background:#fff}.page-columns .column-page-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-left table{background:#fff}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-right figcaption table{background:#fff}#quarto-content.page-columns #quarto-margin-sidebar,#quarto-content.page-columns #quarto-sidebar{z-index:1}@media(max-width: 991.98px){#quarto-content.page-columns #quarto-margin-sidebar.collapse,#quarto-content.page-columns #quarto-sidebar.collapse,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{z-index:1055}}#quarto-content.page-columns main.column-page,#quarto-content.page-columns main.column-page-right,#quarto-content.page-columns main.column-page-left{z-index:0}.page-columns .column-screen-inset{grid-column:screen-start-inset/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:screen-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#f8f9fa;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}.zindex-content{z-index:998;transform:translate3d(0, 0, 0)}.zindex-modal{z-index:1055;transform:translate3d(0, 0, 0)}.zindex-over-content{z-index:999;transform:translate3d(0, 0, 0)}img.img-fluid.column-screen,img.img-fluid.column-screen-inset-shaded,img.img-fluid.column-screen-inset,img.img-fluid.column-screen-inset-left,img.img-fluid.column-screen-inset-right,img.img-fluid.column-screen-left,img.img-fluid.column-screen-right{width:100%}@media(min-width: 992px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.column-sidebar{grid-column:page-start/body-start !important;z-index:998}.column-leftmargin{grid-column:screen-start-inset/body-start !important;z-index:998}.no-row-height{height:1em;overflow:visible}}@media(max-width: 991.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.no-row-height{height:1em;overflow:visible}.page-columns.page-full{overflow:visible}.page-columns.toc-left .margin-caption,.page-columns.toc-left div.aside,.page-columns.toc-left aside,.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{display:none}.hidden-sm{display:none}}.panel-grid{display:grid;grid-template-rows:repeat(1, 1fr);grid-template-columns:repeat(24, 1fr);gap:1em}.panel-grid .g-col-1{grid-column:auto/span 1}.panel-grid .g-col-2{grid-column:auto/span 2}.panel-grid .g-col-3{grid-column:auto/span 3}.panel-grid .g-col-4{grid-column:auto/span 4}.panel-grid .g-col-5{grid-column:auto/span 5}.panel-grid .g-col-6{grid-column:auto/span 6}.panel-grid .g-col-7{grid-column:auto/span 7}.panel-grid .g-col-8{grid-column:auto/span 8}.panel-grid .g-col-9{grid-column:auto/span 9}.panel-grid .g-col-10{grid-column:auto/span 10}.panel-grid .g-col-11{grid-column:auto/span 11}.panel-grid .g-col-12{grid-column:auto/span 12}.panel-grid .g-col-13{grid-column:auto/span 13}.panel-grid .g-col-14{grid-column:auto/span 14}.panel-grid .g-col-15{grid-column:auto/span 15}.panel-grid .g-col-16{grid-column:auto/span 16}.panel-grid .g-col-17{grid-column:auto/span 17}.panel-grid .g-col-18{grid-column:auto/span 18}.panel-grid .g-col-19{grid-column:auto/span 19}.panel-grid .g-col-20{grid-column:auto/span 20}.panel-grid .g-col-21{grid-column:auto/span 21}.panel-grid .g-col-22{grid-column:auto/span 22}.panel-grid .g-col-23{grid-column:auto/span 23}.panel-grid .g-col-24{grid-column:auto/span 24}.panel-grid .g-start-1{grid-column-start:1}.panel-grid .g-start-2{grid-column-start:2}.panel-grid .g-start-3{grid-column-start:3}.panel-grid .g-start-4{grid-column-start:4}.panel-grid .g-start-5{grid-column-start:5}.panel-grid .g-start-6{grid-column-start:6}.panel-grid .g-start-7{grid-column-start:7}.panel-grid .g-start-8{grid-column-start:8}.panel-grid .g-start-9{grid-column-start:9}.panel-grid .g-start-10{grid-column-start:10}.panel-grid .g-start-11{grid-column-start:11}.panel-grid .g-start-12{grid-column-start:12}.panel-grid .g-start-13{grid-column-start:13}.panel-grid .g-start-14{grid-column-start:14}.panel-grid .g-start-15{grid-column-start:15}.panel-grid .g-start-16{grid-column-start:16}.panel-grid .g-start-17{grid-column-start:17}.panel-grid .g-start-18{grid-column-start:18}.panel-grid .g-start-19{grid-column-start:19}.panel-grid .g-start-20{grid-column-start:20}.panel-grid .g-start-21{grid-column-start:21}.panel-grid .g-start-22{grid-column-start:22}.panel-grid .g-start-23{grid-column-start:23}@media(min-width: 576px){.panel-grid .g-col-sm-1{grid-column:auto/span 1}.panel-grid .g-col-sm-2{grid-column:auto/span 2}.panel-grid .g-col-sm-3{grid-column:auto/span 3}.panel-grid .g-col-sm-4{grid-column:auto/span 4}.panel-grid .g-col-sm-5{grid-column:auto/span 5}.panel-grid .g-col-sm-6{grid-column:auto/span 6}.panel-grid .g-col-sm-7{grid-column:auto/span 7}.panel-grid .g-col-sm-8{grid-column:auto/span 8}.panel-grid .g-col-sm-9{grid-column:auto/span 9}.panel-grid .g-col-sm-10{grid-column:auto/span 10}.panel-grid .g-col-sm-11{grid-column:auto/span 11}.panel-grid .g-col-sm-12{grid-column:auto/span 12}.panel-grid .g-col-sm-13{grid-column:auto/span 13}.panel-grid .g-col-sm-14{grid-column:auto/span 14}.panel-grid .g-col-sm-15{grid-column:auto/span 15}.panel-grid .g-col-sm-16{grid-column:auto/span 16}.panel-grid .g-col-sm-17{grid-column:auto/span 17}.panel-grid .g-col-sm-18{grid-column:auto/span 18}.panel-grid .g-col-sm-19{grid-column:auto/span 19}.panel-grid .g-col-sm-20{grid-column:auto/span 20}.panel-grid .g-col-sm-21{grid-column:auto/span 21}.panel-grid .g-col-sm-22{grid-column:auto/span 22}.panel-grid .g-col-sm-23{grid-column:auto/span 23}.panel-grid .g-col-sm-24{grid-column:auto/span 24}.panel-grid .g-start-sm-1{grid-column-start:1}.panel-grid .g-start-sm-2{grid-column-start:2}.panel-grid .g-start-sm-3{grid-column-start:3}.panel-grid .g-start-sm-4{grid-column-start:4}.panel-grid .g-start-sm-5{grid-column-start:5}.panel-grid .g-start-sm-6{grid-column-start:6}.panel-grid .g-start-sm-7{grid-column-start:7}.panel-grid .g-start-sm-8{grid-column-start:8}.panel-grid .g-start-sm-9{grid-column-start:9}.panel-grid .g-start-sm-10{grid-column-start:10}.panel-grid .g-start-sm-11{grid-column-start:11}.panel-grid .g-start-sm-12{grid-column-start:12}.panel-grid .g-start-sm-13{grid-column-start:13}.panel-grid .g-start-sm-14{grid-column-start:14}.panel-grid .g-start-sm-15{grid-column-start:15}.panel-grid .g-start-sm-16{grid-column-start:16}.panel-grid .g-start-sm-17{grid-column-start:17}.panel-grid .g-start-sm-18{grid-column-start:18}.panel-grid .g-start-sm-19{grid-column-start:19}.panel-grid .g-start-sm-20{grid-column-start:20}.panel-grid .g-start-sm-21{grid-column-start:21}.panel-grid .g-start-sm-22{grid-column-start:22}.panel-grid .g-start-sm-23{grid-column-start:23}}@media(min-width: 768px){.panel-grid .g-col-md-1{grid-column:auto/span 1}.panel-grid .g-col-md-2{grid-column:auto/span 2}.panel-grid .g-col-md-3{grid-column:auto/span 3}.panel-grid .g-col-md-4{grid-column:auto/span 4}.panel-grid .g-col-md-5{grid-column:auto/span 5}.panel-grid .g-col-md-6{grid-column:auto/span 6}.panel-grid .g-col-md-7{grid-column:auto/span 7}.panel-grid .g-col-md-8{grid-column:auto/span 8}.panel-grid .g-col-md-9{grid-column:auto/span 9}.panel-grid .g-col-md-10{grid-column:auto/span 10}.panel-grid .g-col-md-11{grid-column:auto/span 11}.panel-grid .g-col-md-12{grid-column:auto/span 12}.panel-grid .g-col-md-13{grid-column:auto/span 13}.panel-grid .g-col-md-14{grid-column:auto/span 14}.panel-grid .g-col-md-15{grid-column:auto/span 15}.panel-grid .g-col-md-16{grid-column:auto/span 16}.panel-grid .g-col-md-17{grid-column:auto/span 17}.panel-grid .g-col-md-18{grid-column:auto/span 18}.panel-grid .g-col-md-19{grid-column:auto/span 19}.panel-grid .g-col-md-20{grid-column:auto/span 20}.panel-grid .g-col-md-21{grid-column:auto/span 21}.panel-grid .g-col-md-22{grid-column:auto/span 22}.panel-grid .g-col-md-23{grid-column:auto/span 23}.panel-grid .g-col-md-24{grid-column:auto/span 24}.panel-grid .g-start-md-1{grid-column-start:1}.panel-grid .g-start-md-2{grid-column-start:2}.panel-grid .g-start-md-3{grid-column-start:3}.panel-grid .g-start-md-4{grid-column-start:4}.panel-grid .g-start-md-5{grid-column-start:5}.panel-grid .g-start-md-6{grid-column-start:6}.panel-grid .g-start-md-7{grid-column-start:7}.panel-grid .g-start-md-8{grid-column-start:8}.panel-grid .g-start-md-9{grid-column-start:9}.panel-grid .g-start-md-10{grid-column-start:10}.panel-grid .g-start-md-11{grid-column-start:11}.panel-grid .g-start-md-12{grid-column-start:12}.panel-grid .g-start-md-13{grid-column-start:13}.panel-grid .g-start-md-14{grid-column-start:14}.panel-grid .g-start-md-15{grid-column-start:15}.panel-grid .g-start-md-16{grid-column-start:16}.panel-grid .g-start-md-17{grid-column-start:17}.panel-grid .g-start-md-18{grid-column-start:18}.panel-grid .g-start-md-19{grid-column-start:19}.panel-grid .g-start-md-20{grid-column-start:20}.panel-grid .g-start-md-21{grid-column-start:21}.panel-grid .g-start-md-22{grid-column-start:22}.panel-grid .g-start-md-23{grid-column-start:23}}@media(min-width: 992px){.panel-grid .g-col-lg-1{grid-column:auto/span 1}.panel-grid .g-col-lg-2{grid-column:auto/span 2}.panel-grid .g-col-lg-3{grid-column:auto/span 3}.panel-grid .g-col-lg-4{grid-column:auto/span 4}.panel-grid .g-col-lg-5{grid-column:auto/span 5}.panel-grid .g-col-lg-6{grid-column:auto/span 6}.panel-grid .g-col-lg-7{grid-column:auto/span 7}.panel-grid .g-col-lg-8{grid-column:auto/span 8}.panel-grid .g-col-lg-9{grid-column:auto/span 9}.panel-grid .g-col-lg-10{grid-column:auto/span 10}.panel-grid .g-col-lg-11{grid-column:auto/span 11}.panel-grid .g-col-lg-12{grid-column:auto/span 12}.panel-grid .g-col-lg-13{grid-column:auto/span 13}.panel-grid .g-col-lg-14{grid-column:auto/span 14}.panel-grid .g-col-lg-15{grid-column:auto/span 15}.panel-grid .g-col-lg-16{grid-column:auto/span 16}.panel-grid .g-col-lg-17{grid-column:auto/span 17}.panel-grid .g-col-lg-18{grid-column:auto/span 18}.panel-grid .g-col-lg-19{grid-column:auto/span 19}.panel-grid .g-col-lg-20{grid-column:auto/span 20}.panel-grid .g-col-lg-21{grid-column:auto/span 21}.panel-grid .g-col-lg-22{grid-column:auto/span 22}.panel-grid .g-col-lg-23{grid-column:auto/span 23}.panel-grid .g-col-lg-24{grid-column:auto/span 24}.panel-grid .g-start-lg-1{grid-column-start:1}.panel-grid .g-start-lg-2{grid-column-start:2}.panel-grid .g-start-lg-3{grid-column-start:3}.panel-grid .g-start-lg-4{grid-column-start:4}.panel-grid .g-start-lg-5{grid-column-start:5}.panel-grid .g-start-lg-6{grid-column-start:6}.panel-grid .g-start-lg-7{grid-column-start:7}.panel-grid .g-start-lg-8{grid-column-start:8}.panel-grid .g-start-lg-9{grid-column-start:9}.panel-grid .g-start-lg-10{grid-column-start:10}.panel-grid .g-start-lg-11{grid-column-start:11}.panel-grid .g-start-lg-12{grid-column-start:12}.panel-grid .g-start-lg-13{grid-column-start:13}.panel-grid .g-start-lg-14{grid-column-start:14}.panel-grid .g-start-lg-15{grid-column-start:15}.panel-grid .g-start-lg-16{grid-column-start:16}.panel-grid .g-start-lg-17{grid-column-start:17}.panel-grid .g-start-lg-18{grid-column-start:18}.panel-grid .g-start-lg-19{grid-column-start:19}.panel-grid .g-start-lg-20{grid-column-start:20}.panel-grid .g-start-lg-21{grid-column-start:21}.panel-grid .g-start-lg-22{grid-column-start:22}.panel-grid .g-start-lg-23{grid-column-start:23}}@media(min-width: 1200px){.panel-grid .g-col-xl-1{grid-column:auto/span 1}.panel-grid .g-col-xl-2{grid-column:auto/span 2}.panel-grid .g-col-xl-3{grid-column:auto/span 3}.panel-grid .g-col-xl-4{grid-column:auto/span 4}.panel-grid .g-col-xl-5{grid-column:auto/span 5}.panel-grid .g-col-xl-6{grid-column:auto/span 6}.panel-grid .g-col-xl-7{grid-column:auto/span 7}.panel-grid .g-col-xl-8{grid-column:auto/span 8}.panel-grid .g-col-xl-9{grid-column:auto/span 9}.panel-grid .g-col-xl-10{grid-column:auto/span 10}.panel-grid .g-col-xl-11{grid-column:auto/span 11}.panel-grid .g-col-xl-12{grid-column:auto/span 12}.panel-grid .g-col-xl-13{grid-column:auto/span 13}.panel-grid .g-col-xl-14{grid-column:auto/span 14}.panel-grid .g-col-xl-15{grid-column:auto/span 15}.panel-grid .g-col-xl-16{grid-column:auto/span 16}.panel-grid .g-col-xl-17{grid-column:auto/span 17}.panel-grid .g-col-xl-18{grid-column:auto/span 18}.panel-grid .g-col-xl-19{grid-column:auto/span 19}.panel-grid .g-col-xl-20{grid-column:auto/span 20}.panel-grid .g-col-xl-21{grid-column:auto/span 21}.panel-grid .g-col-xl-22{grid-column:auto/span 22}.panel-grid .g-col-xl-23{grid-column:auto/span 23}.panel-grid .g-col-xl-24{grid-column:auto/span 24}.panel-grid .g-start-xl-1{grid-column-start:1}.panel-grid .g-start-xl-2{grid-column-start:2}.panel-grid .g-start-xl-3{grid-column-start:3}.panel-grid .g-start-xl-4{grid-column-start:4}.panel-grid .g-start-xl-5{grid-column-start:5}.panel-grid .g-start-xl-6{grid-column-start:6}.panel-grid .g-start-xl-7{grid-column-start:7}.panel-grid .g-start-xl-8{grid-column-start:8}.panel-grid .g-start-xl-9{grid-column-start:9}.panel-grid .g-start-xl-10{grid-column-start:10}.panel-grid .g-start-xl-11{grid-column-start:11}.panel-grid .g-start-xl-12{grid-column-start:12}.panel-grid .g-start-xl-13{grid-column-start:13}.panel-grid .g-start-xl-14{grid-column-start:14}.panel-grid .g-start-xl-15{grid-column-start:15}.panel-grid .g-start-xl-16{grid-column-start:16}.panel-grid .g-start-xl-17{grid-column-start:17}.panel-grid .g-start-xl-18{grid-column-start:18}.panel-grid .g-start-xl-19{grid-column-start:19}.panel-grid .g-start-xl-20{grid-column-start:20}.panel-grid .g-start-xl-21{grid-column-start:21}.panel-grid .g-start-xl-22{grid-column-start:22}.panel-grid .g-start-xl-23{grid-column-start:23}}@media(min-width: 1400px){.panel-grid .g-col-xxl-1{grid-column:auto/span 1}.panel-grid .g-col-xxl-2{grid-column:auto/span 2}.panel-grid .g-col-xxl-3{grid-column:auto/span 3}.panel-grid .g-col-xxl-4{grid-column:auto/span 4}.panel-grid .g-col-xxl-5{grid-column:auto/span 5}.panel-grid .g-col-xxl-6{grid-column:auto/span 6}.panel-grid .g-col-xxl-7{grid-column:auto/span 7}.panel-grid .g-col-xxl-8{grid-column:auto/span 8}.panel-grid .g-col-xxl-9{grid-column:auto/span 9}.panel-grid .g-col-xxl-10{grid-column:auto/span 10}.panel-grid .g-col-xxl-11{grid-column:auto/span 11}.panel-grid .g-col-xxl-12{grid-column:auto/span 12}.panel-grid .g-col-xxl-13{grid-column:auto/span 13}.panel-grid .g-col-xxl-14{grid-column:auto/span 14}.panel-grid .g-col-xxl-15{grid-column:auto/span 15}.panel-grid .g-col-xxl-16{grid-column:auto/span 16}.panel-grid .g-col-xxl-17{grid-column:auto/span 17}.panel-grid .g-col-xxl-18{grid-column:auto/span 18}.panel-grid .g-col-xxl-19{grid-column:auto/span 19}.panel-grid .g-col-xxl-20{grid-column:auto/span 20}.panel-grid .g-col-xxl-21{grid-column:auto/span 21}.panel-grid .g-col-xxl-22{grid-column:auto/span 22}.panel-grid .g-col-xxl-23{grid-column:auto/span 23}.panel-grid .g-col-xxl-24{grid-column:auto/span 24}.panel-grid .g-start-xxl-1{grid-column-start:1}.panel-grid .g-start-xxl-2{grid-column-start:2}.panel-grid .g-start-xxl-3{grid-column-start:3}.panel-grid .g-start-xxl-4{grid-column-start:4}.panel-grid .g-start-xxl-5{grid-column-start:5}.panel-grid .g-start-xxl-6{grid-column-start:6}.panel-grid .g-start-xxl-7{grid-column-start:7}.panel-grid .g-start-xxl-8{grid-column-start:8}.panel-grid .g-start-xxl-9{grid-column-start:9}.panel-grid .g-start-xxl-10{grid-column-start:10}.panel-grid .g-start-xxl-11{grid-column-start:11}.panel-grid .g-start-xxl-12{grid-column-start:12}.panel-grid .g-start-xxl-13{grid-column-start:13}.panel-grid .g-start-xxl-14{grid-column-start:14}.panel-grid .g-start-xxl-15{grid-column-start:15}.panel-grid .g-start-xxl-16{grid-column-start:16}.panel-grid .g-start-xxl-17{grid-column-start:17}.panel-grid .g-start-xxl-18{grid-column-start:18}.panel-grid .g-start-xxl-19{grid-column-start:19}.panel-grid .g-start-xxl-20{grid-column-start:20}.panel-grid .g-start-xxl-21{grid-column-start:21}.panel-grid .g-start-xxl-22{grid-column-start:22}.panel-grid .g-start-xxl-23{grid-column-start:23}}main{margin-top:1em;margin-bottom:1em}h1,.h1,h2,.h2{opacity:.9;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#747a7f}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,caption,.figure-caption{font-size:.9rem}.panel-caption,.figure-caption,figcaption{color:#747a7f}.table-caption,caption{color:#373a3c}.quarto-layout-cell[data-ref-parent] caption{color:#747a7f}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#747a7f;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse){padding-top:.5em;padding-bottom:.5em;display:block}.column-margin.column-container>*.collapse:not(.show){display:none}@media(min-width: 768px){.column-margin.column-container .callout-margin-content:first-child{margin-top:4.5em}.column-margin.column-container .callout-margin-content-simple:first-child{margin-top:3.5em}}.margin-caption>*{padding-top:.5em;padding-bottom:.5em}@media(max-width: 767.98px){.quarto-layout-row{flex-direction:column}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.tab-content{margin-top:0px;border-left:#dee2e6 1px solid;border-right:#dee2e6 1px solid;border-bottom:#dee2e6 1px solid;margin-left:0;padding:1em;margin-bottom:1em}@media(max-width: 767.98px){.layout-sidebar{margin-left:0;margin-right:0}}.panel-sidebar,.panel-sidebar .form-control,.panel-input,.panel-input .form-control,.selectize-dropdown{font-size:.9rem}.panel-sidebar .form-control,.panel-input .form-control{padding-top:.1rem}.tab-pane div.sourceCode{margin-top:0px}.tab-pane>p{padding-top:1em}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.65);border:1px solid rgba(233,236,239,.65);border-radius:.25rem}pre.sourceCode{background-color:rgba(0,0,0,0)}pre.sourceCode{border:none;font-size:.875em;overflow:visible !important;padding:.4em}.callout pre.sourceCode{padding-left:0}div.sourceCode{overflow-y:hidden}.callout div.sourceCode{margin-left:initial}.blockquote{font-size:inherit;padding-left:1rem;padding-right:1.5rem;color:#747a7f}.blockquote h1:first-child,.blockquote .h1:first-child,.blockquote h2:first-child,.blockquote .h2:first-child,.blockquote h3:first-child,.blockquote .h3:first-child,.blockquote h4:first-child,.blockquote .h4:first-child,.blockquote h5:first-child,.blockquote .h5:first-child{margin-top:0}pre{background-color:initial;padding:initial;border:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#f7f7f7;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#quarto-embedded-source-code-modal>.modal-dialog{max-width:1000px;padding-left:1.75rem;padding-right:1.75rem}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body{padding:0}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body div.sourceCode{margin:0;padding:.2rem .2rem;border-radius:0px;border:none}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-header{padding:.7rem}.code-tools-button{font-size:1rem;padding:.15rem .15rem;margin-left:5px;color:#6c757d;background-color:rgba(0,0,0,0);transition:initial;cursor:pointer}.code-tools-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.sidebar{will-change:top;transition:top 200ms linear;position:sticky;overflow-y:auto;padding-top:1.2em;max-height:100vh}.sidebar.toc-left,.sidebar.margin-sidebar{top:0px;padding-top:1em}.sidebar.toc-left>*,.sidebar.margin-sidebar>*{padding-top:.5em}.sidebar.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:#6c757d;margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#2780e3}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.sidebar .quarto-alternate-formats a,.sidebar .quarto-alternate-notebooks a{text-decoration:none}.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#2780e3}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem;font-weight:400;margin-bottom:.5rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2{margin-top:1rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul,.sidebar nav[role=doc-toc] ul{padding-left:0;list-style:none;font-size:.875rem;font-weight:300}.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.sidebar nav[role=doc-toc]>ul li a{line-height:1.1rem;padding-bottom:.2rem;padding-top:.2rem;color:inherit}.sidebar nav[role=doc-toc] ul>li>ul>li>a{padding-left:1.2em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>a{padding-left:2.4em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>a{padding-left:3.6em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:4.8em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:6em}.sidebar nav[role=doc-toc] ul>li>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #2780e3;color:#2780e3 !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#2780e3 !important}kbd,.kbd{color:#373a3c;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}div.hanging-indent{margin-left:1em;text-indent:-1em}.citation a,.footnote-ref{text-decoration:none}.footnotes ol{padding-left:1em}.tippy-content>*{margin-bottom:.7em}.tippy-content>*:last-child{margin-bottom:0}.table a{word-break:break-word}.table>thead{border-top-width:1px;border-top-color:#dee2e6;border-bottom:1px solid #b6babc}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.callout.callout-style-simple{padding:.4em .7em;border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout.callout-style-default{border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout .callout-body-container{flex-grow:1}.callout.callout-style-simple .callout-body{font-size:.9rem;font-weight:400}.callout.callout-style-default .callout-body{font-size:.9rem;font-weight:400}.callout.callout-titled .callout-body{margin-top:.2em}.callout:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.callout-header{padding-top:.2em;margin-bottom:-0.2em}.callout.callout-style-simple>div.callout-header{border-bottom:none;font-size:.9rem;font-weight:600;opacity:75%}.callout.callout-style-default>div.callout-header{border-bottom:none;font-weight:600;opacity:85%;font-size:.9rem;padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body>:first-child{margin-top:.5em}.callout>div.callout-header[data-bs-toggle=collapse]{cursor:pointer}.callout.callout-style-default .callout-header[aria-expanded=false],.callout.callout-style-default .callout-header[aria-expanded=true]{padding-top:0px;margin-bottom:0px;align-items:center}.callout.callout-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){margin-bottom:.5rem}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .callout-body>div>:last-child{margin-bottom:.2rem}.callout.callout-style-simple .callout-icon::before,.callout.callout-style-simple .callout-toggle::before{height:1rem;width:1rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.callout.callout-style-default .callout-icon::before,.callout.callout-style-default .callout-toggle::before{height:.9rem;width:.9rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:.9rem .9rem}.callout.callout-style-default .callout-toggle::before{margin-top:5px}.callout .callout-btn-toggle .callout-toggle::before{transition:transform .2s linear}.callout .callout-header[aria-expanded=false] .callout-toggle::before{transform:rotate(-90deg)}.callout .callout-header[aria-expanded=true] .callout-toggle::before{transform:none}.callout.callout-style-simple:not(.no-icon) div.callout-icon-container{padding-top:.2em;padding-right:.55em}.callout.callout-style-default:not(.no-icon) div.callout-icon-container{padding-top:.1em;padding-right:.35em}.callout.callout-style-default:not(.no-icon) div.callout-title-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:#6c757d}div.callout.callout-style-default>.callout-header{background-color:#6c757d}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#e9f2fc}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#ecf8e8}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#fff1e8}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#fef7ed}div.callout-caution:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#ffe6eb}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important .callout-toggle::before{background-image:url('data:image/svg+xml,')}.quarto-toggle-container{display:flex;align-items:center}.quarto-reader-toggle .bi::before,.quarto-color-scheme-toggle .bi::before{display:inline-block;height:1rem;width:1rem;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.sidebar-navigation{padding-left:20px}.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.quarto-sidebar-toggle{border-color:#dee2e6;border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem;border-style:solid;border-width:1px;overflow:hidden;border-top-width:0px;padding-top:0px !important}.quarto-sidebar-toggle-title{cursor:pointer;padding-bottom:2px;margin-left:.25em;text-align:center;font-weight:400;font-size:.775em}#quarto-content .quarto-sidebar-toggle{background:#fafafa}#quarto-content .quarto-sidebar-toggle-title{color:#373a3c}.quarto-sidebar-toggle-icon{color:#dee2e6;margin-right:.5em;float:right;transition:transform .2s ease}.quarto-sidebar-toggle-icon::before{padding-top:5px}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-icon{transform:rotate(-180deg)}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-title{border-bottom:solid #dee2e6 1px}.quarto-sidebar-toggle-contents{background-color:#fff;padding-right:10px;padding-left:10px;margin-top:0px !important;transition:max-height .5s ease}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-contents{padding-top:1em;padding-bottom:10px}.quarto-sidebar-toggle:not(.expanded) .quarto-sidebar-toggle-contents{padding-top:0px !important;padding-bottom:0px}nav[role=doc-toc]{z-index:1020}#quarto-sidebar>*,nav[role=doc-toc]>*{transition:opacity .1s ease,border .1s ease}#quarto-sidebar.slow>*,nav[role=doc-toc].slow>*{transition:opacity .4s ease,border .4s ease}.quarto-color-scheme-toggle:not(.alternate).top-right .bi::before{background-image:url('data:image/svg+xml,')}.quarto-color-scheme-toggle.alternate.top-right .bi::before{background-image:url('data:image/svg+xml,')}#quarto-appendix.default{border-top:1px solid #dee2e6}#quarto-appendix.default{background-color:#fff;padding-top:1.5em;margin-top:2em;z-index:998}#quarto-appendix.default .quarto-appendix-heading{margin-top:0;line-height:1.4em;font-weight:600;opacity:.9;border-bottom:none;margin-bottom:0}#quarto-appendix.default .footnotes ol,#quarto-appendix.default .footnotes ol li>p:last-of-type,#quarto-appendix.default .quarto-appendix-contents>p:last-of-type{margin-bottom:0}#quarto-appendix.default .quarto-appendix-secondary-label{margin-bottom:.4em}#quarto-appendix.default .quarto-appendix-bibtex{font-size:.7em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-bibtex code.sourceCode{white-space:pre-wrap}#quarto-appendix.default .quarto-appendix-citeas{font-size:.9em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-heading{font-size:1em !important}#quarto-appendix.default *[role=doc-endnotes]>ol,#quarto-appendix.default .quarto-appendix-contents>*:not(h2):not(.h2){font-size:.9em}#quarto-appendix.default section{padding-bottom:1.5em}#quarto-appendix.default section *[role=doc-endnotes],#quarto-appendix.default section>*:not(a){opacity:.9;word-wrap:break-word}.btn.btn-quarto,div.cell-output-display .btn-quarto{color:#cbcccc;background-color:#373a3c;border-color:#373a3c}.btn.btn-quarto:hover,div.cell-output-display .btn-quarto:hover{color:#cbcccc;background-color:#555859;border-color:#4b4e50}.btn-check:focus+.btn.btn-quarto,.btn.btn-quarto:focus,.btn-check:focus+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:focus{color:#cbcccc;background-color:#555859;border-color:#4b4e50;box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn-check:checked+.btn.btn-quarto,.btn-check:active+.btn.btn-quarto,.btn.btn-quarto:active,.btn.btn-quarto.active,.show>.btn.btn-quarto.dropdown-toggle,.btn-check:checked+div.cell-output-display .btn-quarto,.btn-check:active+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:active,div.cell-output-display .btn-quarto.active,.show>div.cell-output-display .btn-quarto.dropdown-toggle{color:#fff;background-color:#5f6163;border-color:#4b4e50}.btn-check:checked+.btn.btn-quarto:focus,.btn-check:active+.btn.btn-quarto:focus,.btn.btn-quarto:active:focus,.btn.btn-quarto.active:focus,.show>.btn.btn-quarto.dropdown-toggle:focus,.btn-check:checked+div.cell-output-display .btn-quarto:focus,.btn-check:active+div.cell-output-display .btn-quarto:focus,div.cell-output-display .btn-quarto:active:focus,div.cell-output-display .btn-quarto.active:focus,.show>div.cell-output-display .btn-quarto.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn.btn-quarto:disabled,.btn.btn-quarto.disabled,div.cell-output-display .btn-quarto:disabled,div.cell-output-display .btn-quarto.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}nav.quarto-secondary-nav.color-navbar{background-color:#f8f9fa;color:#545555}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#545555}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:0}body.nav-sidebar #title-block-header{margin-block-end:0}}p.subtitle{margin-top:.25em;margin-bottom:.5em}code a:any-link{color:inherit;text-decoration-color:#6c757d}/*! light */div.observablehq table thead tr th{background-color:var(--bs-body-bg)}input,button,select,optgroup,textarea{background-color:var(--bs-body-bg)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#fff}.code-annotation-gutter{background-color:rgba(233,236,239,.65)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:var(--bs-font-monospace);color:#4f5457;border:solid #4f5457 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#fff;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:var(--bs-font-monospace);color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:page-start-inset/page-end-inset;padding:1em;background:#f8f9fa;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table>thead{border-top-width:0}.table>:not(caption)>*:not(:last-child)>*{border-bottom-color:#ebeced;border-bottom-style:solid;border-bottom-width:1px}.table>:not(:first-child){border-top:1px solid #b6babc;border-bottom:1px solid inherit}.table tbody{border-bottom-color:#b6babc}a.external:after{display:inline-block;height:.75rem;width:.75rem;margin-bottom:.15em;margin-left:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}div.sourceCode code a.external:after{content:none}a.external:after:hover{cursor:pointer}.quarto-ext-icon{display:inline-block;font-size:.75em;padding-left:.3em}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file,.code-with-filename .code-with-filename-file pre{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file,.quarto-dark .code-with-filename .code-with-filename-file pre{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.quarto-title-banner{margin-bottom:1em;color:#545555;background:#f8f9fa}.quarto-title-banner .code-tools-button{color:#878888}.quarto-title-banner .code-tools-button:hover{color:#545555}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;column-gap:.4em;padding-bottom:.5em;margin-top:.75em}.quarto-title .quarto-categories .quarto-category{padding:.25em .75em;font-size:.65em;text-transform:uppercase;border:solid 1px;border-radius:.25rem;opacity:.6}.quarto-title .quarto-categories .quarto-category a{color:inherit}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:repeat(2, 1fr)}#title-block-header.quarto-title-block.default .quarto-title .title{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-author-orcid img{margin-top:-5px}#title-block-header.quarto-title-block.default .quarto-description p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p,#title-block-header.quarto-title-block.default .quarto-title-authors p,#title-block-header.quarto-title-block.default .quarto-title-affiliations p{margin-bottom:.1em}#title-block-header.quarto-title-block.default .quarto-title-meta-heading{text-transform:uppercase;margin-top:1em;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-contents{font-size:.9em}#title-block-header.quarto-title-block.default .quarto-title-meta-contents a{color:#373a3c}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p.affiliation:last-of-type{margin-bottom:.7em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .description,#title-block-header.quarto-title-block.default .abstract{margin-top:0}#title-block-header.quarto-title-block.default .description>p,#title-block-header.quarto-title-block.default .abstract>p{font-size:.9em}#title-block-header.quarto-title-block.default .description>p:last-of-type,#title-block-header.quarto-title-block.default .abstract>p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .description .abstract-title,#title-block-header.quarto-title-block.default .abstract .abstract-title{margin-top:1em;text-transform:uppercase;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-author{display:grid;grid-template-columns:1fr 1fr}.quarto-title-tools-only{display:flex;justify-content:right}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#373a3c}.progress .progress-bar{font-size:8px;line-height:8px}/*# sourceMappingURL=9161419e6f82ea4435380a70856fa72b.css.map */ +*/.ansi-black-fg{color:#3e424d}.ansi-black-bg{background-color:#3e424d}.ansi-black-intense-fg{color:#282c36}.ansi-black-intense-bg{background-color:#282c36}.ansi-red-fg{color:#e75c58}.ansi-red-bg{background-color:#e75c58}.ansi-red-intense-fg{color:#b22b31}.ansi-red-intense-bg{background-color:#b22b31}.ansi-green-fg{color:#00a250}.ansi-green-bg{background-color:#00a250}.ansi-green-intense-fg{color:#007427}.ansi-green-intense-bg{background-color:#007427}.ansi-yellow-fg{color:#ddb62b}.ansi-yellow-bg{background-color:#ddb62b}.ansi-yellow-intense-fg{color:#b27d12}.ansi-yellow-intense-bg{background-color:#b27d12}.ansi-blue-fg{color:#208ffb}.ansi-blue-bg{background-color:#208ffb}.ansi-blue-intense-fg{color:#0065ca}.ansi-blue-intense-bg{background-color:#0065ca}.ansi-magenta-fg{color:#d160c4}.ansi-magenta-bg{background-color:#d160c4}.ansi-magenta-intense-fg{color:#a03196}.ansi-magenta-intense-bg{background-color:#a03196}.ansi-cyan-fg{color:#60c6c8}.ansi-cyan-bg{background-color:#60c6c8}.ansi-cyan-intense-fg{color:#258f8f}.ansi-cyan-intense-bg{background-color:#258f8f}.ansi-white-fg{color:#c5c1b4}.ansi-white-bg{background-color:#c5c1b4}.ansi-white-intense-fg{color:#a1a6b2}.ansi-white-intense-bg{background-color:#a1a6b2}.ansi-default-inverse-fg{color:#fff}.ansi-default-inverse-bg{background-color:#000}.ansi-bold{font-weight:bold}.ansi-underline{text-decoration:underline}:root{--quarto-body-bg: #fff;--quarto-body-color: #373a3c;--quarto-text-muted: #6c757d;--quarto-border-color: #dee2e6;--quarto-border-width: 1px;--quarto-border-radius: 0.25rem}table.gt_table{color:var(--quarto-body-color);font-size:1em;width:100%;background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_column_spanner_outer{color:var(--quarto-body-color);background-color:rgba(0,0,0,0);border-top-width:inherit;border-bottom-width:inherit;border-color:var(--quarto-border-color)}table.gt_table th.gt_col_heading{color:var(--quarto-body-color);font-weight:bold;background-color:rgba(0,0,0,0)}table.gt_table thead.gt_col_headings{border-bottom:1px solid currentColor;border-top-width:inherit;border-top-color:var(--quarto-border-color)}table.gt_table thead.gt_col_headings:not(:first-child){border-top-width:1px;border-top-color:var(--quarto-border-color)}table.gt_table td.gt_row{border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-width:0px}table.gt_table tbody.gt_table_body{border-top-width:1px;border-bottom-width:1px;border-bottom-color:var(--quarto-border-color);border-top-color:currentColor}div.columns{display:initial;gap:initial}div.column{display:inline-block;overflow-x:initial;vertical-align:top;width:50%}.code-annotation-tip-content{word-wrap:break-word}.code-annotation-container-hidden{display:none !important}dl.code-annotation-container-grid{display:grid;grid-template-columns:min-content auto}dl.code-annotation-container-grid dt{grid-column:1}dl.code-annotation-container-grid dd{grid-column:2}pre.sourceCode.code-annotation-code{padding-right:0}code.sourceCode .code-annotation-anchor{z-index:100;position:absolute;right:.5em;left:inherit;background-color:rgba(0,0,0,0)}:root{--mermaid-bg-color: #fff;--mermaid-edge-color: #373a3c;--mermaid-node-fg-color: #373a3c;--mermaid-fg-color: #373a3c;--mermaid-fg-color--lighter: #4f5457;--mermaid-fg-color--lightest: #686d71;--mermaid-font-family: Source Sans Pro, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;--mermaid-label-bg-color: #fff;--mermaid-label-fg-color: #2780e3;--mermaid-node-bg-color: rgba(39, 128, 227, 0.1);--mermaid-node-fg-color: #373a3c}@media print{:root{font-size:11pt}#quarto-sidebar,#TOC,.nav-page{display:none}.page-columns .content{grid-column-start:page-start}.fixed-top{position:relative}.panel-caption,.figure-caption,figcaption{color:#666}}.code-copy-button{position:absolute;top:0;right:0;border:0;margin-top:5px;margin-right:5px;background-color:rgba(0,0,0,0);z-index:3}.code-copy-button:focus{outline:none}.code-copy-button-tooltip{font-size:.75em}.code-copy-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.code-copy-button-checked:hover>.bi::before{background-image:url('data:image/svg+xml,')}main ol ol,main ul ul,main ol ul,main ul ol{margin-bottom:1em}ul>li:not(:has(>p))>ul,ol>li:not(:has(>p))>ul,ul>li:not(:has(>p))>ol,ol>li:not(:has(>p))>ol{margin-bottom:0}ul>li:not(:has(>p))>ul>li:has(>p),ol>li:not(:has(>p))>ul>li:has(>p),ul>li:not(:has(>p))>ol>li:has(>p),ol>li:not(:has(>p))>ol>li:has(>p){margin-top:1rem}body{margin:0}main.page-columns>header>h1.title,main.page-columns>header>.title.h1{margin-bottom:0}@media(min-width: 992px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] 35px [page-end-inset page-end] 5fr [screen-end-inset] 1.5em}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset] 56px [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 56px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 850px - 3em )) [body-content-end] 3em [body-end] 80px [body-end-outset] minmax(0px, 250px) [page-end-inset] minmax(80px, 160px) [page-end] 1fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 280px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 100px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start] minmax(80px, 160px) [page-start-inset] 80px [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(0px, 200px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 80px [page-start-inset] minmax(80px, 240px) [body-start-outset] 80px [body-start] 1.5em [body-content-start] minmax(450px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(50px, 150px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] minmax(40px, 80px) [page-start-inset] minmax(80px, 240px) [body-start-outset] minmax(40px, 80px) [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] minmax(25px, 50px) [body-end-outset] minmax(50px, 150px) [page-end-inset] minmax(25px, 50px) [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 991.98px){body .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.fullcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.slimcontent:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.listing:not(.floating):not(.docked) .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset] 5fr [body-start] 1.5em [body-content-start] minmax(500px, calc( 1250px - 3em )) [body-content-end body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start] 56px [page-start-inset] minmax(0px, 232px) [body-start-outset] 56px [body-start] 1.5em [body-content-start] minmax(450px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1.5em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 1000px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 800px - 3em )) [body-content-end] 1.5em [body-end body-end-outset page-end-inset page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.docked.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.docked.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(25px, 50px) [page-end-inset] 50px [page-end] 5fr [screen-end-inset] 1.5em [screen-end]}body.floating.slimcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 35px [body-end-outset] minmax(75px, 145px) [page-end-inset] 35px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}body.floating.listing .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset] 5fr [page-start page-start-inset body-start-outset body-start] 1em [body-content-start] minmax(500px, calc( 750px - 3em )) [body-content-end] 1.5em [body-end] 50px [body-end-outset] minmax(75px, 150px) [page-end-inset] 25px [page-end] 4fr [screen-end-inset] 1.5em [screen-end]}}@media(max-width: 767.98px){body .page-columns,body.fullcontent:not(.floating):not(.docked) .page-columns,body.slimcontent:not(.floating):not(.docked) .page-columns,body.docked .page-columns,body.docked.slimcontent .page-columns,body.docked.fullcontent .page-columns,body.floating .page-columns,body.floating.slimcontent .page-columns,body.floating.fullcontent .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}body:not(.floating):not(.docked) .page-columns.toc-left .page-columns{display:grid;gap:0;grid-template-columns:[screen-start] 1.5em [screen-start-inset page-start page-start-inset body-start-outset body-start body-content-start] minmax(0px, 1fr) [body-content-end body-end body-end-outset page-end-inset page-end screen-end-inset] 1.5em [screen-end]}nav[role=doc-toc]{display:none}}body,.page-row-navigation{grid-template-rows:[page-top] max-content [contents-top] max-content [contents-bottom] max-content [page-bottom]}.page-rows-contents{grid-template-rows:[content-top] minmax(max-content, 1fr) [content-bottom] minmax(60px, max-content) [page-bottom]}.page-full{grid-column:screen-start/screen-end !important}.page-columns>*{grid-column:body-content-start/body-content-end}.page-columns.column-page>*{grid-column:page-start/page-end}.page-columns.column-page-left>*{grid-column:page-start/body-content-end}.page-columns.column-page-right>*{grid-column:body-content-start/page-end}.page-rows{grid-auto-rows:auto}.header{grid-column:screen-start/screen-end;grid-row:page-top/contents-top}#quarto-content{padding:0;grid-column:screen-start/screen-end;grid-row:contents-top/contents-bottom}body.floating .sidebar.sidebar-navigation{grid-column:page-start/body-start;grid-row:content-top/page-bottom}body.docked .sidebar.sidebar-navigation{grid-column:screen-start/body-start;grid-row:content-top/page-bottom}.sidebar.toc-left{grid-column:page-start/body-start;grid-row:content-top/page-bottom}.sidebar.margin-sidebar{grid-column:body-end/page-end;grid-row:content-top/page-bottom}.page-columns .content{grid-column:body-content-start/body-content-end;grid-row:content-top/content-bottom;align-content:flex-start}.page-columns .page-navigation{grid-column:body-content-start/body-content-end;grid-row:content-bottom/page-bottom}.page-columns .footer{grid-column:screen-start/screen-end;grid-row:contents-bottom/page-bottom}.page-columns .column-body{grid-column:body-content-start/body-content-end}.page-columns .column-body-fullbleed{grid-column:body-start/body-end}.page-columns .column-body-outset{grid-column:body-start-outset/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset table{background:#fff}.page-columns .column-body-outset-left{grid-column:body-start-outset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-left table{background:#fff}.page-columns .column-body-outset-right{grid-column:body-content-start/body-end-outset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-body-outset-right table{background:#fff}.page-columns .column-page{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page table{background:#fff}.page-columns .column-page-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset table{background:#fff}.page-columns .column-page-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-left table{background:#fff}.page-columns .column-page-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-inset-right figcaption table{background:#fff}.page-columns .column-page-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-left table{background:#fff}.page-columns .column-page-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-page-right figcaption table{background:#fff}#quarto-content.page-columns #quarto-margin-sidebar,#quarto-content.page-columns #quarto-sidebar{z-index:1}@media(max-width: 991.98px){#quarto-content.page-columns #quarto-margin-sidebar.collapse,#quarto-content.page-columns #quarto-sidebar.collapse,#quarto-content.page-columns #quarto-margin-sidebar.collapsing,#quarto-content.page-columns #quarto-sidebar.collapsing{z-index:1055}}#quarto-content.page-columns main.column-page,#quarto-content.page-columns main.column-page-right,#quarto-content.page-columns main.column-page-left{z-index:0}.page-columns .column-screen-inset{grid-column:screen-start-inset/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:screen-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/screen-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:screen-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:screen-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/screen-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:screen-start/screen-end;padding:1em;background:#f8f9fa;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}.zindex-content{z-index:998;transform:translate3d(0, 0, 0)}.zindex-modal{z-index:1055;transform:translate3d(0, 0, 0)}.zindex-over-content{z-index:999;transform:translate3d(0, 0, 0)}img.img-fluid.column-screen,img.img-fluid.column-screen-inset-shaded,img.img-fluid.column-screen-inset,img.img-fluid.column-screen-inset-left,img.img-fluid.column-screen-inset-right,img.img-fluid.column-screen-left,img.img-fluid.column-screen-right{width:100%}@media(min-width: 992px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.column-sidebar{grid-column:page-start/body-start !important;z-index:998}.column-leftmargin{grid-column:screen-start-inset/body-start !important;z-index:998}.no-row-height{height:1em;overflow:visible}}@media(max-width: 991.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-end/page-end !important;z-index:998}.no-row-height{height:1em;overflow:visible}.page-columns.page-full{overflow:visible}.page-columns.toc-left .margin-caption,.page-columns.toc-left div.aside,.page-columns.toc-left aside,.page-columns.toc-left .column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.page-columns.toc-left .no-row-height{height:initial;overflow:initial}}@media(max-width: 767.98px){.margin-caption,div.aside,aside,.column-margin{grid-column:body-content-start/body-content-end !important;z-index:998;transform:translate3d(0, 0, 0)}.no-row-height{height:initial;overflow:initial}#quarto-margin-sidebar{display:none}#quarto-sidebar-toc-left{display:none}.hidden-sm{display:none}}.panel-grid{display:grid;grid-template-rows:repeat(1, 1fr);grid-template-columns:repeat(24, 1fr);gap:1em}.panel-grid .g-col-1{grid-column:auto/span 1}.panel-grid .g-col-2{grid-column:auto/span 2}.panel-grid .g-col-3{grid-column:auto/span 3}.panel-grid .g-col-4{grid-column:auto/span 4}.panel-grid .g-col-5{grid-column:auto/span 5}.panel-grid .g-col-6{grid-column:auto/span 6}.panel-grid .g-col-7{grid-column:auto/span 7}.panel-grid .g-col-8{grid-column:auto/span 8}.panel-grid .g-col-9{grid-column:auto/span 9}.panel-grid .g-col-10{grid-column:auto/span 10}.panel-grid .g-col-11{grid-column:auto/span 11}.panel-grid .g-col-12{grid-column:auto/span 12}.panel-grid .g-col-13{grid-column:auto/span 13}.panel-grid .g-col-14{grid-column:auto/span 14}.panel-grid .g-col-15{grid-column:auto/span 15}.panel-grid .g-col-16{grid-column:auto/span 16}.panel-grid .g-col-17{grid-column:auto/span 17}.panel-grid .g-col-18{grid-column:auto/span 18}.panel-grid .g-col-19{grid-column:auto/span 19}.panel-grid .g-col-20{grid-column:auto/span 20}.panel-grid .g-col-21{grid-column:auto/span 21}.panel-grid .g-col-22{grid-column:auto/span 22}.panel-grid .g-col-23{grid-column:auto/span 23}.panel-grid .g-col-24{grid-column:auto/span 24}.panel-grid .g-start-1{grid-column-start:1}.panel-grid .g-start-2{grid-column-start:2}.panel-grid .g-start-3{grid-column-start:3}.panel-grid .g-start-4{grid-column-start:4}.panel-grid .g-start-5{grid-column-start:5}.panel-grid .g-start-6{grid-column-start:6}.panel-grid .g-start-7{grid-column-start:7}.panel-grid .g-start-8{grid-column-start:8}.panel-grid .g-start-9{grid-column-start:9}.panel-grid .g-start-10{grid-column-start:10}.panel-grid .g-start-11{grid-column-start:11}.panel-grid .g-start-12{grid-column-start:12}.panel-grid .g-start-13{grid-column-start:13}.panel-grid .g-start-14{grid-column-start:14}.panel-grid .g-start-15{grid-column-start:15}.panel-grid .g-start-16{grid-column-start:16}.panel-grid .g-start-17{grid-column-start:17}.panel-grid .g-start-18{grid-column-start:18}.panel-grid .g-start-19{grid-column-start:19}.panel-grid .g-start-20{grid-column-start:20}.panel-grid .g-start-21{grid-column-start:21}.panel-grid .g-start-22{grid-column-start:22}.panel-grid .g-start-23{grid-column-start:23}@media(min-width: 576px){.panel-grid .g-col-sm-1{grid-column:auto/span 1}.panel-grid .g-col-sm-2{grid-column:auto/span 2}.panel-grid .g-col-sm-3{grid-column:auto/span 3}.panel-grid .g-col-sm-4{grid-column:auto/span 4}.panel-grid .g-col-sm-5{grid-column:auto/span 5}.panel-grid .g-col-sm-6{grid-column:auto/span 6}.panel-grid .g-col-sm-7{grid-column:auto/span 7}.panel-grid .g-col-sm-8{grid-column:auto/span 8}.panel-grid .g-col-sm-9{grid-column:auto/span 9}.panel-grid .g-col-sm-10{grid-column:auto/span 10}.panel-grid .g-col-sm-11{grid-column:auto/span 11}.panel-grid .g-col-sm-12{grid-column:auto/span 12}.panel-grid .g-col-sm-13{grid-column:auto/span 13}.panel-grid .g-col-sm-14{grid-column:auto/span 14}.panel-grid .g-col-sm-15{grid-column:auto/span 15}.panel-grid .g-col-sm-16{grid-column:auto/span 16}.panel-grid .g-col-sm-17{grid-column:auto/span 17}.panel-grid .g-col-sm-18{grid-column:auto/span 18}.panel-grid .g-col-sm-19{grid-column:auto/span 19}.panel-grid .g-col-sm-20{grid-column:auto/span 20}.panel-grid .g-col-sm-21{grid-column:auto/span 21}.panel-grid .g-col-sm-22{grid-column:auto/span 22}.panel-grid .g-col-sm-23{grid-column:auto/span 23}.panel-grid .g-col-sm-24{grid-column:auto/span 24}.panel-grid .g-start-sm-1{grid-column-start:1}.panel-grid .g-start-sm-2{grid-column-start:2}.panel-grid .g-start-sm-3{grid-column-start:3}.panel-grid .g-start-sm-4{grid-column-start:4}.panel-grid .g-start-sm-5{grid-column-start:5}.panel-grid .g-start-sm-6{grid-column-start:6}.panel-grid .g-start-sm-7{grid-column-start:7}.panel-grid .g-start-sm-8{grid-column-start:8}.panel-grid .g-start-sm-9{grid-column-start:9}.panel-grid .g-start-sm-10{grid-column-start:10}.panel-grid .g-start-sm-11{grid-column-start:11}.panel-grid .g-start-sm-12{grid-column-start:12}.panel-grid .g-start-sm-13{grid-column-start:13}.panel-grid .g-start-sm-14{grid-column-start:14}.panel-grid .g-start-sm-15{grid-column-start:15}.panel-grid .g-start-sm-16{grid-column-start:16}.panel-grid .g-start-sm-17{grid-column-start:17}.panel-grid .g-start-sm-18{grid-column-start:18}.panel-grid .g-start-sm-19{grid-column-start:19}.panel-grid .g-start-sm-20{grid-column-start:20}.panel-grid .g-start-sm-21{grid-column-start:21}.panel-grid .g-start-sm-22{grid-column-start:22}.panel-grid .g-start-sm-23{grid-column-start:23}}@media(min-width: 768px){.panel-grid .g-col-md-1{grid-column:auto/span 1}.panel-grid .g-col-md-2{grid-column:auto/span 2}.panel-grid .g-col-md-3{grid-column:auto/span 3}.panel-grid .g-col-md-4{grid-column:auto/span 4}.panel-grid .g-col-md-5{grid-column:auto/span 5}.panel-grid .g-col-md-6{grid-column:auto/span 6}.panel-grid .g-col-md-7{grid-column:auto/span 7}.panel-grid .g-col-md-8{grid-column:auto/span 8}.panel-grid .g-col-md-9{grid-column:auto/span 9}.panel-grid .g-col-md-10{grid-column:auto/span 10}.panel-grid .g-col-md-11{grid-column:auto/span 11}.panel-grid .g-col-md-12{grid-column:auto/span 12}.panel-grid .g-col-md-13{grid-column:auto/span 13}.panel-grid .g-col-md-14{grid-column:auto/span 14}.panel-grid .g-col-md-15{grid-column:auto/span 15}.panel-grid .g-col-md-16{grid-column:auto/span 16}.panel-grid .g-col-md-17{grid-column:auto/span 17}.panel-grid .g-col-md-18{grid-column:auto/span 18}.panel-grid .g-col-md-19{grid-column:auto/span 19}.panel-grid .g-col-md-20{grid-column:auto/span 20}.panel-grid .g-col-md-21{grid-column:auto/span 21}.panel-grid .g-col-md-22{grid-column:auto/span 22}.panel-grid .g-col-md-23{grid-column:auto/span 23}.panel-grid .g-col-md-24{grid-column:auto/span 24}.panel-grid .g-start-md-1{grid-column-start:1}.panel-grid .g-start-md-2{grid-column-start:2}.panel-grid .g-start-md-3{grid-column-start:3}.panel-grid .g-start-md-4{grid-column-start:4}.panel-grid .g-start-md-5{grid-column-start:5}.panel-grid .g-start-md-6{grid-column-start:6}.panel-grid .g-start-md-7{grid-column-start:7}.panel-grid .g-start-md-8{grid-column-start:8}.panel-grid .g-start-md-9{grid-column-start:9}.panel-grid .g-start-md-10{grid-column-start:10}.panel-grid .g-start-md-11{grid-column-start:11}.panel-grid .g-start-md-12{grid-column-start:12}.panel-grid .g-start-md-13{grid-column-start:13}.panel-grid .g-start-md-14{grid-column-start:14}.panel-grid .g-start-md-15{grid-column-start:15}.panel-grid .g-start-md-16{grid-column-start:16}.panel-grid .g-start-md-17{grid-column-start:17}.panel-grid .g-start-md-18{grid-column-start:18}.panel-grid .g-start-md-19{grid-column-start:19}.panel-grid .g-start-md-20{grid-column-start:20}.panel-grid .g-start-md-21{grid-column-start:21}.panel-grid .g-start-md-22{grid-column-start:22}.panel-grid .g-start-md-23{grid-column-start:23}}@media(min-width: 992px){.panel-grid .g-col-lg-1{grid-column:auto/span 1}.panel-grid .g-col-lg-2{grid-column:auto/span 2}.panel-grid .g-col-lg-3{grid-column:auto/span 3}.panel-grid .g-col-lg-4{grid-column:auto/span 4}.panel-grid .g-col-lg-5{grid-column:auto/span 5}.panel-grid .g-col-lg-6{grid-column:auto/span 6}.panel-grid .g-col-lg-7{grid-column:auto/span 7}.panel-grid .g-col-lg-8{grid-column:auto/span 8}.panel-grid .g-col-lg-9{grid-column:auto/span 9}.panel-grid .g-col-lg-10{grid-column:auto/span 10}.panel-grid .g-col-lg-11{grid-column:auto/span 11}.panel-grid .g-col-lg-12{grid-column:auto/span 12}.panel-grid .g-col-lg-13{grid-column:auto/span 13}.panel-grid .g-col-lg-14{grid-column:auto/span 14}.panel-grid .g-col-lg-15{grid-column:auto/span 15}.panel-grid .g-col-lg-16{grid-column:auto/span 16}.panel-grid .g-col-lg-17{grid-column:auto/span 17}.panel-grid .g-col-lg-18{grid-column:auto/span 18}.panel-grid .g-col-lg-19{grid-column:auto/span 19}.panel-grid .g-col-lg-20{grid-column:auto/span 20}.panel-grid .g-col-lg-21{grid-column:auto/span 21}.panel-grid .g-col-lg-22{grid-column:auto/span 22}.panel-grid .g-col-lg-23{grid-column:auto/span 23}.panel-grid .g-col-lg-24{grid-column:auto/span 24}.panel-grid .g-start-lg-1{grid-column-start:1}.panel-grid .g-start-lg-2{grid-column-start:2}.panel-grid .g-start-lg-3{grid-column-start:3}.panel-grid .g-start-lg-4{grid-column-start:4}.panel-grid .g-start-lg-5{grid-column-start:5}.panel-grid .g-start-lg-6{grid-column-start:6}.panel-grid .g-start-lg-7{grid-column-start:7}.panel-grid .g-start-lg-8{grid-column-start:8}.panel-grid .g-start-lg-9{grid-column-start:9}.panel-grid .g-start-lg-10{grid-column-start:10}.panel-grid .g-start-lg-11{grid-column-start:11}.panel-grid .g-start-lg-12{grid-column-start:12}.panel-grid .g-start-lg-13{grid-column-start:13}.panel-grid .g-start-lg-14{grid-column-start:14}.panel-grid .g-start-lg-15{grid-column-start:15}.panel-grid .g-start-lg-16{grid-column-start:16}.panel-grid .g-start-lg-17{grid-column-start:17}.panel-grid .g-start-lg-18{grid-column-start:18}.panel-grid .g-start-lg-19{grid-column-start:19}.panel-grid .g-start-lg-20{grid-column-start:20}.panel-grid .g-start-lg-21{grid-column-start:21}.panel-grid .g-start-lg-22{grid-column-start:22}.panel-grid .g-start-lg-23{grid-column-start:23}}@media(min-width: 1200px){.panel-grid .g-col-xl-1{grid-column:auto/span 1}.panel-grid .g-col-xl-2{grid-column:auto/span 2}.panel-grid .g-col-xl-3{grid-column:auto/span 3}.panel-grid .g-col-xl-4{grid-column:auto/span 4}.panel-grid .g-col-xl-5{grid-column:auto/span 5}.panel-grid .g-col-xl-6{grid-column:auto/span 6}.panel-grid .g-col-xl-7{grid-column:auto/span 7}.panel-grid .g-col-xl-8{grid-column:auto/span 8}.panel-grid .g-col-xl-9{grid-column:auto/span 9}.panel-grid .g-col-xl-10{grid-column:auto/span 10}.panel-grid .g-col-xl-11{grid-column:auto/span 11}.panel-grid .g-col-xl-12{grid-column:auto/span 12}.panel-grid .g-col-xl-13{grid-column:auto/span 13}.panel-grid .g-col-xl-14{grid-column:auto/span 14}.panel-grid .g-col-xl-15{grid-column:auto/span 15}.panel-grid .g-col-xl-16{grid-column:auto/span 16}.panel-grid .g-col-xl-17{grid-column:auto/span 17}.panel-grid .g-col-xl-18{grid-column:auto/span 18}.panel-grid .g-col-xl-19{grid-column:auto/span 19}.panel-grid .g-col-xl-20{grid-column:auto/span 20}.panel-grid .g-col-xl-21{grid-column:auto/span 21}.panel-grid .g-col-xl-22{grid-column:auto/span 22}.panel-grid .g-col-xl-23{grid-column:auto/span 23}.panel-grid .g-col-xl-24{grid-column:auto/span 24}.panel-grid .g-start-xl-1{grid-column-start:1}.panel-grid .g-start-xl-2{grid-column-start:2}.panel-grid .g-start-xl-3{grid-column-start:3}.panel-grid .g-start-xl-4{grid-column-start:4}.panel-grid .g-start-xl-5{grid-column-start:5}.panel-grid .g-start-xl-6{grid-column-start:6}.panel-grid .g-start-xl-7{grid-column-start:7}.panel-grid .g-start-xl-8{grid-column-start:8}.panel-grid .g-start-xl-9{grid-column-start:9}.panel-grid .g-start-xl-10{grid-column-start:10}.panel-grid .g-start-xl-11{grid-column-start:11}.panel-grid .g-start-xl-12{grid-column-start:12}.panel-grid .g-start-xl-13{grid-column-start:13}.panel-grid .g-start-xl-14{grid-column-start:14}.panel-grid .g-start-xl-15{grid-column-start:15}.panel-grid .g-start-xl-16{grid-column-start:16}.panel-grid .g-start-xl-17{grid-column-start:17}.panel-grid .g-start-xl-18{grid-column-start:18}.panel-grid .g-start-xl-19{grid-column-start:19}.panel-grid .g-start-xl-20{grid-column-start:20}.panel-grid .g-start-xl-21{grid-column-start:21}.panel-grid .g-start-xl-22{grid-column-start:22}.panel-grid .g-start-xl-23{grid-column-start:23}}@media(min-width: 1400px){.panel-grid .g-col-xxl-1{grid-column:auto/span 1}.panel-grid .g-col-xxl-2{grid-column:auto/span 2}.panel-grid .g-col-xxl-3{grid-column:auto/span 3}.panel-grid .g-col-xxl-4{grid-column:auto/span 4}.panel-grid .g-col-xxl-5{grid-column:auto/span 5}.panel-grid .g-col-xxl-6{grid-column:auto/span 6}.panel-grid .g-col-xxl-7{grid-column:auto/span 7}.panel-grid .g-col-xxl-8{grid-column:auto/span 8}.panel-grid .g-col-xxl-9{grid-column:auto/span 9}.panel-grid .g-col-xxl-10{grid-column:auto/span 10}.panel-grid .g-col-xxl-11{grid-column:auto/span 11}.panel-grid .g-col-xxl-12{grid-column:auto/span 12}.panel-grid .g-col-xxl-13{grid-column:auto/span 13}.panel-grid .g-col-xxl-14{grid-column:auto/span 14}.panel-grid .g-col-xxl-15{grid-column:auto/span 15}.panel-grid .g-col-xxl-16{grid-column:auto/span 16}.panel-grid .g-col-xxl-17{grid-column:auto/span 17}.panel-grid .g-col-xxl-18{grid-column:auto/span 18}.panel-grid .g-col-xxl-19{grid-column:auto/span 19}.panel-grid .g-col-xxl-20{grid-column:auto/span 20}.panel-grid .g-col-xxl-21{grid-column:auto/span 21}.panel-grid .g-col-xxl-22{grid-column:auto/span 22}.panel-grid .g-col-xxl-23{grid-column:auto/span 23}.panel-grid .g-col-xxl-24{grid-column:auto/span 24}.panel-grid .g-start-xxl-1{grid-column-start:1}.panel-grid .g-start-xxl-2{grid-column-start:2}.panel-grid .g-start-xxl-3{grid-column-start:3}.panel-grid .g-start-xxl-4{grid-column-start:4}.panel-grid .g-start-xxl-5{grid-column-start:5}.panel-grid .g-start-xxl-6{grid-column-start:6}.panel-grid .g-start-xxl-7{grid-column-start:7}.panel-grid .g-start-xxl-8{grid-column-start:8}.panel-grid .g-start-xxl-9{grid-column-start:9}.panel-grid .g-start-xxl-10{grid-column-start:10}.panel-grid .g-start-xxl-11{grid-column-start:11}.panel-grid .g-start-xxl-12{grid-column-start:12}.panel-grid .g-start-xxl-13{grid-column-start:13}.panel-grid .g-start-xxl-14{grid-column-start:14}.panel-grid .g-start-xxl-15{grid-column-start:15}.panel-grid .g-start-xxl-16{grid-column-start:16}.panel-grid .g-start-xxl-17{grid-column-start:17}.panel-grid .g-start-xxl-18{grid-column-start:18}.panel-grid .g-start-xxl-19{grid-column-start:19}.panel-grid .g-start-xxl-20{grid-column-start:20}.panel-grid .g-start-xxl-21{grid-column-start:21}.panel-grid .g-start-xxl-22{grid-column-start:22}.panel-grid .g-start-xxl-23{grid-column-start:23}}main{margin-top:1em;margin-bottom:1em}h1,.h1,h2,.h2{opacity:.9;margin-top:2rem;margin-bottom:1rem;font-weight:600}h1.title,.title.h1{margin-top:0}h2,.h2{border-bottom:1px solid #dee2e6;padding-bottom:.5rem}h3,.h3{font-weight:600}h3,.h3,h4,.h4{opacity:.9;margin-top:1.5rem}h5,.h5,h6,.h6{opacity:.9}.header-section-number{color:#747a7f}.nav-link.active .header-section-number{color:inherit}mark,.mark{padding:0em}.panel-caption,caption,.figure-caption{font-size:.9rem}.panel-caption,.figure-caption,figcaption{color:#747a7f}.table-caption,caption{color:#373a3c}.quarto-layout-cell[data-ref-parent] caption{color:#747a7f}.column-margin figcaption,.margin-caption,div.aside,aside,.column-margin{color:#747a7f;font-size:.825rem}.panel-caption.margin-caption{text-align:inherit}.column-margin.column-container p{margin-bottom:0}.column-margin.column-container>*:not(.collapse){padding-top:.5em;padding-bottom:.5em;display:block}.column-margin.column-container>*.collapse:not(.show){display:none}@media(min-width: 768px){.column-margin.column-container .callout-margin-content:first-child{margin-top:4.5em}.column-margin.column-container .callout-margin-content-simple:first-child{margin-top:3.5em}}.margin-caption>*{padding-top:.5em;padding-bottom:.5em}@media(max-width: 767.98px){.quarto-layout-row{flex-direction:column}}.nav-tabs .nav-item{margin-top:1px;cursor:pointer}.tab-content{margin-top:0px;border-left:#dee2e6 1px solid;border-right:#dee2e6 1px solid;border-bottom:#dee2e6 1px solid;margin-left:0;padding:1em;margin-bottom:1em}@media(max-width: 767.98px){.layout-sidebar{margin-left:0;margin-right:0}}.panel-sidebar,.panel-sidebar .form-control,.panel-input,.panel-input .form-control,.selectize-dropdown{font-size:.9rem}.panel-sidebar .form-control,.panel-input .form-control{padding-top:.1rem}.tab-pane div.sourceCode{margin-top:0px}.tab-pane>p{padding-top:1em}.tab-content>.tab-pane:not(.active){display:none !important}div.sourceCode{background-color:rgba(233,236,239,.65);border:1px solid rgba(233,236,239,.65);border-radius:.25rem}pre.sourceCode{background-color:rgba(0,0,0,0)}pre.sourceCode{border:none;font-size:.875em;overflow:visible !important;padding:.4em}.callout pre.sourceCode{padding-left:0}div.sourceCode{overflow-y:hidden}.callout div.sourceCode{margin-left:initial}.blockquote{font-size:inherit;padding-left:1rem;padding-right:1.5rem;color:#747a7f}.blockquote h1:first-child,.blockquote .h1:first-child,.blockquote h2:first-child,.blockquote .h2:first-child,.blockquote h3:first-child,.blockquote .h3:first-child,.blockquote h4:first-child,.blockquote .h4:first-child,.blockquote h5:first-child,.blockquote .h5:first-child{margin-top:0}pre{background-color:initial;padding:initial;border:initial}p code:not(.sourceCode),li code:not(.sourceCode),td code:not(.sourceCode){background-color:#f7f7f7;padding:.2em}nav p code:not(.sourceCode),nav li code:not(.sourceCode),nav td code:not(.sourceCode){background-color:rgba(0,0,0,0);padding:0}td code:not(.sourceCode){white-space:pre-wrap}#quarto-embedded-source-code-modal>.modal-dialog{max-width:1000px;padding-left:1.75rem;padding-right:1.75rem}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body{padding:0}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-body div.sourceCode{margin:0;padding:.2rem .2rem;border-radius:0px;border:none}#quarto-embedded-source-code-modal>.modal-dialog>.modal-content>.modal-header{padding:.7rem}.code-tools-button{font-size:1rem;padding:.15rem .15rem;margin-left:5px;color:#6c757d;background-color:rgba(0,0,0,0);transition:initial;cursor:pointer}.code-tools-button>.bi::before{display:inline-block;height:1rem;width:1rem;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:1rem 1rem}.code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button>.bi::before{background-image:url('data:image/svg+xml,')}#quarto-embedded-source-code-modal .code-copy-button-checked>.bi::before{background-image:url('data:image/svg+xml,')}.sidebar{will-change:top;transition:top 200ms linear;position:sticky;overflow-y:auto;padding-top:1.2em;max-height:100vh}.sidebar.toc-left,.sidebar.margin-sidebar{top:0px;padding-top:1em}.sidebar.toc-left>*,.sidebar.margin-sidebar>*{padding-top:.5em}.sidebar.quarto-banner-title-block-sidebar>*{padding-top:1.65em}figure .quarto-notebook-link{margin-top:.5em}.quarto-notebook-link{font-size:.75em;color:#6c757d;margin-bottom:1em;text-decoration:none;display:block}.quarto-notebook-link:hover{text-decoration:underline;color:#2780e3}.quarto-notebook-link::before{display:inline-block;height:.75rem;width:.75rem;margin-bottom:0em;margin-right:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}.quarto-alternate-notebooks i.bi,.quarto-alternate-formats i.bi{margin-right:.4em}.quarto-notebook .cell-container{display:flex}.quarto-notebook .cell-container .cell{flex-grow:4}.quarto-notebook .cell-container .cell-decorator{padding-top:1.5em;padding-right:1em;text-align:right}.quarto-notebook h2,.quarto-notebook .h2{border-bottom:none}.sidebar .quarto-alternate-formats a,.sidebar .quarto-alternate-notebooks a{text-decoration:none}.sidebar .quarto-alternate-formats a:hover,.sidebar .quarto-alternate-notebooks a:hover{color:#2780e3}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2,.sidebar nav[role=doc-toc]>h2,.sidebar nav[role=doc-toc]>.h2{font-size:.875rem;font-weight:400;margin-bottom:.5rem;margin-top:.3rem;font-family:inherit;border-bottom:0;padding-bottom:0;padding-top:0px}.sidebar .quarto-alternate-notebooks h2,.sidebar .quarto-alternate-notebooks .h2,.sidebar .quarto-alternate-formats h2,.sidebar .quarto-alternate-formats .h2{margin-top:1rem}.sidebar nav[role=doc-toc]>ul a{border-left:1px solid #e9ecef;padding-left:.6rem}.sidebar .quarto-alternate-notebooks h2>ul a,.sidebar .quarto-alternate-notebooks .h2>ul a,.sidebar .quarto-alternate-formats h2>ul a,.sidebar .quarto-alternate-formats .h2>ul a{border-left:none;padding-left:.6rem}.sidebar .quarto-alternate-notebooks ul a:empty,.sidebar .quarto-alternate-formats ul a:empty,.sidebar nav[role=doc-toc]>ul a:empty{display:none}.sidebar .quarto-alternate-notebooks ul,.sidebar .quarto-alternate-formats ul,.sidebar nav[role=doc-toc] ul{padding-left:0;list-style:none;font-size:.875rem;font-weight:300}.sidebar .quarto-alternate-notebooks ul li a,.sidebar .quarto-alternate-formats ul li a,.sidebar nav[role=doc-toc]>ul li a{line-height:1.1rem;padding-bottom:.2rem;padding-top:.2rem;color:inherit}.sidebar nav[role=doc-toc] ul>li>ul>li>a{padding-left:1.2em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>a{padding-left:2.4em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>a{padding-left:3.6em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:4.8em}.sidebar nav[role=doc-toc] ul>li>ul>li>ul>li>ul>li>ul>li>ul>li>a{padding-left:6em}.sidebar nav[role=doc-toc] ul>li>a.active,.sidebar nav[role=doc-toc] ul>li>ul>li>a.active{border-left:1px solid #2780e3;color:#2780e3 !important}.sidebar nav[role=doc-toc] ul>li>a:hover,.sidebar nav[role=doc-toc] ul>li>ul>li>a:hover{color:#2780e3 !important}kbd,.kbd{color:#373a3c;background-color:#f8f9fa;border:1px solid;border-radius:5px;border-color:#dee2e6}div.hanging-indent{margin-left:1em;text-indent:-1em}.citation a,.footnote-ref{text-decoration:none}.footnotes ol{padding-left:1em}.tippy-content>*{margin-bottom:.7em}.tippy-content>*:last-child{margin-bottom:0}.table a{word-break:break-word}.table>thead{border-top-width:1px;border-top-color:#dee2e6;border-bottom:1px solid #b6babc}.callout{margin-top:1.25rem;margin-bottom:1.25rem;border-radius:.25rem;overflow-wrap:break-word}.callout .callout-title-container{overflow-wrap:anywhere}.callout.callout-style-simple{padding:.4em .7em;border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout.callout-style-default{border-left:5px solid;border-right:1px solid #dee2e6;border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.callout .callout-body-container{flex-grow:1}.callout.callout-style-simple .callout-body{font-size:.9rem;font-weight:400}.callout.callout-style-default .callout-body{font-size:.9rem;font-weight:400}.callout.callout-titled .callout-body{margin-top:.2em}.callout:not(.no-icon).callout-titled.callout-style-simple .callout-body{padding-left:1.6em}.callout.callout-titled>.callout-header{padding-top:.2em;margin-bottom:-0.2em}.callout.callout-style-simple>div.callout-header{border-bottom:none;font-size:.9rem;font-weight:600;opacity:75%}.callout.callout-style-default>div.callout-header{border-bottom:none;font-weight:600;opacity:85%;font-size:.9rem;padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body{padding-left:.5em;padding-right:.5em}.callout.callout-style-default div.callout-body>:first-child{margin-top:.5em}.callout>div.callout-header[data-bs-toggle=collapse]{cursor:pointer}.callout.callout-style-default .callout-header[aria-expanded=false],.callout.callout-style-default .callout-header[aria-expanded=true]{padding-top:0px;margin-bottom:0px;align-items:center}.callout.callout-titled .callout-body>:last-child:not(.sourceCode),.callout.callout-titled .callout-body>div>:last-child:not(.sourceCode){margin-bottom:.5rem}.callout:not(.callout-titled) .callout-body>:first-child,.callout:not(.callout-titled) .callout-body>div>:first-child{margin-top:.25rem}.callout:not(.callout-titled) .callout-body>:last-child,.callout:not(.callout-titled) .callout-body>div>:last-child{margin-bottom:.2rem}.callout.callout-style-simple .callout-icon::before,.callout.callout-style-simple .callout-toggle::before{height:1rem;width:1rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.callout.callout-style-default .callout-icon::before,.callout.callout-style-default .callout-toggle::before{height:.9rem;width:.9rem;display:inline-block;content:"";background-repeat:no-repeat;background-size:.9rem .9rem}.callout.callout-style-default .callout-toggle::before{margin-top:5px}.callout .callout-btn-toggle .callout-toggle::before{transition:transform .2s linear}.callout .callout-header[aria-expanded=false] .callout-toggle::before{transform:rotate(-90deg)}.callout .callout-header[aria-expanded=true] .callout-toggle::before{transform:none}.callout.callout-style-simple:not(.no-icon) div.callout-icon-container{padding-top:.2em;padding-right:.55em}.callout.callout-style-default:not(.no-icon) div.callout-icon-container{padding-top:.1em;padding-right:.35em}.callout.callout-style-default:not(.no-icon) div.callout-title-container{margin-top:-1px}.callout.callout-style-default.callout-caution:not(.no-icon) div.callout-icon-container{padding-top:.3em;padding-right:.35em}.callout>.callout-body>.callout-icon-container>.no-icon,.callout>.callout-header>.callout-icon-container>.no-icon{display:none}div.callout.callout{border-left-color:#6c757d}div.callout.callout-style-default>.callout-header{background-color:#6c757d}div.callout-note.callout{border-left-color:#2780e3}div.callout-note.callout-style-default>.callout-header{background-color:#e9f2fc}div.callout-note:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-note .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-tip.callout{border-left-color:#3fb618}div.callout-tip.callout-style-default>.callout-header{background-color:#ecf8e8}div.callout-tip:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-tip .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-warning.callout{border-left-color:#ff7518}div.callout-warning.callout-style-default>.callout-header{background-color:#fff1e8}div.callout-warning:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-warning .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-caution.callout{border-left-color:#f0ad4e}div.callout-caution.callout-style-default>.callout-header{background-color:#fef7ed}div.callout-caution:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-caution .callout-toggle::before{background-image:url('data:image/svg+xml,')}div.callout-important.callout{border-left-color:#ff0039}div.callout-important.callout-style-default>.callout-header{background-color:#ffe6eb}div.callout-important:not(.callout-titled) .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important.callout-titled .callout-icon::before{background-image:url('data:image/svg+xml,');}div.callout-important .callout-toggle::before{background-image:url('data:image/svg+xml,')}.quarto-toggle-container{display:flex;align-items:center}.quarto-reader-toggle .bi::before,.quarto-color-scheme-toggle .bi::before{display:inline-block;height:1rem;width:1rem;content:"";background-repeat:no-repeat;background-size:1rem 1rem}.sidebar-navigation{padding-left:20px}.navbar .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.navbar .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle:not(.alternate) .bi::before{background-image:url('data:image/svg+xml,')}.sidebar-navigation .quarto-color-scheme-toggle.alternate .bi::before{background-image:url('data:image/svg+xml,')}.quarto-sidebar-toggle{border-color:#dee2e6;border-bottom-left-radius:.25rem;border-bottom-right-radius:.25rem;border-style:solid;border-width:1px;overflow:hidden;border-top-width:0px;padding-top:0px !important}.quarto-sidebar-toggle-title{cursor:pointer;padding-bottom:2px;margin-left:.25em;text-align:center;font-weight:400;font-size:.775em}#quarto-content .quarto-sidebar-toggle{background:#fafafa}#quarto-content .quarto-sidebar-toggle-title{color:#373a3c}.quarto-sidebar-toggle-icon{color:#dee2e6;margin-right:.5em;float:right;transition:transform .2s ease}.quarto-sidebar-toggle-icon::before{padding-top:5px}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-icon{transform:rotate(-180deg)}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-title{border-bottom:solid #dee2e6 1px}.quarto-sidebar-toggle-contents{background-color:#fff;padding-right:10px;padding-left:10px;margin-top:0px !important;transition:max-height .5s ease}.quarto-sidebar-toggle.expanded .quarto-sidebar-toggle-contents{padding-top:1em;padding-bottom:10px}.quarto-sidebar-toggle:not(.expanded) .quarto-sidebar-toggle-contents{padding-top:0px !important;padding-bottom:0px}nav[role=doc-toc]{z-index:1020}#quarto-sidebar>*,nav[role=doc-toc]>*{transition:opacity .1s ease,border .1s ease}#quarto-sidebar.slow>*,nav[role=doc-toc].slow>*{transition:opacity .4s ease,border .4s ease}.quarto-color-scheme-toggle:not(.alternate).top-right .bi::before{background-image:url('data:image/svg+xml,')}.quarto-color-scheme-toggle.alternate.top-right .bi::before{background-image:url('data:image/svg+xml,')}#quarto-appendix.default{border-top:1px solid #dee2e6}#quarto-appendix.default{background-color:#fff;padding-top:1.5em;margin-top:2em;z-index:998}#quarto-appendix.default .quarto-appendix-heading{margin-top:0;line-height:1.4em;font-weight:600;opacity:.9;border-bottom:none;margin-bottom:0}#quarto-appendix.default .footnotes ol,#quarto-appendix.default .footnotes ol li>p:last-of-type,#quarto-appendix.default .quarto-appendix-contents>p:last-of-type{margin-bottom:0}#quarto-appendix.default .quarto-appendix-secondary-label{margin-bottom:.4em}#quarto-appendix.default .quarto-appendix-bibtex{font-size:.7em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-bibtex code.sourceCode{white-space:pre-wrap}#quarto-appendix.default .quarto-appendix-citeas{font-size:.9em;padding:1em;border:solid 1px #dee2e6;margin-bottom:1em}#quarto-appendix.default .quarto-appendix-heading{font-size:1em !important}#quarto-appendix.default *[role=doc-endnotes]>ol,#quarto-appendix.default .quarto-appendix-contents>*:not(h2):not(.h2){font-size:.9em}#quarto-appendix.default section{padding-bottom:1.5em}#quarto-appendix.default section *[role=doc-endnotes],#quarto-appendix.default section>*:not(a){opacity:.9;word-wrap:break-word}.btn.btn-quarto,div.cell-output-display .btn-quarto{color:#cbcccc;background-color:#373a3c;border-color:#373a3c}.btn.btn-quarto:hover,div.cell-output-display .btn-quarto:hover{color:#cbcccc;background-color:#555859;border-color:#4b4e50}.btn-check:focus+.btn.btn-quarto,.btn.btn-quarto:focus,.btn-check:focus+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:focus{color:#cbcccc;background-color:#555859;border-color:#4b4e50;box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn-check:checked+.btn.btn-quarto,.btn-check:active+.btn.btn-quarto,.btn.btn-quarto:active,.btn.btn-quarto.active,.show>.btn.btn-quarto.dropdown-toggle,.btn-check:checked+div.cell-output-display .btn-quarto,.btn-check:active+div.cell-output-display .btn-quarto,div.cell-output-display .btn-quarto:active,div.cell-output-display .btn-quarto.active,.show>div.cell-output-display .btn-quarto.dropdown-toggle{color:#fff;background-color:#5f6163;border-color:#4b4e50}.btn-check:checked+.btn.btn-quarto:focus,.btn-check:active+.btn.btn-quarto:focus,.btn.btn-quarto:active:focus,.btn.btn-quarto.active:focus,.show>.btn.btn-quarto.dropdown-toggle:focus,.btn-check:checked+div.cell-output-display .btn-quarto:focus,.btn-check:active+div.cell-output-display .btn-quarto:focus,div.cell-output-display .btn-quarto:active:focus,div.cell-output-display .btn-quarto.active:focus,.show>div.cell-output-display .btn-quarto.dropdown-toggle:focus{box-shadow:0 0 0 .25rem rgba(77,80,82,.5)}.btn.btn-quarto:disabled,.btn.btn-quarto.disabled,div.cell-output-display .btn-quarto:disabled,div.cell-output-display .btn-quarto.disabled{color:#fff;background-color:#373a3c;border-color:#373a3c}nav.quarto-secondary-nav.color-navbar{background-color:#f8f9fa;color:#545555}nav.quarto-secondary-nav.color-navbar h1,nav.quarto-secondary-nav.color-navbar .h1,nav.quarto-secondary-nav.color-navbar .quarto-btn-toggle{color:#545555}@media(max-width: 991.98px){body.nav-sidebar .quarto-title-banner{margin-bottom:0;padding-bottom:0}body.nav-sidebar #title-block-header{margin-block-end:0}}p.subtitle{margin-top:.25em;margin-bottom:.5em}code a:any-link{color:inherit;text-decoration-color:#6c757d}/*! light */div.observablehq table thead tr th{background-color:var(--bs-body-bg)}input,button,select,optgroup,textarea{background-color:var(--bs-body-bg)}.code-annotated .code-copy-button{margin-right:1.25em;margin-top:0;padding-bottom:0;padding-top:3px}.code-annotation-gutter-bg{background-color:#fff}.code-annotation-gutter{background-color:rgba(233,236,239,.65)}.code-annotation-gutter,.code-annotation-gutter-bg{height:100%;width:calc(20px + .5em);position:absolute;top:0;right:0}dl.code-annotation-container-grid dt{margin-right:1em;margin-top:.25rem}dl.code-annotation-container-grid dt{font-family:var(--bs-font-monospace);color:#4f5457;border:solid #4f5457 1px;border-radius:50%;height:22px;width:22px;line-height:22px;font-size:11px;text-align:center;vertical-align:middle;text-decoration:none}dl.code-annotation-container-grid dt[data-target-cell]{cursor:pointer}dl.code-annotation-container-grid dt[data-target-cell].code-annotation-active{color:#fff;border:solid #aaa 1px;background-color:#aaa}pre.code-annotation-code{padding-top:0;padding-bottom:0}pre.code-annotation-code code{z-index:3}#code-annotation-line-highlight-gutter{width:100%;border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}#code-annotation-line-highlight{margin-left:-4em;width:calc(100% + 4em);border-top:solid rgba(170,170,170,.2666666667) 1px;border-bottom:solid rgba(170,170,170,.2666666667) 1px;z-index:2;background-color:rgba(170,170,170,.1333333333)}code.sourceCode .code-annotation-anchor.code-annotation-active{background-color:var(--quarto-hl-normal-color, #aaaaaa);border:solid var(--quarto-hl-normal-color, #aaaaaa) 1px;color:#e9ecef;font-weight:bolder}code.sourceCode .code-annotation-anchor{font-family:var(--bs-font-monospace);color:var(--quarto-hl-co-color);border:solid var(--quarto-hl-co-color) 1px;border-radius:50%;height:18px;width:18px;font-size:9px;margin-top:2px}code.sourceCode button.code-annotation-anchor{padding:2px}code.sourceCode a.code-annotation-anchor{line-height:18px;text-align:center;vertical-align:middle;cursor:default;text-decoration:none}@media print{.page-columns .column-screen-inset{grid-column:page-start-inset/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset table{background:#fff}.page-columns .column-screen-inset-left{grid-column:page-start-inset/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-left table{background:#fff}.page-columns .column-screen-inset-right{grid-column:body-content-start/page-end-inset;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-inset-right table{background:#fff}.page-columns .column-screen{grid-column:page-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen table{background:#fff}.page-columns .column-screen-left{grid-column:page-start/body-content-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-left table{background:#fff}.page-columns .column-screen-right{grid-column:body-content-start/page-end;z-index:998;transform:translate3d(0, 0, 0)}.page-columns .column-screen-right table{background:#fff}.page-columns .column-screen-inset-shaded{grid-column:page-start-inset/page-end-inset;padding:1em;background:#f8f9fa;z-index:998;transform:translate3d(0, 0, 0);margin-bottom:1em}}.quarto-video{margin-bottom:1em}.table>thead{border-top-width:0}.table>:not(caption)>*:not(:last-child)>*{border-bottom-color:#ebeced;border-bottom-style:solid;border-bottom-width:1px}.table>:not(:first-child){border-top:1px solid #b6babc;border-bottom:1px solid inherit}.table tbody{border-bottom-color:#b6babc}a.external:after{display:inline-block;height:.75rem;width:.75rem;margin-bottom:.15em;margin-left:.25em;content:"";vertical-align:-0.125em;background-image:url('data:image/svg+xml,');background-repeat:no-repeat;background-size:.75rem .75rem}div.sourceCode code a.external:after{content:none}a.external:after:hover{cursor:pointer}.quarto-ext-icon{display:inline-block;font-size:.75em;padding-left:.3em}.code-with-filename .code-with-filename-file{margin-bottom:0;padding-bottom:2px;padding-top:2px;padding-left:.7em;border:var(--quarto-border-width) solid var(--quarto-border-color);border-radius:var(--quarto-border-radius);border-bottom:0;border-bottom-left-radius:0%;border-bottom-right-radius:0%}.code-with-filename div.sourceCode,.reveal .code-with-filename div.sourceCode{margin-top:0;border-top-left-radius:0%;border-top-right-radius:0%}.code-with-filename .code-with-filename-file pre{margin-bottom:0}.code-with-filename .code-with-filename-file,.code-with-filename .code-with-filename-file pre{background-color:rgba(219,219,219,.8)}.quarto-dark .code-with-filename .code-with-filename-file,.quarto-dark .code-with-filename .code-with-filename-file pre{background-color:#555}.code-with-filename .code-with-filename-file strong{font-weight:400}.quarto-title-banner{margin-bottom:1em;color:#545555;background:#f8f9fa}.quarto-title-banner .code-tools-button{color:#878888}.quarto-title-banner .code-tools-button:hover{color:#545555}.quarto-title-banner .code-tools-button>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .code-tools-button:hover>.bi::before{background-image:url('data:image/svg+xml,')}.quarto-title-banner .quarto-title .title{font-weight:600}.quarto-title-banner .quarto-categories{margin-top:.75em}@media(min-width: 992px){.quarto-title-banner{padding-top:2.5em;padding-bottom:2.5em}}@media(max-width: 991.98px){.quarto-title-banner{padding-top:1em;padding-bottom:1em}}main.quarto-banner-title-block>section:first-child>h2,main.quarto-banner-title-block>section:first-child>.h2,main.quarto-banner-title-block>section:first-child>h3,main.quarto-banner-title-block>section:first-child>.h3,main.quarto-banner-title-block>section:first-child>h4,main.quarto-banner-title-block>section:first-child>.h4{margin-top:0}.quarto-title .quarto-categories{display:flex;flex-wrap:wrap;row-gap:.5em;column-gap:.4em;padding-bottom:.5em;margin-top:.75em}.quarto-title .quarto-categories .quarto-category{padding:.25em .75em;font-size:.65em;text-transform:uppercase;border:solid 1px;border-radius:.25rem;opacity:.6}.quarto-title .quarto-categories .quarto-category a{color:inherit}#title-block-header.quarto-title-block.default .quarto-title-meta{display:grid;grid-template-columns:repeat(2, 1fr)}#title-block-header.quarto-title-block.default .quarto-title .title{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-author-orcid img{margin-top:-5px}#title-block-header.quarto-title-block.default .quarto-description p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p,#title-block-header.quarto-title-block.default .quarto-title-authors p,#title-block-header.quarto-title-block.default .quarto-title-affiliations p{margin-bottom:.1em}#title-block-header.quarto-title-block.default .quarto-title-meta-heading{text-transform:uppercase;margin-top:1em;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-contents{font-size:.9em}#title-block-header.quarto-title-block.default .quarto-title-meta-contents a{color:#373a3c}#title-block-header.quarto-title-block.default .quarto-title-meta-contents p.affiliation:last-of-type{margin-bottom:.7em}#title-block-header.quarto-title-block.default p.affiliation{margin-bottom:.1em}#title-block-header.quarto-title-block.default .description,#title-block-header.quarto-title-block.default .abstract{margin-top:0}#title-block-header.quarto-title-block.default .description>p,#title-block-header.quarto-title-block.default .abstract>p{font-size:.9em}#title-block-header.quarto-title-block.default .description>p:last-of-type,#title-block-header.quarto-title-block.default .abstract>p:last-of-type{margin-bottom:0}#title-block-header.quarto-title-block.default .description .abstract-title,#title-block-header.quarto-title-block.default .abstract .abstract-title{margin-top:1em;text-transform:uppercase;font-size:.8em;opacity:.8;font-weight:400}#title-block-header.quarto-title-block.default .quarto-title-meta-author{display:grid;grid-template-columns:1fr 1fr}.quarto-title-tools-only{display:flex;justify-content:right}body{-webkit-font-smoothing:antialiased}.badge.bg-light{color:#373a3c}.progress .progress-bar{font-size:8px;line-height:8px}/*# sourceMappingURL=d6b77e37a12f878a50f9f8a85e535bdc.css.map */ diff --git a/pr-preview/pr-55/site_libs/bootstrap/bootstrap.min.js b/site_libs/bootstrap/bootstrap.min.js similarity index 100% rename from pr-preview/pr-55/site_libs/bootstrap/bootstrap.min.js rename to site_libs/bootstrap/bootstrap.min.js diff --git a/pr-preview/pr-55/site_libs/clipboard/clipboard.min.js b/site_libs/clipboard/clipboard.min.js similarity index 100% rename from pr-preview/pr-55/site_libs/clipboard/clipboard.min.js rename to site_libs/clipboard/clipboard.min.js diff --git a/pr-preview/pr-55/site_libs/quarto-html/anchor.min.js b/site_libs/quarto-html/anchor.min.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/anchor.min.js rename to site_libs/quarto-html/anchor.min.js diff --git a/site_libs/quarto-html/light-border.css b/site_libs/quarto-html/light-border.css new file mode 100644 index 0000000..2b25c61 --- /dev/null +++ b/site_libs/quarto-html/light-border.css @@ -0,0 +1 @@ +.tippy-box[data-theme~=light-border]{background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,8,16,.15);color:#333;box-shadow:0 4px 14px -2px rgba(0,8,16,.08)}.tippy-box[data-theme~=light-border]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light-border]>.tippy-arrow:after,.tippy-box[data-theme~=light-border]>.tippy-svg-arrow:after{content:"";position:absolute;z-index:-1}.tippy-box[data-theme~=light-border]>.tippy-arrow:after{border-color:transparent;border-style:solid}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-arrow:after{border-top-color:rgba(0,8,16,.2);border-width:7px 7px 0;top:17px;left:1px}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-svg-arrow>svg{top:16px}.tippy-box[data-theme~=light-border][data-placement^=top]>.tippy-svg-arrow:after{top:17px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff;bottom:16px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-arrow:after{border-bottom-color:rgba(0,8,16,.2);border-width:0 7px 7px;bottom:17px;left:1px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-svg-arrow>svg{bottom:16px}.tippy-box[data-theme~=light-border][data-placement^=bottom]>.tippy-svg-arrow:after{bottom:17px}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-arrow:after{border-left-color:rgba(0,8,16,.2);border-width:7px 0 7px 7px;left:17px;top:1px}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-svg-arrow>svg{left:11px}.tippy-box[data-theme~=light-border][data-placement^=left]>.tippy-svg-arrow:after{left:12px}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff;right:16px}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-arrow:after{border-width:7px 7px 7px 0;right:17px;top:1px;border-right-color:rgba(0,8,16,.2)}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-svg-arrow>svg{right:11px}.tippy-box[data-theme~=light-border][data-placement^=right]>.tippy-svg-arrow:after{right:12px}.tippy-box[data-theme~=light-border]>.tippy-svg-arrow{fill:#fff}.tippy-box[data-theme~=light-border]>.tippy-svg-arrow:after{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMCA2czEuNzk2LS4wMTMgNC42Ny0zLjYxNUM1Ljg1MS45IDYuOTMuMDA2IDggMGMxLjA3LS4wMDYgMi4xNDguODg3IDMuMzQzIDIuMzg1QzE0LjIzMyA2LjAwNSAxNiA2IDE2IDZIMHoiIGZpbGw9InJnYmEoMCwgOCwgMTYsIDAuMikiLz48L3N2Zz4=);background-size:16px 6px;width:16px;height:6px} \ No newline at end of file diff --git a/pr-preview/pr-55/site_libs/quarto-html/popper.min.js b/site_libs/quarto-html/popper.min.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/popper.min.js rename to site_libs/quarto-html/popper.min.js diff --git a/site_libs/quarto-html/quarto-html.min.css b/site_libs/quarto-html/quarto-html.min.css new file mode 100644 index 0000000..c2857c3 --- /dev/null +++ b/site_libs/quarto-html/quarto-html.min.css @@ -0,0 +1 @@ +/*# sourceMappingURL=0a6b880beb84f9b6f36107a76f82c5b1.css.map */ diff --git a/pr-preview/pr-55/site_libs/quarto-html/quarto-syntax-highlighting-dark.css b/site_libs/quarto-html/quarto-syntax-highlighting-dark.css similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/quarto-syntax-highlighting-dark.css rename to site_libs/quarto-html/quarto-syntax-highlighting-dark.css diff --git a/pr-preview/pr-55/site_libs/quarto-html/quarto-syntax-highlighting.css b/site_libs/quarto-html/quarto-syntax-highlighting.css similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/quarto-syntax-highlighting.css rename to site_libs/quarto-html/quarto-syntax-highlighting.css diff --git a/pr-preview/pr-55/site_libs/quarto-html/quarto.js b/site_libs/quarto-html/quarto.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/quarto.js rename to site_libs/quarto-html/quarto.js diff --git a/site_libs/quarto-html/tabby.min.js b/site_libs/quarto-html/tabby.min.js new file mode 100644 index 0000000..4f44c7d --- /dev/null +++ b/site_libs/quarto-html/tabby.min.js @@ -0,0 +1,418 @@ +(function (root, factory) { + if (typeof define === "function" && define.amd) { + define([], function () { + return factory(root); + }); + } else if (typeof exports === "object") { + module.exports = factory(root); + } else { + root.Tabby = factory(root); + } +})( + typeof global !== "undefined" + ? global + : typeof window !== "undefined" + ? window + : this, + function (window) { + "use strict"; + + // + // Variables + // + + var defaults = { + idPrefix: "tabby-toggle_", + default: "[data-tabby-default]", + }; + + // + // Methods + // + + /** + * Merge two or more objects together. + * @param {Object} objects The objects to merge together + * @returns {Object} Merged values of defaults and options + */ + var extend = function () { + var merged = {}; + Array.prototype.forEach.call(arguments, function (obj) { + for (var key in obj) { + if (!obj.hasOwnProperty(key)) return; + merged[key] = obj[key]; + } + }); + return merged; + }; + + /** + * Emit a custom event + * @param {String} type The event type + * @param {Node} tab The tab to attach the event to + * @param {Node} details Details about the event + */ + var emitEvent = function (tab, details) { + // Create a new event + var event; + if (typeof window.CustomEvent === "function") { + event = new CustomEvent("tabby", { + bubbles: true, + cancelable: true, + detail: details, + }); + } else { + event = document.createEvent("CustomEvent"); + event.initCustomEvent("tabby", true, true, details); + } + + // Dispatch the event + tab.dispatchEvent(event); + }; + + var focusHandler = function (event) { + toggle(event.target); + }; + + var getKeyboardFocusableElements = function (element) { + return [ + ...element.querySelectorAll( + 'a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])' + ), + ].filter( + (el) => !el.hasAttribute("disabled") && !el.getAttribute("aria-hidden") + ); + }; + + /** + * Remove roles and attributes from a tab and its content + * @param {Node} tab The tab + * @param {Node} content The tab content + * @param {Object} settings User settings and options + */ + var destroyTab = function (tab, content, settings) { + // Remove the generated ID + if (tab.id.slice(0, settings.idPrefix.length) === settings.idPrefix) { + tab.id = ""; + } + + // remove event listener + tab.removeEventListener("focus", focusHandler, true); + + // Remove roles + tab.removeAttribute("role"); + tab.removeAttribute("aria-controls"); + tab.removeAttribute("aria-selected"); + tab.removeAttribute("tabindex"); + tab.closest("li").removeAttribute("role"); + content.removeAttribute("role"); + content.removeAttribute("aria-labelledby"); + content.removeAttribute("hidden"); + }; + + /** + * Add the required roles and attributes to a tab and its content + * @param {Node} tab The tab + * @param {Node} content The tab content + * @param {Object} settings User settings and options + */ + var setupTab = function (tab, content, settings) { + // Give tab an ID if it doesn't already have one + if (!tab.id) { + tab.id = settings.idPrefix + content.id; + } + + // Add roles + tab.setAttribute("role", "tab"); + tab.setAttribute("aria-controls", content.id); + tab.closest("li").setAttribute("role", "presentation"); + content.setAttribute("role", "tabpanel"); + content.setAttribute("aria-labelledby", tab.id); + + // Add selected state + if (tab.matches(settings.default)) { + tab.setAttribute("aria-selected", "true"); + } else { + tab.setAttribute("aria-selected", "false"); + content.setAttribute("hidden", "hidden"); + } + + // add focus event listender + tab.addEventListener("focus", focusHandler); + }; + + /** + * Hide a tab and its content + * @param {Node} newTab The new tab that's replacing it + */ + var hide = function (newTab) { + // Variables + var tabGroup = newTab.closest('[role="tablist"]'); + if (!tabGroup) return {}; + var tab = tabGroup.querySelector('[role="tab"][aria-selected="true"]'); + if (!tab) return {}; + var content = document.querySelector(tab.hash); + + // Hide the tab + tab.setAttribute("aria-selected", "false"); + + // Hide the content + if (!content) return { previousTab: tab }; + content.setAttribute("hidden", "hidden"); + + // Return the hidden tab and content + return { + previousTab: tab, + previousContent: content, + }; + }; + + /** + * Show a tab and its content + * @param {Node} tab The tab + * @param {Node} content The tab content + */ + var show = function (tab, content) { + tab.setAttribute("aria-selected", "true"); + content.removeAttribute("hidden"); + tab.focus(); + }; + + /** + * Toggle a new tab + * @param {Node} tab The tab to show + */ + var toggle = function (tab) { + // Make sure there's a tab to toggle and it's not already active + if (!tab || tab.getAttribute("aria-selected") == "true") return; + + // Variables + var content = document.querySelector(tab.hash); + if (!content) return; + + // Hide active tab and content + var details = hide(tab); + + // Show new tab and content + show(tab, content); + + // Add event details + details.tab = tab; + details.content = content; + + // Emit a custom event + emitEvent(tab, details); + }; + + /** + * Get all of the tabs in a tablist + * @param {Node} tab A tab from the list + * @return {Object} The tabs and the index of the currently active one + */ + var getTabsMap = function (tab) { + var tabGroup = tab.closest('[role="tablist"]'); + var tabs = tabGroup ? tabGroup.querySelectorAll('[role="tab"]') : null; + if (!tabs) return; + return { + tabs: tabs, + index: Array.prototype.indexOf.call(tabs, tab), + }; + }; + + /** + * Switch the active tab based on keyboard activity + * @param {Node} tab The currently active tab + * @param {Key} key The key that was pressed + */ + var switchTabs = function (tab, key) { + // Get a map of tabs + var map = getTabsMap(tab); + if (!map) return; + var length = map.tabs.length - 1; + var index; + + // Go to previous tab + if (["ArrowUp", "ArrowLeft", "Up", "Left"].indexOf(key) > -1) { + index = map.index < 1 ? length : map.index - 1; + } + + // Go to next tab + else if (["ArrowDown", "ArrowRight", "Down", "Right"].indexOf(key) > -1) { + index = map.index === length ? 0 : map.index + 1; + } + + // Go to home + else if (key === "Home") { + index = 0; + } + + // Go to end + else if (key === "End") { + index = length; + } + + // Toggle the tab + toggle(map.tabs[index]); + }; + + /** + * Create the Constructor object + */ + var Constructor = function (selector, options) { + // + // Variables + // + + var publicAPIs = {}; + var settings, tabWrapper; + + // + // Methods + // + + publicAPIs.destroy = function () { + // Get all tabs + var tabs = tabWrapper.querySelectorAll("a"); + + // Add roles to tabs + Array.prototype.forEach.call(tabs, function (tab) { + // Get the tab content + var content = document.querySelector(tab.hash); + if (!content) return; + + // Setup the tab + destroyTab(tab, content, settings); + }); + + // Remove role from wrapper + tabWrapper.removeAttribute("role"); + + // Remove event listeners + document.documentElement.removeEventListener( + "click", + clickHandler, + true + ); + tabWrapper.removeEventListener("keydown", keyHandler, true); + + // Reset variables + settings = null; + tabWrapper = null; + }; + + /** + * Setup the DOM with the proper attributes + */ + publicAPIs.setup = function () { + // Variables + tabWrapper = document.querySelector(selector); + if (!tabWrapper) return; + var tabs = tabWrapper.querySelectorAll("a"); + + // Add role to wrapper + tabWrapper.setAttribute("role", "tablist"); + + // Add roles to tabs. provide dynanmic tab indexes if we are within reveal + var contentTabindexes = + window.document.body.classList.contains("reveal-viewport"); + var nextTabindex = 1; + Array.prototype.forEach.call(tabs, function (tab) { + if (contentTabindexes) { + tab.setAttribute("tabindex", "" + nextTabindex++); + } else { + tab.setAttribute("tabindex", "0"); + } + + // Get the tab content + var content = document.querySelector(tab.hash); + if (!content) return; + + // set tab indexes for content + if (contentTabindexes) { + getKeyboardFocusableElements(content).forEach(function (el) { + el.setAttribute("tabindex", "" + nextTabindex++); + }); + } + + // Setup the tab + setupTab(tab, content, settings); + }); + }; + + /** + * Toggle a tab based on an ID + * @param {String|Node} id The tab to toggle + */ + publicAPIs.toggle = function (id) { + // Get the tab + var tab = id; + if (typeof id === "string") { + tab = document.querySelector( + selector + ' [role="tab"][href*="' + id + '"]' + ); + } + + // Toggle the tab + toggle(tab); + }; + + /** + * Handle click events + */ + var clickHandler = function (event) { + // Only run on toggles + var tab = event.target.closest(selector + ' [role="tab"]'); + if (!tab) return; + + // Prevent link behavior + event.preventDefault(); + + // Toggle the tab + toggle(tab); + }; + + /** + * Handle keydown events + */ + var keyHandler = function (event) { + // Only run if a tab is in focus + var tab = document.activeElement; + if (!tab.matches(selector + ' [role="tab"]')) return; + + // Only run for specific keys + if (["Home", "End"].indexOf(event.key) < 0) return; + + // Switch tabs + switchTabs(tab, event.key); + }; + + /** + * Initialize the instance + */ + var init = function () { + // Merge user options with defaults + settings = extend(defaults, options || {}); + + // Setup the DOM + publicAPIs.setup(); + + // Add event listeners + document.documentElement.addEventListener("click", clickHandler, true); + tabWrapper.addEventListener("keydown", keyHandler, true); + }; + + // + // Initialize and return the Public APIs + // + + init(); + return publicAPIs; + }; + + // + // Return the Constructor + // + + return Constructor; + } +); diff --git a/pr-preview/pr-55/site_libs/quarto-html/tippy.css b/site_libs/quarto-html/tippy.css similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/tippy.css rename to site_libs/quarto-html/tippy.css diff --git a/pr-preview/pr-55/site_libs/quarto-html/tippy.umd.min.js b/site_libs/quarto-html/tippy.umd.min.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-html/tippy.umd.min.js rename to site_libs/quarto-html/tippy.umd.min.js diff --git a/pr-preview/pr-55/site_libs/quarto-nav/quarto-nav.js b/site_libs/quarto-nav/quarto-nav.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-nav/quarto-nav.js rename to site_libs/quarto-nav/quarto-nav.js diff --git a/pr-preview/pr-55/site_libs/quarto-search/autocomplete.umd.js b/site_libs/quarto-search/autocomplete.umd.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-search/autocomplete.umd.js rename to site_libs/quarto-search/autocomplete.umd.js diff --git a/pr-preview/pr-55/site_libs/quarto-search/fuse.min.js b/site_libs/quarto-search/fuse.min.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-search/fuse.min.js rename to site_libs/quarto-search/fuse.min.js diff --git a/pr-preview/pr-55/site_libs/quarto-search/quarto-search.js b/site_libs/quarto-search/quarto-search.js similarity index 100% rename from pr-preview/pr-55/site_libs/quarto-search/quarto-search.js rename to site_libs/quarto-search/quarto-search.js diff --git a/site_libs/revealjs/dist/reset.css b/site_libs/revealjs/dist/reset.css new file mode 100644 index 0000000..e238539 --- /dev/null +++ b/site_libs/revealjs/dist/reset.css @@ -0,0 +1,30 @@ +/* http://meyerweb.com/eric/tools/css/reset/ + v4.0 | 20180602 + License: none (public domain) +*/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +main, menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, main, menu, nav, section { + display: block; +} \ No newline at end of file diff --git a/site_libs/revealjs/dist/reveal.css b/site_libs/revealjs/dist/reveal.css new file mode 100644 index 0000000..5f80fd0 --- /dev/null +++ b/site_libs/revealjs/dist/reveal.css @@ -0,0 +1,8 @@ +/*! +* reveal.js 4.3.1 +* https://revealjs.com +* MIT licensed +* +* Copyright (C) 2011-2022 Hakim El Hattab, https://hakim.se +*/ +.reveal .r-stretch,.reveal .stretch{max-width:none;max-height:none}.reveal pre.r-stretch code,.reveal pre.stretch code{height:100%;max-height:100%;box-sizing:border-box}.reveal .r-fit-text{display:inline-block;white-space:nowrap}.reveal .r-stack{display:grid}.reveal .r-stack>*{grid-area:1/1;margin:auto}.reveal .r-hstack,.reveal .r-vstack{display:flex}.reveal .r-hstack img,.reveal .r-hstack video,.reveal .r-vstack img,.reveal .r-vstack video{min-width:0;min-height:0;-o-object-fit:contain;object-fit:contain}.reveal .r-vstack{flex-direction:column;align-items:center;justify-content:center}.reveal .r-hstack{flex-direction:row;align-items:center;justify-content:center}.reveal .items-stretch{align-items:stretch}.reveal .items-start{align-items:flex-start}.reveal .items-center{align-items:center}.reveal .items-end{align-items:flex-end}.reveal .justify-between{justify-content:space-between}.reveal .justify-around{justify-content:space-around}.reveal .justify-start{justify-content:flex-start}.reveal .justify-center{justify-content:center}.reveal .justify-end{justify-content:flex-end}html.reveal-full-page{width:100%;height:100%;height:100vh;height:calc(var(--vh,1vh) * 100);overflow:hidden}.reveal-viewport{height:100%;overflow:hidden;position:relative;line-height:1;margin:0;background-color:#fff;color:#000}.reveal-viewport:-webkit-full-screen{top:0!important;left:0!important;width:100%!important;height:100%!important;transform:none!important}.reveal-viewport:-ms-fullscreen{top:0!important;left:0!important;width:100%!important;height:100%!important;transform:none!important}.reveal-viewport:fullscreen{top:0!important;left:0!important;width:100%!important;height:100%!important;transform:none!important}.reveal .slides section .fragment{opacity:0;visibility:hidden;transition:all .2s ease;will-change:opacity}.reveal .slides section .fragment.visible{opacity:1;visibility:inherit}.reveal .slides section .fragment.disabled{transition:none}.reveal .slides section .fragment.grow{opacity:1;visibility:inherit}.reveal .slides section .fragment.grow.visible{transform:scale(1.3)}.reveal .slides section .fragment.shrink{opacity:1;visibility:inherit}.reveal .slides section .fragment.shrink.visible{transform:scale(.7)}.reveal .slides section .fragment.zoom-in{transform:scale(.1)}.reveal .slides section .fragment.zoom-in.visible{transform:none}.reveal .slides section .fragment.fade-out{opacity:1;visibility:inherit}.reveal .slides section .fragment.fade-out.visible{opacity:0;visibility:hidden}.reveal .slides section .fragment.semi-fade-out{opacity:1;visibility:inherit}.reveal .slides section .fragment.semi-fade-out.visible{opacity:.5;visibility:inherit}.reveal .slides section .fragment.strike{opacity:1;visibility:inherit}.reveal .slides section .fragment.strike.visible{text-decoration:line-through}.reveal .slides section .fragment.fade-up{transform:translate(0,40px)}.reveal .slides section .fragment.fade-up.visible{transform:translate(0,0)}.reveal .slides section .fragment.fade-down{transform:translate(0,-40px)}.reveal .slides section .fragment.fade-down.visible{transform:translate(0,0)}.reveal .slides section .fragment.fade-right{transform:translate(-40px,0)}.reveal .slides section .fragment.fade-right.visible{transform:translate(0,0)}.reveal .slides section .fragment.fade-left{transform:translate(40px,0)}.reveal .slides section .fragment.fade-left.visible{transform:translate(0,0)}.reveal .slides section .fragment.current-visible,.reveal .slides section .fragment.fade-in-then-out{opacity:0;visibility:hidden}.reveal .slides section .fragment.current-visible.current-fragment,.reveal .slides section .fragment.fade-in-then-out.current-fragment{opacity:1;visibility:inherit}.reveal .slides section .fragment.fade-in-then-semi-out{opacity:0;visibility:hidden}.reveal .slides section .fragment.fade-in-then-semi-out.visible{opacity:.5;visibility:inherit}.reveal .slides section .fragment.fade-in-then-semi-out.current-fragment{opacity:1;visibility:inherit}.reveal .slides section .fragment.highlight-blue,.reveal .slides section .fragment.highlight-current-blue,.reveal .slides section .fragment.highlight-current-green,.reveal .slides section .fragment.highlight-current-red,.reveal .slides section .fragment.highlight-green,.reveal .slides section .fragment.highlight-red{opacity:1;visibility:inherit}.reveal .slides section .fragment.highlight-red.visible{color:#ff2c2d}.reveal .slides section .fragment.highlight-green.visible{color:#17ff2e}.reveal .slides section .fragment.highlight-blue.visible{color:#1b91ff}.reveal .slides section .fragment.highlight-current-red.current-fragment{color:#ff2c2d}.reveal .slides section .fragment.highlight-current-green.current-fragment{color:#17ff2e}.reveal .slides section .fragment.highlight-current-blue.current-fragment{color:#1b91ff}.reveal:after{content:"";font-style:italic}.reveal iframe{z-index:1}.reveal a{position:relative}@keyframes bounce-right{0%,10%,25%,40%,50%{transform:translateX(0)}20%{transform:translateX(10px)}30%{transform:translateX(-5px)}}@keyframes bounce-left{0%,10%,25%,40%,50%{transform:translateX(0)}20%{transform:translateX(-10px)}30%{transform:translateX(5px)}}@keyframes bounce-down{0%,10%,25%,40%,50%{transform:translateY(0)}20%{transform:translateY(10px)}30%{transform:translateY(-5px)}}.reveal .controls{display:none;position:absolute;top:auto;bottom:12px;right:12px;left:auto;z-index:11;color:#000;pointer-events:none;font-size:10px}.reveal .controls button{position:absolute;padding:0;background-color:transparent;border:0;outline:0;cursor:pointer;color:currentColor;transform:scale(.9999);transition:color .2s ease,opacity .2s ease,transform .2s ease;z-index:2;pointer-events:auto;font-size:inherit;visibility:hidden;opacity:0;-webkit-appearance:none;-webkit-tap-highlight-color:transparent}.reveal .controls .controls-arrow:after,.reveal .controls .controls-arrow:before{content:"";position:absolute;top:0;left:0;width:2.6em;height:.5em;border-radius:.25em;background-color:currentColor;transition:all .15s ease,background-color .8s ease;transform-origin:.2em 50%;will-change:transform}.reveal .controls .controls-arrow{position:relative;width:3.6em;height:3.6em}.reveal .controls .controls-arrow:before{transform:translateX(.5em) translateY(1.55em) rotate(45deg)}.reveal .controls .controls-arrow:after{transform:translateX(.5em) translateY(1.55em) rotate(-45deg)}.reveal .controls .controls-arrow:hover:before{transform:translateX(.5em) translateY(1.55em) rotate(40deg)}.reveal .controls .controls-arrow:hover:after{transform:translateX(.5em) translateY(1.55em) rotate(-40deg)}.reveal .controls .controls-arrow:active:before{transform:translateX(.5em) translateY(1.55em) rotate(36deg)}.reveal .controls .controls-arrow:active:after{transform:translateX(.5em) translateY(1.55em) rotate(-36deg)}.reveal .controls .navigate-left{right:6.4em;bottom:3.2em;transform:translateX(-10px)}.reveal .controls .navigate-left.highlight{animation:bounce-left 2s 50 both ease-out}.reveal .controls .navigate-right{right:0;bottom:3.2em;transform:translateX(10px)}.reveal .controls .navigate-right .controls-arrow{transform:rotate(180deg)}.reveal .controls .navigate-right.highlight{animation:bounce-right 2s 50 both ease-out}.reveal .controls .navigate-up{right:3.2em;bottom:6.4em;transform:translateY(-10px)}.reveal .controls .navigate-up .controls-arrow{transform:rotate(90deg)}.reveal .controls .navigate-down{right:3.2em;bottom:-1.4em;padding-bottom:1.4em;transform:translateY(10px)}.reveal .controls .navigate-down .controls-arrow{transform:rotate(-90deg)}.reveal .controls .navigate-down.highlight{animation:bounce-down 2s 50 both ease-out}.reveal .controls[data-controls-back-arrows=faded] .navigate-up.enabled{opacity:.3}.reveal .controls[data-controls-back-arrows=faded] .navigate-up.enabled:hover{opacity:1}.reveal .controls[data-controls-back-arrows=hidden] .navigate-up.enabled{opacity:0;visibility:hidden}.reveal .controls .enabled{visibility:visible;opacity:.9;cursor:pointer;transform:none}.reveal .controls .enabled.fragmented{opacity:.5}.reveal .controls .enabled.fragmented:hover,.reveal .controls .enabled:hover{opacity:1}.reveal:not(.rtl) .controls[data-controls-back-arrows=faded] .navigate-left.enabled{opacity:.3}.reveal:not(.rtl) .controls[data-controls-back-arrows=faded] .navigate-left.enabled:hover{opacity:1}.reveal:not(.rtl) .controls[data-controls-back-arrows=hidden] .navigate-left.enabled{opacity:0;visibility:hidden}.reveal.rtl .controls[data-controls-back-arrows=faded] .navigate-right.enabled{opacity:.3}.reveal.rtl .controls[data-controls-back-arrows=faded] .navigate-right.enabled:hover{opacity:1}.reveal.rtl .controls[data-controls-back-arrows=hidden] .navigate-right.enabled{opacity:0;visibility:hidden}.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-down,.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-up{display:none}.reveal:not(.has-vertical-slides) .controls .navigate-left,.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-left{bottom:1.4em;right:5.5em}.reveal:not(.has-vertical-slides) .controls .navigate-right,.reveal[data-navigation-mode=linear].has-horizontal-slides .navigate-right{bottom:1.4em;right:.5em}.reveal:not(.has-horizontal-slides) .controls .navigate-up{right:1.4em;bottom:5em}.reveal:not(.has-horizontal-slides) .controls .navigate-down{right:1.4em;bottom:.5em}.reveal.has-dark-background .controls{color:#fff}.reveal.has-light-background .controls{color:#000}.reveal.no-hover .controls .controls-arrow:active:before,.reveal.no-hover .controls .controls-arrow:hover:before{transform:translateX(.5em) translateY(1.55em) rotate(45deg)}.reveal.no-hover .controls .controls-arrow:active:after,.reveal.no-hover .controls .controls-arrow:hover:after{transform:translateX(.5em) translateY(1.55em) rotate(-45deg)}@media screen and (min-width:500px){.reveal .controls[data-controls-layout=edges]{top:0;right:0;bottom:0;left:0}.reveal .controls[data-controls-layout=edges] .navigate-down,.reveal .controls[data-controls-layout=edges] .navigate-left,.reveal .controls[data-controls-layout=edges] .navigate-right,.reveal .controls[data-controls-layout=edges] .navigate-up{bottom:auto;right:auto}.reveal .controls[data-controls-layout=edges] .navigate-left{top:50%;left:.8em;margin-top:-1.8em}.reveal .controls[data-controls-layout=edges] .navigate-right{top:50%;right:.8em;margin-top:-1.8em}.reveal .controls[data-controls-layout=edges] .navigate-up{top:.8em;left:50%;margin-left:-1.8em}.reveal .controls[data-controls-layout=edges] .navigate-down{bottom:-.3em;left:50%;margin-left:-1.8em}}.reveal .progress{position:absolute;display:none;height:3px;width:100%;bottom:0;left:0;z-index:10;background-color:rgba(0,0,0,.2);color:#fff}.reveal .progress:after{content:"";display:block;position:absolute;height:10px;width:100%;top:-10px}.reveal .progress span{display:block;height:100%;width:100%;background-color:currentColor;transition:transform .8s cubic-bezier(.26,.86,.44,.985);transform-origin:0 0;transform:scaleX(0)}.reveal .slide-number{position:absolute;display:block;right:8px;bottom:8px;z-index:31;font-family:Helvetica,sans-serif;font-size:12px;line-height:1;color:#fff;background-color:rgba(0,0,0,.4);padding:5px}.reveal .slide-number a{color:currentColor}.reveal .slide-number-delimiter{margin:0 3px}.reveal{position:relative;width:100%;height:100%;overflow:hidden;touch-action:pinch-zoom}.reveal.embedded{touch-action:pan-y}.reveal .slides{position:absolute;width:100%;height:100%;top:0;right:0;bottom:0;left:0;margin:auto;pointer-events:none;overflow:visible;z-index:1;text-align:center;perspective:600px;perspective-origin:50% 40%}.reveal .slides>section{perspective:600px}.reveal .slides>section,.reveal .slides>section>section{display:none;position:absolute;width:100%;pointer-events:auto;z-index:10;transform-style:flat;transition:transform-origin .8s cubic-bezier(.26,.86,.44,.985),transform .8s cubic-bezier(.26,.86,.44,.985),visibility .8s cubic-bezier(.26,.86,.44,.985),opacity .8s cubic-bezier(.26,.86,.44,.985)}.reveal[data-transition-speed=fast] .slides section{transition-duration:.4s}.reveal[data-transition-speed=slow] .slides section{transition-duration:1.2s}.reveal .slides section[data-transition-speed=fast]{transition-duration:.4s}.reveal .slides section[data-transition-speed=slow]{transition-duration:1.2s}.reveal .slides>section.stack{padding-top:0;padding-bottom:0;pointer-events:none;height:100%}.reveal .slides>section.present,.reveal .slides>section>section.present{display:block;z-index:11;opacity:1}.reveal .slides>section:empty,.reveal .slides>section>section:empty,.reveal .slides>section>section[data-background-interactive],.reveal .slides>section[data-background-interactive]{pointer-events:none}.reveal.center,.reveal.center .slides,.reveal.center .slides section{min-height:0!important}.reveal .slides>section:not(.present),.reveal .slides>section>section:not(.present){pointer-events:none}.reveal.overview .slides>section,.reveal.overview .slides>section>section{pointer-events:auto}.reveal .slides>section.future,.reveal .slides>section.past,.reveal .slides>section>section.future,.reveal .slides>section>section.past{opacity:0}.reveal .slides>section[data-transition=slide].past,.reveal .slides>section[data-transition~=slide-out].past,.reveal.slide .slides>section:not([data-transition]).past{transform:translate(-150%,0)}.reveal .slides>section[data-transition=slide].future,.reveal .slides>section[data-transition~=slide-in].future,.reveal.slide .slides>section:not([data-transition]).future{transform:translate(150%,0)}.reveal .slides>section>section[data-transition=slide].past,.reveal .slides>section>section[data-transition~=slide-out].past,.reveal.slide .slides>section>section:not([data-transition]).past{transform:translate(0,-150%)}.reveal .slides>section>section[data-transition=slide].future,.reveal .slides>section>section[data-transition~=slide-in].future,.reveal.slide .slides>section>section:not([data-transition]).future{transform:translate(0,150%)}.reveal .slides>section[data-transition=linear].past,.reveal .slides>section[data-transition~=linear-out].past,.reveal.linear .slides>section:not([data-transition]).past{transform:translate(-150%,0)}.reveal .slides>section[data-transition=linear].future,.reveal .slides>section[data-transition~=linear-in].future,.reveal.linear .slides>section:not([data-transition]).future{transform:translate(150%,0)}.reveal .slides>section>section[data-transition=linear].past,.reveal .slides>section>section[data-transition~=linear-out].past,.reveal.linear .slides>section>section:not([data-transition]).past{transform:translate(0,-150%)}.reveal .slides>section>section[data-transition=linear].future,.reveal .slides>section>section[data-transition~=linear-in].future,.reveal.linear .slides>section>section:not([data-transition]).future{transform:translate(0,150%)}.reveal .slides section[data-transition=default].stack,.reveal.default .slides section.stack{transform-style:preserve-3d}.reveal .slides>section[data-transition=default].past,.reveal .slides>section[data-transition~=default-out].past,.reveal.default .slides>section:not([data-transition]).past{transform:translate3d(-100%,0,0) rotateY(-90deg) translate3d(-100%,0,0)}.reveal .slides>section[data-transition=default].future,.reveal .slides>section[data-transition~=default-in].future,.reveal.default .slides>section:not([data-transition]).future{transform:translate3d(100%,0,0) rotateY(90deg) translate3d(100%,0,0)}.reveal .slides>section>section[data-transition=default].past,.reveal .slides>section>section[data-transition~=default-out].past,.reveal.default .slides>section>section:not([data-transition]).past{transform:translate3d(0,-300px,0) rotateX(70deg) translate3d(0,-300px,0)}.reveal .slides>section>section[data-transition=default].future,.reveal .slides>section>section[data-transition~=default-in].future,.reveal.default .slides>section>section:not([data-transition]).future{transform:translate3d(0,300px,0) rotateX(-70deg) translate3d(0,300px,0)}.reveal .slides section[data-transition=convex].stack,.reveal.convex .slides section.stack{transform-style:preserve-3d}.reveal .slides>section[data-transition=convex].past,.reveal .slides>section[data-transition~=convex-out].past,.reveal.convex .slides>section:not([data-transition]).past{transform:translate3d(-100%,0,0) rotateY(-90deg) translate3d(-100%,0,0)}.reveal .slides>section[data-transition=convex].future,.reveal .slides>section[data-transition~=convex-in].future,.reveal.convex .slides>section:not([data-transition]).future{transform:translate3d(100%,0,0) rotateY(90deg) translate3d(100%,0,0)}.reveal .slides>section>section[data-transition=convex].past,.reveal .slides>section>section[data-transition~=convex-out].past,.reveal.convex .slides>section>section:not([data-transition]).past{transform:translate3d(0,-300px,0) rotateX(70deg) translate3d(0,-300px,0)}.reveal .slides>section>section[data-transition=convex].future,.reveal .slides>section>section[data-transition~=convex-in].future,.reveal.convex .slides>section>section:not([data-transition]).future{transform:translate3d(0,300px,0) rotateX(-70deg) translate3d(0,300px,0)}.reveal .slides section[data-transition=concave].stack,.reveal.concave .slides section.stack{transform-style:preserve-3d}.reveal .slides>section[data-transition=concave].past,.reveal .slides>section[data-transition~=concave-out].past,.reveal.concave .slides>section:not([data-transition]).past{transform:translate3d(-100%,0,0) rotateY(90deg) translate3d(-100%,0,0)}.reveal .slides>section[data-transition=concave].future,.reveal .slides>section[data-transition~=concave-in].future,.reveal.concave .slides>section:not([data-transition]).future{transform:translate3d(100%,0,0) rotateY(-90deg) translate3d(100%,0,0)}.reveal .slides>section>section[data-transition=concave].past,.reveal .slides>section>section[data-transition~=concave-out].past,.reveal.concave .slides>section>section:not([data-transition]).past{transform:translate3d(0,-80%,0) rotateX(-70deg) translate3d(0,-80%,0)}.reveal .slides>section>section[data-transition=concave].future,.reveal .slides>section>section[data-transition~=concave-in].future,.reveal.concave .slides>section>section:not([data-transition]).future{transform:translate3d(0,80%,0) rotateX(70deg) translate3d(0,80%,0)}.reveal .slides section[data-transition=zoom],.reveal.zoom .slides section:not([data-transition]){transition-timing-function:ease}.reveal .slides>section[data-transition=zoom].past,.reveal .slides>section[data-transition~=zoom-out].past,.reveal.zoom .slides>section:not([data-transition]).past{visibility:hidden;transform:scale(16)}.reveal .slides>section[data-transition=zoom].future,.reveal .slides>section[data-transition~=zoom-in].future,.reveal.zoom .slides>section:not([data-transition]).future{visibility:hidden;transform:scale(.2)}.reveal .slides>section>section[data-transition=zoom].past,.reveal .slides>section>section[data-transition~=zoom-out].past,.reveal.zoom .slides>section>section:not([data-transition]).past{transform:scale(16)}.reveal .slides>section>section[data-transition=zoom].future,.reveal .slides>section>section[data-transition~=zoom-in].future,.reveal.zoom .slides>section>section:not([data-transition]).future{transform:scale(.2)}.reveal.cube .slides{perspective:1300px}.reveal.cube .slides section{padding:30px;min-height:700px;-webkit-backface-visibility:hidden;backface-visibility:hidden;box-sizing:border-box;transform-style:preserve-3d}.reveal.center.cube .slides section{min-height:0}.reveal.cube .slides section:not(.stack):before{content:"";position:absolute;display:block;width:100%;height:100%;left:0;top:0;background:rgba(0,0,0,.1);border-radius:4px;transform:translateZ(-20px)}.reveal.cube .slides section:not(.stack):after{content:"";position:absolute;display:block;width:90%;height:30px;left:5%;bottom:0;background:0 0;z-index:1;border-radius:4px;box-shadow:0 95px 25px rgba(0,0,0,.2);transform:translateZ(-90px) rotateX(65deg)}.reveal.cube .slides>section.stack{padding:0;background:0 0}.reveal.cube .slides>section.past{transform-origin:100% 0;transform:translate3d(-100%,0,0) rotateY(-90deg)}.reveal.cube .slides>section.future{transform-origin:0 0;transform:translate3d(100%,0,0) rotateY(90deg)}.reveal.cube .slides>section>section.past{transform-origin:0 100%;transform:translate3d(0,-100%,0) rotateX(90deg)}.reveal.cube .slides>section>section.future{transform-origin:0 0;transform:translate3d(0,100%,0) rotateX(-90deg)}.reveal.page .slides{perspective-origin:0 50%;perspective:3000px}.reveal.page .slides section{padding:30px;min-height:700px;box-sizing:border-box;transform-style:preserve-3d}.reveal.page .slides section.past{z-index:12}.reveal.page .slides section:not(.stack):before{content:"";position:absolute;display:block;width:100%;height:100%;left:0;top:0;background:rgba(0,0,0,.1);transform:translateZ(-20px)}.reveal.page .slides section:not(.stack):after{content:"";position:absolute;display:block;width:90%;height:30px;left:5%;bottom:0;background:0 0;z-index:1;border-radius:4px;box-shadow:0 95px 25px rgba(0,0,0,.2);-webkit-transform:translateZ(-90px) rotateX(65deg)}.reveal.page .slides>section.stack{padding:0;background:0 0}.reveal.page .slides>section.past{transform-origin:0 0;transform:translate3d(-40%,0,0) rotateY(-80deg)}.reveal.page .slides>section.future{transform-origin:100% 0;transform:translate3d(0,0,0)}.reveal.page .slides>section>section.past{transform-origin:0 0;transform:translate3d(0,-40%,0) rotateX(80deg)}.reveal.page .slides>section>section.future{transform-origin:0 100%;transform:translate3d(0,0,0)}.reveal .slides section[data-transition=fade],.reveal.fade .slides section:not([data-transition]),.reveal.fade .slides>section>section:not([data-transition]){transform:none;transition:opacity .5s}.reveal.fade.overview .slides section,.reveal.fade.overview .slides>section>section{transition:none}.reveal .slides section[data-transition=none],.reveal.none .slides section:not([data-transition]){transform:none;transition:none}.reveal .pause-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:#000;visibility:hidden;opacity:0;z-index:100;transition:all 1s ease}.reveal .pause-overlay .resume-button{position:absolute;bottom:20px;right:20px;color:#ccc;border-radius:2px;padding:6px 14px;border:2px solid #ccc;font-size:16px;background:0 0;cursor:pointer}.reveal .pause-overlay .resume-button:hover{color:#fff;border-color:#fff}.reveal.paused .pause-overlay{visibility:visible;opacity:1}.reveal .no-transition,.reveal .no-transition *,.reveal .slides.disable-slide-transitions section{transition:none!important}.reveal .slides.disable-slide-transitions section{transform:none!important}.reveal .backgrounds{position:absolute;width:100%;height:100%;top:0;left:0;perspective:600px}.reveal .slide-background{display:none;position:absolute;width:100%;height:100%;opacity:0;visibility:hidden;overflow:hidden;background-color:rgba(0,0,0,0);transition:all .8s cubic-bezier(.26,.86,.44,.985)}.reveal .slide-background-content{position:absolute;width:100%;height:100%;background-position:50% 50%;background-repeat:no-repeat;background-size:cover}.reveal .slide-background.stack{display:block}.reveal .slide-background.present{opacity:1;visibility:visible;z-index:2}.print-pdf .reveal .slide-background{opacity:1!important;visibility:visible!important}.reveal .slide-background video{position:absolute;width:100%;height:100%;max-width:none;max-height:none;top:0;left:0;-o-object-fit:cover;object-fit:cover}.reveal .slide-background[data-background-size=contain] video{-o-object-fit:contain;object-fit:contain}.reveal>.backgrounds .slide-background[data-background-transition=none],.reveal[data-background-transition=none]>.backgrounds .slide-background:not([data-background-transition]){transition:none}.reveal>.backgrounds .slide-background[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background:not([data-background-transition]){opacity:1}.reveal>.backgrounds .slide-background.past[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background.past:not([data-background-transition]){transform:translate(-100%,0)}.reveal>.backgrounds .slide-background.future[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background.future:not([data-background-transition]){transform:translate(100%,0)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){transform:translate(0,-100%)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide],.reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){transform:translate(0,100%)}.reveal>.backgrounds .slide-background.past[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(-100%,0,0) rotateY(-90deg) translate3d(-100%,0,0)}.reveal>.backgrounds .slide-background.future[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(100%,0,0) rotateY(90deg) translate3d(100%,0,0)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(0,-100%,0) rotateX(90deg) translate3d(0,-100%,0)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=convex],.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(0,100%,0) rotateX(-90deg) translate3d(0,100%,0)}.reveal>.backgrounds .slide-background.past[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(-100%,0,0) rotateY(90deg) translate3d(-100%,0,0)}.reveal>.backgrounds .slide-background.future[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(100%,0,0) rotateY(-90deg) translate3d(100%,0,0)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){opacity:0;transform:translate3d(0,-100%,0) rotateX(-90deg) translate3d(0,-100%,0)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=concave],.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){opacity:0;transform:translate3d(0,100%,0) rotateX(90deg) translate3d(0,100%,0)}.reveal>.backgrounds .slide-background[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background:not([data-background-transition]){transition-timing-function:ease}.reveal>.backgrounds .slide-background.past[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background.past:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(16)}.reveal>.backgrounds .slide-background.future[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background.future:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(.2)}.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.past:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(16)}.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=zoom],.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.future:not([data-background-transition]){opacity:0;visibility:hidden;transform:scale(.2)}.reveal[data-transition-speed=fast]>.backgrounds .slide-background{transition-duration:.4s}.reveal[data-transition-speed=slow]>.backgrounds .slide-background{transition-duration:1.2s}.reveal [data-auto-animate-target^=unmatched]{will-change:opacity}.reveal section[data-auto-animate]:not(.stack):not([data-auto-animate=running]) [data-auto-animate-target^=unmatched]{opacity:0}.reveal.overview{perspective-origin:50% 50%;perspective:700px}.reveal.overview .slides{-moz-transform-style:preserve-3d}.reveal.overview .slides section{height:100%;top:0!important;opacity:1!important;overflow:hidden;visibility:visible!important;cursor:pointer;box-sizing:border-box}.reveal.overview .slides section.present,.reveal.overview .slides section:hover{outline:10px solid rgba(150,150,150,.4);outline-offset:10px}.reveal.overview .slides section .fragment{opacity:1;transition:none}.reveal.overview .slides section:after,.reveal.overview .slides section:before{display:none!important}.reveal.overview .slides>section.stack{padding:0;top:0!important;background:0 0;outline:0;overflow:visible}.reveal.overview .backgrounds{perspective:inherit;-moz-transform-style:preserve-3d}.reveal.overview .backgrounds .slide-background{opacity:1;visibility:visible;outline:10px solid rgba(150,150,150,.1);outline-offset:10px}.reveal.overview .backgrounds .slide-background.stack{overflow:visible}.reveal.overview .slides section,.reveal.overview-deactivating .slides section{transition:none}.reveal.overview .backgrounds .slide-background,.reveal.overview-deactivating .backgrounds .slide-background{transition:none}.reveal.rtl .slides,.reveal.rtl .slides h1,.reveal.rtl .slides h2,.reveal.rtl .slides h3,.reveal.rtl .slides h4,.reveal.rtl .slides h5,.reveal.rtl .slides h6{direction:rtl;font-family:sans-serif}.reveal.rtl code,.reveal.rtl pre{direction:ltr}.reveal.rtl ol,.reveal.rtl ul{text-align:right}.reveal.rtl .progress span{transform-origin:100% 0}.reveal.has-parallax-background .backgrounds{transition:all .8s ease}.reveal.has-parallax-background[data-transition-speed=fast] .backgrounds{transition-duration:.4s}.reveal.has-parallax-background[data-transition-speed=slow] .backgrounds{transition-duration:1.2s}.reveal>.overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:1000;background:rgba(0,0,0,.9);transition:all .3s ease}.reveal>.overlay .spinner{position:absolute;display:block;top:50%;left:50%;width:32px;height:32px;margin:-16px 0 0 -16px;z-index:10;background-image:url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D);visibility:visible;opacity:.6;transition:all .3s ease}.reveal>.overlay header{position:absolute;left:0;top:0;width:100%;padding:5px;z-index:2;box-sizing:border-box}.reveal>.overlay header a{display:inline-block;width:40px;height:40px;line-height:36px;padding:0 10px;float:right;opacity:.6;box-sizing:border-box}.reveal>.overlay header a:hover{opacity:1}.reveal>.overlay header a .icon{display:inline-block;width:20px;height:20px;background-position:50% 50%;background-size:100%;background-repeat:no-repeat}.reveal>.overlay header a.close .icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC)}.reveal>.overlay header a.external .icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==)}.reveal>.overlay .viewport{position:absolute;display:flex;top:50px;right:0;bottom:0;left:0}.reveal>.overlay.overlay-preview .viewport iframe{width:100%;height:100%;max-width:100%;max-height:100%;border:0;opacity:0;visibility:hidden;transition:all .3s ease}.reveal>.overlay.overlay-preview.loaded .viewport iframe{opacity:1;visibility:visible}.reveal>.overlay.overlay-preview.loaded .viewport-inner{position:absolute;z-index:-1;left:0;top:45%;width:100%;text-align:center;letter-spacing:normal}.reveal>.overlay.overlay-preview .x-frame-error{opacity:0;transition:opacity .3s ease .3s}.reveal>.overlay.overlay-preview.loaded .x-frame-error{opacity:1}.reveal>.overlay.overlay-preview.loaded .spinner{opacity:0;visibility:hidden;transform:scale(.2)}.reveal>.overlay.overlay-help .viewport{overflow:auto;color:#fff}.reveal>.overlay.overlay-help .viewport .viewport-inner{width:600px;margin:auto;padding:20px 20px 80px 20px;text-align:center;letter-spacing:normal}.reveal>.overlay.overlay-help .viewport .viewport-inner .title{font-size:20px}.reveal>.overlay.overlay-help .viewport .viewport-inner table{border:1px solid #fff;border-collapse:collapse;font-size:16px}.reveal>.overlay.overlay-help .viewport .viewport-inner table td,.reveal>.overlay.overlay-help .viewport .viewport-inner table th{width:200px;padding:14px;border:1px solid #fff;vertical-align:middle}.reveal>.overlay.overlay-help .viewport .viewport-inner table th{padding-top:20px;padding-bottom:20px}.reveal .playback{position:absolute;left:15px;bottom:20px;z-index:30;cursor:pointer;transition:all .4s ease;-webkit-tap-highlight-color:transparent}.reveal.overview .playback{opacity:0;visibility:hidden}.reveal .hljs{min-height:100%}.reveal .hljs table{margin:initial}.reveal .hljs-ln-code,.reveal .hljs-ln-numbers{padding:0;border:0}.reveal .hljs-ln-numbers{opacity:.6;padding-right:.75em;text-align:right;vertical-align:top}.reveal .hljs.has-highlights tr:not(.highlight-line){opacity:.4}.reveal .hljs:not(:first-child).fragment{position:absolute;top:0;left:0;width:100%;box-sizing:border-box}.reveal pre[data-auto-animate-target]{overflow:hidden}.reveal pre[data-auto-animate-target] code{height:100%}.reveal .roll{display:inline-block;line-height:1.2;overflow:hidden;vertical-align:top;perspective:400px;perspective-origin:50% 50%}.reveal .roll:hover{background:0 0;text-shadow:none}.reveal .roll span{display:block;position:relative;padding:0 2px;pointer-events:none;transition:all .4s ease;transform-origin:50% 0;transform-style:preserve-3d;-webkit-backface-visibility:hidden;backface-visibility:hidden}.reveal .roll:hover span{background:rgba(0,0,0,.5);transform:translate3d(0,0,-45px) rotateX(90deg)}.reveal .roll span:after{content:attr(data-title);display:block;position:absolute;left:0;top:0;padding:0 2px;-webkit-backface-visibility:hidden;backface-visibility:hidden;transform-origin:50% 0;transform:translate3d(0,110%,0) rotateX(-90deg)}.reveal aside.notes{display:none}.reveal .speaker-notes{display:none;position:absolute;width:33.3333333333%;height:100%;top:0;left:100%;padding:14px 18px 14px 18px;z-index:1;font-size:18px;line-height:1.4;border:1px solid rgba(0,0,0,.05);color:#222;background-color:#f5f5f5;overflow:auto;box-sizing:border-box;text-align:left;font-family:Helvetica,sans-serif;-webkit-overflow-scrolling:touch}.reveal .speaker-notes .notes-placeholder{color:#ccc;font-style:italic}.reveal .speaker-notes:focus{outline:0}.reveal .speaker-notes:before{content:"Speaker notes";display:block;margin-bottom:10px;opacity:.5}.reveal.show-notes{max-width:75%;overflow:visible}.reveal.show-notes .speaker-notes{display:block}@media screen and (min-width:1600px){.reveal .speaker-notes{font-size:20px}}@media screen and (max-width:1024px){.reveal.show-notes{border-left:0;max-width:none;max-height:70%;max-height:70vh;overflow:visible}.reveal.show-notes .speaker-notes{top:100%;left:0;width:100%;height:30vh;border:0}}@media screen and (max-width:600px){.reveal.show-notes{max-height:60%;max-height:60vh}.reveal.show-notes .speaker-notes{top:100%;height:40vh}.reveal .speaker-notes{font-size:14px}}.zoomed .reveal *,.zoomed .reveal :after,.zoomed .reveal :before{-webkit-backface-visibility:visible!important;backface-visibility:visible!important}.zoomed .reveal .controls,.zoomed .reveal .progress{opacity:0}.zoomed .reveal .roll span{background:0 0}.zoomed .reveal .roll span:after{visibility:hidden}html.print-pdf *{-webkit-print-color-adjust:exact}html.print-pdf{width:100%;height:100%;overflow:visible}html.print-pdf body{margin:0 auto!important;border:0;padding:0;float:none!important;overflow:visible}html.print-pdf .nestedarrow,html.print-pdf .reveal .controls,html.print-pdf .reveal .playback,html.print-pdf .reveal .progress,html.print-pdf .reveal.overview,html.print-pdf .state-background{display:none!important}html.print-pdf .reveal pre code{overflow:hidden!important;font-family:Courier,"Courier New",monospace!important}html.print-pdf .reveal{width:auto!important;height:auto!important;overflow:hidden!important}html.print-pdf .reveal .slides{position:static;width:100%!important;height:auto!important;zoom:1!important;pointer-events:initial;left:auto;top:auto;margin:0!important;padding:0!important;overflow:visible;display:block;perspective:none;perspective-origin:50% 50%}html.print-pdf .reveal .slides .pdf-page{position:relative;overflow:hidden;z-index:1;page-break-after:always}html.print-pdf .reveal .slides section{visibility:visible!important;display:block!important;position:absolute!important;margin:0!important;padding:0!important;box-sizing:border-box!important;min-height:1px;opacity:1!important;transform-style:flat!important;transform:none!important}html.print-pdf .reveal section.stack{position:relative!important;margin:0!important;padding:0!important;page-break-after:avoid!important;height:auto!important;min-height:auto!important}html.print-pdf .reveal img{box-shadow:none}html.print-pdf .reveal .backgrounds{display:none}html.print-pdf .reveal .slide-background{display:block!important;position:absolute;top:0;left:0;width:100%;height:100%;z-index:auto!important}html.print-pdf .reveal.show-notes{max-width:none;max-height:none}html.print-pdf .reveal .speaker-notes-pdf{display:block;width:100%;height:auto;max-height:none;top:auto;right:auto;bottom:auto;left:auto;z-index:100}html.print-pdf .reveal .speaker-notes-pdf[data-layout=separate-page]{position:relative;color:inherit;background-color:transparent;padding:20px;page-break-after:always;border:0}html.print-pdf .reveal .slide-number-pdf{display:block;position:absolute;font-size:14px}html.print-pdf .aria-status{display:none}@media print{html:not(.print-pdf){background:#fff;width:auto;height:auto;overflow:visible}html:not(.print-pdf) body{background:#fff;font-size:20pt;width:auto;height:auto;border:0;margin:0 5%;padding:0;overflow:visible;float:none!important}html:not(.print-pdf) .controls,html:not(.print-pdf) .fork-reveal,html:not(.print-pdf) .nestedarrow,html:not(.print-pdf) .reveal .backgrounds,html:not(.print-pdf) .reveal .progress,html:not(.print-pdf) .reveal .slide-number,html:not(.print-pdf) .share-reveal,html:not(.print-pdf) .state-background{display:none!important}html:not(.print-pdf) body,html:not(.print-pdf) li,html:not(.print-pdf) p,html:not(.print-pdf) td{font-size:20pt!important;color:#000}html:not(.print-pdf) h1,html:not(.print-pdf) h2,html:not(.print-pdf) h3,html:not(.print-pdf) h4,html:not(.print-pdf) h5,html:not(.print-pdf) h6{color:#000!important;height:auto;line-height:normal;text-align:left;letter-spacing:normal}html:not(.print-pdf) h1{font-size:28pt!important}html:not(.print-pdf) h2{font-size:24pt!important}html:not(.print-pdf) h3{font-size:22pt!important}html:not(.print-pdf) h4{font-size:22pt!important;font-variant:small-caps}html:not(.print-pdf) h5{font-size:21pt!important}html:not(.print-pdf) h6{font-size:20pt!important;font-style:italic}html:not(.print-pdf) a:link,html:not(.print-pdf) a:visited{color:#000!important;font-weight:700;text-decoration:underline}html:not(.print-pdf) div,html:not(.print-pdf) ol,html:not(.print-pdf) p,html:not(.print-pdf) ul{visibility:visible;position:static;width:auto;height:auto;display:block;overflow:visible;margin:0;text-align:left!important}html:not(.print-pdf) .reveal pre,html:not(.print-pdf) .reveal table{margin-left:0;margin-right:0}html:not(.print-pdf) .reveal pre code{padding:20px}html:not(.print-pdf) .reveal blockquote{margin:20px 0}html:not(.print-pdf) .reveal .slides{position:static!important;width:auto!important;height:auto!important;left:0!important;top:0!important;margin-left:0!important;margin-top:0!important;padding:0!important;zoom:1!important;transform:none!important;overflow:visible!important;display:block!important;text-align:left!important;perspective:none;perspective-origin:50% 50%}html:not(.print-pdf) .reveal .slides section{visibility:visible!important;position:static!important;width:auto!important;height:auto!important;display:block!important;overflow:visible!important;left:0!important;top:0!important;margin-left:0!important;margin-top:0!important;padding:60px 20px!important;z-index:auto!important;opacity:1!important;page-break-after:always!important;transform-style:flat!important;transform:none!important;transition:none!important}html:not(.print-pdf) .reveal .slides section.stack{padding:0!important}html:not(.print-pdf) .reveal section:last-of-type{page-break-after:avoid!important}html:not(.print-pdf) .reveal section .fragment{opacity:1!important;visibility:visible!important;transform:none!important}html:not(.print-pdf) .reveal section img{display:block;margin:15px 0;background:#fff;border:1px solid #666;box-shadow:none}html:not(.print-pdf) .reveal section small{font-size:.8em}html:not(.print-pdf) .reveal .hljs{max-height:100%;white-space:pre-wrap;word-wrap:break-word;word-break:break-word;font-size:15pt}html:not(.print-pdf) .reveal .hljs .hljs-ln-numbers{white-space:nowrap}html:not(.print-pdf) .reveal .hljs td{font-size:inherit!important;color:inherit!important}} \ No newline at end of file diff --git a/site_libs/revealjs/dist/reveal.esm.js b/site_libs/revealjs/dist/reveal.esm.js new file mode 100644 index 0000000..f18da89 --- /dev/null +++ b/site_libs/revealjs/dist/reveal.esm.js @@ -0,0 +1,9 @@ +/*! +* reveal.js 4.3.1 +* https://revealjs.com +* MIT licensed +* +* Copyright (C) 2011-2022 Hakim El Hattab, https://hakim.se +*/ +const e=(e,t)=>{for(let i in t)e[i]=t[i];return e},t=(e,t)=>Array.from(e.querySelectorAll(t)),i=(e,t,i)=>{i?e.classList.add(t):e.classList.remove(t)},s=e=>{if("string"==typeof e){if("null"===e)return null;if("true"===e)return!0;if("false"===e)return!1;if(e.match(/^-?[\d\.]+$/))return parseFloat(e)}return e},a=(e,t)=>{e.style.transform=t},n=(e,t)=>{let i=e.matches||e.matchesSelector||e.msMatchesSelector;return!(!i||!i.call(e,t))},r=(e,t)=>{if("function"==typeof e.closest)return e.closest(t);for(;e;){if(n(e,t))return e;e=e.parentNode}return null},o=(e,t,i,s="")=>{let a=e.querySelectorAll("."+i);for(let t=0;t{let t=document.createElement("style");return t.type="text/css",e&&e.length>0&&(t.styleSheet?t.styleSheet.cssText=e:t.appendChild(document.createTextNode(e))),document.head.appendChild(t),t},d=()=>{let e={};location.search.replace(/[A-Z0-9]+?=([\w\.%-]*)/gi,(t=>{e[t.split("=").shift()]=t.split("=").pop()}));for(let t in e){let i=e[t];e[t]=s(unescape(i))}return void 0!==e.dependencies&&delete e.dependencies,e},c=(e,t=0)=>{if(e){let i,s=e.style.height;return e.style.height="0px",e.parentNode.style.height="auto",i=t-e.parentNode.offsetHeight,e.style.height=s+"px",e.parentNode.style.removeProperty("height"),i}return t},h={mp4:"video/mp4",m4a:"video/mp4",ogv:"video/ogg",mpeg:"video/mpeg",webm:"video/webm"},u=navigator.userAgent,g=/(iphone|ipod|ipad|android)/gi.test(u)||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1;/chrome/i.test(u)&&/edge/i.test(u);const v=/android/gi.test(u);var p={};Object.defineProperty(p,"__esModule",{value:!0});var m=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return"string"==typeof e?x(t(document.querySelectorAll(e)),i):x([e],i)[0]}}("undefined"==typeof window?null:window);class b{constructor(e){this.Reveal=e,this.startEmbeddedIframe=this.startEmbeddedIframe.bind(this)}shouldPreload(e){let t=this.Reveal.getConfig().preloadIframes;return"boolean"!=typeof t&&(t=e.hasAttribute("data-preload")),t}load(e,i={}){e.style.display=this.Reveal.getConfig().display,t(e,"img[data-src], video[data-src], audio[data-src], iframe[data-src]").forEach((e=>{("IFRAME"!==e.tagName||this.shouldPreload(e))&&(e.setAttribute("src",e.getAttribute("data-src")),e.setAttribute("data-lazy-loaded",""),e.removeAttribute("data-src"))})),t(e,"video, audio").forEach((e=>{let i=0;t(e,"source[data-src]").forEach((e=>{e.setAttribute("src",e.getAttribute("data-src")),e.removeAttribute("data-src"),e.setAttribute("data-lazy-loaded",""),i+=1})),g&&"VIDEO"===e.tagName&&e.setAttribute("playsinline",""),i>0&&e.load()}));let s=e.slideBackgroundElement;if(s){s.style.display="block";let t=e.slideBackgroundContentElement,a=e.getAttribute("data-background-iframe");if(!1===s.hasAttribute("data-loaded")){s.setAttribute("data-loaded","true");let n=e.getAttribute("data-background-image"),r=e.getAttribute("data-background-video"),o=e.hasAttribute("data-background-video-loop"),l=e.hasAttribute("data-background-video-muted");if(n)/^data:/.test(n.trim())?t.style.backgroundImage=`url(${n.trim()})`:t.style.backgroundImage=n.split(",").map((e=>`url(${encodeURI(e.trim())})`)).join(",");else if(r&&!this.Reveal.isSpeakerNotes()){let e=document.createElement("video");o&&e.setAttribute("loop",""),l&&(e.muted=!0),g&&(e.muted=!0,e.setAttribute("playsinline","")),r.split(",").forEach((t=>{let i=((e="")=>h[e.split(".").pop()])(t);e.innerHTML+=i?``:``})),t.appendChild(e)}else if(a&&!0!==i.excludeIframes){let e=document.createElement("iframe");e.setAttribute("allowfullscreen",""),e.setAttribute("mozallowfullscreen",""),e.setAttribute("webkitallowfullscreen",""),e.setAttribute("allow","autoplay"),e.setAttribute("data-src",a),e.style.width="100%",e.style.height="100%",e.style.maxHeight="100%",e.style.maxWidth="100%",t.appendChild(e)}}let n=t.querySelector("iframe[data-src]");n&&this.shouldPreload(s)&&!/autoplay=(1|true|yes)/gi.test(a)&&n.getAttribute("src")!==a&&n.setAttribute("src",a)}this.layout(e)}layout(e){Array.from(e.querySelectorAll(".r-fit-text")).forEach((e=>{f(e,{minSize:24,maxSize:.8*this.Reveal.getConfig().height,observeMutations:!1,observeWindow:!1})}))}unload(e){e.style.display="none";let i=this.Reveal.getSlideBackground(e);i&&(i.style.display="none",t(i,"iframe[src]").forEach((e=>{e.removeAttribute("src")}))),t(e,"video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]").forEach((e=>{e.setAttribute("data-src",e.getAttribute("src")),e.removeAttribute("src")})),t(e,"video[data-lazy-loaded] source[src], audio source[src]").forEach((e=>{e.setAttribute("data-src",e.getAttribute("src")),e.removeAttribute("src")}))}formatEmbeddedContent(){let e=(e,i,s)=>{t(this.Reveal.getSlidesElement(),"iframe["+e+'*="'+i+'"]').forEach((t=>{let i=t.getAttribute(e);i&&-1===i.indexOf(s)&&t.setAttribute(e,i+(/\?/.test(i)?"&":"?")+s)}))};e("src","youtube.com/embed/","enablejsapi=1"),e("data-src","youtube.com/embed/","enablejsapi=1"),e("src","player.vimeo.com/","api=1"),e("data-src","player.vimeo.com/","api=1")}startEmbeddedContent(e){e&&!this.Reveal.isSpeakerNotes()&&(t(e,'img[src$=".gif"]').forEach((e=>{e.setAttribute("src",e.getAttribute("src"))})),t(e,"video, audio").forEach((e=>{if(r(e,".fragment")&&!r(e,".fragment.visible"))return;let t=this.Reveal.getConfig().autoPlayMedia;if("boolean"!=typeof t&&(t=e.hasAttribute("data-autoplay")||!!r(e,".slide-background")),t&&"function"==typeof e.play)if(e.readyState>1)this.startEmbeddedMedia({target:e});else if(g){let t=e.play();t&&"function"==typeof t.catch&&!1===e.controls&&t.catch((()=>{e.controls=!0,e.addEventListener("play",(()=>{e.controls=!1}))}))}else e.removeEventListener("loadeddata",this.startEmbeddedMedia),e.addEventListener("loadeddata",this.startEmbeddedMedia)})),t(e,"iframe[src]").forEach((e=>{r(e,".fragment")&&!r(e,".fragment.visible")||this.startEmbeddedIframe({target:e})})),t(e,"iframe[data-src]").forEach((e=>{r(e,".fragment")&&!r(e,".fragment.visible")||e.getAttribute("src")!==e.getAttribute("data-src")&&(e.removeEventListener("load",this.startEmbeddedIframe),e.addEventListener("load",this.startEmbeddedIframe),e.setAttribute("src",e.getAttribute("data-src")))})))}startEmbeddedMedia(e){let t=!!r(e.target,"html"),i=!!r(e.target,".present");t&&i&&(e.target.currentTime=0,e.target.play()),e.target.removeEventListener("loadeddata",this.startEmbeddedMedia)}startEmbeddedIframe(e){let t=e.target;if(t&&t.contentWindow){let i=!!r(e.target,"html"),s=!!r(e.target,".present");if(i&&s){let e=this.Reveal.getConfig().autoPlayMedia;"boolean"!=typeof e&&(e=t.hasAttribute("data-autoplay")||!!r(t,".slide-background")),/youtube\.com\/embed\//.test(t.getAttribute("src"))&&e?t.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}',"*"):/player\.vimeo\.com\//.test(t.getAttribute("src"))&&e?t.contentWindow.postMessage('{"method":"play"}',"*"):t.contentWindow.postMessage("slide:start","*")}}}stopEmbeddedContent(i,s={}){s=e({unloadIframes:!0},s),i&&i.parentNode&&(t(i,"video, audio").forEach((e=>{e.hasAttribute("data-ignore")||"function"!=typeof e.pause||(e.setAttribute("data-paused-by-reveal",""),e.pause())})),t(i,"iframe").forEach((e=>{e.contentWindow&&e.contentWindow.postMessage("slide:stop","*"),e.removeEventListener("load",this.startEmbeddedIframe)})),t(i,'iframe[src*="youtube.com/embed/"]').forEach((e=>{!e.hasAttribute("data-ignore")&&e.contentWindow&&"function"==typeof e.contentWindow.postMessage&&e.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}',"*")})),t(i,'iframe[src*="player.vimeo.com/"]').forEach((e=>{!e.hasAttribute("data-ignore")&&e.contentWindow&&"function"==typeof e.contentWindow.postMessage&&e.contentWindow.postMessage('{"method":"pause"}',"*")})),!0===s.unloadIframes&&t(i,"iframe[data-src]").forEach((e=>{e.setAttribute("src","about:blank"),e.removeAttribute("src")})))}}class y{constructor(e){this.Reveal=e}render(){this.element=document.createElement("div"),this.element.className="slide-number",this.Reveal.getRevealElement().appendChild(this.element)}configure(e,t){let i="none";e.slideNumber&&!this.Reveal.isPrintingPDF()&&("all"===e.showSlideNumber||"speaker"===e.showSlideNumber&&this.Reveal.isSpeakerNotes())&&(i="block"),this.element.style.display=i}update(){this.Reveal.getConfig().slideNumber&&this.element&&(this.element.innerHTML=this.getSlideNumber())}getSlideNumber(e=this.Reveal.getCurrentSlide()){let t,i=this.Reveal.getConfig(),s="h.v";if("function"==typeof i.slideNumber)t=i.slideNumber(e);else{"string"==typeof i.slideNumber&&(s=i.slideNumber),/c/.test(s)||1!==this.Reveal.getHorizontalSlides().length||(s="c");let a=e&&"uncounted"===e.dataset.visibility?0:1;switch(t=[],s){case"c":t.push(this.Reveal.getSlidePastCount(e)+a);break;case"c/t":t.push(this.Reveal.getSlidePastCount(e)+a,"/",this.Reveal.getTotalSlides());break;default:let i=this.Reveal.getIndices(e);t.push(i.h+a);let n="h/v"===s?"/":".";this.Reveal.isVerticalSlide(e)&&t.push(n,i.v+1)}}let a="#"+this.Reveal.location.getHash(e);return this.formatNumber(t[0],t[1],t[2],a)}formatNumber(e,t,i,s="#"+this.Reveal.location.getHash()){return"number"!=typeof i||isNaN(i)?`\n\t\t\t\t\t${e}\n\t\t\t\t\t`:`\n\t\t\t\t\t${e}\n\t\t\t\t\t${t}\n\t\t\t\t\t${i}\n\t\t\t\t\t`}destroy(){this.element.remove()}}const w=e=>{let t=e.match(/^#([0-9a-f]{3})$/i);if(t&&t[1])return t=t[1],{r:17*parseInt(t.charAt(0),16),g:17*parseInt(t.charAt(1),16),b:17*parseInt(t.charAt(2),16)};let i=e.match(/^#([0-9a-f]{6})$/i);if(i&&i[1])return i=i[1],{r:parseInt(i.slice(0,2),16),g:parseInt(i.slice(2,4),16),b:parseInt(i.slice(4,6),16)};let s=e.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);if(s)return{r:parseInt(s[1],10),g:parseInt(s[2],10),b:parseInt(s[3],10)};let a=e.match(/^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i);return a?{r:parseInt(a[1],10),g:parseInt(a[2],10),b:parseInt(a[3],10),a:parseFloat(a[4])}:null};class E{constructor(e){this.Reveal=e}render(){this.element=document.createElement("div"),this.element.className="backgrounds",this.Reveal.getRevealElement().appendChild(this.element)}create(){this.element.innerHTML="",this.element.classList.add("no-transition"),this.Reveal.getHorizontalSlides().forEach((e=>{let i=this.createBackground(e,this.element);t(e,"section").forEach((e=>{this.createBackground(e,i),i.classList.add("stack")}))})),this.Reveal.getConfig().parallaxBackgroundImage?(this.element.style.backgroundImage='url("'+this.Reveal.getConfig().parallaxBackgroundImage+'")',this.element.style.backgroundSize=this.Reveal.getConfig().parallaxBackgroundSize,this.element.style.backgroundRepeat=this.Reveal.getConfig().parallaxBackgroundRepeat,this.element.style.backgroundPosition=this.Reveal.getConfig().parallaxBackgroundPosition,setTimeout((()=>{this.Reveal.getRevealElement().classList.add("has-parallax-background")}),1)):(this.element.style.backgroundImage="",this.Reveal.getRevealElement().classList.remove("has-parallax-background"))}createBackground(e,t){let i=document.createElement("div");i.className="slide-background "+e.className.replace(/present|past|future/,"");let s=document.createElement("div");return s.className="slide-background-content",i.appendChild(s),t.appendChild(i),e.slideBackgroundElement=i,e.slideBackgroundContentElement=s,this.sync(e),i}sync(e){const t=e.slideBackgroundElement,i=e.slideBackgroundContentElement,s={background:e.getAttribute("data-background"),backgroundSize:e.getAttribute("data-background-size"),backgroundImage:e.getAttribute("data-background-image"),backgroundVideo:e.getAttribute("data-background-video"),backgroundIframe:e.getAttribute("data-background-iframe"),backgroundColor:e.getAttribute("data-background-color"),backgroundRepeat:e.getAttribute("data-background-repeat"),backgroundPosition:e.getAttribute("data-background-position"),backgroundTransition:e.getAttribute("data-background-transition"),backgroundOpacity:e.getAttribute("data-background-opacity")},a=e.hasAttribute("data-preload");e.classList.remove("has-dark-background"),e.classList.remove("has-light-background"),t.removeAttribute("data-loaded"),t.removeAttribute("data-background-hash"),t.removeAttribute("data-background-size"),t.removeAttribute("data-background-transition"),t.style.backgroundColor="",i.style.backgroundSize="",i.style.backgroundRepeat="",i.style.backgroundPosition="",i.style.backgroundImage="",i.style.opacity="",i.innerHTML="",s.background&&(/^(http|file|\/\/)/gi.test(s.background)||/\.(svg|png|jpg|jpeg|gif|bmp)([?#\s]|$)/gi.test(s.background)?e.setAttribute("data-background-image",s.background):t.style.background=s.background),(s.background||s.backgroundColor||s.backgroundImage||s.backgroundVideo||s.backgroundIframe)&&t.setAttribute("data-background-hash",s.background+s.backgroundSize+s.backgroundImage+s.backgroundVideo+s.backgroundIframe+s.backgroundColor+s.backgroundRepeat+s.backgroundPosition+s.backgroundTransition+s.backgroundOpacity),s.backgroundSize&&t.setAttribute("data-background-size",s.backgroundSize),s.backgroundColor&&(t.style.backgroundColor=s.backgroundColor),s.backgroundTransition&&t.setAttribute("data-background-transition",s.backgroundTransition),a&&t.setAttribute("data-preload",""),s.backgroundSize&&(i.style.backgroundSize=s.backgroundSize),s.backgroundRepeat&&(i.style.backgroundRepeat=s.backgroundRepeat),s.backgroundPosition&&(i.style.backgroundPosition=s.backgroundPosition),s.backgroundOpacity&&(i.style.opacity=s.backgroundOpacity);let n=s.backgroundColor;if(!n||!w(n)){let e=window.getComputedStyle(t);e&&e.backgroundColor&&(n=e.backgroundColor)}if(n){const t=w(n);t&&0!==t.a&&("string"==typeof(r=n)&&(r=w(r)),(r?(299*r.r+587*r.g+114*r.b)/1e3:null)<128?e.classList.add("has-dark-background"):e.classList.add("has-light-background"))}var r}update(e=!1){let i=this.Reveal.getCurrentSlide(),s=this.Reveal.getIndices(),a=null,n=this.Reveal.getConfig().rtl?"future":"past",r=this.Reveal.getConfig().rtl?"past":"future";if(Array.from(this.element.childNodes).forEach(((i,o)=>{i.classList.remove("past","present","future"),os.h?i.classList.add(r):(i.classList.add("present"),a=i),(e||o===s.h)&&t(i,".slide-background").forEach(((e,t)=>{e.classList.remove("past","present","future"),ts.v?e.classList.add("future"):(e.classList.add("present"),o===s.h&&(a=e))}))})),this.previousBackground&&this.Reveal.slideContent.stopEmbeddedContent(this.previousBackground,{unloadIframes:!this.Reveal.slideContent.shouldPreload(this.previousBackground)}),a){this.Reveal.slideContent.startEmbeddedContent(a);let e=a.querySelector(".slide-background-content");if(e){let t=e.style.backgroundImage||"";/\.gif/i.test(t)&&(e.style.backgroundImage="",window.getComputedStyle(e).opacity,e.style.backgroundImage=t)}let t=this.previousBackground?this.previousBackground.getAttribute("data-background-hash"):null,i=a.getAttribute("data-background-hash");i&&i===t&&a!==this.previousBackground&&this.element.classList.add("no-transition"),this.previousBackground=a}i&&["has-light-background","has-dark-background"].forEach((e=>{i.classList.contains(e)?this.Reveal.getRevealElement().classList.add(e):this.Reveal.getRevealElement().classList.remove(e)}),this),setTimeout((()=>{this.element.classList.remove("no-transition")}),1)}updateParallax(){let e=this.Reveal.getIndices();if(this.Reveal.getConfig().parallaxBackgroundImage){let t,i,s=this.Reveal.getHorizontalSlides(),a=this.Reveal.getVerticalSlides(),n=this.element.style.backgroundSize.split(" ");1===n.length?t=i=parseInt(n[0],10):(t=parseInt(n[0],10),i=parseInt(n[1],10));let r,o,l=this.element.offsetWidth,d=s.length;r="number"==typeof this.Reveal.getConfig().parallaxBackgroundHorizontal?this.Reveal.getConfig().parallaxBackgroundHorizontal:d>1?(t-l)/(d-1):0,o=r*e.h*-1;let c,h,u=this.element.offsetHeight,g=a.length;c="number"==typeof this.Reveal.getConfig().parallaxBackgroundVertical?this.Reveal.getConfig().parallaxBackgroundVertical:(i-u)/(g-1),h=g>0?c*e.v:0,this.element.style.backgroundPosition=o+"px "+-h+"px"}}destroy(){this.element.remove()}}const R=/registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/,S=/fade-(down|up|right|left|out|in-then-out|in-then-semi-out)|semi-fade-out|current-visible|shrink|grow/;let A=0;class k{constructor(e){this.Reveal=e}run(e,t){this.reset();let i=this.Reveal.getSlides(),s=i.indexOf(t),a=i.indexOf(e);if(e.hasAttribute("data-auto-animate")&&t.hasAttribute("data-auto-animate")&&e.getAttribute("data-auto-animate-id")===t.getAttribute("data-auto-animate-id")&&!(s>a?t:e).hasAttribute("data-auto-animate-restart")){this.autoAnimateStyleSheet=this.autoAnimateStyleSheet||l();let i=this.getAutoAnimateOptions(t);e.dataset.autoAnimate="pending",t.dataset.autoAnimate="pending",i.slideDirection=s>a?"forward":"backward";let n=this.getAutoAnimatableElements(e,t).map((e=>this.autoAnimateElements(e.from,e.to,e.options||{},i,A++)));if("false"!==t.dataset.autoAnimateUnmatched&&!0===this.Reveal.getConfig().autoAnimateUnmatched){let e=.8*i.duration,s=.2*i.duration;this.getUnmatchedAutoAnimateElements(t).forEach((e=>{let t=this.getAutoAnimateOptions(e,i),s="unmatched";t.duration===i.duration&&t.delay===i.delay||(s="unmatched-"+A++,n.push(`[data-auto-animate="running"] [data-auto-animate-target="${s}"] { transition: opacity ${t.duration}s ease ${t.delay}s; }`)),e.dataset.autoAnimateTarget=s}),this),n.push(`[data-auto-animate="running"] [data-auto-animate-target="unmatched"] { transition: opacity ${e}s ease ${s}s; }`)}this.autoAnimateStyleSheet.innerHTML=n.join(""),requestAnimationFrame((()=>{this.autoAnimateStyleSheet&&(getComputedStyle(this.autoAnimateStyleSheet).fontWeight,t.dataset.autoAnimate="running")})),this.Reveal.dispatchEvent({type:"autoanimate",data:{fromSlide:e,toSlide:t,sheet:this.autoAnimateStyleSheet}})}}reset(){t(this.Reveal.getRevealElement(),'[data-auto-animate]:not([data-auto-animate=""])').forEach((e=>{e.dataset.autoAnimate=""})),t(this.Reveal.getRevealElement(),"[data-auto-animate-target]").forEach((e=>{delete e.dataset.autoAnimateTarget})),this.autoAnimateStyleSheet&&this.autoAnimateStyleSheet.parentNode&&(this.autoAnimateStyleSheet.parentNode.removeChild(this.autoAnimateStyleSheet),this.autoAnimateStyleSheet=null)}autoAnimateElements(e,t,i,s,a){e.dataset.autoAnimateTarget="",t.dataset.autoAnimateTarget=a;let n=this.getAutoAnimateOptions(t,s);void 0!==i.delay&&(n.delay=i.delay),void 0!==i.duration&&(n.duration=i.duration),void 0!==i.easing&&(n.easing=i.easing);let r=this.getAutoAnimatableProperties("from",e,i),o=this.getAutoAnimatableProperties("to",t,i);if(t.classList.contains("fragment")&&(delete o.styles.opacity,e.classList.contains("fragment"))){(e.className.match(S)||[""])[0]===(t.className.match(S)||[""])[0]&&"forward"===s.slideDirection&&t.classList.add("visible","disabled")}if(!1!==i.translate||!1!==i.scale){let e=this.Reveal.getScale(),t={x:(r.x-o.x)/e,y:(r.y-o.y)/e,scaleX:r.width/o.width,scaleY:r.height/o.height};t.x=Math.round(1e3*t.x)/1e3,t.y=Math.round(1e3*t.y)/1e3,t.scaleX=Math.round(1e3*t.scaleX)/1e3,t.scaleX=Math.round(1e3*t.scaleX)/1e3;let s=!1!==i.translate&&(0!==t.x||0!==t.y),a=!1!==i.scale&&(0!==t.scaleX||0!==t.scaleY);if(s||a){let e=[];s&&e.push(`translate(${t.x}px, ${t.y}px)`),a&&e.push(`scale(${t.scaleX}, ${t.scaleY})`),r.styles.transform=e.join(" "),r.styles["transform-origin"]="top left",o.styles.transform="none"}}for(let e in o.styles){const t=o.styles[e],i=r.styles[e];t===i?delete o.styles[e]:(!0===t.explicitValue&&(o.styles[e]=t.value),!0===i.explicitValue&&(r.styles[e]=i.value))}let l="",d=Object.keys(o.styles);if(d.length>0){r.styles.transition="none",o.styles.transition=`all ${n.duration}s ${n.easing} ${n.delay}s`,o.styles["transition-property"]=d.join(", "),o.styles["will-change"]=d.join(", "),l='[data-auto-animate-target="'+a+'"] {'+Object.keys(r.styles).map((e=>e+": "+r.styles[e]+" !important;")).join("")+'}[data-auto-animate="running"] [data-auto-animate-target="'+a+'"] {'+Object.keys(o.styles).map((e=>e+": "+o.styles[e]+" !important;")).join("")+"}"}return l}getAutoAnimateOptions(t,i){let s={easing:this.Reveal.getConfig().autoAnimateEasing,duration:this.Reveal.getConfig().autoAnimateDuration,delay:0};if(s=e(s,i),t.parentNode){let e=r(t.parentNode,"[data-auto-animate-target]");e&&(s=this.getAutoAnimateOptions(e,s))}return t.dataset.autoAnimateEasing&&(s.easing=t.dataset.autoAnimateEasing),t.dataset.autoAnimateDuration&&(s.duration=parseFloat(t.dataset.autoAnimateDuration)),t.dataset.autoAnimateDelay&&(s.delay=parseFloat(t.dataset.autoAnimateDelay)),s}getAutoAnimatableProperties(e,t,i){let s=this.Reveal.getConfig(),a={styles:[]};if(!1!==i.translate||!1!==i.scale){let e;if("function"==typeof i.measure)e=i.measure(t);else if(s.center)e=t.getBoundingClientRect();else{let i=this.Reveal.getScale();e={x:t.offsetLeft*i,y:t.offsetTop*i,width:t.offsetWidth*i,height:t.offsetHeight*i}}a.x=e.x,a.y=e.y,a.width=e.width,a.height=e.height}const n=getComputedStyle(t);return(i.styles||s.autoAnimateStyles).forEach((t=>{let i;"string"==typeof t&&(t={property:t}),i=void 0!==t.from&&"from"===e?{value:t.from,explicitValue:!0}:void 0!==t.to&&"to"===e?{value:t.to,explicitValue:!0}:n[t.property],""!==i&&(a.styles[t.property]=i)})),a}getAutoAnimatableElements(e,t){let i=("function"==typeof this.Reveal.getConfig().autoAnimateMatcher?this.Reveal.getConfig().autoAnimateMatcher:this.getAutoAnimatePairs).call(this,e,t),s=[];return i.filter(((e,t)=>{if(-1===s.indexOf(e.to))return s.push(e.to),!0}))}getAutoAnimatePairs(e,t){let i=[];const s="h1, h2, h3, h4, h5, h6, p, li";return this.findAutoAnimateMatches(i,e,t,"[data-id]",(e=>e.nodeName+":::"+e.getAttribute("data-id"))),this.findAutoAnimateMatches(i,e,t,s,(e=>e.nodeName+":::"+e.innerText)),this.findAutoAnimateMatches(i,e,t,"img, video, iframe",(e=>e.nodeName+":::"+(e.getAttribute("src")||e.getAttribute("data-src")))),this.findAutoAnimateMatches(i,e,t,"pre",(e=>e.nodeName+":::"+e.innerText)),i.forEach((e=>{n(e.from,s)?e.options={scale:!1}:n(e.from,"pre")&&(e.options={scale:!1,styles:["width","height"]},this.findAutoAnimateMatches(i,e.from,e.to,".hljs .hljs-ln-code",(e=>e.textContent),{scale:!1,styles:[],measure:this.getLocalBoundingBox.bind(this)}),this.findAutoAnimateMatches(i,e.from,e.to,".hljs .hljs-ln-line[data-line-number]",(e=>e.getAttribute("data-line-number")),{scale:!1,styles:["width"],measure:this.getLocalBoundingBox.bind(this)}))}),this),i}getLocalBoundingBox(e){const t=this.Reveal.getScale();return{x:Math.round(e.offsetLeft*t*100)/100,y:Math.round(e.offsetTop*t*100)/100,width:Math.round(e.offsetWidth*t*100)/100,height:Math.round(e.offsetHeight*t*100)/100}}findAutoAnimateMatches(e,t,i,s,a,n){let r={},o={};[].slice.call(t.querySelectorAll(s)).forEach(((e,t)=>{const i=a(e);"string"==typeof i&&i.length&&(r[i]=r[i]||[],r[i].push(e))})),[].slice.call(i.querySelectorAll(s)).forEach(((t,i)=>{const s=a(t);let l;if(o[s]=o[s]||[],o[s].push(t),r[s]){const e=o[s].length-1,t=r[s].length-1;r[s][e]?(l=r[s][e],r[s][e]=null):r[s][t]&&(l=r[s][t],r[s][t]=null)}l&&e.push({from:l,to:t,options:n})}))}getUnmatchedAutoAnimateElements(e){return[].slice.call(e.children).reduce(((e,t)=>{const i=t.querySelector("[data-auto-animate-target]");return t.hasAttribute("data-auto-animate-target")||i||e.push(t),t.querySelector("[data-auto-animate-target]")&&(e=e.concat(this.getUnmatchedAutoAnimateElements(t))),e}),[])}}class L{constructor(e){this.Reveal=e}configure(e,t){!1===e.fragments?this.disable():!1===t.fragments&&this.enable()}disable(){t(this.Reveal.getSlidesElement(),".fragment").forEach((e=>{e.classList.add("visible"),e.classList.remove("current-fragment")}))}enable(){t(this.Reveal.getSlidesElement(),".fragment").forEach((e=>{e.classList.remove("visible"),e.classList.remove("current-fragment")}))}availableRoutes(){let e=this.Reveal.getCurrentSlide();if(e&&this.Reveal.getConfig().fragments){let t=e.querySelectorAll(".fragment:not(.disabled)"),i=e.querySelectorAll(".fragment:not(.disabled):not(.visible)");return{prev:t.length-i.length>0,next:!!i.length}}return{prev:!1,next:!1}}sort(e,t=!1){e=Array.from(e);let i=[],s=[],a=[];e.forEach((e=>{if(e.hasAttribute("data-fragment-index")){let t=parseInt(e.getAttribute("data-fragment-index"),10);i[t]||(i[t]=[]),i[t].push(e)}else s.push([e])})),i=i.concat(s);let n=0;return i.forEach((e=>{e.forEach((e=>{a.push(e),e.setAttribute("data-fragment-index",n)})),n++})),!0===t?i:a}sortAll(){this.Reveal.getHorizontalSlides().forEach((e=>{let i=t(e,"section");i.forEach(((e,t)=>{this.sort(e.querySelectorAll(".fragment"))}),this),0===i.length&&this.sort(e.querySelectorAll(".fragment"))}))}update(e,t){let i={shown:[],hidden:[]},s=this.Reveal.getCurrentSlide();if(s&&this.Reveal.getConfig().fragments&&(t=t||this.sort(s.querySelectorAll(".fragment"))).length){let a=0;if("number"!=typeof e){let t=this.sort(s.querySelectorAll(".fragment.visible")).pop();t&&(e=parseInt(t.getAttribute("data-fragment-index")||0,10))}Array.from(t).forEach(((t,s)=>{if(t.hasAttribute("data-fragment-index")&&(s=parseInt(t.getAttribute("data-fragment-index"),10)),a=Math.max(a,s),s<=e){let a=t.classList.contains("visible");t.classList.add("visible"),t.classList.remove("current-fragment"),s===e&&(this.Reveal.announceStatus(this.Reveal.getStatusText(t)),t.classList.add("current-fragment"),this.Reveal.slideContent.startEmbeddedContent(t)),a||(i.shown.push(t),this.Reveal.dispatchEvent({target:t,type:"visible",bubbles:!1}))}else{let e=t.classList.contains("visible");t.classList.remove("visible"),t.classList.remove("current-fragment"),e&&(this.Reveal.slideContent.stopEmbeddedContent(t),i.hidden.push(t),this.Reveal.dispatchEvent({target:t,type:"hidden",bubbles:!1}))}})),e="number"==typeof e?e:-1,e=Math.max(Math.min(e,a),-1),s.setAttribute("data-fragment",e)}return i}sync(e=this.Reveal.getCurrentSlide()){return this.sort(e.querySelectorAll(".fragment"))}goto(e,t=0){let i=this.Reveal.getCurrentSlide();if(i&&this.Reveal.getConfig().fragments){let s=this.sort(i.querySelectorAll(".fragment:not(.disabled)"));if(s.length){if("number"!=typeof e){let t=this.sort(i.querySelectorAll(".fragment:not(.disabled).visible")).pop();e=t?parseInt(t.getAttribute("data-fragment-index")||0,10):-1}e+=t;let a=this.update(e,s);return a.hidden.length&&this.Reveal.dispatchEvent({type:"fragmenthidden",data:{fragment:a.hidden[0],fragments:a.hidden}}),a.shown.length&&this.Reveal.dispatchEvent({type:"fragmentshown",data:{fragment:a.shown[0],fragments:a.shown}}),this.Reveal.controls.update(),this.Reveal.progress.update(),this.Reveal.getConfig().fragmentInURL&&this.Reveal.location.writeURL(),!(!a.shown.length&&!a.hidden.length)}}return!1}next(){return this.goto(null,1)}prev(){return this.goto(null,-1)}}class C{constructor(e){this.Reveal=e,this.active=!1,this.onSlideClicked=this.onSlideClicked.bind(this)}activate(){if(this.Reveal.getConfig().overview&&!this.isActive()){this.active=!0,this.Reveal.getRevealElement().classList.add("overview"),this.Reveal.cancelAutoSlide(),this.Reveal.getSlidesElement().appendChild(this.Reveal.getBackgroundsElement()),t(this.Reveal.getRevealElement(),".slides section").forEach((e=>{e.classList.contains("stack")||e.addEventListener("click",this.onSlideClicked,!0)}));const e=70,i=this.Reveal.getComputedSlideSize();this.overviewSlideWidth=i.width+e,this.overviewSlideHeight=i.height+e,this.Reveal.getConfig().rtl&&(this.overviewSlideWidth=-this.overviewSlideWidth),this.Reveal.updateSlidesVisibility(),this.layout(),this.update(),this.Reveal.layout();const s=this.Reveal.getIndices();this.Reveal.dispatchEvent({type:"overviewshown",data:{indexh:s.h,indexv:s.v,currentSlide:this.Reveal.getCurrentSlide()}})}}layout(){this.Reveal.getHorizontalSlides().forEach(((e,i)=>{e.setAttribute("data-index-h",i),a(e,"translate3d("+i*this.overviewSlideWidth+"px, 0, 0)"),e.classList.contains("stack")&&t(e,"section").forEach(((e,t)=>{e.setAttribute("data-index-h",i),e.setAttribute("data-index-v",t),a(e,"translate3d(0, "+t*this.overviewSlideHeight+"px, 0)")}))})),Array.from(this.Reveal.getBackgroundsElement().childNodes).forEach(((e,i)=>{a(e,"translate3d("+i*this.overviewSlideWidth+"px, 0, 0)"),t(e,".slide-background").forEach(((e,t)=>{a(e,"translate3d(0, "+t*this.overviewSlideHeight+"px, 0)")}))}))}update(){const e=Math.min(window.innerWidth,window.innerHeight),t=Math.max(e/5,150)/e,i=this.Reveal.getIndices();this.Reveal.transformSlides({overview:["scale("+t+")","translateX("+-i.h*this.overviewSlideWidth+"px)","translateY("+-i.v*this.overviewSlideHeight+"px)"].join(" ")})}deactivate(){if(this.Reveal.getConfig().overview){this.active=!1,this.Reveal.getRevealElement().classList.remove("overview"),this.Reveal.getRevealElement().classList.add("overview-deactivating"),setTimeout((()=>{this.Reveal.getRevealElement().classList.remove("overview-deactivating")}),1),this.Reveal.getRevealElement().appendChild(this.Reveal.getBackgroundsElement()),t(this.Reveal.getRevealElement(),".slides section").forEach((e=>{a(e,""),e.removeEventListener("click",this.onSlideClicked,!0)})),t(this.Reveal.getBackgroundsElement(),".slide-background").forEach((e=>{a(e,"")})),this.Reveal.transformSlides({overview:""});const e=this.Reveal.getIndices();this.Reveal.slide(e.h,e.v),this.Reveal.layout(),this.Reveal.cueAutoSlide(),this.Reveal.dispatchEvent({type:"overviewhidden",data:{indexh:e.h,indexv:e.v,currentSlide:this.Reveal.getCurrentSlide()}})}}toggle(e){"boolean"==typeof e?e?this.activate():this.deactivate():this.isActive()?this.deactivate():this.activate()}isActive(){return this.active}onSlideClicked(e){if(this.isActive()){e.preventDefault();let t=e.target;for(;t&&!t.nodeName.match(/section/gi);)t=t.parentNode;if(t&&!t.classList.contains("disabled")&&(this.deactivate(),t.nodeName.match(/section/gi))){let e=parseInt(t.getAttribute("data-index-h"),10),i=parseInt(t.getAttribute("data-index-v"),10);this.Reveal.slide(e,i)}}}}class x{constructor(e){this.Reveal=e,this.shortcuts={},this.bindings={},this.onDocumentKeyDown=this.onDocumentKeyDown.bind(this),this.onDocumentKeyPress=this.onDocumentKeyPress.bind(this)}configure(e,t){"linear"===e.navigationMode?(this.shortcuts["→ , ↓ , SPACE , N , L , J"]="Next slide",this.shortcuts["← , ↑ , P , H , K"]="Previous slide"):(this.shortcuts["N , SPACE"]="Next slide",this.shortcuts["P , Shift SPACE"]="Previous slide",this.shortcuts["← , H"]="Navigate left",this.shortcuts["→ , L"]="Navigate right",this.shortcuts["↑ , K"]="Navigate up",this.shortcuts["↓ , J"]="Navigate down"),this.shortcuts["Alt + ←/↑/→/↓"]="Navigate without fragments",this.shortcuts["Shift + ←/↑/→/↓"]="Jump to first/last slide",this.shortcuts["B , ."]="Pause",this.shortcuts.F="Fullscreen",this.shortcuts["ESC, O"]="Slide overview"}bind(){document.addEventListener("keydown",this.onDocumentKeyDown,!1),document.addEventListener("keypress",this.onDocumentKeyPress,!1)}unbind(){document.removeEventListener("keydown",this.onDocumentKeyDown,!1),document.removeEventListener("keypress",this.onDocumentKeyPress,!1)}addKeyBinding(e,t){"object"==typeof e&&e.keyCode?this.bindings[e.keyCode]={callback:t,key:e.key,description:e.description}:this.bindings[e]={callback:t,key:null,description:null}}removeKeyBinding(e){delete this.bindings[e]}triggerKey(e){this.onDocumentKeyDown({keyCode:e})}registerKeyboardShortcut(e,t){this.shortcuts[e]=t}getShortcuts(){return this.shortcuts}getBindings(){return this.bindings}onDocumentKeyPress(e){e.shiftKey&&63===e.charCode&&this.Reveal.toggleHelp()}onDocumentKeyDown(e){let t=this.Reveal.getConfig();if("function"==typeof t.keyboardCondition&&!1===t.keyboardCondition(e))return!0;if("focused"===t.keyboardCondition&&!this.Reveal.isFocused())return!0;let i=e.keyCode,s=!this.Reveal.isAutoSliding();this.Reveal.onUserInput(e);let a=document.activeElement&&!0===document.activeElement.isContentEditable,n=document.activeElement&&document.activeElement.tagName&&/input|textarea/i.test(document.activeElement.tagName),r=document.activeElement&&document.activeElement.className&&/speaker-notes/i.test(document.activeElement.className),o=!(-1!==[32,37,38,39,40,78,80].indexOf(e.keyCode)&&e.shiftKey||e.altKey)&&(e.shiftKey||e.altKey||e.ctrlKey||e.metaKey);if(a||n||r||o)return;let l,d=[66,86,190,191];if("object"==typeof t.keyboard)for(l in t.keyboard)"togglePause"===t.keyboard[l]&&d.push(parseInt(l,10));if(this.Reveal.isPaused()&&-1===d.indexOf(i))return!1;let c="linear"===t.navigationMode||!this.Reveal.hasHorizontalSlides()||!this.Reveal.hasVerticalSlides(),h=!1;if("object"==typeof t.keyboard)for(l in t.keyboard)if(parseInt(l,10)===i){let i=t.keyboard[l];"function"==typeof i?i.apply(null,[e]):"string"==typeof i&&"function"==typeof this.Reveal[i]&&this.Reveal[i].call(),h=!0}if(!1===h)for(l in this.bindings)if(parseInt(l,10)===i){let t=this.bindings[l].callback;"function"==typeof t?t.apply(null,[e]):"string"==typeof t&&"function"==typeof this.Reveal[t]&&this.Reveal[t].call(),h=!0}!1===h&&(h=!0,80===i||33===i?this.Reveal.prev({skipFragments:e.altKey}):78===i||34===i?this.Reveal.next({skipFragments:e.altKey}):72===i||37===i?e.shiftKey?this.Reveal.slide(0):!this.Reveal.overview.isActive()&&c?this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.left({skipFragments:e.altKey}):76===i||39===i?e.shiftKey?this.Reveal.slide(this.Reveal.getHorizontalSlides().length-1):!this.Reveal.overview.isActive()&&c?this.Reveal.next({skipFragments:e.altKey}):this.Reveal.right({skipFragments:e.altKey}):75===i||38===i?e.shiftKey?this.Reveal.slide(void 0,0):!this.Reveal.overview.isActive()&&c?this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.up({skipFragments:e.altKey}):74===i||40===i?e.shiftKey?this.Reveal.slide(void 0,Number.MAX_VALUE):!this.Reveal.overview.isActive()&&c?this.Reveal.next({skipFragments:e.altKey}):this.Reveal.down({skipFragments:e.altKey}):36===i?this.Reveal.slide(0):35===i?this.Reveal.slide(this.Reveal.getHorizontalSlides().length-1):32===i?(this.Reveal.overview.isActive()&&this.Reveal.overview.deactivate(),e.shiftKey?this.Reveal.prev({skipFragments:e.altKey}):this.Reveal.next({skipFragments:e.altKey})):58===i||59===i||66===i||86===i||190===i||191===i?this.Reveal.togglePause():70===i?(e=>{let t=(e=e||document.documentElement).requestFullscreen||e.webkitRequestFullscreen||e.webkitRequestFullScreen||e.mozRequestFullScreen||e.msRequestFullscreen;t&&t.apply(e)})(t.embedded?this.Reveal.getViewportElement():document.documentElement):65===i?t.autoSlideStoppable&&this.Reveal.toggleAutoSlide(s):h=!1),h?e.preventDefault&&e.preventDefault():27!==i&&79!==i||(!1===this.Reveal.closeOverlay()&&this.Reveal.overview.toggle(),e.preventDefault&&e.preventDefault()),this.Reveal.cueAutoSlide()}}class P{constructor(e){var t,i,s;s=1e3,(i="MAX_REPLACE_STATE_FREQUENCY")in(t=this)?Object.defineProperty(t,i,{value:s,enumerable:!0,configurable:!0,writable:!0}):t[i]=s,this.Reveal=e,this.writeURLTimeout=0,this.replaceStateTimestamp=0,this.onWindowHashChange=this.onWindowHashChange.bind(this)}bind(){window.addEventListener("hashchange",this.onWindowHashChange,!1)}unbind(){window.removeEventListener("hashchange",this.onWindowHashChange,!1)}getIndicesFromHash(e=window.location.hash){let t=e.replace(/^#\/?/,""),i=t.split("/");if(/^[0-9]*$/.test(i[0])||!t.length){const e=this.Reveal.getConfig();let t,s=e.hashOneBasedIndex?1:0,a=parseInt(i[0],10)-s||0,n=parseInt(i[1],10)-s||0;return e.fragmentInURL&&(t=parseInt(i[2],10),isNaN(t)&&(t=void 0)),{h:a,v:n,f:t}}{let e,i;/\/[-\d]+$/g.test(t)&&(i=parseInt(t.split("/").pop(),10),i=isNaN(i)?void 0:i,t=t.split("/").shift());try{e=document.getElementById(decodeURIComponent(t))}catch(e){}if(e)return{...this.Reveal.getIndices(e),f:i}}return null}readURL(){const e=this.Reveal.getIndices(),t=this.getIndicesFromHash();t?t.h===e.h&&t.v===e.v&&void 0===t.f||this.Reveal.slide(t.h,t.v,t.f):this.Reveal.slide(e.h||0,e.v||0)}writeURL(e){let t=this.Reveal.getConfig(),i=this.Reveal.getCurrentSlide();if(clearTimeout(this.writeURLTimeout),"number"==typeof e)this.writeURLTimeout=setTimeout(this.writeURL,e);else if(i){let e=this.getHash();t.history?window.location.hash=e:t.hash&&("/"===e?this.debouncedReplaceState(window.location.pathname+window.location.search):this.debouncedReplaceState("#"+e))}}replaceState(e){window.history.replaceState(null,null,e),this.replaceStateTimestamp=Date.now()}debouncedReplaceState(e){clearTimeout(this.replaceStateTimeout),Date.now()-this.replaceStateTimestamp>this.MAX_REPLACE_STATE_FREQUENCY?this.replaceState(e):this.replaceStateTimeout=setTimeout((()=>this.replaceState(e)),this.MAX_REPLACE_STATE_FREQUENCY)}getHash(e){let t="/",i=e||this.Reveal.getCurrentSlide(),s=i?i.getAttribute("id"):null;s&&(s=encodeURIComponent(s));let a=this.Reveal.getIndices(e);if(this.Reveal.getConfig().fragmentInURL||(a.f=void 0),"string"==typeof s&&s.length)t="/"+s,a.f>=0&&(t+="/"+a.f);else{let e=this.Reveal.getConfig().hashOneBasedIndex?1:0;(a.h>0||a.v>0||a.f>=0)&&(t+=a.h+e),(a.v>0||a.f>=0)&&(t+="/"+(a.v+e)),a.f>=0&&(t+="/"+a.f)}return t}onWindowHashChange(e){this.readURL()}}class N{constructor(e){this.Reveal=e,this.onNavigateLeftClicked=this.onNavigateLeftClicked.bind(this),this.onNavigateRightClicked=this.onNavigateRightClicked.bind(this),this.onNavigateUpClicked=this.onNavigateUpClicked.bind(this),this.onNavigateDownClicked=this.onNavigateDownClicked.bind(this),this.onNavigatePrevClicked=this.onNavigatePrevClicked.bind(this),this.onNavigateNextClicked=this.onNavigateNextClicked.bind(this)}render(){const e=this.Reveal.getConfig().rtl,i=this.Reveal.getRevealElement();this.element=document.createElement("aside"),this.element.className="controls",this.element.innerHTML=`\n\t\t\t\n\t\t\t\n\t\t\t`,this.Reveal.getRevealElement().appendChild(this.element),this.controlsLeft=t(i,".navigate-left"),this.controlsRight=t(i,".navigate-right"),this.controlsUp=t(i,".navigate-up"),this.controlsDown=t(i,".navigate-down"),this.controlsPrev=t(i,".navigate-prev"),this.controlsNext=t(i,".navigate-next"),this.controlsRightArrow=this.element.querySelector(".navigate-right"),this.controlsLeftArrow=this.element.querySelector(".navigate-left"),this.controlsDownArrow=this.element.querySelector(".navigate-down")}configure(e,t){this.element.style.display=e.controls?"block":"none",this.element.setAttribute("data-controls-layout",e.controlsLayout),this.element.setAttribute("data-controls-back-arrows",e.controlsBackArrows)}bind(){let e=["touchstart","click"];v&&(e=["touchstart"]),e.forEach((e=>{this.controlsLeft.forEach((t=>t.addEventListener(e,this.onNavigateLeftClicked,!1))),this.controlsRight.forEach((t=>t.addEventListener(e,this.onNavigateRightClicked,!1))),this.controlsUp.forEach((t=>t.addEventListener(e,this.onNavigateUpClicked,!1))),this.controlsDown.forEach((t=>t.addEventListener(e,this.onNavigateDownClicked,!1))),this.controlsPrev.forEach((t=>t.addEventListener(e,this.onNavigatePrevClicked,!1))),this.controlsNext.forEach((t=>t.addEventListener(e,this.onNavigateNextClicked,!1)))}))}unbind(){["touchstart","click"].forEach((e=>{this.controlsLeft.forEach((t=>t.removeEventListener(e,this.onNavigateLeftClicked,!1))),this.controlsRight.forEach((t=>t.removeEventListener(e,this.onNavigateRightClicked,!1))),this.controlsUp.forEach((t=>t.removeEventListener(e,this.onNavigateUpClicked,!1))),this.controlsDown.forEach((t=>t.removeEventListener(e,this.onNavigateDownClicked,!1))),this.controlsPrev.forEach((t=>t.removeEventListener(e,this.onNavigatePrevClicked,!1))),this.controlsNext.forEach((t=>t.removeEventListener(e,this.onNavigateNextClicked,!1)))}))}update(){let e=this.Reveal.availableRoutes();[...this.controlsLeft,...this.controlsRight,...this.controlsUp,...this.controlsDown,...this.controlsPrev,...this.controlsNext].forEach((e=>{e.classList.remove("enabled","fragmented"),e.setAttribute("disabled","disabled")})),e.left&&this.controlsLeft.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),e.right&&this.controlsRight.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),e.up&&this.controlsUp.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),e.down&&this.controlsDown.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),(e.left||e.up)&&this.controlsPrev.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")})),(e.right||e.down)&&this.controlsNext.forEach((e=>{e.classList.add("enabled"),e.removeAttribute("disabled")}));let t=this.Reveal.getCurrentSlide();if(t){let e=this.Reveal.fragments.availableRoutes();e.prev&&this.controlsPrev.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),e.next&&this.controlsNext.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),this.Reveal.isVerticalSlide(t)?(e.prev&&this.controlsUp.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),e.next&&this.controlsDown.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")}))):(e.prev&&this.controlsLeft.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})),e.next&&this.controlsRight.forEach((e=>{e.classList.add("fragmented","enabled"),e.removeAttribute("disabled")})))}if(this.Reveal.getConfig().controlsTutorial){let t=this.Reveal.getIndices();!this.Reveal.hasNavigatedVertically()&&e.down?this.controlsDownArrow.classList.add("highlight"):(this.controlsDownArrow.classList.remove("highlight"),this.Reveal.getConfig().rtl?!this.Reveal.hasNavigatedHorizontally()&&e.left&&0===t.v?this.controlsLeftArrow.classList.add("highlight"):this.controlsLeftArrow.classList.remove("highlight"):!this.Reveal.hasNavigatedHorizontally()&&e.right&&0===t.v?this.controlsRightArrow.classList.add("highlight"):this.controlsRightArrow.classList.remove("highlight"))}}destroy(){this.unbind(),this.element.remove()}onNavigateLeftClicked(e){e.preventDefault(),this.Reveal.onUserInput(),"linear"===this.Reveal.getConfig().navigationMode?this.Reveal.prev():this.Reveal.left()}onNavigateRightClicked(e){e.preventDefault(),this.Reveal.onUserInput(),"linear"===this.Reveal.getConfig().navigationMode?this.Reveal.next():this.Reveal.right()}onNavigateUpClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.up()}onNavigateDownClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.down()}onNavigatePrevClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.prev()}onNavigateNextClicked(e){e.preventDefault(),this.Reveal.onUserInput(),this.Reveal.next()}}class M{constructor(e){this.Reveal=e,this.onProgressClicked=this.onProgressClicked.bind(this)}render(){this.element=document.createElement("div"),this.element.className="progress",this.Reveal.getRevealElement().appendChild(this.element),this.bar=document.createElement("span"),this.element.appendChild(this.bar)}configure(e,t){this.element.style.display=e.progress?"block":"none"}bind(){this.Reveal.getConfig().progress&&this.element&&this.element.addEventListener("click",this.onProgressClicked,!1)}unbind(){this.Reveal.getConfig().progress&&this.element&&this.element.removeEventListener("click",this.onProgressClicked,!1)}update(){if(this.Reveal.getConfig().progress&&this.bar){let e=this.Reveal.getProgress();this.Reveal.getTotalSlides()<2&&(e=0),this.bar.style.transform="scaleX("+e+")"}}getMaxWidth(){return this.Reveal.getRevealElement().offsetWidth}onProgressClicked(e){this.Reveal.onUserInput(e),e.preventDefault();let t=this.Reveal.getSlides(),i=t.length,s=Math.floor(e.clientX/this.getMaxWidth()*i);this.Reveal.getConfig().rtl&&(s=i-s);let a=this.Reveal.getIndices(t[s]);this.Reveal.slide(a.h,a.v)}destroy(){this.element.remove()}}class D{constructor(e){this.Reveal=e,this.lastMouseWheelStep=0,this.cursorHidden=!1,this.cursorInactiveTimeout=0,this.onDocumentCursorActive=this.onDocumentCursorActive.bind(this),this.onDocumentMouseScroll=this.onDocumentMouseScroll.bind(this)}configure(e,t){e.mouseWheel?(document.addEventListener("DOMMouseScroll",this.onDocumentMouseScroll,!1),document.addEventListener("mousewheel",this.onDocumentMouseScroll,!1)):(document.removeEventListener("DOMMouseScroll",this.onDocumentMouseScroll,!1),document.removeEventListener("mousewheel",this.onDocumentMouseScroll,!1)),e.hideInactiveCursor?(document.addEventListener("mousemove",this.onDocumentCursorActive,!1),document.addEventListener("mousedown",this.onDocumentCursorActive,!1)):(this.showCursor(),document.removeEventListener("mousemove",this.onDocumentCursorActive,!1),document.removeEventListener("mousedown",this.onDocumentCursorActive,!1))}showCursor(){this.cursorHidden&&(this.cursorHidden=!1,this.Reveal.getRevealElement().style.cursor="")}hideCursor(){!1===this.cursorHidden&&(this.cursorHidden=!0,this.Reveal.getRevealElement().style.cursor="none")}destroy(){this.showCursor(),document.removeEventListener("DOMMouseScroll",this.onDocumentMouseScroll,!1),document.removeEventListener("mousewheel",this.onDocumentMouseScroll,!1),document.removeEventListener("mousemove",this.onDocumentCursorActive,!1),document.removeEventListener("mousedown",this.onDocumentCursorActive,!1)}onDocumentCursorActive(e){this.showCursor(),clearTimeout(this.cursorInactiveTimeout),this.cursorInactiveTimeout=setTimeout(this.hideCursor.bind(this),this.Reveal.getConfig().hideCursorTime)}onDocumentMouseScroll(e){if(Date.now()-this.lastMouseWheelStep>1e3){this.lastMouseWheelStep=Date.now();let t=e.detail||-e.wheelDelta;t>0?this.Reveal.next():t<0&&this.Reveal.prev()}}}const I=(e,t)=>{const i=document.createElement("script");i.type="text/javascript",i.async=!1,i.defer=!1,i.src=e,"function"==typeof t&&(i.onload=i.onreadystatechange=e=>{("load"===e.type||/loaded|complete/.test(i.readyState))&&(i.onload=i.onreadystatechange=i.onerror=null,t())},i.onerror=e=>{i.onload=i.onreadystatechange=i.onerror=null,t(new Error("Failed loading script: "+i.src+"\n"+e))});const s=document.querySelector("head");s.insertBefore(i,s.lastChild)};class T{constructor(e){this.Reveal=e,this.state="idle",this.registeredPlugins={},this.asyncDependencies=[]}load(e,t){return this.state="loading",e.forEach(this.registerPlugin.bind(this)),new Promise((e=>{let i=[],s=0;if(t.forEach((e=>{e.condition&&!e.condition()||(e.async?this.asyncDependencies.push(e):i.push(e))})),i.length){s=i.length;const t=t=>{t&&"function"==typeof t.callback&&t.callback(),0==--s&&this.initPlugins().then(e)};i.forEach((e=>{"string"==typeof e.id?(this.registerPlugin(e),t(e)):"string"==typeof e.src?I(e.src,(()=>t(e))):(console.warn("Unrecognized plugin format",e),t())}))}else this.initPlugins().then(e)}))}initPlugins(){return new Promise((e=>{let t=Object.values(this.registeredPlugins),i=t.length;if(0===i)this.loadAsync().then(e);else{let s,a=()=>{0==--i?this.loadAsync().then(e):s()},n=0;s=()=>{let e=t[n++];if("function"==typeof e.init){let t=e.init(this.Reveal);t&&"function"==typeof t.then?t.then(a):a()}else a()},s()}}))}loadAsync(){return this.state="loaded",this.asyncDependencies.length&&this.asyncDependencies.forEach((e=>{I(e.src,e.callback)})),Promise.resolve()}registerPlugin(e){2===arguments.length&&"string"==typeof arguments[0]?(e=arguments[1]).id=arguments[0]:"function"==typeof e&&(e=e());let t=e.id;"string"!=typeof t?console.warn("Unrecognized plugin format; can't find plugin.id",e):void 0===this.registeredPlugins[t]?(this.registeredPlugins[t]=e,"loaded"===this.state&&"function"==typeof e.init&&e.init(this.Reveal)):console.warn('reveal.js: "'+t+'" plugin has already been registered')}hasPlugin(e){return!!this.registeredPlugins[e]}getPlugin(e){return this.registeredPlugins[e]}getRegisteredPlugins(){return this.registeredPlugins}destroy(){Object.values(this.registeredPlugins).forEach((e=>{"function"==typeof e.destroy&&e.destroy()})),this.registeredPlugins={},this.asyncDependencies=[]}}class F{constructor(e){this.Reveal=e}async setupPDF(){const e=this.Reveal.getConfig(),i=t(this.Reveal.getRevealElement(),".slides section"),s=e.slideNumber&&/all|print/i.test(e.showSlideNumber),a=this.Reveal.getComputedSlideSize(window.innerWidth,window.innerHeight),n=Math.floor(a.width*(1+e.margin)),r=Math.floor(a.height*(1+e.margin)),o=a.width,d=a.height;await new Promise(requestAnimationFrame),l("@page{size:"+n+"px "+r+"px; margin: 0px;}"),l(".reveal section>img, .reveal section>video, .reveal section>iframe{max-width: "+o+"px; max-height:"+d+"px}"),document.documentElement.classList.add("print-pdf"),document.body.style.width=n+"px",document.body.style.height=r+"px";const c=document.querySelector(".reveal-viewport");let h;if(c){const e=window.getComputedStyle(c);e&&e.background&&(h=e.background)}await new Promise(requestAnimationFrame),this.Reveal.layoutSlideContents(o,d),await new Promise(requestAnimationFrame);const u=i.map((e=>e.scrollHeight)),g=[],v=i[0].parentNode;i.forEach((function(i,a){if(!1===i.classList.contains("stack")){let l=(n-o)/2,c=(r-d)/2;const v=u[a];let p=Math.max(Math.ceil(v/r),1);p=Math.min(p,e.pdfMaxPagesPerSlide),(1===p&&e.center||i.classList.contains("center"))&&(c=Math.max((r-v)/2,0));const m=document.createElement("div");if(g.push(m),m.className="pdf-page",m.style.height=(r+e.pdfPageHeightOffset)*p+"px",h&&(m.style.background=h),m.appendChild(i),i.style.left=l+"px",i.style.top=c+"px",i.style.width=o+"px",this.Reveal.slideContent.layout(i),i.slideBackgroundElement&&m.insertBefore(i.slideBackgroundElement,i),e.showNotes){const t=this.Reveal.getSlideNotes(i);if(t){const i=8,s="string"==typeof e.showNotes?e.showNotes:"inline",a=document.createElement("div");a.classList.add("speaker-notes"),a.classList.add("speaker-notes-pdf"),a.setAttribute("data-layout",s),a.innerHTML=t,"separate-page"===s?g.push(a):(a.style.left=i+"px",a.style.bottom=i+"px",a.style.width=n-2*i+"px",m.appendChild(a))}}if(s){const e=a+1,t=document.createElement("div");t.classList.add("slide-number"),t.classList.add("slide-number-pdf"),t.innerHTML=e,m.appendChild(t)}if(e.pdfSeparateFragments){const e=this.Reveal.fragments.sort(m.querySelectorAll(".fragment"),!0);let t;e.forEach((function(e){t&&t.forEach((function(e){e.classList.remove("current-fragment")})),e.forEach((function(e){e.classList.add("visible","current-fragment")}),this);const i=m.cloneNode(!0);g.push(i),t=e}),this),e.forEach((function(e){e.forEach((function(e){e.classList.remove("visible","current-fragment")}))}))}else t(m,".fragment:not(.fade-out)").forEach((function(e){e.classList.add("visible")}))}}),this),await new Promise(requestAnimationFrame),g.forEach((e=>v.appendChild(e))),this.Reveal.dispatchEvent({type:"pdf-ready"})}isPrintingPDF(){return/print-pdf/gi.test(window.location.search)}}class z{constructor(e){this.Reveal=e,this.touchStartX=0,this.touchStartY=0,this.touchStartCount=0,this.touchCaptured=!1,this.onPointerDown=this.onPointerDown.bind(this),this.onPointerMove=this.onPointerMove.bind(this),this.onPointerUp=this.onPointerUp.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this)}bind(){let e=this.Reveal.getRevealElement();"onpointerdown"in window?(e.addEventListener("pointerdown",this.onPointerDown,!1),e.addEventListener("pointermove",this.onPointerMove,!1),e.addEventListener("pointerup",this.onPointerUp,!1)):window.navigator.msPointerEnabled?(e.addEventListener("MSPointerDown",this.onPointerDown,!1),e.addEventListener("MSPointerMove",this.onPointerMove,!1),e.addEventListener("MSPointerUp",this.onPointerUp,!1)):(e.addEventListener("touchstart",this.onTouchStart,!1),e.addEventListener("touchmove",this.onTouchMove,!1),e.addEventListener("touchend",this.onTouchEnd,!1))}unbind(){let e=this.Reveal.getRevealElement();e.removeEventListener("pointerdown",this.onPointerDown,!1),e.removeEventListener("pointermove",this.onPointerMove,!1),e.removeEventListener("pointerup",this.onPointerUp,!1),e.removeEventListener("MSPointerDown",this.onPointerDown,!1),e.removeEventListener("MSPointerMove",this.onPointerMove,!1),e.removeEventListener("MSPointerUp",this.onPointerUp,!1),e.removeEventListener("touchstart",this.onTouchStart,!1),e.removeEventListener("touchmove",this.onTouchMove,!1),e.removeEventListener("touchend",this.onTouchEnd,!1)}isSwipePrevented(e){if(n(e,"video, audio"))return!0;for(;e&&"function"==typeof e.hasAttribute;){if(e.hasAttribute("data-prevent-swipe"))return!0;e=e.parentNode}return!1}onTouchStart(e){if(this.isSwipePrevented(e.target))return!0;this.touchStartX=e.touches[0].clientX,this.touchStartY=e.touches[0].clientY,this.touchStartCount=e.touches.length}onTouchMove(e){if(this.isSwipePrevented(e.target))return!0;let t=this.Reveal.getConfig();if(this.touchCaptured)v&&e.preventDefault();else{this.Reveal.onUserInput(e);let i=e.touches[0].clientX,s=e.touches[0].clientY;if(1===e.touches.length&&2!==this.touchStartCount){let a=this.Reveal.availableRoutes({includeFragments:!0}),n=i-this.touchStartX,r=s-this.touchStartY;n>40&&Math.abs(n)>Math.abs(r)?(this.touchCaptured=!0,"linear"===t.navigationMode?t.rtl?this.Reveal.next():this.Reveal.prev():this.Reveal.left()):n<-40&&Math.abs(n)>Math.abs(r)?(this.touchCaptured=!0,"linear"===t.navigationMode?t.rtl?this.Reveal.prev():this.Reveal.next():this.Reveal.right()):r>40&&a.up?(this.touchCaptured=!0,"linear"===t.navigationMode?this.Reveal.prev():this.Reveal.up()):r<-40&&a.down&&(this.touchCaptured=!0,"linear"===t.navigationMode?this.Reveal.next():this.Reveal.down()),t.embedded?(this.touchCaptured||this.Reveal.isVerticalSlide())&&e.preventDefault():e.preventDefault()}}}onTouchEnd(e){this.touchCaptured=!1}onPointerDown(e){e.pointerType!==e.MSPOINTER_TYPE_TOUCH&&"touch"!==e.pointerType||(e.touches=[{clientX:e.clientX,clientY:e.clientY}],this.onTouchStart(e))}onPointerMove(e){e.pointerType!==e.MSPOINTER_TYPE_TOUCH&&"touch"!==e.pointerType||(e.touches=[{clientX:e.clientX,clientY:e.clientY}],this.onTouchMove(e))}onPointerUp(e){e.pointerType!==e.MSPOINTER_TYPE_TOUCH&&"touch"!==e.pointerType||(e.touches=[{clientX:e.clientX,clientY:e.clientY}],this.onTouchEnd(e))}}class H{constructor(e){this.Reveal=e,this.onRevealPointerDown=this.onRevealPointerDown.bind(this),this.onDocumentPointerDown=this.onDocumentPointerDown.bind(this)}configure(e,t){e.embedded?this.blur():(this.focus(),this.unbind())}bind(){this.Reveal.getConfig().embedded&&this.Reveal.getRevealElement().addEventListener("pointerdown",this.onRevealPointerDown,!1)}unbind(){this.Reveal.getRevealElement().removeEventListener("pointerdown",this.onRevealPointerDown,!1),document.removeEventListener("pointerdown",this.onDocumentPointerDown,!1)}focus(){"focus"!==this.state&&(this.Reveal.getRevealElement().classList.add("focused"),document.addEventListener("pointerdown",this.onDocumentPointerDown,!1)),this.state="focus"}blur(){"blur"!==this.state&&(this.Reveal.getRevealElement().classList.remove("focused"),document.removeEventListener("pointerdown",this.onDocumentPointerDown,!1)),this.state="blur"}isFocused(){return"focus"===this.state}destroy(){this.Reveal.getRevealElement().classList.remove("focused")}onRevealPointerDown(e){this.focus()}onDocumentPointerDown(e){let t=r(e.target,".reveal");t&&t===this.Reveal.getRevealElement()||this.blur()}}class q{constructor(e){this.Reveal=e}render(){this.element=document.createElement("div"),this.element.className="speaker-notes",this.element.setAttribute("data-prevent-swipe",""),this.element.setAttribute("tabindex","0"),this.Reveal.getRevealElement().appendChild(this.element)}configure(e,t){e.showNotes&&this.element.setAttribute("data-layout","string"==typeof e.showNotes?e.showNotes:"inline")}update(){this.Reveal.getConfig().showNotes&&this.element&&this.Reveal.getCurrentSlide()&&!this.Reveal.print.isPrintingPDF()&&(this.element.innerHTML=this.getSlideNotes()||'No notes on this slide.')}updateVisibility(){this.Reveal.getConfig().showNotes&&this.hasNotes()&&!this.Reveal.print.isPrintingPDF()?this.Reveal.getRevealElement().classList.add("show-notes"):this.Reveal.getRevealElement().classList.remove("show-notes")}hasNotes(){return this.Reveal.getSlidesElement().querySelectorAll("[data-notes], aside.notes").length>0}isSpeakerNotesWindow(){return!!window.location.search.match(/receiver/gi)}getSlideNotes(e=this.Reveal.getCurrentSlide()){if(e.hasAttribute("data-notes"))return e.getAttribute("data-notes");let t=e.querySelector("aside.notes");return t?t.innerHTML:null}destroy(){this.element.remove()}}class B{constructor(e,t){this.diameter=100,this.diameter2=this.diameter/2,this.thickness=6,this.playing=!1,this.progress=0,this.progressOffset=1,this.container=e,this.progressCheck=t,this.canvas=document.createElement("canvas"),this.canvas.className="playback",this.canvas.width=this.diameter,this.canvas.height=this.diameter,this.canvas.style.width=this.diameter2+"px",this.canvas.style.height=this.diameter2+"px",this.context=this.canvas.getContext("2d"),this.container.appendChild(this.canvas),this.render()}setPlaying(e){const t=this.playing;this.playing=e,!t&&this.playing?this.animate():this.render()}animate(){const e=this.progress;this.progress=this.progressCheck(),e>.8&&this.progress<.2&&(this.progressOffset=this.progress),this.render(),this.playing&&requestAnimationFrame(this.animate.bind(this))}render(){let e=this.playing?this.progress:0,t=this.diameter2-this.thickness,i=this.diameter2,s=this.diameter2,a=28;this.progressOffset+=.1*(1-this.progressOffset);const n=-Math.PI/2+e*(2*Math.PI),r=-Math.PI/2+this.progressOffset*(2*Math.PI);this.context.save(),this.context.clearRect(0,0,this.diameter,this.diameter),this.context.beginPath(),this.context.arc(i,s,t+4,0,2*Math.PI,!1),this.context.fillStyle="rgba( 0, 0, 0, 0.4 )",this.context.fill(),this.context.beginPath(),this.context.arc(i,s,t,0,2*Math.PI,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle="rgba( 255, 255, 255, 0.2 )",this.context.stroke(),this.playing&&(this.context.beginPath(),this.context.arc(i,s,t,r,n,!1),this.context.lineWidth=this.thickness,this.context.strokeStyle="#fff",this.context.stroke()),this.context.translate(i-14,s-14),this.playing?(this.context.fillStyle="#fff",this.context.fillRect(0,0,10,a),this.context.fillRect(18,0,10,a)):(this.context.beginPath(),this.context.translate(4,0),this.context.moveTo(0,0),this.context.lineTo(24,14),this.context.lineTo(0,a),this.context.fillStyle="#fff",this.context.fill()),this.context.restore()}on(e,t){this.canvas.addEventListener(e,t,!1)}off(e,t){this.canvas.removeEventListener(e,t,!1)}destroy(){this.playing=!1,this.canvas.parentNode&&this.container.removeChild(this.canvas)}}var O={width:960,height:700,margin:.04,minScale:.2,maxScale:2,controls:!0,controlsTutorial:!0,controlsLayout:"bottom-right",controlsBackArrows:"faded",progress:!0,slideNumber:!1,showSlideNumber:"all",hashOneBasedIndex:!1,hash:!1,respondToHashChanges:!0,history:!1,keyboard:!0,keyboardCondition:null,disableLayout:!1,overview:!0,center:!0,touch:!0,loop:!1,rtl:!1,navigationMode:"default",shuffle:!1,fragments:!0,fragmentInURL:!0,embedded:!1,help:!0,pause:!0,showNotes:!1,showHiddenSlides:!1,autoPlayMedia:null,preloadIframes:null,autoAnimate:!0,autoAnimateMatcher:null,autoAnimateEasing:"ease",autoAnimateDuration:1,autoAnimateUnmatched:!0,autoAnimateStyles:["opacity","color","background-color","padding","font-size","line-height","letter-spacing","border-width","border-color","border-radius","outline","outline-offset"],autoSlide:0,autoSlideStoppable:!0,autoSlideMethod:null,defaultTiming:null,mouseWheel:!1,previewLinks:!1,postMessage:!0,postMessageEvents:!1,focusBodyOnPageVisibilityChange:!0,transition:"slide",transitionSpeed:"default",backgroundTransition:"fade",parallaxBackgroundImage:"",parallaxBackgroundSize:"",parallaxBackgroundRepeat:"",parallaxBackgroundPosition:"",parallaxBackgroundHorizontal:null,parallaxBackgroundVertical:null,pdfMaxPagesPerSlide:Number.POSITIVE_INFINITY,pdfSeparateFragments:!0,pdfPageHeightOffset:-1,viewDistance:3,mobileViewDistance:2,display:"block",hideInactiveCursor:!0,hideCursorTime:5e3,dependencies:[],plugins:[]};function U(n,l){arguments.length<2&&(l=arguments[0],n=document.querySelector(".reveal"));const h={};let u,v,p,m,f,w={},S=!1,A={hasNavigatedHorizontally:!1,hasNavigatedVertically:!1},I=[],U=1,W={layout:"",overview:""},K={},V="idle",$=0,j=0,X=-1,Y=!1,_=new b(h),J=new y(h),Q=new k(h),Z=new E(h),G=new L(h),ee=new C(h),te=new x(h),ie=new P(h),se=new N(h),ae=new M(h),ne=new D(h),re=new T(h),oe=new F(h),le=new H(h),de=new z(h),ce=new q(h);function he(e){if(!n)throw'Unable to find presentation root (
).';if(K.wrapper=n,K.slides=n.querySelector(".slides"),!K.slides)throw'Unable to find slides container (
).';return w={...O,...w,...l,...e,...d()},ue(),window.addEventListener("load",He,!1),re.load(w.plugins,w.dependencies).then(ge),new Promise((e=>h.on("ready",e)))}function ue(){!0===w.embedded?K.viewport=r(n,".reveal-viewport")||n:(K.viewport=document.body,document.documentElement.classList.add("reveal-full-page")),K.viewport.classList.add("reveal-viewport")}function ge(){S=!0,ve(),pe(),Ee(),ye(),we(),tt(),Re(),ie.readURL(),Z.update(!0),setTimeout((()=>{K.slides.classList.remove("no-transition"),K.wrapper.classList.add("ready"),Pe({type:"ready",data:{indexh:u,indexv:v,currentSlide:m}})}),1),oe.isPrintingPDF()&&(Ae(),"complete"===document.readyState?oe.setupPDF():window.addEventListener("load",(()=>{oe.setupPDF()})))}function ve(){w.showHiddenSlides||t(K.wrapper,'section[data-visibility="hidden"]').forEach((e=>{e.parentNode.removeChild(e)}))}function pe(){K.slides.classList.add("no-transition"),g?K.wrapper.classList.add("no-hover"):K.wrapper.classList.remove("no-hover"),Z.render(),J.render(),se.render(),ae.render(),ce.render(),K.pauseOverlay=o(K.wrapper,"div","pause-overlay",w.controls?'':null),K.statusElement=me(),K.wrapper.setAttribute("role","application")}function me(){let e=K.wrapper.querySelector(".aria-status");return e||(e=document.createElement("div"),e.style.position="absolute",e.style.height="1px",e.style.width="1px",e.style.overflow="hidden",e.style.clip="rect( 1px, 1px, 1px, 1px )",e.classList.add("aria-status"),e.setAttribute("aria-live","polite"),e.setAttribute("aria-atomic","true"),K.wrapper.appendChild(e)),e}function fe(e){K.statusElement.textContent=e}function be(e){let t="";if(3===e.nodeType)t+=e.textContent;else if(1===e.nodeType){let i=e.getAttribute("aria-hidden"),s="none"===window.getComputedStyle(e).display;"true"===i||s||Array.from(e.childNodes).forEach((e=>{t+=be(e)}))}return t=t.trim(),""===t?"":t+" "}function ye(){setInterval((()=>{0===K.wrapper.scrollTop&&0===K.wrapper.scrollLeft||(K.wrapper.scrollTop=0,K.wrapper.scrollLeft=0)}),1e3)}function we(){document.addEventListener("fullscreenchange",Ht),document.addEventListener("webkitfullscreenchange",Ht)}function Ee(){w.postMessage&&window.addEventListener("message",Dt,!1)}function Re(t){const s={...w};if("object"==typeof t&&e(w,t),!1===h.isReady())return;const a=K.wrapper.querySelectorAll(".slides section").length;K.wrapper.classList.remove(s.transition),K.wrapper.classList.add(w.transition),K.wrapper.setAttribute("data-transition-speed",w.transitionSpeed),K.wrapper.setAttribute("data-background-transition",w.backgroundTransition),K.viewport.style.setProperty("--slide-width",w.width+"px"),K.viewport.style.setProperty("--slide-height",w.height+"px"),w.shuffle&&it(),i(K.wrapper,"embedded",w.embedded),i(K.wrapper,"rtl",w.rtl),i(K.wrapper,"center",w.center),!1===w.pause&&Xe(),w.previewLinks?(Me(),De("[data-preview-link=false]")):(De(),Me("[data-preview-link]:not([data-preview-link=false])")),Q.reset(),f&&(f.destroy(),f=null),a>1&&w.autoSlide&&w.autoSlideStoppable&&(f=new B(K.wrapper,(()=>Math.min(Math.max((Date.now()-X)/$,0),1))),f.on("click",Bt),Y=!1),"default"!==w.navigationMode?K.wrapper.setAttribute("data-navigation-mode",w.navigationMode):K.wrapper.removeAttribute("data-navigation-mode"),ce.configure(w,s),le.configure(w,s),ne.configure(w,s),se.configure(w,s),ae.configure(w,s),te.configure(w,s),G.configure(w,s),J.configure(w,s),Ge()}function Se(){window.addEventListener("resize",Ft,!1),w.touch&&de.bind(),w.keyboard&&te.bind(),w.progress&&ae.bind(),w.respondToHashChanges&&ie.bind(),se.bind(),le.bind(),K.slides.addEventListener("click",Tt,!1),K.slides.addEventListener("transitionend",It,!1),K.pauseOverlay.addEventListener("click",Xe,!1),w.focusBodyOnPageVisibilityChange&&document.addEventListener("visibilitychange",zt,!1)}function Ae(){de.unbind(),le.unbind(),te.unbind(),se.unbind(),ae.unbind(),ie.unbind(),window.removeEventListener("resize",Ft,!1),K.slides.removeEventListener("click",Tt,!1),K.slides.removeEventListener("transitionend",It,!1),K.pauseOverlay.removeEventListener("click",Xe,!1)}function ke(){Ae(),Rt(),De(),ce.destroy(),le.destroy(),re.destroy(),ne.destroy(),se.destroy(),ae.destroy(),Z.destroy(),J.destroy(),document.removeEventListener("fullscreenchange",Ht),document.removeEventListener("webkitfullscreenchange",Ht),document.removeEventListener("visibilitychange",zt,!1),window.removeEventListener("message",Dt,!1),window.removeEventListener("load",He,!1),K.pauseOverlay&&K.pauseOverlay.remove(),K.statusElement&&K.statusElement.remove(),document.documentElement.classList.remove("reveal-full-page"),K.wrapper.classList.remove("ready","center","has-horizontal-slides","has-vertical-slides"),K.wrapper.removeAttribute("data-transition-speed"),K.wrapper.removeAttribute("data-background-transition"),K.viewport.classList.remove("reveal-viewport"),K.viewport.style.removeProperty("--slide-width"),K.viewport.style.removeProperty("--slide-height"),K.slides.style.removeProperty("width"),K.slides.style.removeProperty("height"),K.slides.style.removeProperty("zoom"),K.slides.style.removeProperty("left"),K.slides.style.removeProperty("top"),K.slides.style.removeProperty("bottom"),K.slides.style.removeProperty("right"),K.slides.style.removeProperty("transform"),Array.from(K.wrapper.querySelectorAll(".slides section")).forEach((e=>{e.style.removeProperty("display"),e.style.removeProperty("top"),e.removeAttribute("hidden"),e.removeAttribute("aria-hidden")}))}function Le(e,t,i){n.addEventListener(e,t,i)}function Ce(e,t,i){n.removeEventListener(e,t,i)}function xe(e){"string"==typeof e.layout&&(W.layout=e.layout),"string"==typeof e.overview&&(W.overview=e.overview),W.layout?a(K.slides,W.layout+" "+W.overview):a(K.slides,W.overview)}function Pe({target:t=K.wrapper,type:i,data:s,bubbles:a=!0}){let n=document.createEvent("HTMLEvents",1,2);return n.initEvent(i,a,!0),e(n,s),t.dispatchEvent(n),t===K.wrapper&&Ne(i),n}function Ne(t,i){if(w.postMessageEvents&&window.parent!==window.self){let s={namespace:"reveal",eventName:t,state:yt()};e(s,i),window.parent.postMessage(JSON.stringify(s),"*")}}function Me(e="a"){Array.from(K.wrapper.querySelectorAll(e)).forEach((e=>{/^(http|www)/gi.test(e.getAttribute("href"))&&e.addEventListener("click",qt,!1)}))}function De(e="a"){Array.from(K.wrapper.querySelectorAll(e)).forEach((e=>{/^(http|www)/gi.test(e.getAttribute("href"))&&e.removeEventListener("click",qt,!1)}))}function Ie(e){ze(),K.overlay=document.createElement("div"),K.overlay.classList.add("overlay"),K.overlay.classList.add("overlay-preview"),K.wrapper.appendChild(K.overlay),K.overlay.innerHTML=`
\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\tUnable to load iframe. This is likely due to the site's policy (x-frame-options).\n\t\t\t\t\n\t\t\t
`,K.overlay.querySelector("iframe").addEventListener("load",(e=>{K.overlay.classList.add("loaded")}),!1),K.overlay.querySelector(".close").addEventListener("click",(e=>{ze(),e.preventDefault()}),!1),K.overlay.querySelector(".external").addEventListener("click",(e=>{ze()}),!1)}function Te(e){"boolean"==typeof e?e?Fe():ze():K.overlay?ze():Fe()}function Fe(){if(w.help){ze(),K.overlay=document.createElement("div"),K.overlay.classList.add("overlay"),K.overlay.classList.add("overlay-help"),K.wrapper.appendChild(K.overlay);let e='

Keyboard Shortcuts


',t=te.getShortcuts(),i=te.getBindings();e+="";for(let i in t)e+=``;for(let t in i)i[t].key&&i[t].description&&(e+=``);e+="
KEYACTION
${i}${t[i]}
${i[t].key}${i[t].description}
",K.overlay.innerHTML=`\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
${e}
\n\t\t\t\t
\n\t\t\t`,K.overlay.querySelector(".close").addEventListener("click",(e=>{ze(),e.preventDefault()}),!1)}}function ze(){return!!K.overlay&&(K.overlay.parentNode.removeChild(K.overlay),K.overlay=null,!0)}function He(){if(K.wrapper&&!oe.isPrintingPDF()){if(!w.disableLayout){g&&!w.embedded&&document.documentElement.style.setProperty("--vh",.01*window.innerHeight+"px");const e=Be(),t=U;qe(w.width,w.height),K.slides.style.width=e.width+"px",K.slides.style.height=e.height+"px",U=Math.min(e.presentationWidth/e.width,e.presentationHeight/e.height),U=Math.max(U,w.minScale),U=Math.min(U,w.maxScale),1===U?(K.slides.style.zoom="",K.slides.style.left="",K.slides.style.top="",K.slides.style.bottom="",K.slides.style.right="",xe({layout:""})):(K.slides.style.zoom="",K.slides.style.left="50%",K.slides.style.top="50%",K.slides.style.bottom="auto",K.slides.style.right="auto",xe({layout:"translate(-50%, -50%) scale("+U+")"}));const i=Array.from(K.wrapper.querySelectorAll(".slides section"));for(let t=0,s=i.length;t .stretch, section > .r-stretch").forEach((t=>{let s=c(t,i);if(/(img|video)/gi.test(t.nodeName)){const i=t.naturalWidth||t.videoWidth,a=t.naturalHeight||t.videoHeight,n=Math.min(e/i,s/a);t.style.width=i*n+"px",t.style.height=a*n+"px"}else t.style.width=e+"px",t.style.height=s+"px"}))}function Be(e,t){const i={width:w.width,height:w.height,presentationWidth:e||K.wrapper.offsetWidth,presentationHeight:t||K.wrapper.offsetHeight};return i.presentationWidth-=i.presentationWidth*w.margin,i.presentationHeight-=i.presentationHeight*w.margin,"string"==typeof i.width&&/%$/.test(i.width)&&(i.width=parseInt(i.width,10)/100*i.presentationWidth),"string"==typeof i.height&&/%$/.test(i.height)&&(i.height=parseInt(i.height,10)/100*i.presentationHeight),i}function Oe(e,t){"object"==typeof e&&"function"==typeof e.setAttribute&&e.setAttribute("data-previous-indexv",t||0)}function Ue(e){if("object"==typeof e&&"function"==typeof e.setAttribute&&e.classList.contains("stack")){const t=e.hasAttribute("data-start-indexv")?"data-start-indexv":"data-previous-indexv";return parseInt(e.getAttribute(t)||0,10)}return 0}function We(e=m){return e&&e.parentNode&&!!e.parentNode.nodeName.match(/section/i)}function Ke(){return!(!m||!We(m))&&!m.nextElementSibling}function Ve(){return 0===u&&0===v}function $e(){return!!m&&(!m.nextElementSibling&&(!We(m)||!m.parentNode.nextElementSibling))}function je(){if(w.pause){const e=K.wrapper.classList.contains("paused");Rt(),K.wrapper.classList.add("paused"),!1===e&&Pe({type:"paused"})}}function Xe(){const e=K.wrapper.classList.contains("paused");K.wrapper.classList.remove("paused"),Et(),e&&Pe({type:"resumed"})}function Ye(e){"boolean"==typeof e?e?je():Xe():_e()?Xe():je()}function _e(){return K.wrapper.classList.contains("paused")}function Je(e){"boolean"==typeof e?e?At():St():Y?At():St()}function Qe(){return!(!$||Y)}function Ze(e,t,i,s){if(Pe({type:"beforeslidechange",data:{indexh:void 0===e?u:e,indexv:void 0===t?v:t,origin:s}}).defaultPrevented)return;p=m;const a=K.wrapper.querySelectorAll(".slides>section");if(0===a.length)return;void 0!==t||ee.isActive()||(t=Ue(a[e])),p&&p.parentNode&&p.parentNode.classList.contains("stack")&&Oe(p.parentNode,v);const n=I.concat();I.length=0;let r=u||0,o=v||0;u=st(".slides>section",void 0===e?u:e),v=st(".slides>section.present>section",void 0===t?v:t);let l=u!==r||v!==o;l||(p=null);let d=a[u],c=d.querySelectorAll("section");m=c[v]||d;let h=!1;l&&p&&m&&!ee.isActive()&&(p.hasAttribute("data-auto-animate")&&m.hasAttribute("data-auto-animate")&&p.getAttribute("data-auto-animate-id")===m.getAttribute("data-auto-animate-id")&&!(u>r||v>o?m:p).hasAttribute("data-auto-animate-restart")&&(h=!0,K.slides.classList.add("disable-slide-transitions")),V="running"),at(),He(),ee.isActive()&&ee.update(),void 0!==i&&G.goto(i),p&&p!==m&&(p.classList.remove("present"),p.setAttribute("aria-hidden","true"),Ve()&&setTimeout((()=>{ut().forEach((e=>{Oe(e,0)}))}),0));e:for(let e=0,t=I.length;e{fe(be(m))})),ae.update(),se.update(),ce.update(),Z.update(),Z.updateParallax(),J.update(),G.update(),ie.writeURL(),Et(),h&&(setTimeout((()=>{K.slides.classList.remove("disable-slide-transitions")}),0),w.autoAnimate&&Q.run(p,m))}function Ge(){Ae(),Se(),He(),$=w.autoSlide,Et(),Z.create(),ie.writeURL(),G.sortAll(),se.update(),ae.update(),at(),ce.update(),ce.updateVisibility(),Z.update(!0),J.update(),_.formatEmbeddedContent(),!1===w.autoPlayMedia?_.stopEmbeddedContent(m,{unloadIframes:!1}):_.startEmbeddedContent(m),ee.isActive()&&ee.layout()}function et(e=m){Z.sync(e),G.sync(e),_.load(e),Z.update(),ce.update()}function tt(){ct().forEach((e=>{t(e,"section").forEach(((e,t)=>{t>0&&(e.classList.remove("present"),e.classList.remove("past"),e.classList.add("future"),e.setAttribute("aria-hidden","true"))}))}))}function it(e=ct()){e.forEach(((t,i)=>{let s=e[Math.floor(Math.random()*e.length)];s.parentNode===t.parentNode&&t.parentNode.insertBefore(t,s);let a=t.querySelectorAll("section");a.length&&it(a)}))}function st(e,i){let s=t(K.wrapper,e),a=s.length,n=oe.isPrintingPDF();if(a){w.loop&&(i%=a)<0&&(i=a+i),i=Math.max(Math.min(i,a-1),0);for(let e=0;e{e.classList.add("visible"),e.classList.remove("current-fragment")}))):e>i&&(a.classList.add(r?"past":"future"),w.fragments&&t(a,".fragment.visible").forEach((e=>{e.classList.remove("visible","current-fragment")})))}let e=s[i],r=e.classList.contains("present");e.classList.add("present"),e.removeAttribute("hidden"),e.removeAttribute("aria-hidden"),r||Pe({target:e,type:"visible",bubbles:!1});let o=e.getAttribute("data-state");o&&(I=I.concat(o.split(" ")))}else i=0;return i}function at(){let e,i,s=ct(),a=s.length;if(a&&void 0!==u){let n=ee.isActive()?10:w.viewDistance;g&&(n=ee.isActive()?6:w.mobileViewDistance),oe.isPrintingPDF()&&(n=Number.MAX_VALUE);for(let r=0;rsection"),i=K.wrapper.querySelectorAll(".slides>section.present>section"),s={left:u>0,right:u0,down:v1&&(s.left=!0,s.right=!0),i.length>1&&(s.up=!0,s.down=!0)),t.length>1&&"linear"===w.navigationMode&&(s.right=s.right||s.down,s.left=s.left||s.up),!0===e){let e=G.availableRoutes();s.left=s.left||e.prev,s.up=s.up||e.prev,s.down=s.down||e.next,s.right=s.right||e.next}if(w.rtl){let e=s.left;s.left=s.right,s.right=e}return s}function rt(e=m){let t=ct(),i=0;e:for(let s=0;s0){let i=.9;t+=m.querySelectorAll(".fragment.visible").length/e.length*i}}return Math.min(t/(e-1),1)}function lt(e){let i,s=u,a=v;if(e){let i=We(e),n=i?e.parentNode:e,r=ct();s=Math.max(r.indexOf(n),0),a=void 0,i&&(a=Math.max(t(e.parentNode,"section").indexOf(e),0))}if(!e&&m){if(m.querySelectorAll(".fragment").length>0){let e=m.querySelector(".current-fragment");i=e&&e.hasAttribute("data-fragment-index")?parseInt(e.getAttribute("data-fragment-index"),10):m.querySelectorAll(".fragment.visible").length-1}}return{h:s,v:a,f:i}}function dt(){return t(K.wrapper,'.slides section:not(.stack):not([data-visibility="uncounted"])')}function ct(){return t(K.wrapper,".slides>section")}function ht(){return t(K.wrapper,".slides>section>section")}function ut(){return t(K.wrapper,".slides>section.stack")}function gt(){return ct().length>1}function vt(){return ht().length>1}function pt(){return dt().map((e=>{let t={};for(let i=0;i{e.hasAttribute("data-autoplay")&&$&&1e3*e.duration/e.playbackRate>$&&($=1e3*e.duration/e.playbackRate+1e3)}))),!$||Y||_e()||ee.isActive()||$e()&&!G.availableRoutes().next&&!0!==w.loop||(j=setTimeout((()=>{"function"==typeof w.autoSlideMethod?w.autoSlideMethod():Nt(),Et()}),$),X=Date.now()),f&&f.setPlaying(-1!==j)}}function Rt(){clearTimeout(j),j=-1}function St(){$&&!Y&&(Y=!0,Pe({type:"autoslidepaused"}),clearTimeout(j),f&&f.setPlaying(!1))}function At(){$&&Y&&(Y=!1,Pe({type:"autoslideresumed"}),Et())}function kt({skipFragments:e=!1}={}){A.hasNavigatedHorizontally=!0,w.rtl?(ee.isActive()||e||!1===G.next())&&nt().left&&Ze(u+1,"grid"===w.navigationMode?v:void 0):(ee.isActive()||e||!1===G.prev())&&nt().left&&Ze(u-1,"grid"===w.navigationMode?v:void 0)}function Lt({skipFragments:e=!1}={}){A.hasNavigatedHorizontally=!0,w.rtl?(ee.isActive()||e||!1===G.prev())&&nt().right&&Ze(u-1,"grid"===w.navigationMode?v:void 0):(ee.isActive()||e||!1===G.next())&&nt().right&&Ze(u+1,"grid"===w.navigationMode?v:void 0)}function Ct({skipFragments:e=!1}={}){(ee.isActive()||e||!1===G.prev())&&nt().up&&Ze(u,v-1)}function xt({skipFragments:e=!1}={}){A.hasNavigatedVertically=!0,(ee.isActive()||e||!1===G.next())&&nt().down&&Ze(u,v+1)}function Pt({skipFragments:e=!1}={}){if(e||!1===G.prev())if(nt().up)Ct({skipFragments:e});else{let i;if(i=w.rtl?t(K.wrapper,".slides>section.future").pop():t(K.wrapper,".slides>section.past").pop(),i&&i.classList.contains("stack")){let e=i.querySelectorAll("section").length-1||void 0;Ze(u-1,e)}else kt({skipFragments:e})}}function Nt({skipFragments:e=!1}={}){if(A.hasNavigatedHorizontally=!0,A.hasNavigatedVertically=!0,e||!1===G.next()){let t=nt();t.down&&t.right&&w.loop&&Ke()&&(t.down=!1),t.down?xt({skipFragments:e}):w.rtl?kt({skipFragments:e}):Lt({skipFragments:e})}}function Mt(e){w.autoSlideStoppable&&St()}function Dt(e){let t=e.data;if("string"==typeof t&&"{"===t.charAt(0)&&"}"===t.charAt(t.length-1)&&(t=JSON.parse(t),t.method&&"function"==typeof h[t.method]))if(!1===R.test(t.method)){const e=h[t.method].apply(h,t.args);Ne("callback",{method:t.method,result:e})}else console.warn('reveal.js: "'+t.method+'" is is blacklisted from the postMessage API')}function It(e){"running"===V&&/section/gi.test(e.target.nodeName)&&(V="idle",Pe({type:"slidetransitionend",data:{indexh:u,indexv:v,previousSlide:p,currentSlide:m}}))}function Tt(e){const t=r(e.target,'a[href^="#"]');if(t){const i=t.getAttribute("href"),s=ie.getIndicesFromHash(i);s&&(h.slide(s.h,s.v,s.f),e.preventDefault())}}function Ft(e){He()}function zt(e){!1===document.hidden&&document.activeElement!==document.body&&("function"==typeof document.activeElement.blur&&document.activeElement.blur(),document.body.focus())}function Ht(e){(document.fullscreenElement||document.webkitFullscreenElement)===K.wrapper&&(e.stopImmediatePropagation(),setTimeout((()=>{h.layout(),h.focus.focus()}),1))}function qt(e){if(e.currentTarget&&e.currentTarget.hasAttribute("href")){let t=e.currentTarget.getAttribute("href");t&&(Ie(t),e.preventDefault())}}function Bt(e){$e()&&!1===w.loop?(Ze(0,0),At()):Y?At():St()}const Ot={VERSION:"4.3.1",initialize:he,configure:Re,destroy:ke,sync:Ge,syncSlide:et,syncFragments:G.sync.bind(G),slide:Ze,left:kt,right:Lt,up:Ct,down:xt,prev:Pt,next:Nt,navigateLeft:kt,navigateRight:Lt,navigateUp:Ct,navigateDown:xt,navigatePrev:Pt,navigateNext:Nt,navigateFragment:G.goto.bind(G),prevFragment:G.prev.bind(G),nextFragment:G.next.bind(G),on:Le,off:Ce,addEventListener:Le,removeEventListener:Ce,layout:He,shuffle:it,availableRoutes:nt,availableFragments:G.availableRoutes.bind(G),toggleHelp:Te,toggleOverview:ee.toggle.bind(ee),togglePause:Ye,toggleAutoSlide:Je,isFirstSlide:Ve,isLastSlide:$e,isLastVerticalSlide:Ke,isVerticalSlide:We,isPaused:_e,isAutoSliding:Qe,isSpeakerNotes:ce.isSpeakerNotesWindow.bind(ce),isOverview:ee.isActive.bind(ee),isFocused:le.isFocused.bind(le),isPrintingPDF:oe.isPrintingPDF.bind(oe),isReady:()=>S,loadSlide:_.load.bind(_),unloadSlide:_.unload.bind(_),showPreview:Ie,hidePreview:ze,addEventListeners:Se,removeEventListeners:Ae,dispatchEvent:Pe,getState:yt,setState:wt,getProgress:ot,getIndices:lt,getSlidesAttributes:pt,getSlidePastCount:rt,getTotalSlides:mt,getSlide:ft,getPreviousSlide:()=>p,getCurrentSlide:()=>m,getSlideBackground:bt,getSlideNotes:ce.getSlideNotes.bind(ce),getSlides:dt,getHorizontalSlides:ct,getVerticalSlides:ht,hasHorizontalSlides:gt,hasVerticalSlides:vt,hasNavigatedHorizontally:()=>A.hasNavigatedHorizontally,hasNavigatedVertically:()=>A.hasNavigatedVertically,addKeyBinding:te.addKeyBinding.bind(te),removeKeyBinding:te.removeKeyBinding.bind(te),triggerKey:te.triggerKey.bind(te),registerKeyboardShortcut:te.registerKeyboardShortcut.bind(te),getComputedSlideSize:Be,getScale:()=>U,getConfig:()=>w,getQueryHash:d,getSlidePath:ie.getHash.bind(ie),getRevealElement:()=>n,getSlidesElement:()=>K.slides,getViewportElement:()=>K.viewport,getBackgroundsElement:()=>Z.element,registerPlugin:re.registerPlugin.bind(re),hasPlugin:re.hasPlugin.bind(re),getPlugin:re.getPlugin.bind(re),getPlugins:re.getRegisteredPlugins.bind(re)};return e(h,{...Ot,announceStatus:fe,getStatusText:be,print:oe,focus:le,progress:ae,controls:se,location:ie,overview:ee,fragments:G,slideContent:_,slideNumber:J,onUserInput:Mt,closeOverlay:ze,updateSlidesVisibility:at,layoutSlideContents:qe,transformSlides:xe,cueAutoSlide:Et,cancelAutoSlide:Rt}),Ot}let W=U,K=[];W.initialize=e=>(Object.assign(W,new U(document.querySelector(".reveal"),e)),K.map((e=>e(W))),W.initialize()),["configure","on","off","addEventListener","removeEventListener","registerPlugin"].forEach((e=>{W[e]=(...t)=>{K.push((i=>i[e].call(null,...t)))}})),W.isReady=()=>!1,W.VERSION="4.3.1";export default W; +//# sourceMappingURL=reveal.esm.js.map diff --git a/site_libs/revealjs/dist/reveal.esm.js.map b/site_libs/revealjs/dist/reveal.esm.js.map new file mode 100644 index 0000000..286c75a --- /dev/null +++ b/site_libs/revealjs/dist/reveal.esm.js.map @@ -0,0 +1 @@ +{"version":3,"file":"reveal.esm.js","sources":["../js/utils/util.js","../js/utils/device.js","../node_modules/fitty/dist/fitty.module.js","../js/controllers/slidecontent.js","../js/controllers/slidenumber.js","../js/utils/color.js","../js/controllers/backgrounds.js","../js/utils/constants.js","../js/controllers/autoanimate.js","../js/controllers/fragments.js","../js/controllers/overview.js","../js/controllers/keyboard.js","../js/controllers/location.js","../js/controllers/controls.js","../js/controllers/progress.js","../js/controllers/pointer.js","../js/utils/loader.js","../js/controllers/plugins.js","../js/controllers/print.js","../js/controllers/touch.js","../js/controllers/focus.js","../js/controllers/notes.js","../js/components/playback.js","../js/config.js","../js/reveal.js","../js/index.js"],"sourcesContent":["/**\n * Extend object a with the properties of object b.\n * If there's a conflict, object b takes precedence.\n *\n * @param {object} a\n * @param {object} b\n */\nexport const extend = ( a, b ) => {\n\n\tfor( let i in b ) {\n\t\ta[ i ] = b[ i ];\n\t}\n\n\treturn a;\n\n}\n\n/**\n * querySelectorAll but returns an Array.\n */\nexport const queryAll = ( el, selector ) => {\n\n\treturn Array.from( el.querySelectorAll( selector ) );\n\n}\n\n/**\n * classList.toggle() with cross browser support\n */\nexport const toggleClass = ( el, className, value ) => {\n\tif( value ) {\n\t\tel.classList.add( className );\n\t}\n\telse {\n\t\tel.classList.remove( className );\n\t}\n}\n\n/**\n * Utility for deserializing a value.\n *\n * @param {*} value\n * @return {*}\n */\nexport const deserialize = ( value ) => {\n\n\tif( typeof value === 'string' ) {\n\t\tif( value === 'null' ) return null;\n\t\telse if( value === 'true' ) return true;\n\t\telse if( value === 'false' ) return false;\n\t\telse if( value.match( /^-?[\\d\\.]+$/ ) ) return parseFloat( value );\n\t}\n\n\treturn value;\n\n}\n\n/**\n * Measures the distance in pixels between point a\n * and point b.\n *\n * @param {object} a point with x/y properties\n * @param {object} b point with x/y properties\n *\n * @return {number}\n */\nexport const distanceBetween = ( a, b ) => {\n\n\tlet dx = a.x - b.x,\n\t\tdy = a.y - b.y;\n\n\treturn Math.sqrt( dx*dx + dy*dy );\n\n}\n\n/**\n * Applies a CSS transform to the target element.\n *\n * @param {HTMLElement} element\n * @param {string} transform\n */\nexport const transformElement = ( element, transform ) => {\n\n\telement.style.transform = transform;\n\n}\n\n/**\n * Element.matches with IE support.\n *\n * @param {HTMLElement} target The element to match\n * @param {String} selector The CSS selector to match\n * the element against\n *\n * @return {Boolean}\n */\nexport const matches = ( target, selector ) => {\n\n\tlet matchesMethod = target.matches || target.matchesSelector || target.msMatchesSelector;\n\n\treturn !!( matchesMethod && matchesMethod.call( target, selector ) );\n\n}\n\n/**\n * Find the closest parent that matches the given\n * selector.\n *\n * @param {HTMLElement} target The child element\n * @param {String} selector The CSS selector to match\n * the parents against\n *\n * @return {HTMLElement} The matched parent or null\n * if no matching parent was found\n */\nexport const closest = ( target, selector ) => {\n\n\t// Native Element.closest\n\tif( typeof target.closest === 'function' ) {\n\t\treturn target.closest( selector );\n\t}\n\n\t// Polyfill\n\twhile( target ) {\n\t\tif( matches( target, selector ) ) {\n\t\t\treturn target;\n\t\t}\n\n\t\t// Keep searching\n\t\ttarget = target.parentNode;\n\t}\n\n\treturn null;\n\n}\n\n/**\n * Handling the fullscreen functionality via the fullscreen API\n *\n * @see http://fullscreen.spec.whatwg.org/\n * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode\n */\nexport const enterFullscreen = element => {\n\n\telement = element || document.documentElement;\n\n\t// Check which implementation is available\n\tlet requestMethod = element.requestFullscreen ||\n\t\t\t\t\t\telement.webkitRequestFullscreen ||\n\t\t\t\t\t\telement.webkitRequestFullScreen ||\n\t\t\t\t\t\telement.mozRequestFullScreen ||\n\t\t\t\t\t\telement.msRequestFullscreen;\n\n\tif( requestMethod ) {\n\t\trequestMethod.apply( element );\n\t}\n\n}\n\n/**\n * Creates an HTML element and returns a reference to it.\n * If the element already exists the existing instance will\n * be returned.\n *\n * @param {HTMLElement} container\n * @param {string} tagname\n * @param {string} classname\n * @param {string} innerHTML\n *\n * @return {HTMLElement}\n */\nexport const createSingletonNode = ( container, tagname, classname, innerHTML='' ) => {\n\n\t// Find all nodes matching the description\n\tlet nodes = container.querySelectorAll( '.' + classname );\n\n\t// Check all matches to find one which is a direct child of\n\t// the specified container\n\tfor( let i = 0; i < nodes.length; i++ ) {\n\t\tlet testNode = nodes[i];\n\t\tif( testNode.parentNode === container ) {\n\t\t\treturn testNode;\n\t\t}\n\t}\n\n\t// If no node was found, create it now\n\tlet node = document.createElement( tagname );\n\tnode.className = classname;\n\tnode.innerHTML = innerHTML;\n\tcontainer.appendChild( node );\n\n\treturn node;\n\n}\n\n/**\n * Injects the given CSS styles into the DOM.\n *\n * @param {string} value\n */\nexport const createStyleSheet = ( value ) => {\n\n\tlet tag = document.createElement( 'style' );\n\ttag.type = 'text/css';\n\n\tif( value && value.length > 0 ) {\n\t\tif( tag.styleSheet ) {\n\t\t\ttag.styleSheet.cssText = value;\n\t\t}\n\t\telse {\n\t\t\ttag.appendChild( document.createTextNode( value ) );\n\t\t}\n\t}\n\n\tdocument.head.appendChild( tag );\n\n\treturn tag;\n\n}\n\n/**\n * Returns a key:value hash of all query params.\n */\nexport const getQueryHash = () => {\n\n\tlet query = {};\n\n\tlocation.search.replace( /[A-Z0-9]+?=([\\w\\.%-]*)/gi, a => {\n\t\tquery[ a.split( '=' ).shift() ] = a.split( '=' ).pop();\n\t} );\n\n\t// Basic deserialization\n\tfor( let i in query ) {\n\t\tlet value = query[ i ];\n\n\t\tquery[ i ] = deserialize( unescape( value ) );\n\t}\n\n\t// Do not accept new dependencies via query config to avoid\n\t// the potential of malicious script injection\n\tif( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];\n\n\treturn query;\n\n}\n\n/**\n * Returns the remaining height within the parent of the\n * target element.\n *\n * remaining height = [ configured parent height ] - [ current parent height ]\n *\n * @param {HTMLElement} element\n * @param {number} [height]\n */\nexport const getRemainingHeight = ( element, height = 0 ) => {\n\n\tif( element ) {\n\t\tlet newHeight, oldHeight = element.style.height;\n\n\t\t// Change the .stretch element height to 0 in order find the height of all\n\t\t// the other elements\n\t\telement.style.height = '0px';\n\n\t\t// In Overview mode, the parent (.slide) height is set of 700px.\n\t\t// Restore it temporarily to its natural height.\n\t\telement.parentNode.style.height = 'auto';\n\n\t\tnewHeight = height - element.parentNode.offsetHeight;\n\n\t\t// Restore the old height, just in case\n\t\telement.style.height = oldHeight + 'px';\n\n\t\t// Clear the parent (.slide) height. .removeProperty works in IE9+\n\t\telement.parentNode.style.removeProperty('height');\n\n\t\treturn newHeight;\n\t}\n\n\treturn height;\n\n}\n\nconst fileExtensionToMimeMap = {\n\t'mp4': 'video/mp4',\n\t'm4a': 'video/mp4',\n\t'ogv': 'video/ogg',\n\t'mpeg': 'video/mpeg',\n\t'webm': 'video/webm'\n}\n\n/**\n * Guess the MIME type for common file formats.\n */\nexport const getMimeTypeFromFile = ( filename='' ) => {\n\treturn fileExtensionToMimeMap[filename.split('.').pop()]\n}","const UA = navigator.userAgent;\n\nexport const isMobile = /(iphone|ipod|ipad|android)/gi.test( UA ) ||\n\t\t\t\t\t\t( navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ); // iPadOS\n\nexport const isChrome = /chrome/i.test( UA ) && !/edge/i.test( UA );\n\nexport const isAndroid = /android/gi.test( UA );","/*\n * fitty v2.3.3 - Snugly resizes text to fit its parent container\n * Copyright (c) 2020 Rik Schennink (https://pqina.nl/)\n */\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\nexports.default = function (w) {\n\n // no window, early exit\n if (!w) return;\n\n // node list to array helper method\n var toArray = function toArray(nl) {\n return [].slice.call(nl);\n };\n\n // states\n var DrawState = {\n IDLE: 0,\n DIRTY_CONTENT: 1,\n DIRTY_LAYOUT: 2,\n DIRTY: 3\n };\n\n // all active fitty elements\n var fitties = [];\n\n // group all redraw calls till next frame, we cancel each frame request when a new one comes in. If no support for request animation frame, this is an empty function and supports for fitty stops.\n var redrawFrame = null;\n var requestRedraw = 'requestAnimationFrame' in w ? function () {\n w.cancelAnimationFrame(redrawFrame);\n redrawFrame = w.requestAnimationFrame(function () {\n return redraw(fitties.filter(function (f) {\n return f.dirty && f.active;\n }));\n });\n } : function () {};\n\n // sets all fitties to dirty so they are redrawn on the next redraw loop, then calls redraw\n var redrawAll = function redrawAll(type) {\n return function () {\n fitties.forEach(function (f) {\n return f.dirty = type;\n });\n requestRedraw();\n };\n };\n\n // redraws fitties so they nicely fit their parent container\n var redraw = function redraw(fitties) {\n\n // getting info from the DOM at this point should not trigger a reflow, let's gather as much intel as possible before triggering a reflow\n\n // check if styles of all fitties have been computed\n fitties.filter(function (f) {\n return !f.styleComputed;\n }).forEach(function (f) {\n f.styleComputed = computeStyle(f);\n });\n\n // restyle elements that require pre-styling, this triggers a reflow, please try to prevent by adding CSS rules (see docs)\n fitties.filter(shouldPreStyle).forEach(applyStyle);\n\n // we now determine which fitties should be redrawn\n var fittiesToRedraw = fitties.filter(shouldRedraw);\n\n // we calculate final styles for these fitties\n fittiesToRedraw.forEach(calculateStyles);\n\n // now we apply the calculated styles from our previous loop\n fittiesToRedraw.forEach(function (f) {\n applyStyle(f);\n markAsClean(f);\n });\n\n // now we dispatch events for all restyled fitties\n fittiesToRedraw.forEach(dispatchFitEvent);\n };\n\n var markAsClean = function markAsClean(f) {\n return f.dirty = DrawState.IDLE;\n };\n\n var calculateStyles = function calculateStyles(f) {\n\n // get available width from parent node\n f.availableWidth = f.element.parentNode.clientWidth;\n\n // the space our target element uses\n f.currentWidth = f.element.scrollWidth;\n\n // remember current font size\n f.previousFontSize = f.currentFontSize;\n\n // let's calculate the new font size\n f.currentFontSize = Math.min(Math.max(f.minSize, f.availableWidth / f.currentWidth * f.previousFontSize), f.maxSize);\n\n // if allows wrapping, only wrap when at minimum font size (otherwise would break container)\n f.whiteSpace = f.multiLine && f.currentFontSize === f.minSize ? 'normal' : 'nowrap';\n };\n\n // should always redraw if is not dirty layout, if is dirty layout, only redraw if size has changed\n var shouldRedraw = function shouldRedraw(f) {\n return f.dirty !== DrawState.DIRTY_LAYOUT || f.dirty === DrawState.DIRTY_LAYOUT && f.element.parentNode.clientWidth !== f.availableWidth;\n };\n\n // every fitty element is tested for invalid styles\n var computeStyle = function computeStyle(f) {\n\n // get style properties\n var style = w.getComputedStyle(f.element, null);\n\n // get current font size in pixels (if we already calculated it, use the calculated version)\n f.currentFontSize = parseFloat(style.getPropertyValue('font-size'));\n\n // get display type and wrap mode\n f.display = style.getPropertyValue('display');\n f.whiteSpace = style.getPropertyValue('white-space');\n };\n\n // determines if this fitty requires initial styling, can be prevented by applying correct styles through CSS\n var shouldPreStyle = function shouldPreStyle(f) {\n\n var preStyle = false;\n\n // if we already tested for prestyling we don't have to do it again\n if (f.preStyleTestCompleted) return false;\n\n // should have an inline style, if not, apply\n if (!/inline-/.test(f.display)) {\n preStyle = true;\n f.display = 'inline-block';\n }\n\n // to correctly calculate dimensions the element should have whiteSpace set to nowrap\n if (f.whiteSpace !== 'nowrap') {\n preStyle = true;\n f.whiteSpace = 'nowrap';\n }\n\n // we don't have to do this twice\n f.preStyleTestCompleted = true;\n\n return preStyle;\n };\n\n // apply styles to single fitty\n var applyStyle = function applyStyle(f) {\n f.element.style.whiteSpace = f.whiteSpace;\n f.element.style.display = f.display;\n f.element.style.fontSize = f.currentFontSize + 'px';\n };\n\n // dispatch a fit event on a fitty\n var dispatchFitEvent = function dispatchFitEvent(f) {\n f.element.dispatchEvent(new CustomEvent('fit', {\n detail: {\n oldValue: f.previousFontSize,\n newValue: f.currentFontSize,\n scaleFactor: f.currentFontSize / f.previousFontSize\n }\n }));\n };\n\n // fit method, marks the fitty as dirty and requests a redraw (this will also redraw any other fitty marked as dirty)\n var fit = function fit(f, type) {\n return function () {\n f.dirty = type;\n if (!f.active) return;\n requestRedraw();\n };\n };\n\n var init = function init(f) {\n\n // save some of the original CSS properties before we change them\n f.originalStyle = {\n whiteSpace: f.element.style.whiteSpace,\n display: f.element.style.display,\n fontSize: f.element.style.fontSize\n };\n\n // should we observe DOM mutations\n observeMutations(f);\n\n // this is a new fitty so we need to validate if it's styles are in order\n f.newbie = true;\n\n // because it's a new fitty it should also be dirty, we want it to redraw on the first loop\n f.dirty = true;\n\n // we want to be able to update this fitty\n fitties.push(f);\n };\n\n var destroy = function destroy(f) {\n return function () {\n\n // remove from fitties array\n fitties = fitties.filter(function (_) {\n return _.element !== f.element;\n });\n\n // stop observing DOM\n if (f.observeMutations) f.observer.disconnect();\n\n // reset the CSS properties we changes\n f.element.style.whiteSpace = f.originalStyle.whiteSpace;\n f.element.style.display = f.originalStyle.display;\n f.element.style.fontSize = f.originalStyle.fontSize;\n };\n };\n\n // add a new fitty, does not redraw said fitty\n var subscribe = function subscribe(f) {\n return function () {\n if (f.active) return;\n f.active = true;\n requestRedraw();\n };\n };\n\n // remove an existing fitty\n var unsubscribe = function unsubscribe(f) {\n return function () {\n return f.active = false;\n };\n };\n\n var observeMutations = function observeMutations(f) {\n\n // no observing?\n if (!f.observeMutations) return;\n\n // start observing mutations\n f.observer = new MutationObserver(fit(f, DrawState.DIRTY_CONTENT));\n\n // start observing\n f.observer.observe(f.element, f.observeMutations);\n };\n\n // default mutation observer settings\n var mutationObserverDefaultSetting = {\n subtree: true,\n childList: true,\n characterData: true\n };\n\n // default fitty options\n var defaultOptions = {\n minSize: 16,\n maxSize: 512,\n multiLine: true,\n observeMutations: 'MutationObserver' in w ? mutationObserverDefaultSetting : false\n };\n\n // array of elements in, fitty instances out\n function fittyCreate(elements, options) {\n\n // set options object\n var fittyOptions = _extends({}, defaultOptions, options);\n\n // create fitties\n var publicFitties = elements.map(function (element) {\n\n // create fitty instance\n var f = _extends({}, fittyOptions, {\n\n // internal options for this fitty\n element: element,\n active: true\n });\n\n // initialise this fitty\n init(f);\n\n // expose API\n return {\n element: element,\n fit: fit(f, DrawState.DIRTY),\n unfreeze: subscribe(f),\n freeze: unsubscribe(f),\n unsubscribe: destroy(f)\n };\n });\n\n // call redraw on newly initiated fitties\n requestRedraw();\n\n // expose fitties\n return publicFitties;\n }\n\n // fitty creation function\n function fitty(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n\n\n // if target is a string\n return typeof target === 'string' ?\n\n // treat it as a querySelector\n fittyCreate(toArray(document.querySelectorAll(target)), options) :\n\n // create single fitty\n fittyCreate([target], options)[0];\n }\n\n // handles viewport changes, redraws all fitties, but only does so after a timeout\n var resizeDebounce = null;\n var onWindowResized = function onWindowResized() {\n w.clearTimeout(resizeDebounce);\n resizeDebounce = w.setTimeout(redrawAll(DrawState.DIRTY_LAYOUT), fitty.observeWindowDelay);\n };\n\n // define observe window property, so when we set it to true or false events are automatically added and removed\n var events = ['resize', 'orientationchange'];\n Object.defineProperty(fitty, 'observeWindow', {\n set: function set(enabled) {\n var method = (enabled ? 'add' : 'remove') + 'EventListener';\n events.forEach(function (e) {\n w[method](e, onWindowResized);\n });\n }\n });\n\n // fitty global properties (by setting observeWindow to true the events above get added)\n fitty.observeWindow = true;\n fitty.observeWindowDelay = 100;\n\n // public fit all method, will force redraw no matter what\n fitty.fitAll = redrawAll(DrawState.DIRTY);\n\n // export our fitty function, we don't want to keep it to our selves\n return fitty;\n}(typeof window === 'undefined' ? null : window);","import { extend, queryAll, closest, getMimeTypeFromFile } from '../utils/util.js'\nimport { isMobile } from '../utils/device.js'\n\nimport fitty from 'fitty';\n\n/**\n * Handles loading, unloading and playback of slide\n * content such as images, videos and iframes.\n */\nexport default class SlideContent {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.startEmbeddedIframe = this.startEmbeddedIframe.bind( this );\n\n\t}\n\n\t/**\n\t * Should the given element be preloaded?\n\t * Decides based on local element attributes and global config.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tshouldPreload( element ) {\n\n\t\t// Prefer an explicit global preload setting\n\t\tlet preload = this.Reveal.getConfig().preloadIframes;\n\n\t\t// If no global setting is available, fall back on the element's\n\t\t// own preload setting\n\t\tif( typeof preload !== 'boolean' ) {\n\t\t\tpreload = element.hasAttribute( 'data-preload' );\n\t\t}\n\n\t\treturn preload;\n\t}\n\n\t/**\n\t * Called when the given slide is within the configured view\n\t * distance. Shows the slide element and loads any content\n\t * that is set to load lazily (data-src).\n\t *\n\t * @param {HTMLElement} slide Slide to show\n\t */\n\tload( slide, options = {} ) {\n\n\t\t// Show the slide element\n\t\tslide.style.display = this.Reveal.getConfig().display;\n\n\t\t// Media elements with data-src attributes\n\t\tqueryAll( slide, 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ).forEach( element => {\n\t\t\tif( element.tagName !== 'IFRAME' || this.shouldPreload( element ) ) {\n\t\t\t\telement.setAttribute( 'src', element.getAttribute( 'data-src' ) );\n\t\t\t\telement.setAttribute( 'data-lazy-loaded', '' );\n\t\t\t\telement.removeAttribute( 'data-src' );\n\t\t\t}\n\t\t} );\n\n\t\t// Media elements with children\n\t\tqueryAll( slide, 'video, audio' ).forEach( media => {\n\t\t\tlet sources = 0;\n\n\t\t\tqueryAll( media, 'source[data-src]' ).forEach( source => {\n\t\t\t\tsource.setAttribute( 'src', source.getAttribute( 'data-src' ) );\n\t\t\t\tsource.removeAttribute( 'data-src' );\n\t\t\t\tsource.setAttribute( 'data-lazy-loaded', '' );\n\t\t\t\tsources += 1;\n\t\t\t} );\n\n\t\t\t// Enable inline video playback in mobile Safari\n\t\t\tif( isMobile && media.tagName === 'VIDEO' ) {\n\t\t\t\tmedia.setAttribute( 'playsinline', '' );\n\t\t\t}\n\n\t\t\t// If we rewrote sources for this video/audio element, we need\n\t\t\t// to manually tell it to load from its new origin\n\t\t\tif( sources > 0 ) {\n\t\t\t\tmedia.load();\n\t\t\t}\n\t\t} );\n\n\n\t\t// Show the corresponding background element\n\t\tlet background = slide.slideBackgroundElement;\n\t\tif( background ) {\n\t\t\tbackground.style.display = 'block';\n\n\t\t\tlet backgroundContent = slide.slideBackgroundContentElement;\n\t\t\tlet backgroundIframe = slide.getAttribute( 'data-background-iframe' );\n\n\t\t\t// If the background contains media, load it\n\t\t\tif( background.hasAttribute( 'data-loaded' ) === false ) {\n\t\t\t\tbackground.setAttribute( 'data-loaded', 'true' );\n\n\t\t\t\tlet backgroundImage = slide.getAttribute( 'data-background-image' ),\n\t\t\t\t\tbackgroundVideo = slide.getAttribute( 'data-background-video' ),\n\t\t\t\t\tbackgroundVideoLoop = slide.hasAttribute( 'data-background-video-loop' ),\n\t\t\t\t\tbackgroundVideoMuted = slide.hasAttribute( 'data-background-video-muted' );\n\n\t\t\t\t// Images\n\t\t\t\tif( backgroundImage ) {\n\t\t\t\t\t// base64\n\t\t\t\t\tif( /^data:/.test( backgroundImage.trim() ) ) {\n\t\t\t\t\t\tbackgroundContent.style.backgroundImage = `url(${backgroundImage.trim()})`;\n\t\t\t\t\t}\n\t\t\t\t\t// URL(s)\n\t\t\t\t\telse {\n\t\t\t\t\t\tbackgroundContent.style.backgroundImage = backgroundImage.split( ',' ).map( background => {\n\t\t\t\t\t\t\treturn `url(${encodeURI(background.trim())})`;\n\t\t\t\t\t\t}).join( ',' );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Videos\n\t\t\t\telse if ( backgroundVideo && !this.Reveal.isSpeakerNotes() ) {\n\t\t\t\t\tlet video = document.createElement( 'video' );\n\n\t\t\t\t\tif( backgroundVideoLoop ) {\n\t\t\t\t\t\tvideo.setAttribute( 'loop', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\tif( backgroundVideoMuted ) {\n\t\t\t\t\t\tvideo.muted = true;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Enable inline playback in mobile Safari\n\t\t\t\t\t//\n\t\t\t\t\t// Mute is required for video to play when using\n\t\t\t\t\t// swipe gestures to navigate since they don't\n\t\t\t\t\t// count as direct user actions :'(\n\t\t\t\t\tif( isMobile ) {\n\t\t\t\t\t\tvideo.muted = true;\n\t\t\t\t\t\tvideo.setAttribute( 'playsinline', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\t// Support comma separated lists of video sources\n\t\t\t\t\tbackgroundVideo.split( ',' ).forEach( source => {\n\t\t\t\t\t\tlet type = getMimeTypeFromFile( source );\n\t\t\t\t\t\tif( type ) {\n\t\t\t\t\t\t\tvideo.innerHTML += ``;\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tvideo.innerHTML += ``;\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\n\t\t\t\t\tbackgroundContent.appendChild( video );\n\t\t\t\t}\n\t\t\t\t// Iframes\n\t\t\t\telse if( backgroundIframe && options.excludeIframes !== true ) {\n\t\t\t\t\tlet iframe = document.createElement( 'iframe' );\n\t\t\t\t\tiframe.setAttribute( 'allowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'mozallowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'webkitallowfullscreen', '' );\n\t\t\t\t\tiframe.setAttribute( 'allow', 'autoplay' );\n\n\t\t\t\t\tiframe.setAttribute( 'data-src', backgroundIframe );\n\n\t\t\t\t\tiframe.style.width = '100%';\n\t\t\t\t\tiframe.style.height = '100%';\n\t\t\t\t\tiframe.style.maxHeight = '100%';\n\t\t\t\t\tiframe.style.maxWidth = '100%';\n\n\t\t\t\t\tbackgroundContent.appendChild( iframe );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Start loading preloadable iframes\n\t\t\tlet backgroundIframeElement = backgroundContent.querySelector( 'iframe[data-src]' );\n\t\t\tif( backgroundIframeElement ) {\n\n\t\t\t\t// Check if this iframe is eligible to be preloaded\n\t\t\t\tif( this.shouldPreload( background ) && !/autoplay=(1|true|yes)/gi.test( backgroundIframe ) ) {\n\t\t\t\t\tif( backgroundIframeElement.getAttribute( 'src' ) !== backgroundIframe ) {\n\t\t\t\t\t\tbackgroundIframeElement.setAttribute( 'src', backgroundIframe );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\tthis.layout( slide );\n\n\t}\n\n\t/**\n\t * Applies JS-dependent layout helpers for the given slide,\n\t * if there are any.\n\t */\n\tlayout( slide ) {\n\n\t\t// Autosize text with the r-fit-text class based on the\n\t\t// size of its container. This needs to happen after the\n\t\t// slide is visible in order to measure the text.\n\t\tArray.from( slide.querySelectorAll( '.r-fit-text' ) ).forEach( element => {\n\t\t\tfitty( element, {\n\t\t\t\tminSize: 24,\n\t\t\t\tmaxSize: this.Reveal.getConfig().height * 0.8,\n\t\t\t\tobserveMutations: false,\n\t\t\t\tobserveWindow: false\n\t\t\t} );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Unloads and hides the given slide. This is called when the\n\t * slide is moved outside of the configured view distance.\n\t *\n\t * @param {HTMLElement} slide\n\t */\n\tunload( slide ) {\n\n\t\t// Hide the slide element\n\t\tslide.style.display = 'none';\n\n\t\t// Hide the corresponding background element\n\t\tlet background = this.Reveal.getSlideBackground( slide );\n\t\tif( background ) {\n\t\t\tbackground.style.display = 'none';\n\n\t\t\t// Unload any background iframes\n\t\t\tqueryAll( background, 'iframe[src]' ).forEach( element => {\n\t\t\t\telement.removeAttribute( 'src' );\n\t\t\t} );\n\t\t}\n\n\t\t// Reset lazy-loaded media elements with src attributes\n\t\tqueryAll( slide, 'video[data-lazy-loaded][src], audio[data-lazy-loaded][src], iframe[data-lazy-loaded][src]' ).forEach( element => {\n\t\t\telement.setAttribute( 'data-src', element.getAttribute( 'src' ) );\n\t\t\telement.removeAttribute( 'src' );\n\t\t} );\n\n\t\t// Reset lazy-loaded media elements with children\n\t\tqueryAll( slide, 'video[data-lazy-loaded] source[src], audio source[src]' ).forEach( source => {\n\t\t\tsource.setAttribute( 'data-src', source.getAttribute( 'src' ) );\n\t\t\tsource.removeAttribute( 'src' );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Enforces origin-specific format rules for embedded media.\n\t */\n\tformatEmbeddedContent() {\n\n\t\tlet _appendParamToIframeSource = ( sourceAttribute, sourceURL, param ) => {\n\t\t\tqueryAll( this.Reveal.getSlidesElement(), 'iframe['+ sourceAttribute +'*=\"'+ sourceURL +'\"]' ).forEach( el => {\n\t\t\t\tlet src = el.getAttribute( sourceAttribute );\n\t\t\t\tif( src && src.indexOf( param ) === -1 ) {\n\t\t\t\t\tel.setAttribute( sourceAttribute, src + ( !/\\?/.test( src ) ? '?' : '&' ) + param );\n\t\t\t\t}\n\t\t\t});\n\t\t};\n\n\t\t// YouTube frames must include \"?enablejsapi=1\"\n\t\t_appendParamToIframeSource( 'src', 'youtube.com/embed/', 'enablejsapi=1' );\n\t\t_appendParamToIframeSource( 'data-src', 'youtube.com/embed/', 'enablejsapi=1' );\n\n\t\t// Vimeo frames must include \"?api=1\"\n\t\t_appendParamToIframeSource( 'src', 'player.vimeo.com/', 'api=1' );\n\t\t_appendParamToIframeSource( 'data-src', 'player.vimeo.com/', 'api=1' );\n\n\t}\n\n\t/**\n\t * Start playback of any embedded content inside of\n\t * the given element.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tstartEmbeddedContent( element ) {\n\n\t\tif( element && !this.Reveal.isSpeakerNotes() ) {\n\n\t\t\t// Restart GIFs\n\t\t\tqueryAll( element, 'img[src$=\".gif\"]' ).forEach( el => {\n\t\t\t\t// Setting the same unchanged source like this was confirmed\n\t\t\t\t// to work in Chrome, FF & Safari\n\t\t\t\tel.setAttribute( 'src', el.getAttribute( 'src' ) );\n\t\t\t} );\n\n\t\t\t// HTML5 media elements\n\t\t\tqueryAll( element, 'video, audio' ).forEach( el => {\n\t\t\t\tif( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Prefer an explicit global autoplay setting\n\t\t\t\tlet autoplay = this.Reveal.getConfig().autoPlayMedia;\n\n\t\t\t\t// If no global setting is available, fall back on the element's\n\t\t\t\t// own autoplay setting\n\t\t\t\tif( typeof autoplay !== 'boolean' ) {\n\t\t\t\t\tautoplay = el.hasAttribute( 'data-autoplay' ) || !!closest( el, '.slide-background' );\n\t\t\t\t}\n\n\t\t\t\tif( autoplay && typeof el.play === 'function' ) {\n\n\t\t\t\t\t// If the media is ready, start playback\n\t\t\t\t\tif( el.readyState > 1 ) {\n\t\t\t\t\t\tthis.startEmbeddedMedia( { target: el } );\n\t\t\t\t\t}\n\t\t\t\t\t// Mobile devices never fire a loaded event so instead\n\t\t\t\t\t// of waiting, we initiate playback\n\t\t\t\t\telse if( isMobile ) {\n\t\t\t\t\t\tlet promise = el.play();\n\n\t\t\t\t\t\t// If autoplay does not work, ensure that the controls are visible so\n\t\t\t\t\t\t// that the viewer can start the media on their own\n\t\t\t\t\t\tif( promise && typeof promise.catch === 'function' && el.controls === false ) {\n\t\t\t\t\t\t\tpromise.catch( () => {\n\t\t\t\t\t\t\t\tel.controls = true;\n\n\t\t\t\t\t\t\t\t// Once the video does start playing, hide the controls again\n\t\t\t\t\t\t\t\tel.addEventListener( 'play', () => {\n\t\t\t\t\t\t\t\t\tel.controls = false;\n\t\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// If the media isn't loaded, wait before playing\n\t\t\t\t\telse {\n\t\t\t\t\t\tel.removeEventListener( 'loadeddata', this.startEmbeddedMedia ); // remove first to avoid dupes\n\t\t\t\t\t\tel.addEventListener( 'loadeddata', this.startEmbeddedMedia );\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Normal iframes\n\t\t\tqueryAll( element, 'iframe[src]' ).forEach( el => {\n\t\t\t\tif( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tthis.startEmbeddedIframe( { target: el } );\n\t\t\t} );\n\n\t\t\t// Lazy loading iframes\n\t\t\tqueryAll( element, 'iframe[data-src]' ).forEach( el => {\n\t\t\t\tif( closest( el, '.fragment' ) && !closest( el, '.fragment.visible' ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif( el.getAttribute( 'src' ) !== el.getAttribute( 'data-src' ) ) {\n\t\t\t\t\tel.removeEventListener( 'load', this.startEmbeddedIframe ); // remove first to avoid dupes\n\t\t\t\t\tel.addEventListener( 'load', this.startEmbeddedIframe );\n\t\t\t\t\tel.setAttribute( 'src', el.getAttribute( 'data-src' ) );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Starts playing an embedded video/audio element after\n\t * it has finished loading.\n\t *\n\t * @param {object} event\n\t */\n\tstartEmbeddedMedia( event ) {\n\n\t\tlet isAttachedToDOM = !!closest( event.target, 'html' ),\n\t\t\tisVisible \t\t= !!closest( event.target, '.present' );\n\n\t\tif( isAttachedToDOM && isVisible ) {\n\t\t\tevent.target.currentTime = 0;\n\t\t\tevent.target.play();\n\t\t}\n\n\t\tevent.target.removeEventListener( 'loadeddata', this.startEmbeddedMedia );\n\n\t}\n\n\t/**\n\t * \"Starts\" the content of an embedded iframe using the\n\t * postMessage API.\n\t *\n\t * @param {object} event\n\t */\n\tstartEmbeddedIframe( event ) {\n\n\t\tlet iframe = event.target;\n\n\t\tif( iframe && iframe.contentWindow ) {\n\n\t\t\tlet isAttachedToDOM = !!closest( event.target, 'html' ),\n\t\t\t\tisVisible \t\t= !!closest( event.target, '.present' );\n\n\t\t\tif( isAttachedToDOM && isVisible ) {\n\n\t\t\t\t// Prefer an explicit global autoplay setting\n\t\t\t\tlet autoplay = this.Reveal.getConfig().autoPlayMedia;\n\n\t\t\t\t// If no global setting is available, fall back on the element's\n\t\t\t\t// own autoplay setting\n\t\t\t\tif( typeof autoplay !== 'boolean' ) {\n\t\t\t\t\tautoplay = iframe.hasAttribute( 'data-autoplay' ) || !!closest( iframe, '.slide-background' );\n\t\t\t\t}\n\n\t\t\t\t// YouTube postMessage API\n\t\t\t\tif( /youtube\\.com\\/embed\\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {\n\t\t\t\t\tiframe.contentWindow.postMessage( '{\"event\":\"command\",\"func\":\"playVideo\",\"args\":\"\"}', '*' );\n\t\t\t\t}\n\t\t\t\t// Vimeo postMessage API\n\t\t\t\telse if( /player\\.vimeo\\.com\\//.test( iframe.getAttribute( 'src' ) ) && autoplay ) {\n\t\t\t\t\tiframe.contentWindow.postMessage( '{\"method\":\"play\"}', '*' );\n\t\t\t\t}\n\t\t\t\t// Generic postMessage API\n\t\t\t\telse {\n\t\t\t\t\tiframe.contentWindow.postMessage( 'slide:start', '*' );\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Stop playback of any embedded content inside of\n\t * the targeted slide.\n\t *\n\t * @param {HTMLElement} element\n\t */\n\tstopEmbeddedContent( element, options = {} ) {\n\n\t\toptions = extend( {\n\t\t\t// Defaults\n\t\t\tunloadIframes: true\n\t\t}, options );\n\n\t\tif( element && element.parentNode ) {\n\t\t\t// HTML5 media elements\n\t\t\tqueryAll( element, 'video, audio' ).forEach( el => {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && typeof el.pause === 'function' ) {\n\t\t\t\t\tel.setAttribute('data-paused-by-reveal', '');\n\t\t\t\t\tel.pause();\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Generic postMessage API for non-lazy loaded iframes\n\t\t\tqueryAll( element, 'iframe' ).forEach( el => {\n\t\t\t\tif( el.contentWindow ) el.contentWindow.postMessage( 'slide:stop', '*' );\n\t\t\t\tel.removeEventListener( 'load', this.startEmbeddedIframe );\n\t\t\t});\n\n\t\t\t// YouTube postMessage API\n\t\t\tqueryAll( element, 'iframe[src*=\"youtube.com/embed/\"]' ).forEach( el => {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {\n\t\t\t\t\tel.contentWindow.postMessage( '{\"event\":\"command\",\"func\":\"pauseVideo\",\"args\":\"\"}', '*' );\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Vimeo postMessage API\n\t\t\tqueryAll( element, 'iframe[src*=\"player.vimeo.com/\"]' ).forEach( el => {\n\t\t\t\tif( !el.hasAttribute( 'data-ignore' ) && el.contentWindow && typeof el.contentWindow.postMessage === 'function' ) {\n\t\t\t\t\tel.contentWindow.postMessage( '{\"method\":\"pause\"}', '*' );\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif( options.unloadIframes === true ) {\n\t\t\t\t// Unload lazy-loaded iframes\n\t\t\t\tqueryAll( element, 'iframe[data-src]' ).forEach( el => {\n\t\t\t\t\t// Only removing the src doesn't actually unload the frame\n\t\t\t\t\t// in all browsers (Firefox) so we set it to blank first\n\t\t\t\t\tel.setAttribute( 'src', 'about:blank' );\n\t\t\t\t\tel.removeAttribute( 'src' );\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n","/**\n * Handles the display of reveal.js' optional slide number.\n */\nexport default class SlideNumber {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'slide-number';\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tlet slideNumberDisplay = 'none';\n\t\tif( config.slideNumber && !this.Reveal.isPrintingPDF() ) {\n\t\t\tif( config.showSlideNumber === 'all' ) {\n\t\t\t\tslideNumberDisplay = 'block';\n\t\t\t}\n\t\t\telse if( config.showSlideNumber === 'speaker' && this.Reveal.isSpeakerNotes() ) {\n\t\t\t\tslideNumberDisplay = 'block';\n\t\t\t}\n\t\t}\n\n\t\tthis.element.style.display = slideNumberDisplay;\n\n\t}\n\n\t/**\n\t * Updates the slide number to match the current slide.\n\t */\n\tupdate() {\n\n\t\t// Update slide number if enabled\n\t\tif( this.Reveal.getConfig().slideNumber && this.element ) {\n\t\t\tthis.element.innerHTML = this.getSlideNumber();\n\t\t}\n\n\t}\n\n\t/**\n\t * Returns the HTML string corresponding to the current slide\n\t * number, including formatting.\n\t */\n\tgetSlideNumber( slide = this.Reveal.getCurrentSlide() ) {\n\n\t\tlet config = this.Reveal.getConfig();\n\t\tlet value;\n\t\tlet format = 'h.v';\n\n\t\tif ( typeof config.slideNumber === 'function' ) {\n\t\t\tvalue = config.slideNumber( slide );\n\t\t} else {\n\t\t\t// Check if a custom number format is available\n\t\t\tif( typeof config.slideNumber === 'string' ) {\n\t\t\t\tformat = config.slideNumber;\n\t\t\t}\n\n\t\t\t// If there are ONLY vertical slides in this deck, always use\n\t\t\t// a flattened slide number\n\t\t\tif( !/c/.test( format ) && this.Reveal.getHorizontalSlides().length === 1 ) {\n\t\t\t\tformat = 'c';\n\t\t\t}\n\n\t\t\t// Offset the current slide number by 1 to make it 1-indexed\n\t\t\tlet horizontalOffset = slide && slide.dataset.visibility === 'uncounted' ? 0 : 1;\n\n\t\t\tvalue = [];\n\t\t\tswitch( format ) {\n\t\t\t\tcase 'c':\n\t\t\t\t\tvalue.push( this.Reveal.getSlidePastCount( slide ) + horizontalOffset );\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'c/t':\n\t\t\t\t\tvalue.push( this.Reveal.getSlidePastCount( slide ) + horizontalOffset, '/', this.Reveal.getTotalSlides() );\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tlet indices = this.Reveal.getIndices( slide );\n\t\t\t\t\tvalue.push( indices.h + horizontalOffset );\n\t\t\t\t\tlet sep = format === 'h/v' ? '/' : '.';\n\t\t\t\t\tif( this.Reveal.isVerticalSlide( slide ) ) value.push( sep, indices.v + 1 );\n\t\t\t}\n\t\t}\n\n\t\tlet url = '#' + this.Reveal.location.getHash( slide );\n\t\treturn this.formatNumber( value[0], value[1], value[2], url );\n\n\t}\n\n\t/**\n\t * Applies HTML formatting to a slide number before it's\n\t * written to the DOM.\n\t *\n\t * @param {number} a Current slide\n\t * @param {string} delimiter Character to separate slide numbers\n\t * @param {(number|*)} b Total slides\n\t * @param {HTMLElement} [url='#'+locationHash()] The url to link to\n\t * @return {string} HTML string fragment\n\t */\n\tformatNumber( a, delimiter, b, url = '#' + this.Reveal.location.getHash() ) {\n\n\t\tif( typeof b === 'number' && !isNaN( b ) ) {\n\t\t\treturn `\n\t\t\t\t\t${a}\n\t\t\t\t\t${delimiter}\n\t\t\t\t\t${b}\n\t\t\t\t\t`;\n\t\t}\n\t\telse {\n\t\t\treturn `\n\t\t\t\t\t${a}\n\t\t\t\t\t`;\n\t\t}\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.element.remove();\n\n\t}\n\n}","/**\n * Converts various color input formats to an {r:0,g:0,b:0} object.\n *\n * @param {string} color The string representation of a color\n * @example\n * colorToRgb('#000');\n * @example\n * colorToRgb('#000000');\n * @example\n * colorToRgb('rgb(0,0,0)');\n * @example\n * colorToRgb('rgba(0,0,0)');\n *\n * @return {{r: number, g: number, b: number, [a]: number}|null}\n */\nexport const colorToRgb = ( color ) => {\n\n\tlet hex3 = color.match( /^#([0-9a-f]{3})$/i );\n\tif( hex3 && hex3[1] ) {\n\t\thex3 = hex3[1];\n\t\treturn {\n\t\t\tr: parseInt( hex3.charAt( 0 ), 16 ) * 0x11,\n\t\t\tg: parseInt( hex3.charAt( 1 ), 16 ) * 0x11,\n\t\t\tb: parseInt( hex3.charAt( 2 ), 16 ) * 0x11\n\t\t};\n\t}\n\n\tlet hex6 = color.match( /^#([0-9a-f]{6})$/i );\n\tif( hex6 && hex6[1] ) {\n\t\thex6 = hex6[1];\n\t\treturn {\n\t\t\tr: parseInt( hex6.slice( 0, 2 ), 16 ),\n\t\t\tg: parseInt( hex6.slice( 2, 4 ), 16 ),\n\t\t\tb: parseInt( hex6.slice( 4, 6 ), 16 )\n\t\t};\n\t}\n\n\tlet rgb = color.match( /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i );\n\tif( rgb ) {\n\t\treturn {\n\t\t\tr: parseInt( rgb[1], 10 ),\n\t\t\tg: parseInt( rgb[2], 10 ),\n\t\t\tb: parseInt( rgb[3], 10 )\n\t\t};\n\t}\n\n\tlet rgba = color.match( /^rgba\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\,\\s*([\\d]+|[\\d]*.[\\d]+)\\s*\\)$/i );\n\tif( rgba ) {\n\t\treturn {\n\t\t\tr: parseInt( rgba[1], 10 ),\n\t\t\tg: parseInt( rgba[2], 10 ),\n\t\t\tb: parseInt( rgba[3], 10 ),\n\t\t\ta: parseFloat( rgba[4] )\n\t\t};\n\t}\n\n\treturn null;\n\n}\n\n/**\n * Calculates brightness on a scale of 0-255.\n *\n * @param {string} color See colorToRgb for supported formats.\n * @see {@link colorToRgb}\n */\nexport const colorBrightness = ( color ) => {\n\n\tif( typeof color === 'string' ) color = colorToRgb( color );\n\n\tif( color ) {\n\t\treturn ( color.r * 299 + color.g * 587 + color.b * 114 ) / 1000;\n\t}\n\n\treturn null;\n\n}","import { queryAll } from '../utils/util.js'\nimport { colorToRgb, colorBrightness } from '../utils/color.js'\n\n/**\n * Creates and updates slide backgrounds.\n */\nexport default class Backgrounds {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'backgrounds';\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t}\n\n\t/**\n\t * Creates the slide background elements and appends them\n\t * to the background container. One element is created per\n\t * slide no matter if the given slide has visible background.\n\t */\n\tcreate() {\n\n\t\t// Clear prior backgrounds\n\t\tthis.element.innerHTML = '';\n\t\tthis.element.classList.add( 'no-transition' );\n\n\t\t// Iterate over all horizontal slides\n\t\tthis.Reveal.getHorizontalSlides().forEach( slideh => {\n\n\t\t\tlet backgroundStack = this.createBackground( slideh, this.element );\n\n\t\t\t// Iterate over all vertical slides\n\t\t\tqueryAll( slideh, 'section' ).forEach( slidev => {\n\n\t\t\t\tthis.createBackground( slidev, backgroundStack );\n\n\t\t\t\tbackgroundStack.classList.add( 'stack' );\n\n\t\t\t} );\n\n\t\t} );\n\n\t\t// Add parallax background if specified\n\t\tif( this.Reveal.getConfig().parallaxBackgroundImage ) {\n\n\t\t\tthis.element.style.backgroundImage = 'url(\"' + this.Reveal.getConfig().parallaxBackgroundImage + '\")';\n\t\t\tthis.element.style.backgroundSize = this.Reveal.getConfig().parallaxBackgroundSize;\n\t\t\tthis.element.style.backgroundRepeat = this.Reveal.getConfig().parallaxBackgroundRepeat;\n\t\t\tthis.element.style.backgroundPosition = this.Reveal.getConfig().parallaxBackgroundPosition;\n\n\t\t\t// Make sure the below properties are set on the element - these properties are\n\t\t\t// needed for proper transitions to be set on the element via CSS. To remove\n\t\t\t// annoying background slide-in effect when the presentation starts, apply\n\t\t\t// these properties after short time delay\n\t\t\tsetTimeout( () => {\n\t\t\t\tthis.Reveal.getRevealElement().classList.add( 'has-parallax-background' );\n\t\t\t}, 1 );\n\n\t\t}\n\t\telse {\n\n\t\t\tthis.element.style.backgroundImage = '';\n\t\t\tthis.Reveal.getRevealElement().classList.remove( 'has-parallax-background' );\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a background for the given slide.\n\t *\n\t * @param {HTMLElement} slide\n\t * @param {HTMLElement} container The element that the background\n\t * should be appended to\n\t * @return {HTMLElement} New background div\n\t */\n\tcreateBackground( slide, container ) {\n\n\t\t// Main slide background element\n\t\tlet element = document.createElement( 'div' );\n\t\telement.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' );\n\n\t\t// Inner background element that wraps images/videos/iframes\n\t\tlet contentElement = document.createElement( 'div' );\n\t\tcontentElement.className = 'slide-background-content';\n\n\t\telement.appendChild( contentElement );\n\t\tcontainer.appendChild( element );\n\n\t\tslide.slideBackgroundElement = element;\n\t\tslide.slideBackgroundContentElement = contentElement;\n\n\t\t// Syncs the background to reflect all current background settings\n\t\tthis.sync( slide );\n\n\t\treturn element;\n\n\t}\n\n\t/**\n\t * Renders all of the visual properties of a slide background\n\t * based on the various background attributes.\n\t *\n\t * @param {HTMLElement} slide\n\t */\n\tsync( slide ) {\n\n\t\tconst element = slide.slideBackgroundElement,\n\t\t\tcontentElement = slide.slideBackgroundContentElement;\n\n\t\tconst data = {\n\t\t\tbackground: slide.getAttribute( 'data-background' ),\n\t\t\tbackgroundSize: slide.getAttribute( 'data-background-size' ),\n\t\t\tbackgroundImage: slide.getAttribute( 'data-background-image' ),\n\t\t\tbackgroundVideo: slide.getAttribute( 'data-background-video' ),\n\t\t\tbackgroundIframe: slide.getAttribute( 'data-background-iframe' ),\n\t\t\tbackgroundColor: slide.getAttribute( 'data-background-color' ),\n\t\t\tbackgroundRepeat: slide.getAttribute( 'data-background-repeat' ),\n\t\t\tbackgroundPosition: slide.getAttribute( 'data-background-position' ),\n\t\t\tbackgroundTransition: slide.getAttribute( 'data-background-transition' ),\n\t\t\tbackgroundOpacity: slide.getAttribute( 'data-background-opacity' ),\n\t\t};\n\n\t\tconst dataPreload = slide.hasAttribute( 'data-preload' );\n\n\t\t// Reset the prior background state in case this is not the\n\t\t// initial sync\n\t\tslide.classList.remove( 'has-dark-background' );\n\t\tslide.classList.remove( 'has-light-background' );\n\n\t\telement.removeAttribute( 'data-loaded' );\n\t\telement.removeAttribute( 'data-background-hash' );\n\t\telement.removeAttribute( 'data-background-size' );\n\t\telement.removeAttribute( 'data-background-transition' );\n\t\telement.style.backgroundColor = '';\n\n\t\tcontentElement.style.backgroundSize = '';\n\t\tcontentElement.style.backgroundRepeat = '';\n\t\tcontentElement.style.backgroundPosition = '';\n\t\tcontentElement.style.backgroundImage = '';\n\t\tcontentElement.style.opacity = '';\n\t\tcontentElement.innerHTML = '';\n\n\t\tif( data.background ) {\n\t\t\t// Auto-wrap image urls in url(...)\n\t\t\tif( /^(http|file|\\/\\/)/gi.test( data.background ) || /\\.(svg|png|jpg|jpeg|gif|bmp)([?#\\s]|$)/gi.test( data.background ) ) {\n\t\t\t\tslide.setAttribute( 'data-background-image', data.background );\n\t\t\t}\n\t\t\telse {\n\t\t\t\telement.style.background = data.background;\n\t\t\t}\n\t\t}\n\n\t\t// Create a hash for this combination of background settings.\n\t\t// This is used to determine when two slide backgrounds are\n\t\t// the same.\n\t\tif( data.background || data.backgroundColor || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) {\n\t\t\telement.setAttribute( 'data-background-hash', data.background +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundSize +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundImage +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundVideo +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundIframe +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundColor +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundRepeat +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundPosition +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundTransition +\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdata.backgroundOpacity );\n\t\t}\n\n\t\t// Additional and optional background properties\n\t\tif( data.backgroundSize ) element.setAttribute( 'data-background-size', data.backgroundSize );\n\t\tif( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;\n\t\tif( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );\n\n\t\tif( dataPreload ) element.setAttribute( 'data-preload', '' );\n\n\t\t// Background image options are set on the content wrapper\n\t\tif( data.backgroundSize ) contentElement.style.backgroundSize = data.backgroundSize;\n\t\tif( data.backgroundRepeat ) contentElement.style.backgroundRepeat = data.backgroundRepeat;\n\t\tif( data.backgroundPosition ) contentElement.style.backgroundPosition = data.backgroundPosition;\n\t\tif( data.backgroundOpacity ) contentElement.style.opacity = data.backgroundOpacity;\n\n\t\t// If this slide has a background color, we add a class that\n\t\t// signals if it is light or dark. If the slide has no background\n\t\t// color, no class will be added\n\t\tlet contrastColor = data.backgroundColor;\n\n\t\t// If no bg color was found, or it cannot be converted by colorToRgb, check the computed background\n\t\tif( !contrastColor || !colorToRgb( contrastColor ) ) {\n\t\t\tlet computedBackgroundStyle = window.getComputedStyle( element );\n\t\t\tif( computedBackgroundStyle && computedBackgroundStyle.backgroundColor ) {\n\t\t\t\tcontrastColor = computedBackgroundStyle.backgroundColor;\n\t\t\t}\n\t\t}\n\n\t\tif( contrastColor ) {\n\t\t\tconst rgb = colorToRgb( contrastColor );\n\n\t\t\t// Ignore fully transparent backgrounds. Some browsers return\n\t\t\t// rgba(0,0,0,0) when reading the computed background color of\n\t\t\t// an element with no background\n\t\t\tif( rgb && rgb.a !== 0 ) {\n\t\t\t\tif( colorBrightness( contrastColor ) < 128 ) {\n\t\t\t\t\tslide.classList.add( 'has-dark-background' );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tslide.classList.add( 'has-light-background' );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the background elements to reflect the current\n\t * slide.\n\t *\n\t * @param {boolean} includeAll If true, the backgrounds of\n\t * all vertical slides (not just the present) will be updated.\n\t */\n\tupdate( includeAll = false ) {\n\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\t\tlet indices = this.Reveal.getIndices();\n\n\t\tlet currentBackground = null;\n\n\t\t// Reverse past/future classes when in RTL mode\n\t\tlet horizontalPast = this.Reveal.getConfig().rtl ? 'future' : 'past',\n\t\t\thorizontalFuture = this.Reveal.getConfig().rtl ? 'past' : 'future';\n\n\t\t// Update the classes of all backgrounds to match the\n\t\t// states of their slides (past/present/future)\n\t\tArray.from( this.element.childNodes ).forEach( ( backgroundh, h ) => {\n\n\t\t\tbackgroundh.classList.remove( 'past', 'present', 'future' );\n\n\t\t\tif( h < indices.h ) {\n\t\t\t\tbackgroundh.classList.add( horizontalPast );\n\t\t\t}\n\t\t\telse if ( h > indices.h ) {\n\t\t\t\tbackgroundh.classList.add( horizontalFuture );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbackgroundh.classList.add( 'present' );\n\n\t\t\t\t// Store a reference to the current background element\n\t\t\t\tcurrentBackground = backgroundh;\n\t\t\t}\n\n\t\t\tif( includeAll || h === indices.h ) {\n\t\t\t\tqueryAll( backgroundh, '.slide-background' ).forEach( ( backgroundv, v ) => {\n\n\t\t\t\t\tbackgroundv.classList.remove( 'past', 'present', 'future' );\n\n\t\t\t\t\tif( v < indices.v ) {\n\t\t\t\t\t\tbackgroundv.classList.add( 'past' );\n\t\t\t\t\t}\n\t\t\t\t\telse if ( v > indices.v ) {\n\t\t\t\t\t\tbackgroundv.classList.add( 'future' );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tbackgroundv.classList.add( 'present' );\n\n\t\t\t\t\t\t// Only if this is the present horizontal and vertical slide\n\t\t\t\t\t\tif( h === indices.h ) currentBackground = backgroundv;\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\t\t\t}\n\n\t\t} );\n\n\t\t// Stop content inside of previous backgrounds\n\t\tif( this.previousBackground ) {\n\n\t\t\tthis.Reveal.slideContent.stopEmbeddedContent( this.previousBackground, { unloadIframes: !this.Reveal.slideContent.shouldPreload( this.previousBackground ) } );\n\n\t\t}\n\n\t\t// Start content in the current background\n\t\tif( currentBackground ) {\n\n\t\t\tthis.Reveal.slideContent.startEmbeddedContent( currentBackground );\n\n\t\t\tlet currentBackgroundContent = currentBackground.querySelector( '.slide-background-content' );\n\t\t\tif( currentBackgroundContent ) {\n\n\t\t\t\tlet backgroundImageURL = currentBackgroundContent.style.backgroundImage || '';\n\n\t\t\t\t// Restart GIFs (doesn't work in Firefox)\n\t\t\t\tif( /\\.gif/i.test( backgroundImageURL ) ) {\n\t\t\t\t\tcurrentBackgroundContent.style.backgroundImage = '';\n\t\t\t\t\twindow.getComputedStyle( currentBackgroundContent ).opacity;\n\t\t\t\t\tcurrentBackgroundContent.style.backgroundImage = backgroundImageURL;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// Don't transition between identical backgrounds. This\n\t\t\t// prevents unwanted flicker.\n\t\t\tlet previousBackgroundHash = this.previousBackground ? this.previousBackground.getAttribute( 'data-background-hash' ) : null;\n\t\t\tlet currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' );\n\t\t\tif( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== this.previousBackground ) {\n\t\t\t\tthis.element.classList.add( 'no-transition' );\n\t\t\t}\n\n\t\t\tthis.previousBackground = currentBackground;\n\n\t\t}\n\n\t\t// If there's a background brightness flag for this slide,\n\t\t// bubble it to the .reveal container\n\t\tif( currentSlide ) {\n\t\t\t[ 'has-light-background', 'has-dark-background' ].forEach( classToBubble => {\n\t\t\t\tif( currentSlide.classList.contains( classToBubble ) ) {\n\t\t\t\t\tthis.Reveal.getRevealElement().classList.add( classToBubble );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Reveal.getRevealElement().classList.remove( classToBubble );\n\t\t\t\t}\n\t\t\t}, this );\n\t\t}\n\n\t\t// Allow the first background to apply without transition\n\t\tsetTimeout( () => {\n\t\t\tthis.element.classList.remove( 'no-transition' );\n\t\t}, 1 );\n\n\t}\n\n\t/**\n\t * Updates the position of the parallax background based\n\t * on the current slide index.\n\t */\n\tupdateParallax() {\n\n\t\tlet indices = this.Reveal.getIndices();\n\n\t\tif( this.Reveal.getConfig().parallaxBackgroundImage ) {\n\n\t\t\tlet horizontalSlides = this.Reveal.getHorizontalSlides(),\n\t\t\t\tverticalSlides = this.Reveal.getVerticalSlides();\n\n\t\t\tlet backgroundSize = this.element.style.backgroundSize.split( ' ' ),\n\t\t\t\tbackgroundWidth, backgroundHeight;\n\n\t\t\tif( backgroundSize.length === 1 ) {\n\t\t\t\tbackgroundWidth = backgroundHeight = parseInt( backgroundSize[0], 10 );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbackgroundWidth = parseInt( backgroundSize[0], 10 );\n\t\t\t\tbackgroundHeight = parseInt( backgroundSize[1], 10 );\n\t\t\t}\n\n\t\t\tlet slideWidth = this.element.offsetWidth,\n\t\t\t\thorizontalSlideCount = horizontalSlides.length,\n\t\t\t\thorizontalOffsetMultiplier,\n\t\t\t\thorizontalOffset;\n\n\t\t\tif( typeof this.Reveal.getConfig().parallaxBackgroundHorizontal === 'number' ) {\n\t\t\t\thorizontalOffsetMultiplier = this.Reveal.getConfig().parallaxBackgroundHorizontal;\n\t\t\t}\n\t\t\telse {\n\t\t\t\thorizontalOffsetMultiplier = horizontalSlideCount > 1 ? ( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) : 0;\n\t\t\t}\n\n\t\t\thorizontalOffset = horizontalOffsetMultiplier * indices.h * -1;\n\n\t\t\tlet slideHeight = this.element.offsetHeight,\n\t\t\t\tverticalSlideCount = verticalSlides.length,\n\t\t\t\tverticalOffsetMultiplier,\n\t\t\t\tverticalOffset;\n\n\t\t\tif( typeof this.Reveal.getConfig().parallaxBackgroundVertical === 'number' ) {\n\t\t\t\tverticalOffsetMultiplier = this.Reveal.getConfig().parallaxBackgroundVertical;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tverticalOffsetMultiplier = ( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 );\n\t\t\t}\n\n\t\t\tverticalOffset = verticalSlideCount > 0 ? verticalOffsetMultiplier * indices.v : 0;\n\n\t\t\tthis.element.style.backgroundPosition = horizontalOffset + 'px ' + -verticalOffset + 'px';\n\n\t\t}\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.element.remove();\n\n\t}\n\n}\n","\nexport const SLIDES_SELECTOR = '.slides section';\nexport const HORIZONTAL_SLIDES_SELECTOR = '.slides>section';\nexport const VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section';\n\n// Methods that may not be invoked via the postMessage API\nexport const POST_MESSAGE_METHOD_BLACKLIST = /registerPlugin|registerKeyboardShortcut|addKeyBinding|addEventListener/;\n\n// Regex for retrieving the fragment style from a class attribute\nexport const FRAGMENT_STYLE_REGEX = /fade-(down|up|right|left|out|in-then-out|in-then-semi-out)|semi-fade-out|current-visible|shrink|grow/;","import { queryAll, extend, createStyleSheet, matches, closest } from '../utils/util.js'\nimport { FRAGMENT_STYLE_REGEX } from '../utils/constants.js'\n\n// Counter used to generate unique IDs for auto-animated elements\nlet autoAnimateCounter = 0;\n\n/**\n * Automatically animates matching elements across\n * slides with the [data-auto-animate] attribute.\n */\nexport default class AutoAnimate {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\t/**\n\t * Runs an auto-animation between the given slides.\n\t *\n\t * @param {HTMLElement} fromSlide\n\t * @param {HTMLElement} toSlide\n\t */\n\trun( fromSlide, toSlide ) {\n\n\t\t// Clean up after prior animations\n\t\tthis.reset();\n\n\t\tlet allSlides = this.Reveal.getSlides();\n\t\tlet toSlideIndex = allSlides.indexOf( toSlide );\n\t\tlet fromSlideIndex = allSlides.indexOf( fromSlide );\n\n\t\t// Ensure that both slides are auto-animate targets with the same data-auto-animate-id value\n\t\t// (including null if absent on both) and that data-auto-animate-restart isn't set on the\n\t\t// physically latter slide (independent of slide direction)\n\t\tif( fromSlide.hasAttribute( 'data-auto-animate' ) && toSlide.hasAttribute( 'data-auto-animate' )\n\t\t\t\t&& fromSlide.getAttribute( 'data-auto-animate-id' ) === toSlide.getAttribute( 'data-auto-animate-id' ) \n\t\t\t\t&& !( toSlideIndex > fromSlideIndex ? toSlide : fromSlide ).hasAttribute( 'data-auto-animate-restart' ) ) {\n\n\t\t\t// Create a new auto-animate sheet\n\t\t\tthis.autoAnimateStyleSheet = this.autoAnimateStyleSheet || createStyleSheet();\n\n\t\t\tlet animationOptions = this.getAutoAnimateOptions( toSlide );\n\n\t\t\t// Set our starting state\n\t\t\tfromSlide.dataset.autoAnimate = 'pending';\n\t\t\ttoSlide.dataset.autoAnimate = 'pending';\n\n\t\t\t// Flag the navigation direction, needed for fragment buildup\n\t\t\tanimationOptions.slideDirection = toSlideIndex > fromSlideIndex ? 'forward' : 'backward';\n\n\t\t\t// Inject our auto-animate styles for this transition\n\t\t\tlet css = this.getAutoAnimatableElements( fromSlide, toSlide ).map( elements => {\n\t\t\t\treturn this.autoAnimateElements( elements.from, elements.to, elements.options || {}, animationOptions, autoAnimateCounter++ );\n\t\t\t} );\n\n\t\t\t// Animate unmatched elements, if enabled\n\t\t\tif( toSlide.dataset.autoAnimateUnmatched !== 'false' && this.Reveal.getConfig().autoAnimateUnmatched === true ) {\n\n\t\t\t\t// Our default timings for unmatched elements\n\t\t\t\tlet defaultUnmatchedDuration = animationOptions.duration * 0.8,\n\t\t\t\t\tdefaultUnmatchedDelay = animationOptions.duration * 0.2;\n\n\t\t\t\tthis.getUnmatchedAutoAnimateElements( toSlide ).forEach( unmatchedElement => {\n\n\t\t\t\t\tlet unmatchedOptions = this.getAutoAnimateOptions( unmatchedElement, animationOptions );\n\t\t\t\t\tlet id = 'unmatched';\n\n\t\t\t\t\t// If there is a duration or delay set specifically for this\n\t\t\t\t\t// element our unmatched elements should adhere to those\n\t\t\t\t\tif( unmatchedOptions.duration !== animationOptions.duration || unmatchedOptions.delay !== animationOptions.delay ) {\n\t\t\t\t\t\tid = 'unmatched-' + autoAnimateCounter++;\n\t\t\t\t\t\tcss.push( `[data-auto-animate=\"running\"] [data-auto-animate-target=\"${id}\"] { transition: opacity ${unmatchedOptions.duration}s ease ${unmatchedOptions.delay}s; }` );\n\t\t\t\t\t}\n\n\t\t\t\t\tunmatchedElement.dataset.autoAnimateTarget = id;\n\n\t\t\t\t}, this );\n\n\t\t\t\t// Our default transition for unmatched elements\n\t\t\t\tcss.push( `[data-auto-animate=\"running\"] [data-auto-animate-target=\"unmatched\"] { transition: opacity ${defaultUnmatchedDuration}s ease ${defaultUnmatchedDelay}s; }` );\n\n\t\t\t}\n\n\t\t\t// Setting the whole chunk of CSS at once is the most\n\t\t\t// efficient way to do this. Using sheet.insertRule\n\t\t\t// is multiple factors slower.\n\t\t\tthis.autoAnimateStyleSheet.innerHTML = css.join( '' );\n\n\t\t\t// Start the animation next cycle\n\t\t\trequestAnimationFrame( () => {\n\t\t\t\tif( this.autoAnimateStyleSheet ) {\n\t\t\t\t\t// This forces our newly injected styles to be applied in Firefox\n\t\t\t\t\tgetComputedStyle( this.autoAnimateStyleSheet ).fontWeight;\n\n\t\t\t\t\ttoSlide.dataset.autoAnimate = 'running';\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\ttype: 'autoanimate',\n\t\t\t\tdata: {\n\t\t\t\t\tfromSlide,\n\t\t\t\t\ttoSlide,\n\t\t\t\t\tsheet: this.autoAnimateStyleSheet\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Rolls back all changes that we've made to the DOM so\n\t * that as part of animating.\n\t */\n\treset() {\n\n\t\t// Reset slides\n\t\tqueryAll( this.Reveal.getRevealElement(), '[data-auto-animate]:not([data-auto-animate=\"\"])' ).forEach( element => {\n\t\t\telement.dataset.autoAnimate = '';\n\t\t} );\n\n\t\t// Reset elements\n\t\tqueryAll( this.Reveal.getRevealElement(), '[data-auto-animate-target]' ).forEach( element => {\n\t\t\tdelete element.dataset.autoAnimateTarget;\n\t\t} );\n\n\t\t// Remove the animation sheet\n\t\tif( this.autoAnimateStyleSheet && this.autoAnimateStyleSheet.parentNode ) {\n\t\t\tthis.autoAnimateStyleSheet.parentNode.removeChild( this.autoAnimateStyleSheet );\n\t\t\tthis.autoAnimateStyleSheet = null;\n\t\t}\n\n\t}\n\n\t/**\n\t * Creates a FLIP animation where the `to` element starts out\n\t * in the `from` element position and animates to its original\n\t * state.\n\t *\n\t * @param {HTMLElement} from\n\t * @param {HTMLElement} to\n\t * @param {Object} elementOptions Options for this element pair\n\t * @param {Object} animationOptions Options set at the slide level\n\t * @param {String} id Unique ID that we can use to identify this\n\t * auto-animate element in the DOM\n\t */\n\tautoAnimateElements( from, to, elementOptions, animationOptions, id ) {\n\n\t\t// 'from' elements are given a data-auto-animate-target with no value,\n\t\t// 'to' elements are are given a data-auto-animate-target with an ID\n\t\tfrom.dataset.autoAnimateTarget = '';\n\t\tto.dataset.autoAnimateTarget = id;\n\n\t\t// Each element may override any of the auto-animate options\n\t\t// like transition easing, duration and delay via data-attributes\n\t\tlet options = this.getAutoAnimateOptions( to, animationOptions );\n\n\t\t// If we're using a custom element matcher the element options\n\t\t// may contain additional transition overrides\n\t\tif( typeof elementOptions.delay !== 'undefined' ) options.delay = elementOptions.delay;\n\t\tif( typeof elementOptions.duration !== 'undefined' ) options.duration = elementOptions.duration;\n\t\tif( typeof elementOptions.easing !== 'undefined' ) options.easing = elementOptions.easing;\n\n\t\tlet fromProps = this.getAutoAnimatableProperties( 'from', from, elementOptions ),\n\t\t\ttoProps = this.getAutoAnimatableProperties( 'to', to, elementOptions );\n\n\t\t// Maintain fragment visibility for matching elements when\n\t\t// we're navigating forwards, this way the viewer won't need\n\t\t// to step through the same fragments twice\n\t\tif( to.classList.contains( 'fragment' ) ) {\n\n\t\t\t// Don't auto-animate the opacity of fragments to avoid\n\t\t\t// conflicts with fragment animations\n\t\t\tdelete toProps.styles['opacity'];\n\n\t\t\tif( from.classList.contains( 'fragment' ) ) {\n\n\t\t\t\tlet fromFragmentStyle = ( from.className.match( FRAGMENT_STYLE_REGEX ) || [''] )[0];\n\t\t\t\tlet toFragmentStyle = ( to.className.match( FRAGMENT_STYLE_REGEX ) || [''] )[0];\n\n\t\t\t\t// Only skip the fragment if the fragment animation style\n\t\t\t\t// remains unchanged\n\t\t\t\tif( fromFragmentStyle === toFragmentStyle && animationOptions.slideDirection === 'forward' ) {\n\t\t\t\t\tto.classList.add( 'visible', 'disabled' );\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// If translation and/or scaling are enabled, css transform\n\t\t// the 'to' element so that it matches the position and size\n\t\t// of the 'from' element\n\t\tif( elementOptions.translate !== false || elementOptions.scale !== false ) {\n\n\t\t\tlet presentationScale = this.Reveal.getScale();\n\n\t\t\tlet delta = {\n\t\t\t\tx: ( fromProps.x - toProps.x ) / presentationScale,\n\t\t\t\ty: ( fromProps.y - toProps.y ) / presentationScale,\n\t\t\t\tscaleX: fromProps.width / toProps.width,\n\t\t\t\tscaleY: fromProps.height / toProps.height\n\t\t\t};\n\n\t\t\t// Limit decimal points to avoid 0.0001px blur and stutter\n\t\t\tdelta.x = Math.round( delta.x * 1000 ) / 1000;\n\t\t\tdelta.y = Math.round( delta.y * 1000 ) / 1000;\n\t\t\tdelta.scaleX = Math.round( delta.scaleX * 1000 ) / 1000;\n\t\t\tdelta.scaleX = Math.round( delta.scaleX * 1000 ) / 1000;\n\n\t\t\tlet translate = elementOptions.translate !== false && ( delta.x !== 0 || delta.y !== 0 ),\n\t\t\t\tscale = elementOptions.scale !== false && ( delta.scaleX !== 0 || delta.scaleY !== 0 );\n\n\t\t\t// No need to transform if nothing's changed\n\t\t\tif( translate || scale ) {\n\n\t\t\t\tlet transform = [];\n\n\t\t\t\tif( translate ) transform.push( `translate(${delta.x}px, ${delta.y}px)` );\n\t\t\t\tif( scale ) transform.push( `scale(${delta.scaleX}, ${delta.scaleY})` );\n\n\t\t\t\tfromProps.styles['transform'] = transform.join( ' ' );\n\t\t\t\tfromProps.styles['transform-origin'] = 'top left';\n\n\t\t\t\ttoProps.styles['transform'] = 'none';\n\n\t\t\t}\n\n\t\t}\n\n\t\t// Delete all unchanged 'to' styles\n\t\tfor( let propertyName in toProps.styles ) {\n\t\t\tconst toValue = toProps.styles[propertyName];\n\t\t\tconst fromValue = fromProps.styles[propertyName];\n\n\t\t\tif( toValue === fromValue ) {\n\t\t\t\tdelete toProps.styles[propertyName];\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If these property values were set via a custom matcher providing\n\t\t\t\t// an explicit 'from' and/or 'to' value, we always inject those values.\n\t\t\t\tif( toValue.explicitValue === true ) {\n\t\t\t\t\ttoProps.styles[propertyName] = toValue.value;\n\t\t\t\t}\n\n\t\t\t\tif( fromValue.explicitValue === true ) {\n\t\t\t\t\tfromProps.styles[propertyName] = fromValue.value;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet css = '';\n\n\t\tlet toStyleProperties = Object.keys( toProps.styles );\n\n\t\t// Only create animate this element IF at least one style\n\t\t// property has changed\n\t\tif( toStyleProperties.length > 0 ) {\n\n\t\t\t// Instantly move to the 'from' state\n\t\t\tfromProps.styles['transition'] = 'none';\n\n\t\t\t// Animate towards the 'to' state\n\t\t\ttoProps.styles['transition'] = `all ${options.duration}s ${options.easing} ${options.delay}s`;\n\t\t\ttoProps.styles['transition-property'] = toStyleProperties.join( ', ' );\n\t\t\ttoProps.styles['will-change'] = toStyleProperties.join( ', ' );\n\n\t\t\t// Build up our custom CSS. We need to override inline styles\n\t\t\t// so we need to make our styles vErY IMPORTANT!1!!\n\t\t\tlet fromCSS = Object.keys( fromProps.styles ).map( propertyName => {\n\t\t\t\treturn propertyName + ': ' + fromProps.styles[propertyName] + ' !important;';\n\t\t\t} ).join( '' );\n\n\t\t\tlet toCSS = Object.keys( toProps.styles ).map( propertyName => {\n\t\t\t\treturn propertyName + ': ' + toProps.styles[propertyName] + ' !important;';\n\t\t\t} ).join( '' );\n\n\t\t\tcss = \t'[data-auto-animate-target=\"'+ id +'\"] {'+ fromCSS +'}' +\n\t\t\t\t\t'[data-auto-animate=\"running\"] [data-auto-animate-target=\"'+ id +'\"] {'+ toCSS +'}';\n\n\t\t}\n\n\t\treturn css;\n\n\t}\n\n\t/**\n\t * Returns the auto-animate options for the given element.\n\t *\n\t * @param {HTMLElement} element Element to pick up options\n\t * from, either a slide or an animation target\n\t * @param {Object} [inheritedOptions] Optional set of existing\n\t * options\n\t */\n\tgetAutoAnimateOptions( element, inheritedOptions ) {\n\n\t\tlet options = {\n\t\t\teasing: this.Reveal.getConfig().autoAnimateEasing,\n\t\t\tduration: this.Reveal.getConfig().autoAnimateDuration,\n\t\t\tdelay: 0\n\t\t};\n\n\t\toptions = extend( options, inheritedOptions );\n\n\t\t// Inherit options from parent elements\n\t\tif( element.parentNode ) {\n\t\t\tlet autoAnimatedParent = closest( element.parentNode, '[data-auto-animate-target]' );\n\t\t\tif( autoAnimatedParent ) {\n\t\t\t\toptions = this.getAutoAnimateOptions( autoAnimatedParent, options );\n\t\t\t}\n\t\t}\n\n\t\tif( element.dataset.autoAnimateEasing ) {\n\t\t\toptions.easing = element.dataset.autoAnimateEasing;\n\t\t}\n\n\t\tif( element.dataset.autoAnimateDuration ) {\n\t\t\toptions.duration = parseFloat( element.dataset.autoAnimateDuration );\n\t\t}\n\n\t\tif( element.dataset.autoAnimateDelay ) {\n\t\t\toptions.delay = parseFloat( element.dataset.autoAnimateDelay );\n\t\t}\n\n\t\treturn options;\n\n\t}\n\n\t/**\n\t * Returns an object containing all of the properties\n\t * that can be auto-animated for the given element and\n\t * their current computed values.\n\t *\n\t * @param {String} direction 'from' or 'to'\n\t */\n\tgetAutoAnimatableProperties( direction, element, elementOptions ) {\n\n\t\tlet config = this.Reveal.getConfig();\n\n\t\tlet properties = { styles: [] };\n\n\t\t// Position and size\n\t\tif( elementOptions.translate !== false || elementOptions.scale !== false ) {\n\t\t\tlet bounds;\n\n\t\t\t// Custom auto-animate may optionally return a custom tailored\n\t\t\t// measurement function\n\t\t\tif( typeof elementOptions.measure === 'function' ) {\n\t\t\t\tbounds = elementOptions.measure( element );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif( config.center ) {\n\t\t\t\t\t// More precise, but breaks when used in combination\n\t\t\t\t\t// with zoom for scaling the deck ¯\\_(ツ)_/¯\n\t\t\t\t\tbounds = element.getBoundingClientRect();\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlet scale = this.Reveal.getScale();\n\t\t\t\t\tbounds = {\n\t\t\t\t\t\tx: element.offsetLeft * scale,\n\t\t\t\t\t\ty: element.offsetTop * scale,\n\t\t\t\t\t\twidth: element.offsetWidth * scale,\n\t\t\t\t\t\theight: element.offsetHeight * scale\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tproperties.x = bounds.x;\n\t\t\tproperties.y = bounds.y;\n\t\t\tproperties.width = bounds.width;\n\t\t\tproperties.height = bounds.height;\n\t\t}\n\n\t\tconst computedStyles = getComputedStyle( element );\n\n\t\t// CSS styles\n\t\t( elementOptions.styles || config.autoAnimateStyles ).forEach( style => {\n\t\t\tlet value;\n\n\t\t\t// `style` is either the property name directly, or an object\n\t\t\t// definition of a style property\n\t\t\tif( typeof style === 'string' ) style = { property: style };\n\n\t\t\tif( typeof style.from !== 'undefined' && direction === 'from' ) {\n\t\t\t\tvalue = { value: style.from, explicitValue: true };\n\t\t\t}\n\t\t\telse if( typeof style.to !== 'undefined' && direction === 'to' ) {\n\t\t\t\tvalue = { value: style.to, explicitValue: true };\n\t\t\t}\n\t\t\telse {\n\t\t\t\tvalue = computedStyles[style.property];\n\t\t\t}\n\n\t\t\tif( value !== '' ) {\n\t\t\t\tproperties.styles[style.property] = value;\n\t\t\t}\n\t\t} );\n\n\t\treturn properties;\n\n\t}\n\n\t/**\n\t * Get a list of all element pairs that we can animate\n\t * between the given slides.\n\t *\n\t * @param {HTMLElement} fromSlide\n\t * @param {HTMLElement} toSlide\n\t *\n\t * @return {Array} Each value is an array where [0] is\n\t * the element we're animating from and [1] is the\n\t * element we're animating to\n\t */\n\tgetAutoAnimatableElements( fromSlide, toSlide ) {\n\n\t\tlet matcher = typeof this.Reveal.getConfig().autoAnimateMatcher === 'function' ? this.Reveal.getConfig().autoAnimateMatcher : this.getAutoAnimatePairs;\n\n\t\tlet pairs = matcher.call( this, fromSlide, toSlide );\n\n\t\tlet reserved = [];\n\n\t\t// Remove duplicate pairs\n\t\treturn pairs.filter( ( pair, index ) => {\n\t\t\tif( reserved.indexOf( pair.to ) === -1 ) {\n\t\t\t\treserved.push( pair.to );\n\t\t\t\treturn true;\n\t\t\t}\n\t\t} );\n\n\t}\n\n\t/**\n\t * Identifies matching elements between slides.\n\t *\n\t * You can specify a custom matcher function by using\n\t * the `autoAnimateMatcher` config option.\n\t */\n\tgetAutoAnimatePairs( fromSlide, toSlide ) {\n\n\t\tlet pairs = [];\n\n\t\tconst codeNodes = 'pre';\n\t\tconst textNodes = 'h1, h2, h3, h4, h5, h6, p, li';\n\t\tconst mediaNodes = 'img, video, iframe';\n\n\t\t// Eplicit matches via data-id\n\t\tthis.findAutoAnimateMatches( pairs, fromSlide, toSlide, '[data-id]', node => {\n\t\t\treturn node.nodeName + ':::' + node.getAttribute( 'data-id' );\n\t\t} );\n\n\t\t// Text\n\t\tthis.findAutoAnimateMatches( pairs, fromSlide, toSlide, textNodes, node => {\n\t\t\treturn node.nodeName + ':::' + node.innerText;\n\t\t} );\n\n\t\t// Media\n\t\tthis.findAutoAnimateMatches( pairs, fromSlide, toSlide, mediaNodes, node => {\n\t\t\treturn node.nodeName + ':::' + ( node.getAttribute( 'src' ) || node.getAttribute( 'data-src' ) );\n\t\t} );\n\n\t\t// Code\n\t\tthis.findAutoAnimateMatches( pairs, fromSlide, toSlide, codeNodes, node => {\n\t\t\treturn node.nodeName + ':::' + node.innerText;\n\t\t} );\n\n\t\tpairs.forEach( pair => {\n\n\t\t\t// Disable scale transformations on text nodes, we transition\n\t\t\t// each individual text property instead\n\t\t\tif( matches( pair.from, textNodes ) ) {\n\t\t\t\tpair.options = { scale: false };\n\t\t\t}\n\t\t\t// Animate individual lines of code\n\t\t\telse if( matches( pair.from, codeNodes ) ) {\n\n\t\t\t\t// Transition the code block's width and height instead of scaling\n\t\t\t\t// to prevent its content from being squished\n\t\t\t\tpair.options = { scale: false, styles: [ 'width', 'height' ] };\n\n\t\t\t\t// Lines of code\n\t\t\t\tthis.findAutoAnimateMatches( pairs, pair.from, pair.to, '.hljs .hljs-ln-code', node => {\n\t\t\t\t\treturn node.textContent;\n\t\t\t\t}, {\n\t\t\t\t\tscale: false,\n\t\t\t\t\tstyles: [],\n\t\t\t\t\tmeasure: this.getLocalBoundingBox.bind( this )\n\t\t\t\t} );\n\n\t\t\t\t// Line numbers\n\t\t\t\tthis.findAutoAnimateMatches( pairs, pair.from, pair.to, '.hljs .hljs-ln-line[data-line-number]', node => {\n\t\t\t\t\treturn node.getAttribute( 'data-line-number' );\n\t\t\t\t}, {\n\t\t\t\t\tscale: false,\n\t\t\t\t\tstyles: [ 'width' ],\n\t\t\t\t\tmeasure: this.getLocalBoundingBox.bind( this )\n\t\t\t\t} );\n\n\t\t\t}\n\n\t\t}, this );\n\n\t\treturn pairs;\n\n\t}\n\n\t/**\n\t * Helper method which returns a bounding box based on\n\t * the given elements offset coordinates.\n\t *\n\t * @param {HTMLElement} element\n\t * @return {Object} x, y, width, height\n\t */\n\tgetLocalBoundingBox( element ) {\n\n\t\tconst presentationScale = this.Reveal.getScale();\n\n\t\treturn {\n\t\t\tx: Math.round( ( element.offsetLeft * presentationScale ) * 100 ) / 100,\n\t\t\ty: Math.round( ( element.offsetTop * presentationScale ) * 100 ) / 100,\n\t\t\twidth: Math.round( ( element.offsetWidth * presentationScale ) * 100 ) / 100,\n\t\t\theight: Math.round( ( element.offsetHeight * presentationScale ) * 100 ) / 100\n\t\t};\n\n\t}\n\n\t/**\n\t * Finds matching elements between two slides.\n\t *\n\t * @param {Array} pairs \tList of pairs to push matches to\n\t * @param {HTMLElement} fromScope Scope within the from element exists\n\t * @param {HTMLElement} toScope Scope within the to element exists\n\t * @param {String} selector CSS selector of the element to match\n\t * @param {Function} serializer A function that accepts an element and returns\n\t * a stringified ID based on its contents\n\t * @param {Object} animationOptions Optional config options for this pair\n\t */\n\tfindAutoAnimateMatches( pairs, fromScope, toScope, selector, serializer, animationOptions ) {\n\n\t\tlet fromMatches = {};\n\t\tlet toMatches = {};\n\n\t\t[].slice.call( fromScope.querySelectorAll( selector ) ).forEach( ( element, i ) => {\n\t\t\tconst key = serializer( element );\n\t\t\tif( typeof key === 'string' && key.length ) {\n\t\t\t\tfromMatches[key] = fromMatches[key] || [];\n\t\t\t\tfromMatches[key].push( element );\n\t\t\t}\n\t\t} );\n\n\t\t[].slice.call( toScope.querySelectorAll( selector ) ).forEach( ( element, i ) => {\n\t\t\tconst key = serializer( element );\n\t\t\ttoMatches[key] = toMatches[key] || [];\n\t\t\ttoMatches[key].push( element );\n\n\t\t\tlet fromElement;\n\n\t\t\t// Retrieve the 'from' element\n\t\t\tif( fromMatches[key] ) {\n\t\t\t\tconst pimaryIndex = toMatches[key].length - 1;\n\t\t\t\tconst secondaryIndex = fromMatches[key].length - 1;\n\n\t\t\t\t// If there are multiple identical from elements, retrieve\n\t\t\t\t// the one at the same index as our to-element.\n\t\t\t\tif( fromMatches[key][ pimaryIndex ] ) {\n\t\t\t\t\tfromElement = fromMatches[key][ pimaryIndex ];\n\t\t\t\t\tfromMatches[key][ pimaryIndex ] = null;\n\t\t\t\t}\n\t\t\t\t// If there are no matching from-elements at the same index,\n\t\t\t\t// use the last one.\n\t\t\t\telse if( fromMatches[key][ secondaryIndex ] ) {\n\t\t\t\t\tfromElement = fromMatches[key][ secondaryIndex ];\n\t\t\t\t\tfromMatches[key][ secondaryIndex ] = null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we've got a matching pair, push it to the list of pairs\n\t\t\tif( fromElement ) {\n\t\t\t\tpairs.push({\n\t\t\t\t\tfrom: fromElement,\n\t\t\t\t\tto: element,\n\t\t\t\t\toptions: animationOptions\n\t\t\t\t});\n\t\t\t}\n\t\t} );\n\n\t}\n\n\t/**\n\t * Returns a all elements within the given scope that should\n\t * be considered unmatched in an auto-animate transition. If\n\t * fading of unmatched elements is turned on, these elements\n\t * will fade when going between auto-animate slides.\n\t *\n\t * Note that parents of auto-animate targets are NOT considerd\n\t * unmatched since fading them would break the auto-animation.\n\t *\n\t * @param {HTMLElement} rootElement\n\t * @return {Array}\n\t */\n\tgetUnmatchedAutoAnimateElements( rootElement ) {\n\n\t\treturn [].slice.call( rootElement.children ).reduce( ( result, element ) => {\n\n\t\t\tconst containsAnimatedElements = element.querySelector( '[data-auto-animate-target]' );\n\n\t\t\t// The element is unmatched if\n\t\t\t// - It is not an auto-animate target\n\t\t\t// - It does not contain any auto-animate targets\n\t\t\tif( !element.hasAttribute( 'data-auto-animate-target' ) && !containsAnimatedElements ) {\n\t\t\t\tresult.push( element );\n\t\t\t}\n\n\t\t\tif( element.querySelector( '[data-auto-animate-target]' ) ) {\n\t\t\t\tresult = result.concat( this.getUnmatchedAutoAnimateElements( element ) );\n\t\t\t}\n\n\t\t\treturn result;\n\n\t\t}, [] );\n\n\t}\n\n}\n","import { extend, queryAll } from '../utils/util.js'\n\n/**\n * Handles sorting and navigation of slide fragments.\n * Fragments are elements within a slide that are\n * revealed/animated incrementally.\n */\nexport default class Fragments {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tif( config.fragments === false ) {\n\t\t\tthis.disable();\n\t\t}\n\t\telse if( oldConfig.fragments === false ) {\n\t\t\tthis.enable();\n\t\t}\n\n\t}\n\n\t/**\n\t * If fragments are disabled in the deck, they should all be\n\t * visible rather than stepped through.\n\t */\n\tdisable() {\n\n\t\tqueryAll( this.Reveal.getSlidesElement(), '.fragment' ).forEach( element => {\n\t\t\telement.classList.add( 'visible' );\n\t\t\telement.classList.remove( 'current-fragment' );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Reverse of #disable(). Only called if fragments have\n\t * previously been disabled.\n\t */\n\tenable() {\n\n\t\tqueryAll( this.Reveal.getSlidesElement(), '.fragment' ).forEach( element => {\n\t\t\telement.classList.remove( 'visible' );\n\t\t\telement.classList.remove( 'current-fragment' );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Returns an object describing the available fragment\n\t * directions.\n\t *\n\t * @return {{prev: boolean, next: boolean}}\n\t */\n\tavailableRoutes() {\n\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\t\tif( currentSlide && this.Reveal.getConfig().fragments ) {\n\t\t\tlet fragments = currentSlide.querySelectorAll( '.fragment:not(.disabled)' );\n\t\t\tlet hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.disabled):not(.visible)' );\n\n\t\t\treturn {\n\t\t\t\tprev: fragments.length - hiddenFragments.length > 0,\n\t\t\t\tnext: !!hiddenFragments.length\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\treturn { prev: false, next: false };\n\t\t}\n\n\t}\n\n\t/**\n\t * Return a sorted fragments list, ordered by an increasing\n\t * \"data-fragment-index\" attribute.\n\t *\n\t * Fragments will be revealed in the order that they are returned by\n\t * this function, so you can use the index attributes to control the\n\t * order of fragment appearance.\n\t *\n\t * To maintain a sensible default fragment order, fragments are presumed\n\t * to be passed in document order. This function adds a \"fragment-index\"\n\t * attribute to each node if such an attribute is not already present,\n\t * and sets that attribute to an integer value which is the position of\n\t * the fragment within the fragments list.\n\t *\n\t * @param {object[]|*} fragments\n\t * @param {boolean} grouped If true the returned array will contain\n\t * nested arrays for all fragments with the same index\n\t * @return {object[]} sorted Sorted array of fragments\n\t */\n\tsort( fragments, grouped = false ) {\n\n\t\tfragments = Array.from( fragments );\n\n\t\tlet ordered = [],\n\t\t\tunordered = [],\n\t\t\tsorted = [];\n\n\t\t// Group ordered and unordered elements\n\t\tfragments.forEach( fragment => {\n\t\t\tif( fragment.hasAttribute( 'data-fragment-index' ) ) {\n\t\t\t\tlet index = parseInt( fragment.getAttribute( 'data-fragment-index' ), 10 );\n\n\t\t\t\tif( !ordered[index] ) {\n\t\t\t\t\tordered[index] = [];\n\t\t\t\t}\n\n\t\t\t\tordered[index].push( fragment );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tunordered.push( [ fragment ] );\n\t\t\t}\n\t\t} );\n\n\t\t// Append fragments without explicit indices in their\n\t\t// DOM order\n\t\tordered = ordered.concat( unordered );\n\n\t\t// Manually count the index up per group to ensure there\n\t\t// are no gaps\n\t\tlet index = 0;\n\n\t\t// Push all fragments in their sorted order to an array,\n\t\t// this flattens the groups\n\t\tordered.forEach( group => {\n\t\t\tgroup.forEach( fragment => {\n\t\t\t\tsorted.push( fragment );\n\t\t\t\tfragment.setAttribute( 'data-fragment-index', index );\n\t\t\t} );\n\n\t\t\tindex ++;\n\t\t} );\n\n\t\treturn grouped === true ? ordered : sorted;\n\n\t}\n\n\t/**\n\t * Sorts and formats all of fragments in the\n\t * presentation.\n\t */\n\tsortAll() {\n\n\t\tthis.Reveal.getHorizontalSlides().forEach( horizontalSlide => {\n\n\t\t\tlet verticalSlides = queryAll( horizontalSlide, 'section' );\n\t\t\tverticalSlides.forEach( ( verticalSlide, y ) => {\n\n\t\t\t\tthis.sort( verticalSlide.querySelectorAll( '.fragment' ) );\n\n\t\t\t}, this );\n\n\t\t\tif( verticalSlides.length === 0 ) this.sort( horizontalSlide.querySelectorAll( '.fragment' ) );\n\n\t\t} );\n\n\t}\n\n\t/**\n\t * Refreshes the fragments on the current slide so that they\n\t * have the appropriate classes (.visible + .current-fragment).\n\t *\n\t * @param {number} [index] The index of the current fragment\n\t * @param {array} [fragments] Array containing all fragments\n\t * in the current slide\n\t *\n\t * @return {{shown: array, hidden: array}}\n\t */\n\tupdate( index, fragments ) {\n\n\t\tlet changedFragments = {\n\t\t\tshown: [],\n\t\t\thidden: []\n\t\t};\n\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\t\tif( currentSlide && this.Reveal.getConfig().fragments ) {\n\n\t\t\tfragments = fragments || this.sort( currentSlide.querySelectorAll( '.fragment' ) );\n\n\t\t\tif( fragments.length ) {\n\n\t\t\t\tlet maxIndex = 0;\n\n\t\t\t\tif( typeof index !== 'number' ) {\n\t\t\t\t\tlet currentFragment = this.sort( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();\n\t\t\t\t\tif( currentFragment ) {\n\t\t\t\t\t\tindex = parseInt( currentFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tArray.from( fragments ).forEach( ( el, i ) => {\n\n\t\t\t\t\tif( el.hasAttribute( 'data-fragment-index' ) ) {\n\t\t\t\t\t\ti = parseInt( el.getAttribute( 'data-fragment-index' ), 10 );\n\t\t\t\t\t}\n\n\t\t\t\t\tmaxIndex = Math.max( maxIndex, i );\n\n\t\t\t\t\t// Visible fragments\n\t\t\t\t\tif( i <= index ) {\n\t\t\t\t\t\tlet wasVisible = el.classList.contains( 'visible' )\n\t\t\t\t\t\tel.classList.add( 'visible' );\n\t\t\t\t\t\tel.classList.remove( 'current-fragment' );\n\n\t\t\t\t\t\tif( i === index ) {\n\t\t\t\t\t\t\t// Announce the fragments one by one to the Screen Reader\n\t\t\t\t\t\t\tthis.Reveal.announceStatus( this.Reveal.getStatusText( el ) );\n\n\t\t\t\t\t\t\tel.classList.add( 'current-fragment' );\n\t\t\t\t\t\t\tthis.Reveal.slideContent.startEmbeddedContent( el );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif( !wasVisible ) {\n\t\t\t\t\t\t\tchangedFragments.shown.push( el )\n\t\t\t\t\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\t\t\t\t\ttarget: el,\n\t\t\t\t\t\t\t\ttype: 'visible',\n\t\t\t\t\t\t\t\tbubbles: false\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// Hidden fragments\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet wasVisible = el.classList.contains( 'visible' )\n\t\t\t\t\t\tel.classList.remove( 'visible' );\n\t\t\t\t\t\tel.classList.remove( 'current-fragment' );\n\n\t\t\t\t\t\tif( wasVisible ) {\n\t\t\t\t\t\t\tthis.Reveal.slideContent.stopEmbeddedContent( el );\n\t\t\t\t\t\t\tchangedFragments.hidden.push( el );\n\t\t\t\t\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\t\t\t\t\ttarget: el,\n\t\t\t\t\t\t\t\ttype: 'hidden',\n\t\t\t\t\t\t\t\tbubbles: false\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t} );\n\n\t\t\t\t// Write the current fragment index to the slide
.\n\t\t\t\t// This can be used by end users to apply styles based on\n\t\t\t\t// the current fragment index.\n\t\t\t\tindex = typeof index === 'number' ? index : -1;\n\t\t\t\tindex = Math.max( Math.min( index, maxIndex ), -1 );\n\t\t\t\tcurrentSlide.setAttribute( 'data-fragment', index );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn changedFragments;\n\n\t}\n\n\t/**\n\t * Formats the fragments on the given slide so that they have\n\t * valid indices. Call this if fragments are changed in the DOM\n\t * after reveal.js has already initialized.\n\t *\n\t * @param {HTMLElement} slide\n\t * @return {Array} a list of the HTML fragments that were synced\n\t */\n\tsync( slide = this.Reveal.getCurrentSlide() ) {\n\n\t\treturn this.sort( slide.querySelectorAll( '.fragment' ) );\n\n\t}\n\n\t/**\n\t * Navigate to the specified slide fragment.\n\t *\n\t * @param {?number} index The index of the fragment that\n\t * should be shown, -1 means all are invisible\n\t * @param {number} offset Integer offset to apply to the\n\t * fragment index\n\t *\n\t * @return {boolean} true if a change was made in any\n\t * fragments visibility as part of this call\n\t */\n\tgoto( index, offset = 0 ) {\n\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\t\tif( currentSlide && this.Reveal.getConfig().fragments ) {\n\n\t\t\tlet fragments = this.sort( currentSlide.querySelectorAll( '.fragment:not(.disabled)' ) );\n\t\t\tif( fragments.length ) {\n\n\t\t\t\t// If no index is specified, find the current\n\t\t\t\tif( typeof index !== 'number' ) {\n\t\t\t\t\tlet lastVisibleFragment = this.sort( currentSlide.querySelectorAll( '.fragment:not(.disabled).visible' ) ).pop();\n\n\t\t\t\t\tif( lastVisibleFragment ) {\n\t\t\t\t\t\tindex = parseInt( lastVisibleFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tindex = -1;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Apply the offset if there is one\n\t\t\t\tindex += offset;\n\n\t\t\t\tlet changedFragments = this.update( index, fragments );\n\n\t\t\t\tif( changedFragments.hidden.length ) {\n\t\t\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\t\t\ttype: 'fragmenthidden',\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tfragment: changedFragments.hidden[0],\n\t\t\t\t\t\t\tfragments: changedFragments.hidden\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif( changedFragments.shown.length ) {\n\t\t\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\t\t\ttype: 'fragmentshown',\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tfragment: changedFragments.shown[0],\n\t\t\t\t\t\t\tfragments: changedFragments.shown\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tthis.Reveal.controls.update();\n\t\t\t\tthis.Reveal.progress.update();\n\n\t\t\t\tif( this.Reveal.getConfig().fragmentInURL ) {\n\t\t\t\t\tthis.Reveal.location.writeURL();\n\t\t\t\t}\n\n\t\t\t\treturn !!( changedFragments.shown.length || changedFragments.hidden.length );\n\n\t\t\t}\n\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\t/**\n\t * Navigate to the next slide fragment.\n\t *\n\t * @return {boolean} true if there was a next fragment,\n\t * false otherwise\n\t */\n\tnext() {\n\n\t\treturn this.goto( null, 1 );\n\n\t}\n\n\t/**\n\t * Navigate to the previous slide fragment.\n\t *\n\t * @return {boolean} true if there was a previous fragment,\n\t * false otherwise\n\t */\n\tprev() {\n\n\t\treturn this.goto( null, -1 );\n\n\t}\n\n}","import { SLIDES_SELECTOR } from '../utils/constants.js'\nimport { extend, queryAll, transformElement } from '../utils/util.js'\n\n/**\n * Handles all logic related to the overview mode\n * (birds-eye view of all slides).\n */\nexport default class Overview {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.active = false;\n\n\t\tthis.onSlideClicked = this.onSlideClicked.bind( this );\n\n\t}\n\n\t/**\n\t * Displays the overview of slides (quick nav) by scaling\n\t * down and arranging all slide elements.\n\t */\n\tactivate() {\n\n\t\t// Only proceed if enabled in config\n\t\tif( this.Reveal.getConfig().overview && !this.isActive() ) {\n\n\t\t\tthis.active = true;\n\n\t\t\tthis.Reveal.getRevealElement().classList.add( 'overview' );\n\n\t\t\t// Don't auto-slide while in overview mode\n\t\t\tthis.Reveal.cancelAutoSlide();\n\n\t\t\t// Move the backgrounds element into the slide container to\n\t\t\t// that the same scaling is applied\n\t\t\tthis.Reveal.getSlidesElement().appendChild( this.Reveal.getBackgroundsElement() );\n\n\t\t\t// Clicking on an overview slide navigates to it\n\t\t\tqueryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ).forEach( slide => {\n\t\t\t\tif( !slide.classList.contains( 'stack' ) ) {\n\t\t\t\t\tslide.addEventListener( 'click', this.onSlideClicked, true );\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\t// Calculate slide sizes\n\t\t\tconst margin = 70;\n\t\t\tconst slideSize = this.Reveal.getComputedSlideSize();\n\t\t\tthis.overviewSlideWidth = slideSize.width + margin;\n\t\t\tthis.overviewSlideHeight = slideSize.height + margin;\n\n\t\t\t// Reverse in RTL mode\n\t\t\tif( this.Reveal.getConfig().rtl ) {\n\t\t\t\tthis.overviewSlideWidth = -this.overviewSlideWidth;\n\t\t\t}\n\n\t\t\tthis.Reveal.updateSlidesVisibility();\n\n\t\t\tthis.layout();\n\t\t\tthis.update();\n\n\t\t\tthis.Reveal.layout();\n\n\t\t\tconst indices = this.Reveal.getIndices();\n\n\t\t\t// Notify observers of the overview showing\n\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\ttype: 'overviewshown',\n\t\t\t\tdata: {\n\t\t\t\t\t'indexh': indices.h,\n\t\t\t\t\t'indexv': indices.v,\n\t\t\t\t\t'currentSlide': this.Reveal.getCurrentSlide()\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Uses CSS transforms to position all slides in a grid for\n\t * display inside of the overview mode.\n\t */\n\tlayout() {\n\n\t\t// Layout slides\n\t\tthis.Reveal.getHorizontalSlides().forEach( ( hslide, h ) => {\n\t\t\thslide.setAttribute( 'data-index-h', h );\n\t\t\ttransformElement( hslide, 'translate3d(' + ( h * this.overviewSlideWidth ) + 'px, 0, 0)' );\n\n\t\t\tif( hslide.classList.contains( 'stack' ) ) {\n\n\t\t\t\tqueryAll( hslide, 'section' ).forEach( ( vslide, v ) => {\n\t\t\t\t\tvslide.setAttribute( 'data-index-h', h );\n\t\t\t\t\tvslide.setAttribute( 'data-index-v', v );\n\n\t\t\t\t\ttransformElement( vslide, 'translate3d(0, ' + ( v * this.overviewSlideHeight ) + 'px, 0)' );\n\t\t\t\t} );\n\n\t\t\t}\n\t\t} );\n\n\t\t// Layout slide backgrounds\n\t\tArray.from( this.Reveal.getBackgroundsElement().childNodes ).forEach( ( hbackground, h ) => {\n\t\t\ttransformElement( hbackground, 'translate3d(' + ( h * this.overviewSlideWidth ) + 'px, 0, 0)' );\n\n\t\t\tqueryAll( hbackground, '.slide-background' ).forEach( ( vbackground, v ) => {\n\t\t\t\ttransformElement( vbackground, 'translate3d(0, ' + ( v * this.overviewSlideHeight ) + 'px, 0)' );\n\t\t\t} );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Moves the overview viewport to the current slides.\n\t * Called each time the current slide changes.\n\t */\n\tupdate() {\n\n\t\tconst vmin = Math.min( window.innerWidth, window.innerHeight );\n\t\tconst scale = Math.max( vmin / 5, 150 ) / vmin;\n\t\tconst indices = this.Reveal.getIndices();\n\n\t\tthis.Reveal.transformSlides( {\n\t\t\toverview: [\n\t\t\t\t'scale('+ scale +')',\n\t\t\t\t'translateX('+ ( -indices.h * this.overviewSlideWidth ) +'px)',\n\t\t\t\t'translateY('+ ( -indices.v * this.overviewSlideHeight ) +'px)'\n\t\t\t].join( ' ' )\n\t\t} );\n\n\t}\n\n\t/**\n\t * Exits the slide overview and enters the currently\n\t * active slide.\n\t */\n\tdeactivate() {\n\n\t\t// Only proceed if enabled in config\n\t\tif( this.Reveal.getConfig().overview ) {\n\n\t\t\tthis.active = false;\n\n\t\t\tthis.Reveal.getRevealElement().classList.remove( 'overview' );\n\n\t\t\t// Temporarily add a class so that transitions can do different things\n\t\t\t// depending on whether they are exiting/entering overview, or just\n\t\t\t// moving from slide to slide\n\t\t\tthis.Reveal.getRevealElement().classList.add( 'overview-deactivating' );\n\n\t\t\tsetTimeout( () => {\n\t\t\t\tthis.Reveal.getRevealElement().classList.remove( 'overview-deactivating' );\n\t\t\t}, 1 );\n\n\t\t\t// Move the background element back out\n\t\t\tthis.Reveal.getRevealElement().appendChild( this.Reveal.getBackgroundsElement() );\n\n\t\t\t// Clean up changes made to slides\n\t\t\tqueryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR ).forEach( slide => {\n\t\t\t\ttransformElement( slide, '' );\n\n\t\t\t\tslide.removeEventListener( 'click', this.onSlideClicked, true );\n\t\t\t} );\n\n\t\t\t// Clean up changes made to backgrounds\n\t\t\tqueryAll( this.Reveal.getBackgroundsElement(), '.slide-background' ).forEach( background => {\n\t\t\t\ttransformElement( background, '' );\n\t\t\t} );\n\n\t\t\tthis.Reveal.transformSlides( { overview: '' } );\n\n\t\t\tconst indices = this.Reveal.getIndices();\n\n\t\t\tthis.Reveal.slide( indices.h, indices.v );\n\t\t\tthis.Reveal.layout();\n\t\t\tthis.Reveal.cueAutoSlide();\n\n\t\t\t// Notify observers of the overview hiding\n\t\t\tthis.Reveal.dispatchEvent({\n\t\t\t\ttype: 'overviewhidden',\n\t\t\t\tdata: {\n\t\t\t\t\t'indexh': indices.h,\n\t\t\t\t\t'indexv': indices.v,\n\t\t\t\t\t'currentSlide': this.Reveal.getCurrentSlide()\n\t\t\t\t}\n\t\t\t});\n\n\t\t}\n\t}\n\n\t/**\n\t * Toggles the slide overview mode on and off.\n\t *\n\t * @param {Boolean} [override] Flag which overrides the\n\t * toggle logic and forcibly sets the desired state. True means\n\t * overview is open, false means it's closed.\n\t */\n\ttoggle( override ) {\n\n\t\tif( typeof override === 'boolean' ) {\n\t\t\toverride ? this.activate() : this.deactivate();\n\t\t}\n\t\telse {\n\t\t\tthis.isActive() ? this.deactivate() : this.activate();\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if the overview is currently active.\n\t *\n\t * @return {Boolean} true if the overview is active,\n\t * false otherwise\n\t */\n\tisActive() {\n\n\t\treturn this.active;\n\n\t}\n\n\t/**\n\t * Invoked when a slide is and we're in the overview.\n\t *\n\t * @param {object} event\n\t */\n\tonSlideClicked( event ) {\n\n\t\tif( this.isActive() ) {\n\t\t\tevent.preventDefault();\n\n\t\t\tlet element = event.target;\n\n\t\t\twhile( element && !element.nodeName.match( /section/gi ) ) {\n\t\t\t\telement = element.parentNode;\n\t\t\t}\n\n\t\t\tif( element && !element.classList.contains( 'disabled' ) ) {\n\n\t\t\t\tthis.deactivate();\n\n\t\t\t\tif( element.nodeName.match( /section/gi ) ) {\n\t\t\t\t\tlet h = parseInt( element.getAttribute( 'data-index-h' ), 10 ),\n\t\t\t\t\t\tv = parseInt( element.getAttribute( 'data-index-v' ), 10 );\n\n\t\t\t\t\tthis.Reveal.slide( h, v );\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\n\t}\n\n}","import { enterFullscreen } from '../utils/util.js'\n\n/**\n * Handles all reveal.js keyboard interactions.\n */\nexport default class Keyboard {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\t// A key:value map of keyboard keys and descriptions of\n\t\t// the actions they trigger\n\t\tthis.shortcuts = {};\n\n\t\t// Holds custom key code mappings\n\t\tthis.bindings = {};\n\n\t\tthis.onDocumentKeyDown = this.onDocumentKeyDown.bind( this );\n\t\tthis.onDocumentKeyPress = this.onDocumentKeyPress.bind( this );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tif( config.navigationMode === 'linear' ) {\n\t\t\tthis.shortcuts['→ , ↓ , SPACE , N , L , J'] = 'Next slide';\n\t\t\tthis.shortcuts['← , ↑ , P , H , K'] = 'Previous slide';\n\t\t}\n\t\telse {\n\t\t\tthis.shortcuts['N , SPACE'] = 'Next slide';\n\t\t\tthis.shortcuts['P , Shift SPACE'] = 'Previous slide';\n\t\t\tthis.shortcuts['← , H'] = 'Navigate left';\n\t\t\tthis.shortcuts['→ , L'] = 'Navigate right';\n\t\t\tthis.shortcuts['↑ , K'] = 'Navigate up';\n\t\t\tthis.shortcuts['↓ , J'] = 'Navigate down';\n\t\t}\n\n\t\tthis.shortcuts['Alt + ←/↑/→/↓'] = 'Navigate without fragments';\n\t\tthis.shortcuts['Shift + ←/↑/→/↓'] = 'Jump to first/last slide';\n\t\tthis.shortcuts['B , .'] = 'Pause';\n\t\tthis.shortcuts['F'] = 'Fullscreen';\n\t\tthis.shortcuts['ESC, O'] = 'Slide overview';\n\n\t}\n\n\t/**\n\t * Starts listening for keyboard events.\n\t */\n\tbind() {\n\n\t\tdocument.addEventListener( 'keydown', this.onDocumentKeyDown, false );\n\t\tdocument.addEventListener( 'keypress', this.onDocumentKeyPress, false );\n\n\t}\n\n\t/**\n\t * Stops listening for keyboard events.\n\t */\n\tunbind() {\n\n\t\tdocument.removeEventListener( 'keydown', this.onDocumentKeyDown, false );\n\t\tdocument.removeEventListener( 'keypress', this.onDocumentKeyPress, false );\n\n\t}\n\n\t/**\n\t * Add a custom key binding with optional description to\n\t * be added to the help screen.\n\t */\n\taddKeyBinding( binding, callback ) {\n\n\t\tif( typeof binding === 'object' && binding.keyCode ) {\n\t\t\tthis.bindings[binding.keyCode] = {\n\t\t\t\tcallback: callback,\n\t\t\t\tkey: binding.key,\n\t\t\t\tdescription: binding.description\n\t\t\t};\n\t\t}\n\t\telse {\n\t\t\tthis.bindings[binding] = {\n\t\t\t\tcallback: callback,\n\t\t\t\tkey: null,\n\t\t\t\tdescription: null\n\t\t\t};\n\t\t}\n\n\t}\n\n\t/**\n\t * Removes the specified custom key binding.\n\t */\n\tremoveKeyBinding( keyCode ) {\n\n\t\tdelete this.bindings[keyCode];\n\n\t}\n\n\t/**\n\t * Programmatically triggers a keyboard event\n\t *\n\t * @param {int} keyCode\n\t */\n\ttriggerKey( keyCode ) {\n\n\t\tthis.onDocumentKeyDown( { keyCode } );\n\n\t}\n\n\t/**\n\t * Registers a new shortcut to include in the help overlay\n\t *\n\t * @param {String} key\n\t * @param {String} value\n\t */\n\tregisterKeyboardShortcut( key, value ) {\n\n\t\tthis.shortcuts[key] = value;\n\n\t}\n\n\tgetShortcuts() {\n\n\t\treturn this.shortcuts;\n\n\t}\n\n\tgetBindings() {\n\n\t\treturn this.bindings;\n\n\t}\n\n\t/**\n\t * Handler for the document level 'keypress' event.\n\t *\n\t * @param {object} event\n\t */\n\tonDocumentKeyPress( event ) {\n\n\t\t// Check if the pressed key is question mark\n\t\tif( event.shiftKey && event.charCode === 63 ) {\n\t\t\tthis.Reveal.toggleHelp();\n\t\t}\n\n\t}\n\n\t/**\n\t * Handler for the document level 'keydown' event.\n\t *\n\t * @param {object} event\n\t */\n\tonDocumentKeyDown( event ) {\n\n\t\tlet config = this.Reveal.getConfig();\n\n\t\t// If there's a condition specified and it returns false,\n\t\t// ignore this event\n\t\tif( typeof config.keyboardCondition === 'function' && config.keyboardCondition(event) === false ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If keyboardCondition is set, only capture keyboard events\n\t\t// for embedded decks when they are focused\n\t\tif( config.keyboardCondition === 'focused' && !this.Reveal.isFocused() ) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// Shorthand\n\t\tlet keyCode = event.keyCode;\n\n\t\t// Remember if auto-sliding was paused so we can toggle it\n\t\tlet autoSlideWasPaused = !this.Reveal.isAutoSliding();\n\n\t\tthis.Reveal.onUserInput( event );\n\n\t\t// Is there a focused element that could be using the keyboard?\n\t\tlet activeElementIsCE = document.activeElement && document.activeElement.isContentEditable === true;\n\t\tlet activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );\n\t\tlet activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);\n\n\t\t// Whitelist certain modifiers for slide navigation shortcuts\n\t\tlet isNavigationKey = [32, 37, 38, 39, 40, 78, 80].indexOf( event.keyCode ) !== -1;\n\n\t\t// Prevent all other events when a modifier is pressed\n\t\tlet unusedModifier = \t!( isNavigationKey && event.shiftKey || event.altKey ) &&\n\t\t\t\t\t\t\t\t( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );\n\n\t\t// Disregard the event if there's a focused element or a\n\t\t// keyboard modifier key is present\n\t\tif( activeElementIsCE || activeElementIsInput || activeElementIsNotes || unusedModifier ) return;\n\n\t\t// While paused only allow resume keyboard events; 'b', 'v', '.'\n\t\tlet resumeKeyCodes = [66,86,190,191];\n\t\tlet key;\n\n\t\t// Custom key bindings for togglePause should be able to resume\n\t\tif( typeof config.keyboard === 'object' ) {\n\t\t\tfor( key in config.keyboard ) {\n\t\t\t\tif( config.keyboard[key] === 'togglePause' ) {\n\t\t\t\t\tresumeKeyCodes.push( parseInt( key, 10 ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif( this.Reveal.isPaused() && resumeKeyCodes.indexOf( keyCode ) === -1 ) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Use linear navigation if we're configured to OR if\n\t\t// the presentation is one-dimensional\n\t\tlet useLinearMode = config.navigationMode === 'linear' || !this.Reveal.hasHorizontalSlides() || !this.Reveal.hasVerticalSlides();\n\n\t\tlet triggered = false;\n\n\t\t// 1. User defined key bindings\n\t\tif( typeof config.keyboard === 'object' ) {\n\n\t\t\tfor( key in config.keyboard ) {\n\n\t\t\t\t// Check if this binding matches the pressed key\n\t\t\t\tif( parseInt( key, 10 ) === keyCode ) {\n\n\t\t\t\t\tlet value = config.keyboard[ key ];\n\n\t\t\t\t\t// Callback function\n\t\t\t\t\tif( typeof value === 'function' ) {\n\t\t\t\t\t\tvalue.apply( null, [ event ] );\n\t\t\t\t\t}\n\t\t\t\t\t// String shortcuts to reveal.js API\n\t\t\t\t\telse if( typeof value === 'string' && typeof this.Reveal[ value ] === 'function' ) {\n\t\t\t\t\t\tthis.Reveal[ value ].call();\n\t\t\t\t\t}\n\n\t\t\t\t\ttriggered = true;\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}\n\n\t\t// 2. Registered custom key bindings\n\t\tif( triggered === false ) {\n\n\t\t\tfor( key in this.bindings ) {\n\n\t\t\t\t// Check if this binding matches the pressed key\n\t\t\t\tif( parseInt( key, 10 ) === keyCode ) {\n\n\t\t\t\t\tlet action = this.bindings[ key ].callback;\n\n\t\t\t\t\t// Callback function\n\t\t\t\t\tif( typeof action === 'function' ) {\n\t\t\t\t\t\taction.apply( null, [ event ] );\n\t\t\t\t\t}\n\t\t\t\t\t// String shortcuts to reveal.js API\n\t\t\t\t\telse if( typeof action === 'string' && typeof this.Reveal[ action ] === 'function' ) {\n\t\t\t\t\t\tthis.Reveal[ action ].call();\n\t\t\t\t\t}\n\n\t\t\t\t\ttriggered = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// 3. System defined key bindings\n\t\tif( triggered === false ) {\n\n\t\t\t// Assume true and try to prove false\n\t\t\ttriggered = true;\n\n\t\t\t// P, PAGE UP\n\t\t\tif( keyCode === 80 || keyCode === 33 ) {\n\t\t\t\tthis.Reveal.prev({skipFragments: event.altKey});\n\t\t\t}\n\t\t\t// N, PAGE DOWN\n\t\t\telse if( keyCode === 78 || keyCode === 34 ) {\n\t\t\t\tthis.Reveal.next({skipFragments: event.altKey});\n\t\t\t}\n\t\t\t// H, LEFT\n\t\t\telse if( keyCode === 72 || keyCode === 37 ) {\n\t\t\t\tif( event.shiftKey ) {\n\t\t\t\t\tthis.Reveal.slide( 0 );\n\t\t\t\t}\n\t\t\t\telse if( !this.Reveal.overview.isActive() && useLinearMode ) {\n\t\t\t\t\tthis.Reveal.prev({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Reveal.left({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// L, RIGHT\n\t\t\telse if( keyCode === 76 || keyCode === 39 ) {\n\t\t\t\tif( event.shiftKey ) {\n\t\t\t\t\tthis.Reveal.slide( this.Reveal.getHorizontalSlides().length - 1 );\n\t\t\t\t}\n\t\t\t\telse if( !this.Reveal.overview.isActive() && useLinearMode ) {\n\t\t\t\t\tthis.Reveal.next({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Reveal.right({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// K, UP\n\t\t\telse if( keyCode === 75 || keyCode === 38 ) {\n\t\t\t\tif( event.shiftKey ) {\n\t\t\t\t\tthis.Reveal.slide( undefined, 0 );\n\t\t\t\t}\n\t\t\t\telse if( !this.Reveal.overview.isActive() && useLinearMode ) {\n\t\t\t\t\tthis.Reveal.prev({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Reveal.up({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// J, DOWN\n\t\t\telse if( keyCode === 74 || keyCode === 40 ) {\n\t\t\t\tif( event.shiftKey ) {\n\t\t\t\t\tthis.Reveal.slide( undefined, Number.MAX_VALUE );\n\t\t\t\t}\n\t\t\t\telse if( !this.Reveal.overview.isActive() && useLinearMode ) {\n\t\t\t\t\tthis.Reveal.next({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Reveal.down({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// HOME\n\t\t\telse if( keyCode === 36 ) {\n\t\t\t\tthis.Reveal.slide( 0 );\n\t\t\t}\n\t\t\t// END\n\t\t\telse if( keyCode === 35 ) {\n\t\t\t\tthis.Reveal.slide( this.Reveal.getHorizontalSlides().length - 1 );\n\t\t\t}\n\t\t\t// SPACE\n\t\t\telse if( keyCode === 32 ) {\n\t\t\t\tif( this.Reveal.overview.isActive() ) {\n\t\t\t\t\tthis.Reveal.overview.deactivate();\n\t\t\t\t}\n\t\t\t\tif( event.shiftKey ) {\n\t\t\t\t\tthis.Reveal.prev({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.Reveal.next({skipFragments: event.altKey});\n\t\t\t\t}\n\t\t\t}\n\t\t\t// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS \"BLACK SCREEN\" BUTTON\n\t\t\telse if( keyCode === 58 || keyCode === 59 || keyCode === 66 || keyCode === 86 || keyCode === 190 || keyCode === 191 ) {\n\t\t\t\tthis.Reveal.togglePause();\n\t\t\t}\n\t\t\t// F\n\t\t\telse if( keyCode === 70 ) {\n\t\t\t\tenterFullscreen( config.embedded ? this.Reveal.getViewportElement() : document.documentElement );\n\t\t\t}\n\t\t\t// A\n\t\t\telse if( keyCode === 65 ) {\n\t\t\t\tif ( config.autoSlideStoppable ) {\n\t\t\t\t\tthis.Reveal.toggleAutoSlide( autoSlideWasPaused );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\ttriggered = false;\n\t\t\t}\n\n\t\t}\n\n\t\t// If the input resulted in a triggered action we should prevent\n\t\t// the browsers default behavior\n\t\tif( triggered ) {\n\t\t\tevent.preventDefault && event.preventDefault();\n\t\t}\n\t\t// ESC or O key\n\t\telse if( keyCode === 27 || keyCode === 79 ) {\n\t\t\tif( this.Reveal.closeOverlay() === false ) {\n\t\t\t\tthis.Reveal.overview.toggle();\n\t\t\t}\n\n\t\t\tevent.preventDefault && event.preventDefault();\n\t\t}\n\n\t\t// If auto-sliding is enabled we need to cue up\n\t\t// another timeout\n\t\tthis.Reveal.cueAutoSlide();\n\n\t}\n\n}","/**\n * Reads and writes the URL based on reveal.js' current state.\n */\nexport default class Location {\n\n\t// The minimum number of milliseconds that must pass between\n\t// calls to history.replaceState\n\tMAX_REPLACE_STATE_FREQUENCY = 1000\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\t// Delays updates to the URL due to a Chrome thumbnailer bug\n\t\tthis.writeURLTimeout = 0;\n\n\t\tthis.replaceStateTimestamp = 0;\n\n\t\tthis.onWindowHashChange = this.onWindowHashChange.bind( this );\n\n\t}\n\n\tbind() {\n\n\t\twindow.addEventListener( 'hashchange', this.onWindowHashChange, false );\n\n\t}\n\n\tunbind() {\n\n\t\twindow.removeEventListener( 'hashchange', this.onWindowHashChange, false );\n\n\t}\n\n\t/**\n\t * Returns the slide indices for the given hash link.\n\t *\n\t * @param {string} [hash] the hash string that we want to\n\t * find the indices for\n\t *\n\t * @returns slide indices or null\n\t */\n\tgetIndicesFromHash( hash=window.location.hash ) {\n\n\t\t// Attempt to parse the hash as either an index or name\n\t\tlet name = hash.replace( /^#\\/?/, '' );\n\t\tlet bits = name.split( '/' );\n\n\t\t// If the first bit is not fully numeric and there is a name we\n\t\t// can assume that this is a named link\n\t\tif( !/^[0-9]*$/.test( bits[0] ) && name.length ) {\n\t\t\tlet element;\n\n\t\t\tlet f;\n\n\t\t\t// Parse named links with fragments (#/named-link/2)\n\t\t\tif( /\\/[-\\d]+$/g.test( name ) ) {\n\t\t\t\tf = parseInt( name.split( '/' ).pop(), 10 );\n\t\t\t\tf = isNaN(f) ? undefined : f;\n\t\t\t\tname = name.split( '/' ).shift();\n\t\t\t}\n\n\t\t\t// Ensure the named link is a valid HTML ID attribute\n\t\t\ttry {\n\t\t\t\telement = document.getElementById( decodeURIComponent( name ) );\n\t\t\t}\n\t\t\tcatch ( error ) { }\n\n\t\t\tif( element ) {\n\t\t\t\treturn { ...this.Reveal.getIndices( element ), f };\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tconst config = this.Reveal.getConfig();\n\t\t\tlet hashIndexBase = config.hashOneBasedIndex ? 1 : 0;\n\n\t\t\t// Read the index components of the hash\n\t\t\tlet h = ( parseInt( bits[0], 10 ) - hashIndexBase ) || 0,\n\t\t\t\tv = ( parseInt( bits[1], 10 ) - hashIndexBase ) || 0,\n\t\t\t\tf;\n\n\t\t\tif( config.fragmentInURL ) {\n\t\t\t\tf = parseInt( bits[2], 10 );\n\t\t\t\tif( isNaN( f ) ) {\n\t\t\t\t\tf = undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { h, v, f };\n\t\t}\n\n\t\t// The hash couldn't be parsed or no matching named link was found\n\t\treturn null\n\n\t}\n\n\t/**\n\t * Reads the current URL (hash) and navigates accordingly.\n\t */\n\treadURL() {\n\n\t\tconst currentIndices = this.Reveal.getIndices();\n\t\tconst newIndices = this.getIndicesFromHash();\n\n\t\tif( newIndices ) {\n\t\t\tif( ( newIndices.h !== currentIndices.h || newIndices.v !== currentIndices.v || newIndices.f !== undefined ) ) {\n\t\t\t\t\tthis.Reveal.slide( newIndices.h, newIndices.v, newIndices.f );\n\t\t\t}\n\t\t}\n\t\t// If no new indices are available, we're trying to navigate to\n\t\t// a slide hash that does not exist\n\t\telse {\n\t\t\tthis.Reveal.slide( currentIndices.h || 0, currentIndices.v || 0 );\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the page URL (hash) to reflect the current\n\t * state.\n\t *\n\t * @param {number} delay The time in ms to wait before\n\t * writing the hash\n\t */\n\twriteURL( delay ) {\n\n\t\tlet config = this.Reveal.getConfig();\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\n\t\t// Make sure there's never more than one timeout running\n\t\tclearTimeout( this.writeURLTimeout );\n\n\t\t// If a delay is specified, timeout this call\n\t\tif( typeof delay === 'number' ) {\n\t\t\tthis.writeURLTimeout = setTimeout( this.writeURL, delay );\n\t\t}\n\t\telse if( currentSlide ) {\n\n\t\t\tlet hash = this.getHash();\n\n\t\t\t// If we're configured to push to history OR the history\n\t\t\t// API is not avaialble.\n\t\t\tif( config.history ) {\n\t\t\t\twindow.location.hash = hash;\n\t\t\t}\n\t\t\t// If we're configured to reflect the current slide in the\n\t\t\t// URL without pushing to history.\n\t\t\telse if( config.hash ) {\n\t\t\t\t// If the hash is empty, don't add it to the URL\n\t\t\t\tif( hash === '/' ) {\n\t\t\t\t\tthis.debouncedReplaceState( window.location.pathname + window.location.search );\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tthis.debouncedReplaceState( '#' + hash );\n\t\t\t\t}\n\t\t\t}\n\t\t\t// UPDATE: The below nuking of all hash changes breaks\n\t\t\t// anchors on pages where reveal.js is running. Removed\n\t\t\t// in 4.0. Why was it here in the first place? ¯\\_(ツ)_/¯\n\t\t\t//\n\t\t\t// If history and hash are both disabled, a hash may still\n\t\t\t// be added to the URL by clicking on a href with a hash\n\t\t\t// target. Counter this by always removing the hash.\n\t\t\t// else {\n\t\t\t// \twindow.history.replaceState( null, null, window.location.pathname + window.location.search );\n\t\t\t// }\n\n\t\t}\n\n\t}\n\n\treplaceState( url ) {\n\n\t\twindow.history.replaceState( null, null, url );\n\t\tthis.replaceStateTimestamp = Date.now();\n\n\t}\n\n\tdebouncedReplaceState( url ) {\n\n\t\tclearTimeout( this.replaceStateTimeout );\n\n\t\tif( Date.now() - this.replaceStateTimestamp > this.MAX_REPLACE_STATE_FREQUENCY ) {\n\t\t\tthis.replaceState( url );\n\t\t}\n\t\telse {\n\t\t\tthis.replaceStateTimeout = setTimeout( () => this.replaceState( url ), this.MAX_REPLACE_STATE_FREQUENCY );\n\t\t}\n\n\t}\n\n\t/**\n\t * Return a hash URL that will resolve to the given slide location.\n\t *\n\t * @param {HTMLElement} [slide=currentSlide] The slide to link to\n\t */\n\tgetHash( slide ) {\n\n\t\tlet url = '/';\n\n\t\t// Attempt to create a named link based on the slide's ID\n\t\tlet s = slide || this.Reveal.getCurrentSlide();\n\t\tlet id = s ? s.getAttribute( 'id' ) : null;\n\t\tif( id ) {\n\t\t\tid = encodeURIComponent( id );\n\t\t}\n\n\t\tlet index = this.Reveal.getIndices( slide );\n\t\tif( !this.Reveal.getConfig().fragmentInURL ) {\n\t\t\tindex.f = undefined;\n\t\t}\n\n\t\t// If the current slide has an ID, use that as a named link,\n\t\t// but we don't support named links with a fragment index\n\t\tif( typeof id === 'string' && id.length ) {\n\t\t\turl = '/' + id;\n\n\t\t\t// If there is also a fragment, append that at the end\n\t\t\t// of the named link, like: #/named-link/2\n\t\t\tif( index.f >= 0 ) url += '/' + index.f;\n\t\t}\n\t\t// Otherwise use the /h/v index\n\t\telse {\n\t\t\tlet hashIndexBase = this.Reveal.getConfig().hashOneBasedIndex ? 1 : 0;\n\t\t\tif( index.h > 0 || index.v > 0 || index.f >= 0 ) url += index.h + hashIndexBase;\n\t\t\tif( index.v > 0 || index.f >= 0 ) url += '/' + (index.v + hashIndexBase );\n\t\t\tif( index.f >= 0 ) url += '/' + index.f;\n\t\t}\n\n\t\treturn url;\n\n\t}\n\n\t/**\n\t * Handler for the window level 'hashchange' event.\n\t *\n\t * @param {object} [event]\n\t */\n\tonWindowHashChange( event ) {\n\n\t\tthis.readURL();\n\n\t}\n\n}","import { queryAll } from '../utils/util.js'\nimport { isAndroid } from '../utils/device.js'\n\n/**\n * Manages our presentation controls. This includes both\n * the built-in control arrows as well as event monitoring\n * of any elements within the presentation with either of the\n * following helper classes:\n * - .navigate-up\n * - .navigate-right\n * - .navigate-down\n * - .navigate-left\n * - .navigate-next\n * - .navigate-prev\n */\nexport default class Controls {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.onNavigateLeftClicked = this.onNavigateLeftClicked.bind( this );\n\t\tthis.onNavigateRightClicked = this.onNavigateRightClicked.bind( this );\n\t\tthis.onNavigateUpClicked = this.onNavigateUpClicked.bind( this );\n\t\tthis.onNavigateDownClicked = this.onNavigateDownClicked.bind( this );\n\t\tthis.onNavigatePrevClicked = this.onNavigatePrevClicked.bind( this );\n\t\tthis.onNavigateNextClicked = this.onNavigateNextClicked.bind( this );\n\n\t}\n\n\trender() {\n\n\t\tconst rtl = this.Reveal.getConfig().rtl;\n\t\tconst revealElement = this.Reveal.getRevealElement();\n\n\t\tthis.element = document.createElement( 'aside' );\n\t\tthis.element.className = 'controls';\n\t\tthis.element.innerHTML =\n\t\t\t`\n\t\t\t\n\t\t\t\n\t\t\t`;\n\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t\t// There can be multiple instances of controls throughout the page\n\t\tthis.controlsLeft = queryAll( revealElement, '.navigate-left' );\n\t\tthis.controlsRight = queryAll( revealElement, '.navigate-right' );\n\t\tthis.controlsUp = queryAll( revealElement, '.navigate-up' );\n\t\tthis.controlsDown = queryAll( revealElement, '.navigate-down' );\n\t\tthis.controlsPrev = queryAll( revealElement, '.navigate-prev' );\n\t\tthis.controlsNext = queryAll( revealElement, '.navigate-next' );\n\n\t\t// The left, right and down arrows in the standard reveal.js controls\n\t\tthis.controlsRightArrow = this.element.querySelector( '.navigate-right' );\n\t\tthis.controlsLeftArrow = this.element.querySelector( '.navigate-left' );\n\t\tthis.controlsDownArrow = this.element.querySelector( '.navigate-down' );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tthis.element.style.display = config.controls ? 'block' : 'none';\n\n\t\tthis.element.setAttribute( 'data-controls-layout', config.controlsLayout );\n\t\tthis.element.setAttribute( 'data-controls-back-arrows', config.controlsBackArrows );\n\n\t}\n\n\tbind() {\n\n\t\t// Listen to both touch and click events, in case the device\n\t\t// supports both\n\t\tlet pointerEvents = [ 'touchstart', 'click' ];\n\n\t\t// Only support touch for Android, fixes double navigations in\n\t\t// stock browser\n\t\tif( isAndroid ) {\n\t\t\tpointerEvents = [ 'touchstart' ];\n\t\t}\n\n\t\tpointerEvents.forEach( eventName => {\n\t\t\tthis.controlsLeft.forEach( el => el.addEventListener( eventName, this.onNavigateLeftClicked, false ) );\n\t\t\tthis.controlsRight.forEach( el => el.addEventListener( eventName, this.onNavigateRightClicked, false ) );\n\t\t\tthis.controlsUp.forEach( el => el.addEventListener( eventName, this.onNavigateUpClicked, false ) );\n\t\t\tthis.controlsDown.forEach( el => el.addEventListener( eventName, this.onNavigateDownClicked, false ) );\n\t\t\tthis.controlsPrev.forEach( el => el.addEventListener( eventName, this.onNavigatePrevClicked, false ) );\n\t\t\tthis.controlsNext.forEach( el => el.addEventListener( eventName, this.onNavigateNextClicked, false ) );\n\t\t} );\n\n\t}\n\n\tunbind() {\n\n\t\t[ 'touchstart', 'click' ].forEach( eventName => {\n\t\t\tthis.controlsLeft.forEach( el => el.removeEventListener( eventName, this.onNavigateLeftClicked, false ) );\n\t\t\tthis.controlsRight.forEach( el => el.removeEventListener( eventName, this.onNavigateRightClicked, false ) );\n\t\t\tthis.controlsUp.forEach( el => el.removeEventListener( eventName, this.onNavigateUpClicked, false ) );\n\t\t\tthis.controlsDown.forEach( el => el.removeEventListener( eventName, this.onNavigateDownClicked, false ) );\n\t\t\tthis.controlsPrev.forEach( el => el.removeEventListener( eventName, this.onNavigatePrevClicked, false ) );\n\t\t\tthis.controlsNext.forEach( el => el.removeEventListener( eventName, this.onNavigateNextClicked, false ) );\n\t\t} );\n\n\t}\n\n\t/**\n\t * Updates the state of all control/navigation arrows.\n\t */\n\tupdate() {\n\n\t\tlet routes = this.Reveal.availableRoutes();\n\n\t\t// Remove the 'enabled' class from all directions\n\t\t[...this.controlsLeft, ...this.controlsRight, ...this.controlsUp, ...this.controlsDown, ...this.controlsPrev, ...this.controlsNext].forEach( node => {\n\t\t\tnode.classList.remove( 'enabled', 'fragmented' );\n\n\t\t\t// Set 'disabled' attribute on all directions\n\t\t\tnode.setAttribute( 'disabled', 'disabled' );\n\t\t} );\n\n\t\t// Add the 'enabled' class to the available routes; remove 'disabled' attribute to enable buttons\n\t\tif( routes.left ) this.controlsLeft.forEach( el => { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.right ) this.controlsRight.forEach( el => { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.up ) this.controlsUp.forEach( el => { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.down ) this.controlsDown.forEach( el => { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\n\t\t// Prev/next buttons\n\t\tif( routes.left || routes.up ) this.controlsPrev.forEach( el => { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\tif( routes.right || routes.down ) this.controlsNext.forEach( el => { el.classList.add( 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\n\t\t// Highlight fragment directions\n\t\tlet currentSlide = this.Reveal.getCurrentSlide();\n\t\tif( currentSlide ) {\n\n\t\t\tlet fragmentsRoutes = this.Reveal.fragments.availableRoutes();\n\n\t\t\t// Always apply fragment decorator to prev/next buttons\n\t\t\tif( fragmentsRoutes.prev ) this.controlsPrev.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\tif( fragmentsRoutes.next ) this.controlsNext.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\n\t\t\t// Apply fragment decorators to directional buttons based on\n\t\t\t// what slide axis they are in\n\t\t\tif( this.Reveal.isVerticalSlide( currentSlide ) ) {\n\t\t\t\tif( fragmentsRoutes.prev ) this.controlsUp.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t\tif( fragmentsRoutes.next ) this.controlsDown.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif( fragmentsRoutes.prev ) this.controlsLeft.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t\tif( fragmentsRoutes.next ) this.controlsRight.forEach( el => { el.classList.add( 'fragmented', 'enabled' ); el.removeAttribute( 'disabled' ); } );\n\t\t\t}\n\n\t\t}\n\n\t\tif( this.Reveal.getConfig().controlsTutorial ) {\n\n\t\t\tlet indices = this.Reveal.getIndices();\n\n\t\t\t// Highlight control arrows with an animation to ensure\n\t\t\t// that the viewer knows how to navigate\n\t\t\tif( !this.Reveal.hasNavigatedVertically() && routes.down ) {\n\t\t\t\tthis.controlsDownArrow.classList.add( 'highlight' );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.controlsDownArrow.classList.remove( 'highlight' );\n\n\t\t\t\tif( this.Reveal.getConfig().rtl ) {\n\n\t\t\t\t\tif( !this.Reveal.hasNavigatedHorizontally() && routes.left && indices.v === 0 ) {\n\t\t\t\t\t\tthis.controlsLeftArrow.classList.add( 'highlight' );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.controlsLeftArrow.classList.remove( 'highlight' );\n\t\t\t\t\t}\n\n\t\t\t\t} else {\n\n\t\t\t\t\tif( !this.Reveal.hasNavigatedHorizontally() && routes.right && indices.v === 0 ) {\n\t\t\t\t\t\tthis.controlsRightArrow.classList.add( 'highlight' );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.controlsRightArrow.classList.remove( 'highlight' );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdestroy() {\n\n\t\tthis.unbind();\n\t\tthis.element.remove();\n\n\t}\n\n\t/**\n\t * Event handlers for navigation control buttons.\n\t */\n\tonNavigateLeftClicked( event ) {\n\n\t\tevent.preventDefault();\n\t\tthis.Reveal.onUserInput();\n\n\t\tif( this.Reveal.getConfig().navigationMode === 'linear' ) {\n\t\t\tthis.Reveal.prev();\n\t\t}\n\t\telse {\n\t\t\tthis.Reveal.left();\n\t\t}\n\n\t}\n\n\tonNavigateRightClicked( event ) {\n\n\t\tevent.preventDefault();\n\t\tthis.Reveal.onUserInput();\n\n\t\tif( this.Reveal.getConfig().navigationMode === 'linear' ) {\n\t\t\tthis.Reveal.next();\n\t\t}\n\t\telse {\n\t\t\tthis.Reveal.right();\n\t\t}\n\n\t}\n\n\tonNavigateUpClicked( event ) {\n\n\t\tevent.preventDefault();\n\t\tthis.Reveal.onUserInput();\n\n\t\tthis.Reveal.up();\n\n\t}\n\n\tonNavigateDownClicked( event ) {\n\n\t\tevent.preventDefault();\n\t\tthis.Reveal.onUserInput();\n\n\t\tthis.Reveal.down();\n\n\t}\n\n\tonNavigatePrevClicked( event ) {\n\n\t\tevent.preventDefault();\n\t\tthis.Reveal.onUserInput();\n\n\t\tthis.Reveal.prev();\n\n\t}\n\n\tonNavigateNextClicked( event ) {\n\n\t\tevent.preventDefault();\n\t\tthis.Reveal.onUserInput();\n\n\t\tthis.Reveal.next();\n\n\t}\n\n\n}","/**\n * Creates a visual progress bar for the presentation.\n */\nexport default class Progress {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.onProgressClicked = this.onProgressClicked.bind( this );\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'progress';\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t\tthis.bar = document.createElement( 'span' );\n\t\tthis.element.appendChild( this.bar );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tthis.element.style.display = config.progress ? 'block' : 'none';\n\n\t}\n\n\tbind() {\n\n\t\tif( this.Reveal.getConfig().progress && this.element ) {\n\t\t\tthis.element.addEventListener( 'click', this.onProgressClicked, false );\n\t\t}\n\n\t}\n\n\tunbind() {\n\n\t\tif ( this.Reveal.getConfig().progress && this.element ) {\n\t\t\tthis.element.removeEventListener( 'click', this.onProgressClicked, false );\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the progress bar to reflect the current slide.\n\t */\n\tupdate() {\n\n\t\t// Update progress if enabled\n\t\tif( this.Reveal.getConfig().progress && this.bar ) {\n\n\t\t\tlet scale = this.Reveal.getProgress();\n\n\t\t\t// Don't fill the progress bar if there's only one slide\n\t\t\tif( this.Reveal.getTotalSlides() < 2 ) {\n\t\t\t\tscale = 0;\n\t\t\t}\n\n\t\t\tthis.bar.style.transform = 'scaleX('+ scale +')';\n\n\t\t}\n\n\t}\n\n\tgetMaxWidth() {\n\n\t\treturn this.Reveal.getRevealElement().offsetWidth;\n\n\t}\n\n\t/**\n\t * Clicking on the progress bar results in a navigation to the\n\t * closest approximate horizontal slide using this equation:\n\t *\n\t * ( clickX / presentationWidth ) * numberOfSlides\n\t *\n\t * @param {object} event\n\t */\n\tonProgressClicked( event ) {\n\n\t\tthis.Reveal.onUserInput( event );\n\n\t\tevent.preventDefault();\n\n\t\tlet slides = this.Reveal.getSlides();\n\t\tlet slidesTotal = slides.length;\n\t\tlet slideIndex = Math.floor( ( event.clientX / this.getMaxWidth() ) * slidesTotal );\n\n\t\tif( this.Reveal.getConfig().rtl ) {\n\t\t\tslideIndex = slidesTotal - slideIndex;\n\t\t}\n\n\t\tlet targetIndices = this.Reveal.getIndices(slides[slideIndex]);\n\t\tthis.Reveal.slide( targetIndices.h, targetIndices.v );\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.element.remove();\n\n\t}\n\n}","/**\n * Handles hiding of the pointer/cursor when inactive.\n */\nexport default class Pointer {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\t// Throttles mouse wheel navigation\n\t\tthis.lastMouseWheelStep = 0;\n\n\t\t// Is the mouse pointer currently hidden from view\n\t\tthis.cursorHidden = false;\n\n\t\t// Timeout used to determine when the cursor is inactive\n\t\tthis.cursorInactiveTimeout = 0;\n\n\t\tthis.onDocumentCursorActive = this.onDocumentCursorActive.bind( this );\n\t\tthis.onDocumentMouseScroll = this.onDocumentMouseScroll.bind( this );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tif( config.mouseWheel ) {\n\t\t\tdocument.addEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false ); // FF\n\t\t\tdocument.addEventListener( 'mousewheel', this.onDocumentMouseScroll, false );\n\t\t}\n\t\telse {\n\t\t\tdocument.removeEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false ); // FF\n\t\t\tdocument.removeEventListener( 'mousewheel', this.onDocumentMouseScroll, false );\n\t\t}\n\n\t\t// Auto-hide the mouse pointer when its inactive\n\t\tif( config.hideInactiveCursor ) {\n\t\t\tdocument.addEventListener( 'mousemove', this.onDocumentCursorActive, false );\n\t\t\tdocument.addEventListener( 'mousedown', this.onDocumentCursorActive, false );\n\t\t}\n\t\telse {\n\t\t\tthis.showCursor();\n\n\t\t\tdocument.removeEventListener( 'mousemove', this.onDocumentCursorActive, false );\n\t\t\tdocument.removeEventListener( 'mousedown', this.onDocumentCursorActive, false );\n\t\t}\n\n\t}\n\n\t/**\n\t * Shows the mouse pointer after it has been hidden with\n\t * #hideCursor.\n\t */\n\tshowCursor() {\n\n\t\tif( this.cursorHidden ) {\n\t\t\tthis.cursorHidden = false;\n\t\t\tthis.Reveal.getRevealElement().style.cursor = '';\n\t\t}\n\n\t}\n\n\t/**\n\t * Hides the mouse pointer when it's on top of the .reveal\n\t * container.\n\t */\n\thideCursor() {\n\n\t\tif( this.cursorHidden === false ) {\n\t\t\tthis.cursorHidden = true;\n\t\t\tthis.Reveal.getRevealElement().style.cursor = 'none';\n\t\t}\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.showCursor();\n\n\t\tdocument.removeEventListener( 'DOMMouseScroll', this.onDocumentMouseScroll, false );\n\t\tdocument.removeEventListener( 'mousewheel', this.onDocumentMouseScroll, false );\n\t\tdocument.removeEventListener( 'mousemove', this.onDocumentCursorActive, false );\n\t\tdocument.removeEventListener( 'mousedown', this.onDocumentCursorActive, false );\n\n\t}\n\n\t/**\n\t * Called whenever there is mouse input at the document level\n\t * to determine if the cursor is active or not.\n\t *\n\t * @param {object} event\n\t */\n\tonDocumentCursorActive( event ) {\n\n\t\tthis.showCursor();\n\n\t\tclearTimeout( this.cursorInactiveTimeout );\n\n\t\tthis.cursorInactiveTimeout = setTimeout( this.hideCursor.bind( this ), this.Reveal.getConfig().hideCursorTime );\n\n\t}\n\n\t/**\n\t * Handles mouse wheel scrolling, throttled to avoid skipping\n\t * multiple slides.\n\t *\n\t * @param {object} event\n\t */\n\tonDocumentMouseScroll( event ) {\n\n\t\tif( Date.now() - this.lastMouseWheelStep > 1000 ) {\n\n\t\t\tthis.lastMouseWheelStep = Date.now();\n\n\t\t\tlet delta = event.detail || -event.wheelDelta;\n\t\t\tif( delta > 0 ) {\n\t\t\t\tthis.Reveal.next();\n\t\t\t}\n\t\t\telse if( delta < 0 ) {\n\t\t\t\tthis.Reveal.prev();\n\t\t\t}\n\n\t\t}\n\n\t}\n\n}","/**\n * Loads a JavaScript file from the given URL and executes it.\n *\n * @param {string} url Address of the .js file to load\n * @param {function} callback Method to invoke when the script\n * has loaded and executed\n */\nexport const loadScript = ( url, callback ) => {\n\n\tconst script = document.createElement( 'script' );\n\tscript.type = 'text/javascript';\n\tscript.async = false;\n\tscript.defer = false;\n\tscript.src = url;\n\n\tif( typeof callback === 'function' ) {\n\n\t\t// Success callback\n\t\tscript.onload = script.onreadystatechange = event => {\n\t\t\tif( event.type === 'load' || /loaded|complete/.test( script.readyState ) ) {\n\n\t\t\t\t// Kill event listeners\n\t\t\t\tscript.onload = script.onreadystatechange = script.onerror = null;\n\n\t\t\t\tcallback();\n\n\t\t\t}\n\t\t};\n\n\t\t// Error callback\n\t\tscript.onerror = err => {\n\n\t\t\t// Kill event listeners\n\t\t\tscript.onload = script.onreadystatechange = script.onerror = null;\n\n\t\t\tcallback( new Error( 'Failed loading script: ' + script.src + '\\n' + err ) );\n\n\t\t};\n\n\t}\n\n\t// Append the script at the end of \n\tconst head = document.querySelector( 'head' );\n\thead.insertBefore( script, head.lastChild );\n\n}","import { loadScript } from '../utils/loader.js'\n\n/**\n * Manages loading and registering of reveal.js plugins.\n */\nexport default class Plugins {\n\n\tconstructor( reveal ) {\n\n\t\tthis.Reveal = reveal;\n\n\t\t// Flags our current state (idle -> loading -> loaded)\n\t\tthis.state = 'idle';\n\n\t\t// An id:instance map of currently registed plugins\n\t\tthis.registeredPlugins = {};\n\n\t\tthis.asyncDependencies = [];\n\n\t}\n\n\t/**\n\t * Loads reveal.js dependencies, registers and\n\t * initializes plugins.\n\t *\n\t * Plugins are direct references to a reveal.js plugin\n\t * object that we register and initialize after any\n\t * synchronous dependencies have loaded.\n\t *\n\t * Dependencies are defined via the 'dependencies' config\n\t * option and will be loaded prior to starting reveal.js.\n\t * Some dependencies may have an 'async' flag, if so they\n\t * will load after reveal.js has been started up.\n\t */\n\tload( plugins, dependencies ) {\n\n\t\tthis.state = 'loading';\n\n\t\tplugins.forEach( this.registerPlugin.bind( this ) );\n\n\t\treturn new Promise( resolve => {\n\n\t\t\tlet scripts = [],\n\t\t\t\tscriptsToLoad = 0;\n\n\t\t\tdependencies.forEach( s => {\n\t\t\t\t// Load if there's no condition or the condition is truthy\n\t\t\t\tif( !s.condition || s.condition() ) {\n\t\t\t\t\tif( s.async ) {\n\t\t\t\t\t\tthis.asyncDependencies.push( s );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tscripts.push( s );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tif( scripts.length ) {\n\t\t\t\tscriptsToLoad = scripts.length;\n\n\t\t\t\tconst scriptLoadedCallback = (s) => {\n\t\t\t\t\tif( s && typeof s.callback === 'function' ) s.callback();\n\n\t\t\t\t\tif( --scriptsToLoad === 0 ) {\n\t\t\t\t\t\tthis.initPlugins().then( resolve );\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\t// Load synchronous scripts\n\t\t\t\tscripts.forEach( s => {\n\t\t\t\t\tif( typeof s.id === 'string' ) {\n\t\t\t\t\t\tthis.registerPlugin( s );\n\t\t\t\t\t\tscriptLoadedCallback( s );\n\t\t\t\t\t}\n\t\t\t\t\telse if( typeof s.src === 'string' ) {\n\t\t\t\t\t\tloadScript( s.src, () => scriptLoadedCallback(s) );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tconsole.warn( 'Unrecognized plugin format', s );\n\t\t\t\t\t\tscriptLoadedCallback();\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthis.initPlugins().then( resolve );\n\t\t\t}\n\n\t\t} );\n\n\t}\n\n\t/**\n\t * Initializes our plugins and waits for them to be ready\n\t * before proceeding.\n\t */\n\tinitPlugins() {\n\n\t\treturn new Promise( resolve => {\n\n\t\t\tlet pluginValues = Object.values( this.registeredPlugins );\n\t\t\tlet pluginsToInitialize = pluginValues.length;\n\n\t\t\t// If there are no plugins, skip this step\n\t\t\tif( pluginsToInitialize === 0 ) {\n\t\t\t\tthis.loadAsync().then( resolve );\n\t\t\t}\n\t\t\t// ... otherwise initialize plugins\n\t\t\telse {\n\n\t\t\t\tlet initNextPlugin;\n\n\t\t\t\tlet afterPlugInitialized = () => {\n\t\t\t\t\tif( --pluginsToInitialize === 0 ) {\n\t\t\t\t\t\tthis.loadAsync().then( resolve );\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tinitNextPlugin();\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tlet i = 0;\n\n\t\t\t\t// Initialize plugins serially\n\t\t\t\tinitNextPlugin = () => {\n\n\t\t\t\t\tlet plugin = pluginValues[i++];\n\n\t\t\t\t\t// If the plugin has an 'init' method, invoke it\n\t\t\t\t\tif( typeof plugin.init === 'function' ) {\n\t\t\t\t\t\tlet promise = plugin.init( this.Reveal );\n\n\t\t\t\t\t\t// If the plugin returned a Promise, wait for it\n\t\t\t\t\t\tif( promise && typeof promise.then === 'function' ) {\n\t\t\t\t\t\t\tpromise.then( afterPlugInitialized );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tafterPlugInitialized();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tafterPlugInitialized();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\tinitNextPlugin();\n\n\t\t\t}\n\n\t\t} )\n\n\t}\n\n\t/**\n\t * Loads all async reveal.js dependencies.\n\t */\n\tloadAsync() {\n\n\t\tthis.state = 'loaded';\n\n\t\tif( this.asyncDependencies.length ) {\n\t\t\tthis.asyncDependencies.forEach( s => {\n\t\t\t\tloadScript( s.src, s.callback );\n\t\t\t} );\n\t\t}\n\n\t\treturn Promise.resolve();\n\n\t}\n\n\t/**\n\t * Registers a new plugin with this reveal.js instance.\n\t *\n\t * reveal.js waits for all regisered plugins to initialize\n\t * before considering itself ready, as long as the plugin\n\t * is registered before calling `Reveal.initialize()`.\n\t */\n\tregisterPlugin( plugin ) {\n\n\t\t// Backwards compatibility to make reveal.js ~3.9.0\n\t\t// plugins work with reveal.js 4.0.0\n\t\tif( arguments.length === 2 && typeof arguments[0] === 'string' ) {\n\t\t\tplugin = arguments[1];\n\t\t\tplugin.id = arguments[0];\n\t\t}\n\t\t// Plugin can optionally be a function which we call\n\t\t// to create an instance of the plugin\n\t\telse if( typeof plugin === 'function' ) {\n\t\t\tplugin = plugin();\n\t\t}\n\n\t\tlet id = plugin.id;\n\n\t\tif( typeof id !== 'string' ) {\n\t\t\tconsole.warn( 'Unrecognized plugin format; can\\'t find plugin.id', plugin );\n\t\t}\n\t\telse if( this.registeredPlugins[id] === undefined ) {\n\t\t\tthis.registeredPlugins[id] = plugin;\n\n\t\t\t// If a plugin is registered after reveal.js is loaded,\n\t\t\t// initialize it right away\n\t\t\tif( this.state === 'loaded' && typeof plugin.init === 'function' ) {\n\t\t\t\tplugin.init( this.Reveal );\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tconsole.warn( 'reveal.js: \"'+ id +'\" plugin has already been registered' );\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if a specific plugin has been registered.\n\t *\n\t * @param {String} id Unique plugin identifier\n\t */\n\thasPlugin( id ) {\n\n\t\treturn !!this.registeredPlugins[id];\n\n\t}\n\n\t/**\n\t * Returns the specific plugin instance, if a plugin\n\t * with the given ID has been registered.\n\t *\n\t * @param {String} id Unique plugin identifier\n\t */\n\tgetPlugin( id ) {\n\n\t\treturn this.registeredPlugins[id];\n\n\t}\n\n\tgetRegisteredPlugins() {\n\n\t\treturn this.registeredPlugins;\n\n\t}\n\n\tdestroy() {\n\n\t\tObject.values( this.registeredPlugins ).forEach( plugin => {\n\t\t\tif( typeof plugin.destroy === 'function' ) {\n\t\t\t\tplugin.destroy();\n\t\t\t}\n\t\t} );\n\n\t\tthis.registeredPlugins = {};\n\t\tthis.asyncDependencies = [];\n\n\t}\n\n}\n","import { SLIDES_SELECTOR } from '../utils/constants.js'\nimport { queryAll, createStyleSheet } from '../utils/util.js'\n\n/**\n * Setups up our presentation for printing/exporting to PDF.\n */\nexport default class Print {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\t/**\n\t * Configures the presentation for printing to a static\n\t * PDF.\n\t */\n\tasync setupPDF() {\n\n\t\tconst config = this.Reveal.getConfig();\n\t\tconst slides = queryAll( this.Reveal.getRevealElement(), SLIDES_SELECTOR )\n\n\t\t// Compute slide numbers now, before we start duplicating slides\n\t\tconst doingSlideNumbers = config.slideNumber && /all|print/i.test( config.showSlideNumber );\n\n\t\tconst slideSize = this.Reveal.getComputedSlideSize( window.innerWidth, window.innerHeight );\n\n\t\t// Dimensions of the PDF pages\n\t\tconst pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),\n\t\t\tpageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );\n\n\t\t// Dimensions of slides within the pages\n\t\tconst slideWidth = slideSize.width,\n\t\t\tslideHeight = slideSize.height;\n\n\t\tawait new Promise( requestAnimationFrame );\n\n\t\t// Let the browser know what page size we want to print\n\t\tcreateStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0px;}' );\n\n\t\t// Limit the size of certain elements to the dimensions of the slide\n\t\tcreateStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );\n\n\t\tdocument.documentElement.classList.add( 'print-pdf' );\n\t\tdocument.body.style.width = pageWidth + 'px';\n\t\tdocument.body.style.height = pageHeight + 'px';\n\n\t\tconst viewportElement = document.querySelector( '.reveal-viewport' );\n\t\tlet presentationBackground;\n\t\tif( viewportElement ) {\n\t\t\tconst viewportStyles = window.getComputedStyle( viewportElement );\n\t\t\tif( viewportStyles && viewportStyles.background ) {\n\t\t\t\tpresentationBackground = viewportStyles.background;\n\t\t\t}\n\t\t}\n\n\t\t// Make sure stretch elements fit on slide\n\t\tawait new Promise( requestAnimationFrame );\n\t\tthis.Reveal.layoutSlideContents( slideWidth, slideHeight );\n\n\t\t// Batch scrollHeight access to prevent layout thrashing\n\t\tawait new Promise( requestAnimationFrame );\n\n\t\tconst slideScrollHeights = slides.map( slide => slide.scrollHeight );\n\n\t\tconst pages = [];\n\t\tconst pageContainer = slides[0].parentNode;\n\n\t\t// Slide and slide background layout\n\t\tslides.forEach( function( slide, index ) {\n\n\t\t\t// Vertical stacks are not centred since their section\n\t\t\t// children will be\n\t\t\tif( slide.classList.contains( 'stack' ) === false ) {\n\t\t\t\t// Center the slide inside of the page, giving the slide some margin\n\t\t\t\tlet left = ( pageWidth - slideWidth ) / 2;\n\t\t\t\tlet top = ( pageHeight - slideHeight ) / 2;\n\n\t\t\t\tconst contentHeight = slideScrollHeights[ index ];\n\t\t\t\tlet numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );\n\n\t\t\t\t// Adhere to configured pages per slide limit\n\t\t\t\tnumberOfPages = Math.min( numberOfPages, config.pdfMaxPagesPerSlide );\n\n\t\t\t\t// Center slides vertically\n\t\t\t\tif( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) {\n\t\t\t\t\ttop = Math.max( ( pageHeight - contentHeight ) / 2, 0 );\n\t\t\t\t}\n\n\t\t\t\t// Wrap the slide in a page element and hide its overflow\n\t\t\t\t// so that no page ever flows onto another\n\t\t\t\tconst page = document.createElement( 'div' );\n\t\t\t\tpages.push( page );\n\n\t\t\t\tpage.className = 'pdf-page';\n\t\t\t\tpage.style.height = ( ( pageHeight + config.pdfPageHeightOffset ) * numberOfPages ) + 'px';\n\n\t\t\t\t// Copy the presentation-wide background to each individual\n\t\t\t\t// page when printing\n\t\t\t\tif( presentationBackground ) {\n\t\t\t\t\tpage.style.background = presentationBackground;\n\t\t\t\t}\n\n\t\t\t\tpage.appendChild( slide );\n\n\t\t\t\t// Position the slide inside of the page\n\t\t\t\tslide.style.left = left + 'px';\n\t\t\t\tslide.style.top = top + 'px';\n\t\t\t\tslide.style.width = slideWidth + 'px';\n\n\t\t\t\t// Re-run the slide layout so that r-fit-text is applied based on\n\t\t\t\t// the printed slide size\n\t\t\t\tthis.Reveal.slideContent.layout( slide )\n\n\t\t\t\tif( slide.slideBackgroundElement ) {\n\t\t\t\t\tpage.insertBefore( slide.slideBackgroundElement, slide );\n\t\t\t\t}\n\n\t\t\t\t// Inject notes if `showNotes` is enabled\n\t\t\t\tif( config.showNotes ) {\n\n\t\t\t\t\t// Are there notes for this slide?\n\t\t\t\t\tconst notes = this.Reveal.getSlideNotes( slide );\n\t\t\t\t\tif( notes ) {\n\n\t\t\t\t\t\tconst notesSpacing = 8;\n\t\t\t\t\t\tconst notesLayout = typeof config.showNotes === 'string' ? config.showNotes : 'inline';\n\t\t\t\t\t\tconst notesElement = document.createElement( 'div' );\n\t\t\t\t\t\tnotesElement.classList.add( 'speaker-notes' );\n\t\t\t\t\t\tnotesElement.classList.add( 'speaker-notes-pdf' );\n\t\t\t\t\t\tnotesElement.setAttribute( 'data-layout', notesLayout );\n\t\t\t\t\t\tnotesElement.innerHTML = notes;\n\n\t\t\t\t\t\tif( notesLayout === 'separate-page' ) {\n\t\t\t\t\t\t\tpages.push( notesElement );\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tnotesElement.style.left = notesSpacing + 'px';\n\t\t\t\t\t\t\tnotesElement.style.bottom = notesSpacing + 'px';\n\t\t\t\t\t\t\tnotesElement.style.width = ( pageWidth - notesSpacing*2 ) + 'px';\n\t\t\t\t\t\t\tpage.appendChild( notesElement );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\n\t\t\t\t// Inject slide numbers if `slideNumbers` are enabled\n\t\t\t\tif( doingSlideNumbers ) {\n\t\t\t\t\tconst slideNumber = index + 1;\n\t\t\t\t\tconst numberElement = document.createElement( 'div' );\n\t\t\t\t\tnumberElement.classList.add( 'slide-number' );\n\t\t\t\t\tnumberElement.classList.add( 'slide-number-pdf' );\n\t\t\t\t\tnumberElement.innerHTML = slideNumber;\n\t\t\t\t\tpage.appendChild( numberElement );\n\t\t\t\t}\n\n\t\t\t\t// Copy page and show fragments one after another\n\t\t\t\tif( config.pdfSeparateFragments ) {\n\n\t\t\t\t\t// Each fragment 'group' is an array containing one or more\n\t\t\t\t\t// fragments. Multiple fragments that appear at the same time\n\t\t\t\t\t// are part of the same group.\n\t\t\t\t\tconst fragmentGroups = this.Reveal.fragments.sort( page.querySelectorAll( '.fragment' ), true );\n\n\t\t\t\t\tlet previousFragmentStep;\n\n\t\t\t\t\tfragmentGroups.forEach( function( fragments ) {\n\n\t\t\t\t\t\t// Remove 'current-fragment' from the previous group\n\t\t\t\t\t\tif( previousFragmentStep ) {\n\t\t\t\t\t\t\tpreviousFragmentStep.forEach( function( fragment ) {\n\t\t\t\t\t\t\t\tfragment.classList.remove( 'current-fragment' );\n\t\t\t\t\t\t\t} );\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Show the fragments for the current index\n\t\t\t\t\t\tfragments.forEach( function( fragment ) {\n\t\t\t\t\t\t\tfragment.classList.add( 'visible', 'current-fragment' );\n\t\t\t\t\t\t}, this );\n\n\t\t\t\t\t\t// Create a separate page for the current fragment state\n\t\t\t\t\t\tconst clonedPage = page.cloneNode( true );\n\t\t\t\t\t\tpages.push( clonedPage );\n\n\t\t\t\t\t\tpreviousFragmentStep = fragments;\n\n\t\t\t\t\t}, this );\n\n\t\t\t\t\t// Reset the first/original page so that all fragments are hidden\n\t\t\t\t\tfragmentGroups.forEach( function( fragments ) {\n\t\t\t\t\t\tfragments.forEach( function( fragment ) {\n\t\t\t\t\t\t\tfragment.classList.remove( 'visible', 'current-fragment' );\n\t\t\t\t\t\t} );\n\t\t\t\t\t} );\n\n\t\t\t\t}\n\t\t\t\t// Show all fragments\n\t\t\t\telse {\n\t\t\t\t\tqueryAll( page, '.fragment:not(.fade-out)' ).forEach( function( fragment ) {\n\t\t\t\t\t\tfragment.classList.add( 'visible' );\n\t\t\t\t\t} );\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t}, this );\n\n\t\tawait new Promise( requestAnimationFrame );\n\n\t\tpages.forEach( page => pageContainer.appendChild( page ) );\n\n\t\t// Notify subscribers that the PDF layout is good to go\n\t\tthis.Reveal.dispatchEvent({ type: 'pdf-ready' });\n\n\t}\n\n\t/**\n\t * Checks if this instance is being used to print a PDF.\n\t */\n\tisPrintingPDF() {\n\n\t\treturn ( /print-pdf/gi ).test( window.location.search );\n\n\t}\n\n}\n","import { isAndroid } from '../utils/device.js'\nimport { matches } from '../utils/util.js'\n\nconst SWIPE_THRESHOLD = 40;\n\n/**\n * Controls all touch interactions and navigations for\n * a presentation.\n */\nexport default class Touch {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\t// Holds information about the currently ongoing touch interaction\n\t\tthis.touchStartX = 0;\n\t\tthis.touchStartY = 0;\n\t\tthis.touchStartCount = 0;\n\t\tthis.touchCaptured = false;\n\n\t\tthis.onPointerDown = this.onPointerDown.bind( this );\n\t\tthis.onPointerMove = this.onPointerMove.bind( this );\n\t\tthis.onPointerUp = this.onPointerUp.bind( this );\n\t\tthis.onTouchStart = this.onTouchStart.bind( this );\n\t\tthis.onTouchMove = this.onTouchMove.bind( this );\n\t\tthis.onTouchEnd = this.onTouchEnd.bind( this );\n\n\t}\n\n\t/**\n\t *\n\t */\n\tbind() {\n\n\t\tlet revealElement = this.Reveal.getRevealElement();\n\n\t\tif( 'onpointerdown' in window ) {\n\t\t\t// Use W3C pointer events\n\t\t\trevealElement.addEventListener( 'pointerdown', this.onPointerDown, false );\n\t\t\trevealElement.addEventListener( 'pointermove', this.onPointerMove, false );\n\t\t\trevealElement.addEventListener( 'pointerup', this.onPointerUp, false );\n\t\t}\n\t\telse if( window.navigator.msPointerEnabled ) {\n\t\t\t// IE 10 uses prefixed version of pointer events\n\t\t\trevealElement.addEventListener( 'MSPointerDown', this.onPointerDown, false );\n\t\t\trevealElement.addEventListener( 'MSPointerMove', this.onPointerMove, false );\n\t\t\trevealElement.addEventListener( 'MSPointerUp', this.onPointerUp, false );\n\t\t}\n\t\telse {\n\t\t\t// Fall back to touch events\n\t\t\trevealElement.addEventListener( 'touchstart', this.onTouchStart, false );\n\t\t\trevealElement.addEventListener( 'touchmove', this.onTouchMove, false );\n\t\t\trevealElement.addEventListener( 'touchend', this.onTouchEnd, false );\n\t\t}\n\n\t}\n\n\t/**\n\t *\n\t */\n\tunbind() {\n\n\t\tlet revealElement = this.Reveal.getRevealElement();\n\n\t\trevealElement.removeEventListener( 'pointerdown', this.onPointerDown, false );\n\t\trevealElement.removeEventListener( 'pointermove', this.onPointerMove, false );\n\t\trevealElement.removeEventListener( 'pointerup', this.onPointerUp, false );\n\n\t\trevealElement.removeEventListener( 'MSPointerDown', this.onPointerDown, false );\n\t\trevealElement.removeEventListener( 'MSPointerMove', this.onPointerMove, false );\n\t\trevealElement.removeEventListener( 'MSPointerUp', this.onPointerUp, false );\n\n\t\trevealElement.removeEventListener( 'touchstart', this.onTouchStart, false );\n\t\trevealElement.removeEventListener( 'touchmove', this.onTouchMove, false );\n\t\trevealElement.removeEventListener( 'touchend', this.onTouchEnd, false );\n\n\t}\n\n\t/**\n\t * Checks if the target element prevents the triggering of\n\t * swipe navigation.\n\t */\n\tisSwipePrevented( target ) {\n\n\t\t// Prevent accidental swipes when scrubbing timelines\n\t\tif( matches( target, 'video, audio' ) ) return true;\n\n\t\twhile( target && typeof target.hasAttribute === 'function' ) {\n\t\t\tif( target.hasAttribute( 'data-prevent-swipe' ) ) return true;\n\t\t\ttarget = target.parentNode;\n\t\t}\n\n\t\treturn false;\n\n\t}\n\n\t/**\n\t * Handler for the 'touchstart' event, enables support for\n\t * swipe and pinch gestures.\n\t *\n\t * @param {object} event\n\t */\n\tonTouchStart( event ) {\n\n\t\tif( this.isSwipePrevented( event.target ) ) return true;\n\n\t\tthis.touchStartX = event.touches[0].clientX;\n\t\tthis.touchStartY = event.touches[0].clientY;\n\t\tthis.touchStartCount = event.touches.length;\n\n\t}\n\n\t/**\n\t * Handler for the 'touchmove' event.\n\t *\n\t * @param {object} event\n\t */\n\tonTouchMove( event ) {\n\n\t\tif( this.isSwipePrevented( event.target ) ) return true;\n\n\t\tlet config = this.Reveal.getConfig();\n\n\t\t// Each touch should only trigger one action\n\t\tif( !this.touchCaptured ) {\n\t\t\tthis.Reveal.onUserInput( event );\n\n\t\t\tlet currentX = event.touches[0].clientX;\n\t\t\tlet currentY = event.touches[0].clientY;\n\n\t\t\t// There was only one touch point, look for a swipe\n\t\t\tif( event.touches.length === 1 && this.touchStartCount !== 2 ) {\n\n\t\t\t\tlet availableRoutes = this.Reveal.availableRoutes({ includeFragments: true });\n\n\t\t\t\tlet deltaX = currentX - this.touchStartX,\n\t\t\t\t\tdeltaY = currentY - this.touchStartY;\n\n\t\t\t\tif( deltaX > SWIPE_THRESHOLD && Math.abs( deltaX ) > Math.abs( deltaY ) ) {\n\t\t\t\t\tthis.touchCaptured = true;\n\t\t\t\t\tif( config.navigationMode === 'linear' ) {\n\t\t\t\t\t\tif( config.rtl ) {\n\t\t\t\t\t\t\tthis.Reveal.next();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.Reveal.prev();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.Reveal.left();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if( deltaX < -SWIPE_THRESHOLD && Math.abs( deltaX ) > Math.abs( deltaY ) ) {\n\t\t\t\t\tthis.touchCaptured = true;\n\t\t\t\t\tif( config.navigationMode === 'linear' ) {\n\t\t\t\t\t\tif( config.rtl ) {\n\t\t\t\t\t\t\tthis.Reveal.prev();\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tthis.Reveal.next();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.Reveal.right();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if( deltaY > SWIPE_THRESHOLD && availableRoutes.up ) {\n\t\t\t\t\tthis.touchCaptured = true;\n\t\t\t\t\tif( config.navigationMode === 'linear' ) {\n\t\t\t\t\t\tthis.Reveal.prev();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.Reveal.up();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if( deltaY < -SWIPE_THRESHOLD && availableRoutes.down ) {\n\t\t\t\t\tthis.touchCaptured = true;\n\t\t\t\t\tif( config.navigationMode === 'linear' ) {\n\t\t\t\t\t\tthis.Reveal.next();\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tthis.Reveal.down();\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// If we're embedded, only block touch events if they have\n\t\t\t\t// triggered an action\n\t\t\t\tif( config.embedded ) {\n\t\t\t\t\tif( this.touchCaptured || this.Reveal.isVerticalSlide() ) {\n\t\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Not embedded? Block them all to avoid needless tossing\n\t\t\t\t// around of the viewport in iOS\n\t\t\t\telse {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t\t// There's a bug with swiping on some Android devices unless\n\t\t// the default action is always prevented\n\t\telse if( isAndroid ) {\n\t\t\tevent.preventDefault();\n\t\t}\n\n\t}\n\n\t/**\n\t * Handler for the 'touchend' event.\n\t *\n\t * @param {object} event\n\t */\n\tonTouchEnd( event ) {\n\n\t\tthis.touchCaptured = false;\n\n\t}\n\n\t/**\n\t * Convert pointer down to touch start.\n\t *\n\t * @param {object} event\n\t */\n\tonPointerDown( event ) {\n\n\t\tif( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === \"touch\" ) {\n\t\t\tevent.touches = [{ clientX: event.clientX, clientY: event.clientY }];\n\t\t\tthis.onTouchStart( event );\n\t\t}\n\n\t}\n\n\t/**\n\t * Convert pointer move to touch move.\n\t *\n\t * @param {object} event\n\t */\n\tonPointerMove( event ) {\n\n\t\tif( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === \"touch\" ) {\n\t\t\tevent.touches = [{ clientX: event.clientX, clientY: event.clientY }];\n\t\t\tthis.onTouchMove( event );\n\t\t}\n\n\t}\n\n\t/**\n\t * Convert pointer up to touch end.\n\t *\n\t * @param {object} event\n\t */\n\tonPointerUp( event ) {\n\n\t\tif( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === \"touch\" ) {\n\t\t\tevent.touches = [{ clientX: event.clientX, clientY: event.clientY }];\n\t\t\tthis.onTouchEnd( event );\n\t\t}\n\n\t}\n\n}","import { closest } from '../utils/util.js'\n\n/**\n * Manages focus when a presentation is embedded. This\n * helps us only capture keyboard from the presentation\n * a user is currently interacting with in a page where\n * multiple presentations are embedded.\n */\n\nconst STATE_FOCUS = 'focus';\nconst STATE_BLUR = 'blur';\n\nexport default class Focus {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t\tthis.onRevealPointerDown = this.onRevealPointerDown.bind( this );\n\t\tthis.onDocumentPointerDown = this.onDocumentPointerDown.bind( this );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tif( config.embedded ) {\n\t\t\tthis.blur();\n\t\t}\n\t\telse {\n\t\t\tthis.focus();\n\t\t\tthis.unbind();\n\t\t}\n\n\t}\n\n\tbind() {\n\n\t\tif( this.Reveal.getConfig().embedded ) {\n\t\t\tthis.Reveal.getRevealElement().addEventListener( 'pointerdown', this.onRevealPointerDown, false );\n\t\t}\n\n\t}\n\n\tunbind() {\n\n\t\tthis.Reveal.getRevealElement().removeEventListener( 'pointerdown', this.onRevealPointerDown, false );\n\t\tdocument.removeEventListener( 'pointerdown', this.onDocumentPointerDown, false );\n\n\t}\n\n\tfocus() {\n\n\t\tif( this.state !== STATE_FOCUS ) {\n\t\t\tthis.Reveal.getRevealElement().classList.add( 'focused' );\n\t\t\tdocument.addEventListener( 'pointerdown', this.onDocumentPointerDown, false );\n\t\t}\n\n\t\tthis.state = STATE_FOCUS;\n\n\t}\n\n\tblur() {\n\n\t\tif( this.state !== STATE_BLUR ) {\n\t\t\tthis.Reveal.getRevealElement().classList.remove( 'focused' );\n\t\t\tdocument.removeEventListener( 'pointerdown', this.onDocumentPointerDown, false );\n\t\t}\n\n\t\tthis.state = STATE_BLUR;\n\n\t}\n\n\tisFocused() {\n\n\t\treturn this.state === STATE_FOCUS;\n\n\t}\n\n\tdestroy() {\n\n\t\tthis.Reveal.getRevealElement().classList.remove( 'focused' );\n\n\t}\n\n\tonRevealPointerDown( event ) {\n\n\t\tthis.focus();\n\n\t}\n\n\tonDocumentPointerDown( event ) {\n\n\t\tlet revealElement = closest( event.target, '.reveal' );\n\t\tif( !revealElement || revealElement !== this.Reveal.getRevealElement() ) {\n\t\t\tthis.blur();\n\t\t}\n\n\t}\n\n}","/**\n * Handles the showing and \n */\nexport default class Notes {\n\n\tconstructor( Reveal ) {\n\n\t\tthis.Reveal = Reveal;\n\n\t}\n\n\trender() {\n\n\t\tthis.element = document.createElement( 'div' );\n\t\tthis.element.className = 'speaker-notes';\n\t\tthis.element.setAttribute( 'data-prevent-swipe', '' );\n\t\tthis.element.setAttribute( 'tabindex', '0' );\n\t\tthis.Reveal.getRevealElement().appendChild( this.element );\n\n\t}\n\n\t/**\n\t * Called when the reveal.js config is updated.\n\t */\n\tconfigure( config, oldConfig ) {\n\n\t\tif( config.showNotes ) {\n\t\t\tthis.element.setAttribute( 'data-layout', typeof config.showNotes === 'string' ? config.showNotes : 'inline' );\n\t\t}\n\n\t}\n\n\t/**\n\t * Pick up notes from the current slide and display them\n\t * to the viewer.\n\t *\n\t * @see {@link config.showNotes}\n\t */\n\tupdate() {\n\n\t\tif( this.Reveal.getConfig().showNotes && this.element && this.Reveal.getCurrentSlide() && !this.Reveal.print.isPrintingPDF() ) {\n\n\t\t\tthis.element.innerHTML = this.getSlideNotes() || 'No notes on this slide.';\n\n\t\t}\n\n\t}\n\n\t/**\n\t * Updates the visibility of the speaker notes sidebar that\n\t * is used to share annotated slides. The notes sidebar is\n\t * only visible if showNotes is true and there are notes on\n\t * one or more slides in the deck.\n\t */\n\tupdateVisibility() {\n\n\t\tif( this.Reveal.getConfig().showNotes && this.hasNotes() && !this.Reveal.print.isPrintingPDF() ) {\n\t\t\tthis.Reveal.getRevealElement().classList.add( 'show-notes' );\n\t\t}\n\t\telse {\n\t\t\tthis.Reveal.getRevealElement().classList.remove( 'show-notes' );\n\t\t}\n\n\t}\n\n\t/**\n\t * Checks if there are speaker notes for ANY slide in the\n\t * presentation.\n\t */\n\thasNotes() {\n\n\t\treturn this.Reveal.getSlidesElement().querySelectorAll( '[data-notes], aside.notes' ).length > 0;\n\n\t}\n\n\t/**\n\t * Checks if this presentation is running inside of the\n\t * speaker notes window.\n\t *\n\t * @return {boolean}\n\t */\n\tisSpeakerNotesWindow() {\n\n\t\treturn !!window.location.search.match( /receiver/gi );\n\n\t}\n\n\t/**\n\t * Retrieves the speaker notes from a slide. Notes can be\n\t * defined in two ways:\n\t * 1. As a data-notes attribute on the slide
\n\t * 2. As an