From 8104a48d565ae05af195432e5fe7543026b720ff Mon Sep 17 00:00:00 2001 From: James Douglass Date: Wed, 18 Nov 2020 10:51:00 -0800 Subject: [PATCH 01/33] Fresh start on the docs directory. RE:#47 --- docs/Makefile | 192 ------------ docs/make.bat | 263 ----------------- docs/source/_static/pygeoprocessing_logo.jpg | Bin 62560 -> 0 bytes docs/source/conf.py | 289 ------------------- docs/source/examples.rst | 106 ------- docs/source/history.rst | 1 - docs/source/index.rst | 51 ---- docs/source/package-testing.rst | 45 --- docs/source/packages/fileio.rst | 9 - docs/source/packages/geoprocessing.rst | 9 - docs/source/packages/routing.rst | 12 - docs/source/sampledata.rst | 8 - docs/source/testing.rst | 10 - src/pygeoprocessing/routing/watershed.pyx | 14 +- 14 files changed, 10 insertions(+), 999 deletions(-) delete mode 100644 docs/Makefile delete mode 100644 docs/make.bat delete mode 100644 docs/source/_static/pygeoprocessing_logo.jpg delete mode 100644 docs/source/conf.py delete mode 100644 docs/source/examples.rst delete mode 100644 docs/source/history.rst delete mode 100644 docs/source/index.rst delete mode 100644 docs/source/package-testing.rst delete mode 100644 docs/source/packages/fileio.rst delete mode 100644 docs/source/packages/geoprocessing.rst delete mode 100644 docs/source/packages/routing.rst delete mode 100644 docs/source/sampledata.rst delete mode 100644 docs/source/testing.rst diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 04dc0fdc..00000000 --- a/docs/Makefile +++ /dev/null @@ -1,192 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pygeoprocessing.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pygeoprocessing.qhc" - -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/pygeoprocessing" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pygeoprocessing" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/make.bat b/docs/make.bat deleted file mode 100644 index 915b779a..00000000 --- a/docs/make.bat +++ /dev/null @@ -1,263 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source -set I18NSPHINXOPTS=%SPHINXOPTS% source -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - echo. coverage to run coverage check of the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -REM Check if sphinx-build is available and fallback to Python version if any -%SPHINXBUILD% 2> nul -if errorlevel 9009 goto sphinx_python -goto sphinx_ok - -:sphinx_python - -set SPHINXBUILD=python -m sphinx.__init__ -%SPHINXBUILD% 2> nul -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -:sphinx_ok - - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pygeoprocessing.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pygeoprocessing.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %~dp0 - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "coverage" ( - %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage - if errorlevel 1 exit /b 1 - echo. - echo.Testing of coverage in the sources finished, look at the ^ -results in %BUILDDIR%/coverage/python.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end diff --git a/docs/source/_static/pygeoprocessing_logo.jpg b/docs/source/_static/pygeoprocessing_logo.jpg deleted file mode 100644 index e382d458beef14825b20abdb8dadc868730ee724..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62560 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 KtCaop<^KTKi!T=d diff --git a/docs/source/conf.py b/docs/source/conf.py deleted file mode 100644 index 978bf8f6..00000000 --- a/docs/source/conf.py +++ /dev/null @@ -1,289 +0,0 @@ -# -*- coding: utf-8 -*- -# -# pygeoprocessing documentation build configuration file, created by -# sphinx-quickstart on Fri Aug 7 10:20:26 2015. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os -import shlex -import pygeoprocessing - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.insert(0, os.path.abspath('../../')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'sphinx.ext.mathjax', - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon' -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'pygeoprocessing' -copyright = u'2015, Natural Capital Project' -author = u'Rich Sharp' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = pygeoprocessing.__version__ -# The full version, including alpha/beta/rc tags. -release = pygeoprocessing.__version__ - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'alabaster' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = os.path.abspath('_static/pygeoprocessing_logo.jpg') - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'pygeoprocessingdoc' - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'pygeoprocessing.tex', u'Pygeoprocessing Documentation', - u'Rich Sharp', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'pygeoprocessing', u'Pygeoprocessing Documentation', - [author], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - (master_doc, 'pygeoprocessing', u'Pygeoprocessing Documentation', - author, 'pygeoprocessing', 'PyGeoprocessing is a Python/Cython based library that provides a set of commonly used raster, vector, and hydrological operations for GIS processing.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/docs/source/examples.rst b/docs/source/examples.rst deleted file mode 100644 index 49dd60de..00000000 --- a/docs/source/examples.rst +++ /dev/null @@ -1,106 +0,0 @@ -======== -Examples -======== - -Vectorize Datasets ------------------- - -The `vectorize_datasets` function is a programmable raster algebra routine. - -Demo:: - - import numpy - - import gdal - from pygeoprocessing import geoprocessing - - - def main(): - """main entry point""" - - dataset_uri_list = [ - r"C:\path\to\landuse_90", - r"C:\path\to\precip", - r"C:\path\to\erodibility", - ] - - pixel_size_out = geoprocessing.get_cell_size_from_uri( - dataset_uri_list[0]) - - dataset_out_uri = r"C:\path\to\masked.tif" - datatype_out = gdal.GDT_Float32 - nodata_out = 99 - bounding_box_mode = 'intersection' - vectorize_op = False - aoi_uri = r"C:\path\to\subwatersheds.shp" - - - def mask_wet_regions(precip): - return precip > 2000 - - wet_regions_uri = r"C:\path\to\wet_regions.tif" - - geoprocessing.vectorize_datasets( - [dataset_uri_list[1]], mask_wet_regions, wet_regions_uri, datatype_out, - nodata_out, pixel_size_out, bounding_box_mode, - vectorize_op=vectorize_op) - - - valid_landcovers = numpy.array([58, 59, 60, 61]) - precip_threshold = 2000 - erodibility_nodata = geoprocessing.get_nodata_from_uri( - dataset_uri_list[2]) - def dataset_pixel_op(landuse, precip, erodibility): - """mask forest pixels that have lots of precip""" - precip_mask = precip > precip_threshold - landuse_mask = numpy.in1d(landuse, valid_landcovers).reshape( - landuse.shape) - erodibility_mask = erodibility != erodibility_nodata - return numpy.where( - precip_mask & landuse_mask & erodibility_mask, - precip * erodibility, - numpy.where(erodibility_mask, erodibility, nodata_out)) - - geoprocessing.vectorize_datasets( - dataset_uri_list, dataset_pixel_op, dataset_out_uri, datatype_out, - nodata_out, pixel_size_out, bounding_box_mode, - vectorize_op=vectorize_op, aoi_uri=aoi_uri) - - - if __name__ == '__main__': - main() - - -Routing -------- - -Examples of functions in the hydrological routing library - -Demo:: - - from pygeoprocessing import routing - - def main(): - """main entry point""" - - #dem_uri = r"C:\path\to\dem" - dem_uri = r"C:\path\to\srtm_1sec_uga.tif" - flow_direction_uri = ( - r"C:\path\to\flow_dir.tif") - flow_accumulation_uri = ( - r"C:\path\to\flow_accumulation.tif") - flow_threshold = 1000 - stream_uri = r"C:\path\to\stream.tif" - distance_uri = r"C:\path\to\distance.tif" - - routing.flow_direction_d_inf(dem_uri, flow_direction_uri) - routing.flow_accumulation( - flow_direction_uri, dem_uri, flow_accumulation_uri) - routing.stream_threshold( - flow_accumulation_uri, flow_threshold, stream_uri) - routing.distance_to_stream( - flow_direction_uri, stream_uri, distance_uri) - - if __name__ == '__main__': - main() - diff --git a/docs/source/history.rst b/docs/source/history.rst deleted file mode 100644 index 5f2e348f..00000000 --- a/docs/source/history.rst +++ /dev/null @@ -1 +0,0 @@ -.. include:: ../../HISTORY.rst diff --git a/docs/source/index.rst b/docs/source/index.rst deleted file mode 100644 index 9f28ccf2..00000000 --- a/docs/source/index.rst +++ /dev/null @@ -1,51 +0,0 @@ - -.. include:: ../../README.rst - -============ -Installation -============ - -.. code-block:: shell - - $ pip install pygeoprocessing - -============= -API Reference -============= - -.. toctree:: - :maxdepth: 1 - - packages/geoprocessing.rst - packages/routing.rst - packages/fileio.rst - -======= -Testing -======= - -.. toctree:: - :maxdepth: 1 - - testing.rst - sampledata.rst - - -======================== -Other Useful Information -======================== - -.. toctree:: - :maxdepth: 1 - - examples - package-testing - history - -================== -Indices and Tables -================== - - * :ref:`genindex` - * :ref:`modindex` - * :ref:`search` diff --git a/docs/source/package-testing.rst b/docs/source/package-testing.rst deleted file mode 100644 index 8020ef89..00000000 --- a/docs/source/package-testing.rst +++ /dev/null @@ -1,45 +0,0 @@ -================================= -Running tests for pygeoprocessing -================================= - -Tests for pygeoprocessing are located in pygeoprocessing/tests -and in tests/. Tests that require binary data will be skipped -unless the appropriate SVN repository can be found. - - -Nosetests -^^^^^^^^^ - -.. code-block:: shell: - - $ python setup.py nosetests - -.. code-block:: shell: - - $ nosetests - -Python -^^^^^^ - -.. code-block:: python: - - >>> import pygeoprocessing - >>> pygeoprocessing.test() - -Tox -^^^ - -.. note:: - Calling ``tox`` will also fetch svn sampledata. - -.. code-block:: shell: - - $ tox - -To get binary test data ------------------------ -.. code-block:: shell - - $ python tests/get_data.py - - diff --git a/docs/source/packages/fileio.rst b/docs/source/packages/fileio.rst deleted file mode 100644 index 3bdd99aa..00000000 --- a/docs/source/packages/fileio.rst +++ /dev/null @@ -1,9 +0,0 @@ -FileIO -====== - -.. automodule:: pygeoprocessing.fileio - :members: - :undoc-members: - :show-inheritance: - -.. End of file diff --git a/docs/source/packages/geoprocessing.rst b/docs/source/packages/geoprocessing.rst deleted file mode 100644 index 1cf7bcca..00000000 --- a/docs/source/packages/geoprocessing.rst +++ /dev/null @@ -1,9 +0,0 @@ -Geoprocessing Module -==================== - -.. automodule:: pygeoprocessing.geoprocessing - :members: - :undoc-members: - :show-inheritance: - -.. End of file diff --git a/docs/source/packages/routing.rst b/docs/source/packages/routing.rst deleted file mode 100644 index 745df4b2..00000000 --- a/docs/source/packages/routing.rst +++ /dev/null @@ -1,12 +0,0 @@ -Routing Package -=============== - -Routing Module --------------- - -.. automodule:: pygeoprocessing.routing.routing - :members: - :undoc-members: - :show-inheritance: - -.. End of file diff --git a/docs/source/sampledata.rst b/docs/source/sampledata.rst deleted file mode 100644 index 3223cfe5..00000000 --- a/docs/source/sampledata.rst +++ /dev/null @@ -1,8 +0,0 @@ -====================== -Sample Geospatial Data -====================== - -.. automodule:: pygeoprocessing.testing.sampledata - :members: SRS_COLOMBIA, SRS_WILLAMETTE, dtype_precision, make_geotransform, visualize - -.. End of file diff --git a/docs/source/testing.rst b/docs/source/testing.rst deleted file mode 100644 index 4ea7529f..00000000 --- a/docs/source/testing.rst +++ /dev/null @@ -1,10 +0,0 @@ -================================ -Testing with Geospatial Datasets -================================ - -.. automodule:: pygeoprocessing.testing - :members: - :undoc-members: - :show-inheritance: - -.. End of file diff --git a/src/pygeoprocessing/routing/watershed.pyx b/src/pygeoprocessing/routing/watershed.pyx index bb13c23d..65c36267 100644 --- a/src/pygeoprocessing/routing/watershed.pyx +++ b/src/pygeoprocessing/routing/watershed.pyx @@ -686,8 +686,16 @@ def delineate_watersheds_d8( cdef int flow_dir_n_cols = flow_dir_info['raster_size'][0] cdef int flow_dir_n_rows = flow_dir_info['raster_size'][1] cdef int ws_id + bbox_minx, bbox_miny, bbox_maxx, bbox_maxy = flow_dir_info['bounding_box'] + LOGGER.debug('Creating flow dir bbox') flow_dir_bbox = shapely.prepared.prep( - shapely.geometry.box(*flow_dir_info['bounding_box'])) + shapely.geometry.Polygon([ + (bbox_minx, bbox_maxy), + (bbox_minx, bbox_miny), + (bbox_maxx, bbox_miny), + (bbox_maxx, bbox_maxy), + (bbox_minx, bbox_maxy)])) + LOGGER.debug('Creating flow dir managed raster') flow_dir_managed_raster = _ManagedRaster(d8_flow_dir_raster_path_band[0], d8_flow_dir_raster_path_band[1], 0) @@ -733,9 +741,6 @@ def delineate_watersheds_d8( cdef int n_cells_visited = 0 LOGGER.info('Delineating watersheds') - flow_dir_bbox = shapely.prepared.prep( - shapely.geometry.box(*flow_dir_info['bounding_box'])) - outflow_layer = outflow_vector.GetLayer() outflow_feature_count = outflow_layer.GetFeatureCount() flow_dir_srs = osr.SpatialReference() @@ -758,6 +763,7 @@ def delineate_watersheds_d8( geom_wkb = geom.ExportToWkb() shapely_geom = shapely.wkb.loads(geom_wkb) + LOGGER.debug('Testing geometry bbox') if not flow_dir_bbox.intersects(shapely.geometry.box(*shapely_geom.bounds)): LOGGER.debug( 'Outflow feature %s does not overlap with the flow ' From b90a7a8638dfef1979b2ec8bc695c613aa814cc5 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Wed, 18 Nov 2020 11:14:59 -0800 Subject: [PATCH 02/33] Adding environment and readthedocs config files. RE:#47 --- .readthedocs.yml | 11 +++++++++++ docs/environment-rtd.yml | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .readthedocs.yml create mode 100644 docs/environment-rtd.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..7372aedc --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,11 @@ +# ReadTheDocs configuration file. +# +# See https://docs.readthedocs.io/en/stable/config-file/v2.html?#conda for +# details. +version: 2 + +sphinx: + configuration: docs/conf.py + +conda: + environment: docs/environment-rtd.yml diff --git a/docs/environment-rtd.yml b/docs/environment-rtd.yml new file mode 100644 index 00000000..b2d046c4 --- /dev/null +++ b/docs/environment-rtd.yml @@ -0,0 +1,17 @@ +# These are the requirements needed to build a conda +# environment on ReadTheDocs. +# +# Note that python requirements are intentionally left vague. +# We don't need the tests to pass, we only need to be able to +# import the packages. +name: env-readthedocs +channels: + - conda-forge +dependencies: + - python + - gdal + - numpy + - rtree + - scipy + - shapely + - cython From f2b6d4e0195b3aa4f2ed9d7348f91e3e829a647f Mon Sep 17 00:00:00 2001 From: James Douglass Date: Wed, 18 Nov 2020 12:04:37 -0800 Subject: [PATCH 03/33] Starting pygeoprocessing RTD build. RE:#47 --- docs/Makefile | 20 ++++++++++++++ docs/environment-rtd.yml | 2 ++ docs/make.bat | 35 ++++++++++++++++++++++++ docs/source/conf.py | 58 ++++++++++++++++++++++++++++++++++++++++ docs/source/index.rst | 46 +++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 docs/Makefile create mode 100644 docs/make.bat create mode 100644 docs/source/conf.py create mode 100644 docs/source/index.rst diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..d0c3cbf1 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = source +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/environment-rtd.yml b/docs/environment-rtd.yml index b2d046c4..c5abe0c6 100644 --- a/docs/environment-rtd.yml +++ b/docs/environment-rtd.yml @@ -15,3 +15,5 @@ dependencies: - scipy - shapely - cython + - setuptools_scm + - sphinx_rtd_theme diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..6247f7e2 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=source +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/source/conf.py b/docs/source/conf.py new file mode 100644 index 00000000..297eed71 --- /dev/null +++ b/docs/source/conf.py @@ -0,0 +1,58 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) +import datetime + +# -- Project information ----------------------------------------------------- + +project = 'PyGeoprocessing' +copyright = f'{datetime.datetime.today().year}, The Natural Capital Project' +author = 'The Natural Capital Project' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', # support google style docstrings +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + + +# -- Extension configuration ------------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 00000000..0e12e0dc --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,46 @@ +PyGeoprocessing +=============== + +Version: |release| + +PyGeoprocessing is a Python/Cython based library that provides a set of +commonly used raster, vector, and hydrological operations for GIS processing. +Similar functionality can be found in ArcGIS/QGIS raster algebra, ArcGIS zonal +statistics, and ArcGIS/GRASS/TauDEM hydrological routing routines. + +PyGeoprocessing is developed at the Natural Capital Project to create a +programmable, open source, and free Python based GIS processing library to +support the InVEST toolset. PyGeoprocessing's design prioritizes computation +and memory efficient runtimes, easy installation and cross compatibility with +other open source and proprietary software licenses, and a simplified set of +orthogonal GIS processing routines that interact with GIS data via filename. +Specifically the functionally provided by PyGeoprocessing includes: + + * a suite of raster manipulation functions (warp, align, raster calculator, + reclassification, distance transform, convolution, and fast iteration) + * a suite of vector based manipulation function (zonal statistics, + rasterization, interpolate points, reprojection, and disjoint polygon + sets) + * a simplified hydrological routing library (D8inf/MFD flow direction, + plateau drainage, weighted and unweighted flow accumulation, and weighted + and unweighted flow distance) + +PyGeoprocessing is developed by the `Natural Capital Project `_. + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. automodule:: pygeoprocessing.geoprocessing + :members: + :undoc-members: From 569f9545f709eb4d2201d2cf19382fb459398fd5 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Wed, 18 Nov 2020 15:03:54 -0800 Subject: [PATCH 04/33] Adding members to autodocumentation. RE:#47 --- docs/source/conf.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 297eed71..068c592a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -31,6 +31,7 @@ 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', # support google style docstrings + 'sphinx.ext.autosummary', ] # Add any paths that contain templates here, relative to this directory. @@ -56,3 +57,10 @@ # -- Extension configuration ------------------------------------------------- + +autosummary_generate = True +autosummary_generate_overwrite = True + +autodoc_default_options = { + 'members': True, +} From a920c4404450bc30261cceec136527b0067cc87d Mon Sep 17 00:00:00 2001 From: James Douglass Date: Wed, 18 Nov 2020 15:04:47 -0800 Subject: [PATCH 05/33] Adding __all__ to pygeoprocessing.multiprocessing. RE:#47 --- docs/source/index.rst | 16 +++++++++++++--- src/pygeoprocessing/multiprocessing/__init__.py | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 0e12e0dc..5e51298c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -28,11 +28,24 @@ Specifically the functionally provided by PyGeoprocessing includes: PyGeoprocessing is developed by the `Natural Capital Project `_. +Contents +======== + .. toctree:: :maxdepth: 2 :caption: Contents: +Modules +======= + +.. autosummary:: + :toctree: modules + :caption: Modules + :recursive: + + pygeoprocessing + Indices and tables ================== @@ -41,6 +54,3 @@ Indices and tables * :ref:`modindex` * :ref:`search` -.. automodule:: pygeoprocessing.geoprocessing - :members: - :undoc-members: diff --git a/src/pygeoprocessing/multiprocessing/__init__.py b/src/pygeoprocessing/multiprocessing/__init__.py index 05fa82fa..3c35f04e 100644 --- a/src/pygeoprocessing/multiprocessing/__init__.py +++ b/src/pygeoprocessing/multiprocessing/__init__.py @@ -1 +1,2 @@ from .raster_calculator import raster_calculator +__all__ = ['raster_calculator'] From 2a25f4595e53b1d4b3d15fbb4a7426b4bd665400 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 16:58:05 -0800 Subject: [PATCH 06/33] Updating the miniconda dependency. RE:#47 --- .github/workflows/pythonpackage.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index 5b38fb89..125cbb13 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -22,6 +22,7 @@ jobs: - name: setup-miniconda uses: goanpeca/setup-miniconda@v1.1.2 + uses: conda-incubator/setup-miniconda@v2 with: activate-environment: pyenv auto-update-conda: false From 6c949262ffacb1dd8872428ff7f0b550ad8c841c Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 17:15:05 -0800 Subject: [PATCH 07/33] Updating docstring for pygeoprocessing.symbolic for RST, Google style correctness. RE:#47 --- docs/source/conf.py | 3 +++ src/pygeoprocessing/symbolic.py | 27 ++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 068c592a..290ee7f7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,6 +58,9 @@ # -- Extension configuration ------------------------------------------------- +nitpicky = True + + autosummary_generate = True autosummary_generate_overwrite = True diff --git a/src/pygeoprocessing/symbolic.py b/src/pygeoprocessing/symbolic.py index 6ee8b59d..122d346f 100644 --- a/src/pygeoprocessing/symbolic.py +++ b/src/pygeoprocessing/symbolic.py @@ -20,15 +20,14 @@ def evaluate_raster_calculator_expression( Evaluate the symbolic arithmetic expression in ``expression`` where the symbols represent equally sized GIS rasters. With the following rules: - * any nodata pixels in a raster will cause the entire pixel stack - to be ``target_nodata``. If ``target_nodata`` is None, this will - be 0. - * any calculations the result in NaN or inf values will be replaced - by the corresponding values in ``default_nan`` and ``default_inf``. - If either of these are not defined an NaN or inf result will cause - a ValueError exception to be raised. - * the following arithmetic operators are available: - +, -, *, /, <, <=, >, >=, !=, &, and |. + * any nodata pixels in a raster will cause the entire pixel stack to be + ``target_nodata``. If ``target_nodata`` is None, this will be 0. + * any calculations the result in NaN or inf values will be replaced by the + corresponding values in ``default_nan`` and ``default_inf``. If either of + these are not defined an NaN or inf result will cause a ValueError + exception to be raised. + * the following arithmetic operators are available: + +, -, *, /, <, <=, >, >=, !=, &, and |. Args: expression (str): a valid arithmetic expression whose variables @@ -36,22 +35,24 @@ def evaluate_raster_calculator_expression( symbol_to_path_band_map (dict): a dict of symbol/(path, band) pairs to indicate which symbol maps to which raster and corresponding band. All symbol names correspond to - symbols in ``expression``. Ex: + symbols in ``expression``. Example:: + expression = '2*x+b' symbol_to_path_band_map = { 'x': (path_to_x_raster, 1), 'b': (path_to_b_raster, 1) } + All rasters represented in this structure must have the same raster size. - target_nodata (numeric): desired nodata value for + target_nodata (float): desired nodata value for ``target_raster_path``. target_raster_path (str): path to the raster that is created by ``expression``. - default_nan (numeric): if a calculation results in an NaN that + default_nan (float): if a calculation results in an NaN that value is replaces with this value. A ValueError exception is raised if this case occurs and ``default_nan`` is None. - default_inf (numeric): if a calculation results in an +/- inf + default_inf (float): if a calculation results in an +/- inf that value is replaced with this value. A ValueError exception is raised if this case occurs and ``default_nan`` is None. From acfa083fa6d6febdc04dbe479af08cd9108acb8a Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 17:18:11 -0800 Subject: [PATCH 08/33] Correcting parameter types in watershed.pyx. RE:#47 --- src/pygeoprocessing/routing/watershed.pyx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/pygeoprocessing/routing/watershed.pyx b/src/pygeoprocessing/routing/watershed.pyx index 65c36267..90d583ae 100644 --- a/src/pygeoprocessing/routing/watershed.pyx +++ b/src/pygeoprocessing/routing/watershed.pyx @@ -406,7 +406,7 @@ cdef cset[CoordinatePair] _c_split_geometry_into_seeds( """Split a geometry into 'seeds' of (x, y) coordinate pairs. Parameters: - source_geom_wkb (string): A string of bytes in WKB representing + source_geom_wkb (str): A string of bytes in WKB representing a geometry. Must be in the same projected coordinate system as the flow direction raster from which ``flow_dir_geotransform`` and ``flow_dir_srs`` are derived. @@ -418,7 +418,7 @@ cdef cset[CoordinatePair] _c_split_geometry_into_seeds( direction raster. flow_dir_n_rows (int): the number of rows in the flow direction raster. - target_raster_path (string): The path to a raster onto which + target_raster_path (str): The path to a raster onto which the geometry might be rasterized. If the geometry is small enough to be completely contained within a single pixel, no raster will be written to this location. @@ -578,7 +578,7 @@ def _split_geometry_into_seeds( that is useful for testing. Parameters: - source_geom_wkb (string): A string of bytes in WKB representing + source_geom_wkb (str): A string of bytes in WKB representing a geometry. Must be in the same projected coordinate system as the flow direction raster from which ``flow_dir_geotransform`` and ``flow_dir_srs`` are derived. @@ -590,7 +590,7 @@ def _split_geometry_into_seeds( direction raster. flow_dir_n_rows (int): the number of rows in the flow direction raster. - target_raster_path (string): The path to a raster onto which + target_raster_path (str): The path to a raster onto which the geometry might be rasterized. If the geometry is small enough to be completely contained within a single pixel, no raster will be written to this location. @@ -632,15 +632,15 @@ def delineate_watersheds_d8( d8_flow_dir_raster_path_band (tuple): A (path, band_id) tuple to a D8 flow direction raster. This raster must be a tiled raster with block sizes being a power of 2. - outflow_vector_path (string): The path to a vector on disk containing + outflow_vector_path (str): The path to a vector on disk containing features with valid geometries from which watersheds will be delineated. Only those parts of the geometry that overlap valid flow direction pixels will be included in the output watersheds vector. - target_watersheds_vector_path (string): The path to a vector on disk + target_watersheds_vector_path (str): The path to a vector on disk where the target watersheds will be stored. Must have the extension ``.gpkg``. - working_dir=None (string or None): The path to a directory on disk + working_dir=None (str or None): The path to a directory on disk within which various intermediate files will be stored. If None, a folder will be created within the system's temp directory. write_diagnostic_vector=False (bool): If ``True``, a set of vectors will @@ -652,7 +652,7 @@ def delineate_watersheds_d8( outflow geometries cover many pixels. remove_temp_files=True (bool): Whether to remove the created temp directory at the end of the watershed delineation run. - target_layer_name='watersheds' (string): The string name to use for + target_layer_name='watersheds' (str): The string name to use for the watersheds layer. This layer name may be named anything except for "polygonized_watersheds". From 549c2d1f1c8e99c805ce0a84a7863a45eebe2fce Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 17:21:56 -0800 Subject: [PATCH 09/33] Correcting a few more parameter types. RE:#47 --- src/pygeoprocessing/routing/routing.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pygeoprocessing/routing/routing.pyx b/src/pygeoprocessing/routing/routing.pyx index 86428786..012fe492 100644 --- a/src/pygeoprocessing/routing/routing.pyx +++ b/src/pygeoprocessing/routing/routing.pyx @@ -586,14 +586,14 @@ def fill_pits( Parameters: dem_raster_path_band (tuple): a path, band number tuple indicating the DEM calculate flow direction. - target_filled_dem_raster_path (string): path the pit filled dem, + target_filled_dem_raster_path (str): path the pit filled dem, that's created by a call to this function. It is functionally a single band copy of ``dem_raster_path_band`` with the pit pixels raised to the pour point. For runtime efficiency, this raster is tiled and its blocksize is set to (1<> (4*dir)), where `dir` is one of the 8 directions indicated above. - working_dir (string): If not None, indicates where temporary files + working_dir (str): If not None, indicates where temporary files should be created during this run. If this directory doesn't exist it is created by this call. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver @@ -2039,7 +2039,7 @@ def flow_accumulation_mfd( for a multiple flow direction raster generated from a call to `flow_dir_mfd`. The format of this raster is described in the docstring of that function. - target_flow_accum_raster_path (string): a path to a raster created by + target_flow_accum_raster_path (str): a path to a raster created by a call to this function that is the same dimensions and projection as `flow_dir_mfd_raster_path_band[0]`. The value in each pixel is 1 plus the proportional contribution of all upstream pixels that @@ -2330,7 +2330,7 @@ def distance_to_channel_d8( that indicates where the channels in the problem space lie. A channel is indicated if the value of the pixel is 1. Other values are ignored. - target_distance_to_channel_raster_path (string): path to a raster + target_distance_to_channel_raster_path (str): path to a raster created by this call that has per-pixel distances from a given pixel to the nearest downhill channel. weight_raster_path_band (tuple): optional path and band number to a @@ -2516,7 +2516,7 @@ def distance_to_channel_mfd( that indicates where the channels in the problem space lie. A channel is indicated if the value of the pixel is 1. Other values are ignored. - target_distance_to_channel_raster_path (string): path to a raster + target_distance_to_channel_raster_path (str): path to a raster created by this call that has per-pixel distances from a given pixel to the nearest downhill channel. weight_raster_path_band (tuple): optional path and band number to a From 02734fda0926623162028334ee15385bcebb32b4 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 17:26:13 -0800 Subject: [PATCH 10/33] Undoing nitpicky build. RE:#47 --- docs/source/conf.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 290ee7f7..0583b239 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -58,7 +58,10 @@ # -- Extension configuration ------------------------------------------------- -nitpicky = True +# Nitpicky=True will make sphinx complain about the types matching python types +# _exactly_. So "string" will be wrong, but "str" right. I don't think we +# need to be so picky. +nitpicky = False autosummary_generate = True From 7dce78f95951fccb3c5f9c0cb3fa666e5e34d71b Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 17:29:43 -0800 Subject: [PATCH 11/33] Correcting RST syntax issue caused by a literal *. RE:#47 --- src/pygeoprocessing/symbolic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pygeoprocessing/symbolic.py b/src/pygeoprocessing/symbolic.py index 122d346f..7966c7e7 100644 --- a/src/pygeoprocessing/symbolic.py +++ b/src/pygeoprocessing/symbolic.py @@ -27,7 +27,8 @@ def evaluate_raster_calculator_expression( these are not defined an NaN or inf result will cause a ValueError exception to be raised. * the following arithmetic operators are available: - +, -, *, /, <, <=, >, >=, !=, &, and |. + ``+``, ``-``, ``*``, ``/``, ``<``, ``<=``, ``>``, ``>=``, ``!=``, ``&``, + and ``|``. Args: expression (str): a valid arithmetic expression whose variables From 0c41d726c09edd3170803c60f0da3bd8347ba0eb Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 19 Nov 2020 17:33:27 -0800 Subject: [PATCH 12/33] Mild RST rejiggering in routing.pyx. RE:#47 --- src/pygeoprocessing/routing/routing.pyx | 40 ++++++++++++++----------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/pygeoprocessing/routing/routing.pyx b/src/pygeoprocessing/routing/routing.pyx index 012fe492..27bf8ae5 100644 --- a/src/pygeoprocessing/routing/routing.pyx +++ b/src/pygeoprocessing/routing/routing.pyx @@ -11,10 +11,11 @@ does not support int64 rasters so no precision loss is possible with a float64. D8 float direction conventions follow TauDEM where each flow direction -is encoded as: - 321 - 4x0 - 567 +is encoded as:: + + 3 2 1 + 4 x 0 + 5 6 7 """ import logging import os @@ -1324,10 +1325,12 @@ def flow_accumulation_d8( flow_dir_raster_path_band (tuple): a path, band number tuple for a flow accumulation raster whose pixels indicate the flow out of a pixel in one of 8 directions in the following - configuration: - 321 - 4x0 - 567 + configuration:: + + 3 2 1 + 4 x 0 + 5 6 7 + target_flow_accum_raster_path (str): path to flow accumulation raster created by this call. After this call, the value of each pixel will be 1 plus the number of upstream pixels @@ -2321,10 +2324,12 @@ def distance_to_channel_d8( flow_dir_d8_raster_path_band (tuple): a path/band index tuple indicating the raster that defines the D8 flow direction raster for this call. The pixel values are integers that - correspond to outflow in the following configuration: - 321 - 4x0 - 567 + correspond to outflow in the following configuration:: + + 3 2 1 + 4 x 0 + 5 6 7 + channel_raster_path_band (tuple): a path/band tuple of the same dimensions and projection as `flow_dir_d8_raster_path_band[0]` that indicates where the channels in the problem space lie. A @@ -2509,10 +2514,9 @@ def distance_to_channel_mfd( flow_dir_mfd_raster_path_band (tuple): a path/band index tuple indicating the raster that defines the mfd flow accumulation raster for this call. This raster should be generated by a call - to `pygeoprocessing.routing.flow_dir_mfd. - + to ``pygeoprocessing.routing.flow_dir_mfd``. channel_raster_path_band (tuple): a path/band tuple of the same - dimensions and projection as `flow_dir_mfd_raster_path_band[0]` + dimensions and projection as ``flow_dir_mfd_raster_path_band[0]`` that indicates where the channels in the problem space lie. A channel is indicated if the value of the pixel is 1. Other values are ignored. @@ -2521,13 +2525,13 @@ def distance_to_channel_mfd( pixel to the nearest downhill channel. weight_raster_path_band (tuple): optional path and band number to a raster that will be used as the per-pixel flow distance - weight. If `None`, 1 is the default distance between neighboring + weight. If ``None``, 1 is the default distance between neighboring pixels. This raster must be the same dimensions as - `flow_dir_mfd_raster_path_band`. + ``flow_dir_mfd_raster_path_band``. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver name string as the first element and a GDAL creation options tuple/list as the second. Defaults to a GTiff driver tuple - defined at geoprocessing.DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS. + defined at ``geoprocessing.DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS``. Returns: None. From 96797162483afada4b5c0d44b232922ebd9173a0 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 11:26:16 -0800 Subject: [PATCH 13/33] Adding the auto-generated modules listing to the API docs TOCtree. RE:#47 --- docs/source/index.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 5e51298c..8313143b 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -32,8 +32,11 @@ Contents ======== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Contents: + :glob: + + modules/* Modules From 3372e83c709d2c0af56e145df2a5ce92609b6015 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 11:54:07 -0800 Subject: [PATCH 14/33] Further tweaks to RST in geoprocessing_core. RE:#47 --- src/pygeoprocessing/geoprocessing.py | 53 +++++++++++++++++----------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/src/pygeoprocessing/geoprocessing.py b/src/pygeoprocessing/geoprocessing.py index 7016a4c9..e97abf15 100644 --- a/src/pygeoprocessing/geoprocessing.py +++ b/src/pygeoprocessing/geoprocessing.py @@ -144,8 +144,10 @@ def raster_calculator( name string as the first element and a GDAL creation options tuple/list as the second. Defaults to geoprocessing.DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS. + Returns: None + Raises: ValueError: invalid input provided """ @@ -577,20 +579,24 @@ def align_and_resize_raster_stack( dictionary of options to use an existing vector's geometry to mask out pixels in the target raster that do not overlap the vector's geometry. Keys to this dictionary are: - 'mask_vector_path': (str) path to the mask vector file. This - vector will be automatically projected to the target - projection if its base coordinate system does not match - the target. - 'mask_layer_name': (str) the layer name to use for masking, - if this key is not in the dictionary the default is to use - the layer at index 0. - 'mask_vector_where_filter': (str) an SQL WHERE string that can - be used to filter the geometry in the mask. Ex: - 'id > 10' would use all features whose field value of - 'id' is > 10. + + * ``'mask_vector_path'`` (str): path to the mask vector file. + This vector will be automatically projected to the target + projection if its base coordinate system does not match the + target. + * ``'mask_layer_name'`` (str): the layer name to use for masking. + If this key is not in the dictionary the default is to use + the layer at index 0. + * ``'mask_vector_where_filter'`` (str): an SQL WHERE string. + This will be used to filter the geometry in the mask. Ex: ``'id + > 10'`` would use all features whose field value of 'id' is > + 10. + gdal_warp_options (sequence): if present, the contents of this list are passed to the ``warpOptions`` parameter of ``gdal.Warp``. See - the GDAL Warp documentation for valid options. + the `GDAL Warp documentation + `_ + for valid options. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver name string as the first element and a GDAL creation options tuple/list as the second. Defaults to a GTiff driver tuple @@ -604,20 +610,26 @@ def align_and_resize_raster_stack( None Raises: - ValueError if any combination of the raw bounding boxes, raster + ValueError + If any combination of the raw bounding boxes, raster bounding boxes, vector bounding boxes, and/or vector_mask bounding box does not overlap to produce a valid target. - ValueError if any of the input or target lists are of different + ValueError + If any of the input or target lists are of different lengths. - ValueError if there are duplicate paths on the target list which would + ValueError + If there are duplicate paths on the target list which would risk corrupted output. - ValueError if some combination of base, target, and embedded source + ValueError + If some combination of base, target, and embedded source reference systems results in an ambiguous target coordinate system. - ValueError if ``vector_mask_options`` is not None but the + ValueError + If ``vector_mask_options`` is not None but the ``mask_vector_path`` is undefined or doesn't point to a valid file. - ValueError if ``pixel_size`` is not a 2 element sequence of numbers. + ValueError + If ``pixel_size`` is not a 2 element sequence of numbers. """ # make sure that the input lists are of the same length @@ -2444,6 +2456,7 @@ def convolve_2d( name string as the first element and a GDAL creation options tuple/list as the second. Defaults to a GTiff driver tuple defined at geoprocessing.DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS. + Returns: None @@ -2460,8 +2473,8 @@ def convolve_2d( if ignore_nodata_and_edges and not mask_nodata: raise ValueError( - f'ignore_nodata_and_edges is True while mask_nodata is False -- ' - f'this would yield a nonsensical result.') + 'ignore_nodata_and_edges is True while mask_nodata is False -- ' + 'this would yield a nonsensical result.') bad_raster_path_list = [] for raster_id, raster_path_band in [ From 9285ae841ae9e77b5ad7950785fa946fb981b3ce Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 15:09:02 -0800 Subject: [PATCH 15/33] Updating docstrings throughout geoprocessing.py for RST consistency. RE:#47 --- src/pygeoprocessing/geoprocessing.py | 288 +++++++++++++++------------ 1 file changed, 160 insertions(+), 128 deletions(-) diff --git a/src/pygeoprocessing/geoprocessing.py b/src/pygeoprocessing/geoprocessing.py index e97abf15..d94a6a70 100644 --- a/src/pygeoprocessing/geoprocessing.py +++ b/src/pygeoprocessing/geoprocessing.py @@ -95,18 +95,21 @@ def raster_calculator( be spatially aligned and have the same cell sizes. Args: - base_raster_path_band_const_list (sequence): a sequence containing - either (str, int) tuples, ``numpy.ndarray``s of up to two - dimensions, or an (object, 'raw') tuple. A ``(str, int)`` - tuple refers to a raster path band index pair to use as an input. - The ``numpy.ndarray``s must be broadcastable to each other AND the - size of the raster inputs. Values passed by ``(object, 'raw')`` - tuples pass ``object`` directly into the ``local_op``. All rasters - must have the same raster size. If only arrays are input, numpy - arrays must be broadcastable to each other and the final raster - size will be the final broadcast array shape. A value error is - raised if only "raw" inputs are passed. - local_op (function) a function that must take in as many parameters as + base_raster_path_band_const_list (sequence): a sequence containing: + + * ``(str, int)`` tuples, referring to a raster path/band index pair + to use as an input. + * ``numpy.ndarray`` s of up to two dimensions. These inputs must + all be broadcastable to each other AND the size of the raster + inputs. + * ``(object, 'raw')`` tuples, where ``object`` will be passed + directly into the ``local_op``. + + All rasters must have the same raster size. If only arrays are + input, numpy arrays must be broadcastable to each other and the + final raster size will be the final broadcast array shape. A value + error is raised if only "raw" inputs are passed. + local_op (function): a function that must take in as many parameters as there are elements in ``base_raster_path_band_const_list``. The parameters in ``local_op`` will map 1-to-1 in order with the values in ``base_raster_path_band_const_list``. ``raster_calculator`` will @@ -581,16 +584,16 @@ def align_and_resize_raster_stack( vector's geometry. Keys to this dictionary are: * ``'mask_vector_path'`` (str): path to the mask vector file. - This vector will be automatically projected to the target - projection if its base coordinate system does not match the - target. + This vector will be automatically projected to the target + projection if its base coordinate system does not match the + target. * ``'mask_layer_name'`` (str): the layer name to use for masking. - If this key is not in the dictionary the default is to use - the layer at index 0. + If this key is not in the dictionary the default is to use + the layer at index 0. * ``'mask_vector_where_filter'`` (str): an SQL WHERE string. - This will be used to filter the geometry in the mask. Ex: ``'id - > 10'`` would use all features whose field value of 'id' is > - 10. + This will be used to filter the geometry in the mask. Ex: ``'id + > 10'`` would use all features whose field value of 'id' is > + 10. gdal_warp_options (sequence): if present, the contents of this list are passed to the ``warpOptions`` parameter of ``gdal.Warp``. See @@ -947,7 +950,10 @@ def create_raster_from_vector_extents( box; if not coordinates will be rounded up to contain the original extent. target_pixel_size (list/tuple): the x/y pixel size as a sequence - ex: [30.0, -30.0] + Example:: + + [30.0, -30.0] + target_pixel_type (int): gdal GDT pixel type of target raster target_nodata (numeric): target nodata value. Can be None if no nodata value is needed. @@ -1115,11 +1121,13 @@ def zonal_statistics( in two passes, where first polygons aggregate over pixels in the raster whose centers intersect with the polygon. In the second pass, any polygons that are not aggregated use their bounding box to intersect with the - raster for overlap statistics. Note there may be some degenerate - cases where the bounding box vs. actual geometry intersection would be - incorrect, but these are so unlikely as to be manually constructed. If - you encounter one of these please email the description and dataset - to richsharp@stanford.edu. + raster for overlap statistics. + + Note: + There may be some degenerate cases where the bounding box vs. actual + geometry intersection would be incorrect, but these are so unlikely as + to be manually constructed. If you encounter one of these please email + the description and dataset to richsharp@stanford.edu. Args: base_raster_path_band (tuple): a str/int tuple indicating the path to @@ -1148,12 +1156,21 @@ def zonal_statistics( Returns: nested dictionary indexed by aggregating feature id, and then by one - of 'min' 'max' 'sum' 'count' and 'nodata_count'. Example: - {0: {'min': 0, 'max': 1, 'sum': 1.7, count': 3, 'nodata_count': 1}} + of 'min' 'max' 'sum' 'count' and 'nodata_count'. Example:: + + {0: {'min': 0, + 'max': 1, + 'sum': 1.7, + 'count': 3, + 'nodata_count': 1 + } + } Raises: - ValueError if ``base_raster_path_band`` is incorrectly formatted. - RuntimeError(s) if the aggregate vector or layer cannot open. + ValueError + if ``base_raster_path_band`` is incorrectly formatted. + RuntimeError + if the aggregate vector or layer cannot open. """ if not _is_raster_path_band_formatted(base_raster_path_band): @@ -1480,14 +1497,16 @@ def get_vector_info(vector_path, layer_id=0): opened as a gdal.OF_VECTOR. Returns: - raster_properties (dictionary): a dictionary with the following - properties stored under relevant keys. + raster_properties (dictionary): + a dictionary with the following key-value pairs: - 'projection_wkt' (string): projection of the vector in Well Known - Text. - 'bounding_box' (sequence): sequence of floats representing the - bounding box in projected coordinates in the order - [minx, miny, maxx, maxy]. + * ``'projection_wkt'`` (string): projection of the vector in Well + Known Text. + * ``'bounding_box'`` (sequence): sequence of floats representing + the bounding box in projected coordinates in the order + [minx, miny, maxx, maxy]. + * ``'file_list'`` (sequence): sequence of string paths to the files + that make up this vector. """ vector = gdal.OpenEx(vector_path, gdal.OF_VECTOR) @@ -1519,34 +1538,35 @@ def get_raster_info(raster_path): raster_path (String): a path to a GDAL raster. Raises: - ValueError if ``raster_path`` is not a file or cannot be opened as a - gdal.OF_RASTER. + ValueError + if ``raster_path`` is not a file or cannot be opened as a + ``gdal.OF_RASTER``. Returns: - raster_properties (dictionary): a dictionary with the properties - stored under relevant keys. - - 'pixel_size' (tuple): (pixel x-size, pixel y-size) from - geotransform. - 'raster_size' (tuple): number of raster pixels in (x, y) - direction. - 'nodata' (sequence): a sequence of the nodata values in the bands - of the raster in the same order as increasing band index. - 'n_bands' (int): number of bands in the raster. - 'geotransform' (tuple): a 6-tuple representing the geotransform of - (x orign, x-increase, xy-increase, - y origin, yx-increase, y-increase). - 'datatype' (int): An instance of an enumerated gdal.GDT_* int - that represents the datatype of the raster. - 'projection_wkt' (string): projection of the raster in Well Known - Text. - 'bounding_box' (sequence): sequence of floats representing the - bounding box in projected coordinates in the order - [minx, miny, maxx, maxy] - 'block_size' (tuple): underlying x/y raster block size for - efficient reading. - 'numpy_type' (numpy type): this is the equivalent numpy datatype - for the raster bands including signed bytes. + raster_properties (dictionary): + a dictionary with the properties stored under relevant keys. + + * ``'pixel_size'`` (tuple): (pixel x-size, pixel y-size) + from geotransform. + * ``'raster_size'`` (tuple): number of raster pixels in (x, y) + direction. + * ``'nodata'`` (sequence): a sequence of the nodata values in the bands + of the raster in the same order as increasing band index. + * ``'n_bands'`` (int): number of bands in the raster. + * ``'geotransform'`` (tuple): a 6-tuple representing the geotransform + of (x orign, x-increase, xy-increase, y origin, yx-increase, + y-increase). + * ``'datatype'`` (int): An instance of an enumerated gdal.GDT_* int + that represents the datatype of the raster. + * ``'projection_wkt'`` (string): projection of the raster in Well Known + Text. + * ``'bounding_box'`` (sequence): sequence of floats representing the + bounding box in projected coordinates in the order + [minx, miny, maxx, maxy] + * ``'block_size'`` (tuple): underlying x/y raster block size for + efficient reading. + * ``'numpy_type'`` (numpy type): this is the equivalent numpy datatype + for the raster bands including signed bytes. """ raster = gdal.OpenEx(raster_path, gdal.OF_RASTER) @@ -1773,9 +1793,10 @@ def reclassify_raster( None Raises: - ReclassificationMissingValuesError if ``values_required`` is ``True`` - and a pixel value from ``base_raster_path_band`` is not a key in - ``value_map``. + ReclassificationMissingValuesError + if ``values_required`` is ``True`` + and a pixel value from ``base_raster_path_band`` is not a key in + ``value_map``. """ if len(value_map) == 0: @@ -1831,8 +1852,7 @@ def warp_raster( target_raster_path (string): the location of the resized and resampled raster. resample_method (string): the resampling technique, one of - "near|bilinear|cubic|cubicspline|lanczos|average|mode|max" - "min|med|q1|q3" + ``near|bilinear|cubic|cubicspline|lanczos|average|mode|max|min|med|q1|q3`` target_bb (sequence): if None, target bounding box is the same as the source bounding box. Otherwise it's a sequence of float describing target bounding box in target coordinate system as @@ -1847,17 +1867,19 @@ def warp_raster( dictionary of options to use an existing vector's geometry to mask out pixels in the target raster that do not overlap the vector's geometry. Keys to this dictionary are: - 'mask_vector_path': (str) path to the mask vector file. This - vector will be automatically projected to the target - projection if its base coordinate system does not match - the target. - 'mask_layer_id': (int/str) the layer index or name to use for - masking, if this key is not in the dictionary the default - is to use the layer at index 0. - 'mask_vector_where_filter': (str) an SQL WHERE string that can - be used to filter the geometry in the mask. Ex: - 'id > 10' would use all features whose field value of - 'id' is > 10. + + * ``'mask_vector_path'``: (str) path to the mask vector file. This + vector will be automatically projected to the target + projection if its base coordinate system does not match + the target. + * ``'mask_layer_id'``: (int/str) the layer index or name to use for + masking, if this key is not in the dictionary the default + is to use the layer at index 0. + * ``'mask_vector_where_filter'``: (str) an SQL WHERE string that can + be used to filter the geometry in the mask. Ex: + 'id > 10' would use all features whose field value of + 'id' is > 10. + gdal_warp_options (sequence): if present, the contents of this list are passed to the ``warpOptions`` parameter of ``gdal.Warp``. See the GDAL Warp documentation for valid options. @@ -1877,8 +1899,10 @@ def warp_raster( None Raises: - ValueError if ``pixel_size`` is not a 2 element sequence of numbers. - ValueError if ``vector_mask_options`` is not None but the + ValueError + if ``pixel_size`` is not a 2 element sequence of numbers. + ValueError + if ``vector_mask_options`` is not None but the ``mask_vector_path`` is undefined or doesn't point to a valid file. @@ -2038,33 +2062,38 @@ def rasterize( option_list (list/tuple): optional a sequence of burn options, if None then a valid value for ``burn_values`` must exist. Otherwise, each element is a string of the form: - "ATTRIBUTE=?": Identifies an attribute field on the features - to be used for a burn in value. The value will be burned - into all output bands. If specified, ``burn_values`` - will not be used and can be None. - "CHUNKYSIZE=?": The height in lines of the chunk to operate - on. The larger the chunk size the less times we need to - make a pass through all the shapes. If it is not set or - set to zero the default chunk size will be used. Default - size will be estimated based on the GDAL cache buffer size - using formula: cache_size_bytes/scanline_size_bytes, so - the chunk will not exceed the cache. - "ALL_TOUCHED=TRUE/FALSE": May be set to TRUE to set all pixels - touched by the line or polygons, not just those whose - center is within the polygon or that are selected by - Brezenhams line algorithm. Defaults to FALSE. - "BURN_VALUE_FROM": May be set to "Z" to use the Z values of - the geometries. The value from burn_values or the - attribute field value is added to this before burning. In - default case dfBurnValue is burned as it is (richpsharp: - note, I'm not sure what this means, but copied from formal - docs). This is implemented properly only for points and - lines for now. Polygons will be burned using the Z value - from the first point. - "MERGE_ALG=REPLACE/ADD": REPLACE results in overwriting of - value, while ADD adds the new value to the existing - raster, suitable for heatmaps for instance. - Example: ["ATTRIBUTE=npv", "ALL_TOUCHED=TRUE"] + + * ``"ATTRIBUTE=?"``: Identifies an attribute field on the features + to be used for a burn in value. The value will be burned into all + output bands. If specified, ``burn_values`` will not be used and + can be None. + * ``"CHUNKYSIZE=?"``: The height in lines of the chunk to operate + on. The larger the chunk size the less times we need to make a + pass through all the shapes. If it is not set or set to zero the + default chunk size will be used. Default size will be estimated + based on the GDAL cache buffer size using formula: + ``cache_size_bytes/scanline_size_bytes``, so the chunk will not + exceed the cache. + * ``"ALL_TOUCHED=TRUE/FALSE"``: May be set to ``TRUE`` to set all + pixels touched by the line or polygons, not just those whose + center is within the polygon or that are selected by Brezenhams + line algorithm. Defaults to ``FALSE``. + * ``"BURN_VALUE_FROM"``: May be set to "Z" to use the Z values of + the geometries. The value from burn_values or the + attribute field value is added to this before burning. In + default case dfBurnValue is burned as it is (richpsharp: + note, I'm not sure what this means, but copied from formal + docs). This is implemented properly only for points and + lines for now. Polygons will be burned using the Z value + from the first point. + * ``"MERGE_ALG=REPLACE/ADD"``: REPLACE results in overwriting of + value, while ADD adds the new value to the existing + raster, suitable for heatmaps for instance. + + Example:: + + ["ATTRIBUTE=npv", "ALL_TOUCHED=TRUE"] + layer_id (str/int): name or index of the layer to rasterize. Defaults to 0. where_clause (str): If not None, is an SQL query-like string to filter @@ -2274,14 +2303,14 @@ def distance_transform_edt( target_distance_raster_path (string): path to the target raster that is the exact euclidean distance transform from any pixel in the base raster that is not nodata and not 0. The units are in - (pixel distance * ``sampling_distance``). + ``(pixel distance * sampling_distance)``. sampling_distance (tuple/list): an optional parameter used to scale the pixel distances when calculating the distance transform. Defaults to (1.0, 1.0). First element indicates the distance traveled in the x direction when changing a column index, and the second element in y when changing a row index. Both values must be > 0. - working_dir (string): If not None, indicates where temporary files + working_dir (string): If not None, indicates where temporary files should be created during this run. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver name string as the first element and a GDAL creation options @@ -2458,11 +2487,12 @@ def convolve_2d( defined at geoprocessing.DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS. Returns: - None + ``None`` Raises: - ValueError if ``ignore_nodata_and_edges`` is ``True`` and - ``mask_nodata`` is ``False`` + ValueError + if ``ignore_nodata_and_edges`` is ``True`` and ``mask_nodata`` + is ``False`` """ if target_datatype is not gdal.GDT_Float64 and target_nodata is None: @@ -2733,15 +2763,15 @@ def iterblocks( dict of block data and a 2-dimensional numpy array are yielded. The dict of block data has these attributes: - data['xoff'] - The X offset of the upper-left-hand corner of the - block. - data['yoff'] - The Y offset of the upper-left-hand corner of the - block. - data['win_xsize'] - The width of the block. - data['win_ysize'] - The height of the block. + * ``data['xoff']`` - The X offset of the upper-left-hand corner of the + block. + * ``data['yoff']`` - The Y offset of the upper-left-hand corner of the + block. + * ``data['win_xsize']`` - The width of the block. + * ``data['win_ysize']`` - The height of the block. If ``offset_only`` is True, the function returns only the block offset - data and does not attempt to read binary data from the raster. + data and does not attempt to read binary data from the raster. """ if not _is_raster_path_band_formatted(raster_path_band): @@ -3254,16 +3284,17 @@ def merge_bounding_box_list(bounding_box_list, bounding_box_mode): Args: bounding_box_list (sequence): a sequence of bounding box coordinates - in the order [minx,miny,maxx,maxy]. - mode (string): either 'union' or 'intersection' for the corresponding - reduction mode. + in the order [minx, miny, maxx, maxy]. + mode (string): either ``'union'`` or ``'intersection'`` for the + corresponding reduction mode. Returns: A four tuple bounding box that is the union or intersection of the - input bounding boxes. + input bounding boxes. Raises: - ValueError if the bounding boxes in ``bounding_box_list`` do not + ValueError + if the bounding boxes in ``bounding_box_list`` do not intersect if the ``bounding_box_mode`` is 'intersection'. """ @@ -3308,15 +3339,16 @@ def _greater_than(x_val, y_val): def get_gis_type(path): - """Calculate the GIS type of the file located at `path`. + """Calculate the GIS type of the file located at ``path``. Args: path (str): path to a file on disk. Returns: - a bitwise OR of all GIS types that PyGeoprocessing models, currently - this is UNKNOWN_TYPE, RASTER_TYPE, or VECTOR_TYPE. + A bitwise OR of all GIS types that PyGeoprocessing models, currently + this is ``pygeoprocessing.UNKNOWN_TYPE``, + ``pygeoprocessing.RASTER_TYPE``, or ``pygeoprocessing.VECTOR_TYPE``. """ if not os.path.exists(path): From 69fd8ff9fafe98c36940136fe40a715d0954bb8f Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 15:13:18 -0800 Subject: [PATCH 16/33] Cleaning up RST in geoprocessing_core.pyx. RE:#47 --- src/pygeoprocessing/geoprocessing_core.pyx | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pygeoprocessing/geoprocessing_core.pyx b/src/pygeoprocessing/geoprocessing_core.pyx index d2dc1549..cf15108d 100644 --- a/src/pygeoprocessing/geoprocessing_core.pyx +++ b/src/pygeoprocessing/geoprocessing_core.pyx @@ -85,8 +85,8 @@ def _distance_transform_edt( sample_d_x (float): sample_d_y (float): These parameters scale the pixel distances when calculating the - distance transform. `d_x` is the x direction when changing a - column index, and `d_y` when changing a row index. Both values + distance transform. ``d_x`` is the x direction when changing a + column index, and ``d_y`` when changing a row index. Both values must be > 0. target_distance_raster_path (string): path to the target raster created by this call that is the exact euclidean distance @@ -283,23 +283,23 @@ def calculate_slope( diagonal pixels by classic finite difference analysis. For the following notation, we define each pixel's DEM value by a letter - with this spatial scheme: + with this spatial scheme:: - abc - def - ghi + a b c + d e f + g h i - Then the slope at e is defined at ([dz/dx]^2 + [dz/dy]^2)^0.5 + Then the slope at ``e`` is defined at ``([dz/dx]^2 + [dz/dy]^2)^0.5`` - Where + Where:: - [dz/dx] = ((c+2f+i)-(a+2d+g)/(8*x_cell_size) - [dz/dy] = ((g+2h+i)-(a+2b+c))/(8*y_cell_size) + [dz/dx] = ((c+2f+i)-(a+2d+g)/(8*x_cell_size) + [dz/dy] = ((g+2h+i)-(a+2b+c))/(8*y_cell_size) In cases where a cell is nodata, we attempt to use the middle cell inline with the direction of differentiation (either in x or y direction). If - no inline pixel is defined, we use `e` and multiply the difference by - 2^0.5 to account for the diagonal projection. + no inline pixel is defined, we use ``e`` and multiply the difference by + ``2^0.5`` to account for the diagonal projection. Parameters: base_elevation_raster_path_band (string): a path/band tuple to a @@ -313,7 +313,7 @@ def calculate_slope( geoprocessing.DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS. Returns: - None + ``None`` """ cdef numpy.npy_float64 a, b, c, d, e, f, g, h, i, dem_nodata cdef numpy.npy_float64 x_cell_size, y_cell_size, From ca3442824d7171b2822586d855ddb8a4bdd43b69 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 15:14:56 -0800 Subject: [PATCH 17/33] Correcting RST syntax in multiprocessing's raster_calculator. RE:#47 --- src/pygeoprocessing/multiprocessing/raster_calculator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pygeoprocessing/multiprocessing/raster_calculator.py b/src/pygeoprocessing/multiprocessing/raster_calculator.py index c23182f7..ae132a34 100644 --- a/src/pygeoprocessing/multiprocessing/raster_calculator.py +++ b/src/pygeoprocessing/multiprocessing/raster_calculator.py @@ -383,10 +383,10 @@ def raster_calculator( Args: base_raster_path_band_const_list (sequence): a sequence containing - either (str, int) tuples, ``numpy.ndarray``s of up to two + either (str, int) tuples, ``numpy.ndarray`` s of up to two dimensions, or an (object, 'raw') tuple. A ``(str, int)`` tuple refers to a raster path band index pair to use as an input. - The ``numpy.ndarray``s must be broadcastable to each other AND the + The ``numpy.ndarray`` s must be broadcastable to each other AND the size of the raster inputs. Values passed by ``(object, 'raw')`` tuples pass ``object`` directly into the ``local_op``. All rasters must have the same raster size. If only arrays are input, numpy From 3f488b58dabfd3a1fefde02f6a2d0c8c06b301fb Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 15:23:08 -0800 Subject: [PATCH 18/33] Tweaking RST in routing.pyx. RE:#47 --- src/pygeoprocessing/routing/routing.pyx | 72 ++++++++++++------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/pygeoprocessing/routing/routing.pyx b/src/pygeoprocessing/routing/routing.pyx index 27bf8ae5..73574aec 100644 --- a/src/pygeoprocessing/routing/routing.pyx +++ b/src/pygeoprocessing/routing/routing.pyx @@ -580,9 +580,9 @@ def fill_pits( raster_driver_creation_tuple=DEFAULT_GTIFF_CREATION_TUPLE_OPTIONS): """Fill the pits in a DEM. - This function defines pits as hydrologically connected regions that do - not drain to the edge of the raster or a nodata pixel. After the call - pits are filled to the height of the lowest pour point. + This function defines pits as hydrologically connected regions that do + not drain to the edge of the raster or a nodata pixel. After the call + pits are filled to the height of the lowest pour point. Parameters: dem_raster_path_band (tuple): a path, band number tuple indicating the @@ -591,8 +591,9 @@ def fill_pits( that's created by a call to this function. It is functionally a single band copy of ``dem_raster_path_band`` with the pit pixels raised to the pour point. For runtime efficiency, this raster is - tiled and its blocksize is set to (1<> (4*dir)), where `dir` is - one of the 8 directions indicated above. - + shift and mask as follows ``0xF & (x >> (4*dir))``, where ``dir`` + is one of the 8 directions indicated above. working_dir (str): If not None, indicates where temporary files should be created during this run. If this directory doesn't exist it is created by this call. @@ -2040,11 +2040,11 @@ def flow_accumulation_mfd( Parameters: flow_dir_mfd_raster_path_band (tuple): a path, band number tuple for a multiple flow direction raster generated from a call to - `flow_dir_mfd`. The format of this raster is described in the + ``flow_dir_mfd``. The format of this raster is described in the docstring of that function. target_flow_accum_raster_path (str): a path to a raster created by a call to this function that is the same dimensions and projection - as `flow_dir_mfd_raster_path_band[0]`. The value in each pixel is + as ``flow_dir_mfd_raster_path_band[0]``. The value in each pixel is 1 plus the proportional contribution of all upstream pixels that flow into it. The proportion is determined as the value of the upstream flow dir pixel in the downslope direction pointing to @@ -2052,12 +2052,12 @@ def flow_accumulation_mfd( exiting that pixel. Note the target type of this raster is a 64 bit float so there is minimal risk of overflow and the possibility of handling a float dtype in - `weight_raster_path_band`. + ``weight_raster_path_band``. weight_raster_path_band (tuple): optional path and band number to a raster that will be used as the per-pixel flow accumulation - weight. If `None`, 1 is the default flow accumulation weight. + weight. If ``None``, 1 is the default flow accumulation weight. This raster must be the same dimensions as - `flow_dir_mfd_raster_path_band`. If a weight nodata pixel is + ``flow_dir_mfd_raster_path_band``. If a weight nodata pixel is encountered it will be treated as a weight value of 0. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver name string as the first element and a GDAL creation options @@ -2331,7 +2331,7 @@ def distance_to_channel_d8( 5 6 7 channel_raster_path_band (tuple): a path/band tuple of the same - dimensions and projection as `flow_dir_d8_raster_path_band[0]` + dimensions and projection as ``flow_dir_d8_raster_path_band[0]`` that indicates where the channels in the problem space lie. A channel is indicated if the value of the pixel is 1. Other values are ignored. @@ -2340,9 +2340,9 @@ def distance_to_channel_d8( pixel to the nearest downhill channel. weight_raster_path_band (tuple): optional path and band number to a raster that will be used as the per-pixel flow distance - weight. If `None`, 1 is the default distance between neighboring + weight. If ``None``, 1 is the default distance between neighboring pixels. This raster must be the same dimensions as - `flow_dir_mfd_raster_path_band`. + ``flow_dir_mfd_raster_path_band``. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver name string as the first element and a GDAL creation options tuple/list as the second. Defaults to a GTiff driver tuple @@ -2756,29 +2756,29 @@ def extract_streams_mfd( """Classify a stream raster from MFD flow accumulation. This function classifies pixels as streams that have a flow accumulation - value >= `flow_threshold` and can trace further upstream with a fuzzy - propotion if `trace_threshold_proportion` is set < 1.0 + value >= ``flow_threshold`` and can trace further upstream with a fuzzy + propotion if ``trace_threshold_proportion`` is set < 1.0 Parameters: flow_accum_raster_path_band (tuple): a string/integer tuple indicating the flow accumulation raster to use as a basis for thresholding a stream. Values in this raster that are >= flow_threshold will be classified as streams. This raster should be derived from - `dem_raster_path_band` using - `pygeoprocessing.routing.flow_accumulation_mfd`. + ``dem_raster_path_band`` using + ``pygeoprocessing.routing.flow_accumulation_mfd``. flow_dir_mfd_path_band (str): path to multiple flow direction raster, required to join divergent streams. - flow_threshold (float): the value in `flow_accum_raster_path_band` to + flow_threshold (float): the value in ``flow_accum_raster_path_band`` to indicate where a stream exists. target_stream_raster_path (str): path to the target stream raster. This raster will be the same dimensions and projection as - `dem_raster_path_band` and will contain 1s where a stream is + ``dem_raster_path_band`` and will contain 1s where a stream is defined, 0 where the flow accumulation layer is defined but no stream exists, and nodata otherwise. trace_threshold_proportion (float): this value indicates what proportion of the flow_threshold is enough to classify a pixel as a stream after the stream has been traced from a - `flow_threshold` drain. Setting this value < 1.0 is useful for + ``flow_threshold`` drain. Setting this value < 1.0 is useful for classifying streams in regions that have highly divergent flow directions. raster_driver_creation_tuple (tuple): a tuple containing a GDAL driver From 6dcb86f62f231fe63b55a218678d3ed2a6d396c8 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 15:24:58 -0800 Subject: [PATCH 19/33] Correcting RST for watershed.pyx. RE:#47 --- src/pygeoprocessing/routing/watershed.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pygeoprocessing/routing/watershed.pyx b/src/pygeoprocessing/routing/watershed.pyx index 90d583ae..18f34c70 100644 --- a/src/pygeoprocessing/routing/watershed.pyx +++ b/src/pygeoprocessing/routing/watershed.pyx @@ -628,7 +628,7 @@ def delineate_watersheds_d8( target_layer_name='watersheds'): """Delineate watersheds for a vector of geometries using D8 flow dir. - Parameters: + Args: d8_flow_dir_raster_path_band (tuple): A (path, band_id) tuple to a D8 flow direction raster. This raster must be a tiled raster with block sizes being a power of 2. @@ -656,7 +656,7 @@ def delineate_watersheds_d8( the watersheds layer. This layer name may be named anything except for "polygonized_watersheds". - Returns + Returns: ``None`` """ From aeab1227b82a68d470d65e6b0f99dfeef476953b Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 16:36:52 -0800 Subject: [PATCH 20/33] Adding __all__ to routing subpackage. RE:#47 --- src/pygeoprocessing/routing/__init__.py | 27 +++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/pygeoprocessing/routing/__init__.py b/src/pygeoprocessing/routing/__init__.py index d4b82c3d..d41a7e6e 100644 --- a/src/pygeoprocessing/routing/__init__.py +++ b/src/pygeoprocessing/routing/__init__.py @@ -1,2 +1,25 @@ -from pygeoprocessing.routing.routing import * -from pygeoprocessing.routing.watershed import * +from pygeoprocessing.routing.routing import ( + fill_pits, + fill_pits, + flow_dir_d8, + flow_accumulation_d8, + flow_dir_mfd, + flow_accumulation_mfd, + distance_to_channel_d8, + distance_to_channel_mfd, + extract_streams_mfd) +from pygeoprocessing.routing.watershed import ( + delineate_watersheds_d8) + +__all__ = ( + 'fill_pits', + 'fill_pits', + 'flow_dir_d8', + 'flow_accumulation_d8', + 'flow_dir_mfd', + 'flow_accumulation_mfd', + 'distance_to_channel_d8', + 'distance_to_channel_mfd', + 'extract_streams_mfd', + 'delineate_watersheds_d8' +) From af1545af2c2fa9dc881222f25965e0d0b468d65a Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 17:01:50 -0800 Subject: [PATCH 21/33] Using sphinx.apidoc to generate the docs. RE:#47 --- docs/source/conf.py | 18 ++++++++++-------- docs/source/index.rst | 19 ++++--------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 0583b239..66ca0802 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,10 +10,11 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os # import sys # sys.path.insert(0, os.path.abspath('.')) import datetime +import os +import sphinx.ext.apidoc # -- Project information ----------------------------------------------------- @@ -63,10 +64,11 @@ # need to be so picky. nitpicky = False - -autosummary_generate = True -autosummary_generate_overwrite = True - -autodoc_default_options = { - 'members': True, -} +DOCS_SOURCE_DIR = os.path.dirname(__file__) +sphinx.ext.apidoc.main([ + '--force', + '-d', '1', # max depth for TOC + '--separate', # Put docs for each module on their own pages + '-o', os.path.join(DOCS_SOURCE_DIR, 'api'), + os.path.join(DOCS_SOURCE_DIR, '..', '..', 'src', 'pygeoprocessing'), +]) diff --git a/docs/source/index.rst b/docs/source/index.rst index 8313143b..2000f1b0 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -28,26 +28,15 @@ Specifically the functionally provided by PyGeoprocessing includes: PyGeoprocessing is developed by the `Natural Capital Project `_. -Contents -======== + +Full API Reference +================== .. toctree:: :maxdepth: 1 - :caption: Contents: :glob: - modules/* - - -Modules -======= - -.. autosummary:: - :toctree: modules - :caption: Modules - :recursive: - - pygeoprocessing + api/* Indices and tables From 31fc5af82c1b58c9d9a68ad56376be4de5576813 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 17:05:43 -0800 Subject: [PATCH 22/33] Adding package version. RE:#47 --- docs/source/conf.py | 4 ++++ docs/source/index.rst | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 66ca0802..678cc303 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,6 +15,7 @@ import datetime import os import sphinx.ext.apidoc +from pkg_resources import get_distribution # -- Project information ----------------------------------------------------- @@ -72,3 +73,6 @@ '-o', os.path.join(DOCS_SOURCE_DIR, 'api'), os.path.join(DOCS_SOURCE_DIR, '..', '..', 'src', 'pygeoprocessing'), ]) + +release = get_distribution('pygeoprocessing').version +version = '.'.join(release.split('.')[:2]) diff --git a/docs/source/index.rst b/docs/source/index.rst index 2000f1b0..587a58ac 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -1,7 +1,7 @@ PyGeoprocessing =============== -Version: |release| +Release: |release| PyGeoprocessing is a Python/Cython based library that provides a set of commonly used raster, vector, and hydrological operations for GIS processing. From 8a088eaab2730876701324010d8263817dd7fdd6 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 17:27:19 -0800 Subject: [PATCH 23/33] Correcting path to conf.py. RE:#47: --- .readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 7372aedc..7f63005d 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,7 +5,7 @@ version: 2 sphinx: - configuration: docs/conf.py + configuration: docs/source/conf.py conda: environment: docs/environment-rtd.yml From 1f62a47e3a2419293e4c8b4c63d58afadac05e68 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 19:40:27 -0800 Subject: [PATCH 24/33] Adding a yml section to install pygeoprocessing into the conda environment. RE:#47 --- .readthedocs.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index 7f63005d..eb50dd8f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -9,3 +9,9 @@ sphinx: conda: environment: docs/environment-rtd.yml + +python: + version: 3.8 + install: + - method: setuptools + path: . From 0c0a9b5cead6e4aa69da462cdc6e3d5a27716b2a Mon Sep 17 00:00:00 2001 From: James Douglass Date: Fri, 20 Nov 2020 20:18:08 -0800 Subject: [PATCH 25/33] Adding some tighter restrictions on packages installed to conda environment. RE:#47 --- docs/environment-rtd.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/environment-rtd.yml b/docs/environment-rtd.yml index c5abe0c6..4e720e29 100644 --- a/docs/environment-rtd.yml +++ b/docs/environment-rtd.yml @@ -8,8 +8,8 @@ name: env-readthedocs channels: - conda-forge dependencies: - - python - - gdal + - python=3.8 + - gdal>=3 - numpy - rtree - scipy From e99c881608351c67a3402f6f7c4996bd69c4d371 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Mon, 23 Nov 2020 12:40:12 -0800 Subject: [PATCH 26/33] Adding a getting started section. RE:#47 --- docs/source/index.rst | 12 ++++- docs/source/installing.rst | 92 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 2 deletions(-) create mode 100644 docs/source/installing.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 587a58ac..98ff1af4 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -28,9 +28,17 @@ Specifically the functionally provided by PyGeoprocessing includes: PyGeoprocessing is developed by the `Natural Capital Project `_. +Getting Started +--------------- + +.. toctree:: + :maxdepth: 1 + + installing + Full API Reference -================== +------------------ .. toctree:: :maxdepth: 1 @@ -40,7 +48,7 @@ Full API Reference Indices and tables -================== +------------------ * :ref:`genindex` * :ref:`modindex` diff --git a/docs/source/installing.rst b/docs/source/installing.rst new file mode 100644 index 00000000..fdc40a78 --- /dev/null +++ b/docs/source/installing.rst @@ -0,0 +1,92 @@ +Installing PyGeoprocessing +========================== + +Basic Installation +****************** + +.. _InstallWithPip: + +Installing via pip +------------------ + +PyGeoprocessing can be installed via pip:: + + pip install pygeoprocessing + +Note that `GDAL `_ is a required dependency of +``pyeoprocessing`` and prebuilt binaries are not available on PyPI. +See `Dependencies`_ section for alternatives. + +.. _InstallWithConda: + +Installing via conda +-------------------- + +PyGeoprocessing can also be installed from ``conda-forge``:: + + conda install -c conda-forge pygeoprocessing + +Unlike the ``pip`` approach, this will install the complete dependency tree. + + +Installing from source +---------------------- + +If you have a ``git`` installation and the `appropriate compiler for your system +`_ +available, PyGeoprocessing can also be installed from its source tree. This is +particularly useful for installing the very latest development build of +PyGeoprocessing:: + + pip install git+https://github.com/natcap/pygeoprocessing.git@main + + +Numpy dtype error ++++++++++++++++++ + +If you ``import pygeoprocessing`` and see a ``ValueError: numpy.dtype has the +wrong size, try recompiling``, do this:: + + pip uninstall -y pygeoprocessing + pip install pygeoprocessing --no-deps --no-binary :all: + +This error is the result of a version compatibility issue with the numpy API in +the precompiled pygeoprocessing wheel. The solution is to recompile +pygeoprocessing on your computer using the above steps. + + +.. _Dependencies: + +Dependencies +************ + +PyGeoprocessing's dependencies are listed in ``requirements.txt``, reproduced here: + +.. include:: ../../requirements.txt + :literal: + +All of these dependencies will be installed automatically to your conda +environment if you're using conda (see `Installing via conda`_). Below are a few +alternate ways to install the required packages. + +Ubuntu & Debian: apt +-------------------- +:: + sudo apt install python3-dev python3-gdal cython3 python3-shapely python3-rtree + +Fedora: yum +----------- +:: + sudo yum install python3-devel python3-gdal python3-rtree python3-shapely + +Mac: brew +--------- +:: + brew install gdal spatialindex + +Windows: pip +------------ + +For installing on Windows, take a look at Christoph Gohlke's page of unofficial builds: +http://www.lfd.uci.edu/~gohlke/pythonlibs/ + From 6487276a56aa2687c073e1df3c593d7be73d30ca Mon Sep 17 00:00:00 2001 From: James Douglass Date: Mon, 23 Nov 2020 12:56:27 -0800 Subject: [PATCH 27/33] Adding intersphinx to try it out. RE:#47 --- docs/source/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 678cc303..1de10d13 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,6 +34,7 @@ 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', # support google style docstrings 'sphinx.ext.autosummary', + 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. @@ -76,3 +77,8 @@ release = get_distribution('pygeoprocessing').version version = '.'.join(release.split('.')[:2]) + +intersphinx_mapping = { + 'python': ('https://docs.python.org/3', (None, 'python-inv.txt')), + 'gdal': ('https://gdal.org/', (None,)), +} From 02cf9e6ea3909589fa3a62f0d39f760e672c14e9 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Mon, 23 Nov 2020 13:31:29 -0800 Subject: [PATCH 28/33] Removing intersphinx, adding assumptions page. RE:#47 --- docs/source/conf.py | 6 ------ docs/source/index.rst | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 1de10d13..678cc303 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -34,7 +34,6 @@ 'sphinx.ext.viewcode', 'sphinx.ext.napoleon', # support google style docstrings 'sphinx.ext.autosummary', - 'sphinx.ext.intersphinx', ] # Add any paths that contain templates here, relative to this directory. @@ -77,8 +76,3 @@ release = get_distribution('pygeoprocessing').version version = '.'.join(release.split('.')[:2]) - -intersphinx_mapping = { - 'python': ('https://docs.python.org/3', (None, 'python-inv.txt')), - 'gdal': ('https://gdal.org/', (None,)), -} diff --git a/docs/source/index.rst b/docs/source/index.rst index 98ff1af4..2d702c26 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -35,6 +35,7 @@ Getting Started :maxdepth: 1 installing + assumptions Full API Reference From f44472769e11bed5ff5d603e34ab5160f7630eb3 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Mon, 23 Nov 2020 14:56:05 -0800 Subject: [PATCH 29/33] Adding a logo. RE:#47 --- docs/source/_static/pygeoprocessing_logo.jpg | Bin 0 -> 24154 bytes docs/source/conf.py | 4 +++- docs/source/index.rst | 4 +--- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 docs/source/_static/pygeoprocessing_logo.jpg diff --git a/docs/source/_static/pygeoprocessing_logo.jpg b/docs/source/_static/pygeoprocessing_logo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6e429f3af4ac864b309dc49c7f511d385a4fef0c GIT binary patch literal 24154 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%0ATO{$W~Z@2 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 literal 0 HcmV?d00001 diff --git a/docs/source/conf.py b/docs/source/conf.py index 678cc303..515740d8 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -37,7 +37,7 @@ ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +# templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -56,6 +56,8 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +html_logo = os.path.join(os.path.dirname(__file__), '_static', + 'pygeoprocessing_logo.jpg') # -- Extension configuration ------------------------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 2d702c26..e97570e3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -38,11 +38,9 @@ Getting Started assumptions -Full API Reference ------------------- - .. toctree:: :maxdepth: 1 + :caption: API Reference :glob: api/* From c27cecf9aa7f55e85e4874434c0d02ee52adc5da Mon Sep 17 00:00:00 2001 From: Doug Date: Thu, 3 Dec 2020 11:24:40 -0500 Subject: [PATCH 30/33] I. 133 Pass metadata to mask_raster From warp_raster call, make sure the metadata is updated and passed properly to mask_raster. --- HISTORY.rst | 3 +++ src/pygeoprocessing/geoprocessing.py | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index b4828dd5..c0ab2308 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,6 +5,9 @@ Unreleased Changes ------------------ * ``pygeoprocessing.warp_raster`` now raises a ``ValueError`` when an invalid resampling method is provided. +* Fixed bug in ``pygeoprocessing.warp_raster`` that would not properly handle + GDAL Byte type signing when masking warped raster with a vector. + resampling method is provided. * Fixed issue in ``convolve_2d`` that would cause excessive memory use leading to out of memory errors. * Fixed issue in ``convolve_2d`` that could lead to a file removal race diff --git a/src/pygeoprocessing/geoprocessing.py b/src/pygeoprocessing/geoprocessing.py index d94a6a70..ea9d809b 100644 --- a/src/pygeoprocessing/geoprocessing.py +++ b/src/pygeoprocessing/geoprocessing.py @@ -2030,6 +2030,10 @@ def warp_raster( callback_data=[target_raster_path]) if vector_mask_options: + # Make sure the raster creation options passed to ``mask_raster`` + # reflect any metadata updates + updated_raster_driver_creation_tuple = ( + raster_driver_creation_tuple[0], tuple(raster_creation_options)) # there was a cutline vector, so mask it out now, otherwise target # is already the result. mask_raster( @@ -2039,7 +2043,7 @@ def warp_raster( where_clause=mask_vector_where_filter, target_mask_value=None, working_dir=temp_working_dir, all_touched=False, - raster_driver_creation_tuple=raster_driver_creation_tuple) + raster_driver_creation_tuple=updated_raster_driver_creation_tuple) shutil.rmtree(temp_working_dir) From 375f529a6d4dc07153ec52de4fc8ab1e12b4fd0e Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 3 Dec 2020 11:40:13 -0800 Subject: [PATCH 31/33] Adding a note about the API docs to README. RE:#47 --- README.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rst b/README.rst index bb7199b7..755c7cd6 100644 --- a/README.rst +++ b/README.rst @@ -49,3 +49,8 @@ Note the pip-installable requirements in `requirements.txt` are for best results, but older package versions may also work. If necessary, PyGeoprocessing can be installed without dependencies with `pip install --no-deps`. + +API Documentation +================= + +API documentation is available at https://pygeoprocessing.readthedocs.io/en/latest/ From 9ba3010053c824e7188438495c538c6ceb8cbe76 Mon Sep 17 00:00:00 2001 From: James Douglass Date: Thu, 3 Dec 2020 14:55:45 -0800 Subject: [PATCH 32/33] Removing the assumptions index page; not a part of this generation of API docs. RE:#47 --- docs/source/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index e97570e3..143f9bab 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -35,7 +35,6 @@ Getting Started :maxdepth: 1 installing - assumptions .. toctree:: From 405f513f4b5a819adccaa18950dd0abb737e3ba0 Mon Sep 17 00:00:00 2001 From: Richard Sharp Date: Thu, 3 Dec 2020 16:35:48 -0800 Subject: [PATCH 33/33] Update HISTORY.rst deleted duplicated line --- HISTORY.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index c0ab2308..006d2515 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,7 +7,6 @@ Unreleased Changes resampling method is provided. * Fixed bug in ``pygeoprocessing.warp_raster`` that would not properly handle GDAL Byte type signing when masking warped raster with a vector. - resampling method is provided. * Fixed issue in ``convolve_2d`` that would cause excessive memory use leading to out of memory errors. * Fixed issue in ``convolve_2d`` that could lead to a file removal race